Skip to content

Authenticator (TOTP)

/api/devices

The Authenticator (TOTP) API lets you generate one-time passwords and manage virtual 2FA devices for testing authentication systems.

You can either save an authenticator device and request codes by device ID, or generate a current OTP directly from a Base32 shared secret without saving anything.

INFO

The Node and Cypress SDKs do not expose Authenticator (TOTP) helpers yet. Use the REST API or generated OpenAPI reference for now.

WARNING

Shared secrets are accepted as input only. Mailisk never returns a saved shared secret in API responses.

Endpoints

MethodEndpointDescription
GET/api/devicesList saved authenticator devices
POST/api/devicesCreate a saved device from sharedSecret
POST/api/devices/customCreate a saved device with custom TOTP settings
POST/api/devices/base32-secret-keyCreate a saved device from base32SecretKey
POST/api/devices/otpauth-urlCreate a saved device from an otpauth:// URL
POST/api/devices/otpGenerate an OTP from a shared secret
GET/api/devices/{deviceId}/otpGenerate an OTP for a saved authenticator device
DELETE/api/devices/{deviceId}Delete a saved authenticator device

Generate OTP Without Saving

POST /api/devices/otp

Use this endpoint when a test already has a Base32 shared secret and only needs the current OTP.

This endpoint uses the default TOTP settings: 6 digits, a 30 second period, and SHA1. It does not accept custom digits, period, or algorithm values.

sh
curl --request POST \
     --url https://api.mailisk.com/api/devices/otp \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'X-Api-Key: {Api Key}' \
     --data '{"sharedSecret": "JBSWY3DPEHPK3PXP"}'

Body

NameRequiredTypeDescription
sharedSecretRequiredStringBase32 shared secret.

Response

json
{
  "code": "123456",
  "expires": "2026-05-18T12:00:30.000Z"
}

Create Saved Device

POST /api/devices

Use this endpoint for the simplest saved-device flow. It stores a Base32 shared secret using the default TOTP settings: 6 digits, a 30 second period, and SHA1.

sh
curl --request POST \
     --url https://api.mailisk.com/api/devices \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'X-Api-Key: {Api Key}' \
     --data '{
       "name": "GitHub staging",
       "sharedSecret": "JBSWY3DPEHPK3PXP"
     }'

Body

NameRequiredTypeDescription
sharedSecretRequiredStringBase32 shared secret.
nameOptionalStringHuman-readable device name. If omitted, Mailisk derives one.
expiresAtOptionalStringFuture ISO timestamp after which the saved device expires.

Response

json
{
  "id": "9b1f6ec0-b90d-4bd8-8dd0-f6b2d5138273",
  "organisation_id": "7f0a9c32-66b2-4e25-a4cf-1f77db8f7f3b",
  "name": "GitHub staging",
  "username": null,
  "issuer": null,
  "digits": 6,
  "period": 30,
  "algorithm": "SHA1",
  "source": "shared_secret",
  "expiresAt": null,
  "created_at": "2026-05-18T12:00:00.000Z",
  "updated_at": "2026-05-18T12:00:00.000Z"
}

Create Custom Saved Device

POST /api/devices/custom

Use this endpoint when you want to save an authenticator device and reuse it across test steps.

sh
curl --request POST \
     --url https://api.mailisk.com/api/devices/custom \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'X-Api-Key: {Api Key}' \
     --data '{
       "name": "GitHub staging",
       "secret": "JBSWY3DPEHPK3PXP",
       "issuer": "GitHub",
       "username": "qa@example.com",
       "digits": 6,
       "period": 30,
       "algorithm": "SHA1"
     }'

Saved-device creation can return 403 when TOTP devices are not available for the organisation or when the active saved-device limit has been reached. Expired devices do not count against that limit.

The organisation is inferred from the organisation API key.

Body

NameRequiredTypeDescription
nameOptionalStringHuman-readable device name. If omitted, Mailisk derives one.
secretRequiredStringBase32 shared secret.
usernameOptionalStringAccount label associated with the device.
issuerOptionalStringIssuer or application label associated with the device.
digitsOptionalNumberOTP length. Supported values are 6 and 8. Defaults to 6.
periodOptionalNumberTOTP period in seconds. Must be between 10 and 300. Defaults to 30.
algorithmOptionalStringOne of SHA1, SHA256, or SHA512. Defaults to SHA1.
expiresAtOptionalStringFuture ISO timestamp after which the saved device expires.

Response

json
{
  "id": "9b1f6ec0-b90d-4bd8-8dd0-f6b2d5138273",
  "organisation_id": "7f0a9c32-66b2-4e25-a4cf-1f77db8f7f3b",
  "name": "GitHub staging",
  "username": "qa@example.com",
  "issuer": "GitHub",
  "digits": 6,
  "period": 30,
  "algorithm": "SHA1",
  "source": "custom",
  "expiresAt": null,
  "created_at": "2026-05-18T12:00:00.000Z",
  "updated_at": "2026-05-18T12:00:00.000Z"
}

Create From Base32 Secret Key

POST /api/devices/base32-secret-key

This endpoint is equivalent to the custom create endpoint, but uses the base32SecretKey field name for compatibility with clients that expose a Base32 secret-key flow.

sh
curl --request POST \
     --url https://api.mailisk.com/api/devices/base32-secret-key \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'X-Api-Key: {Api Key}' \
     --data '{
       "name": "GitHub staging",
       "base32SecretKey": "JBSWY3DPEHPK3PXP",
       "issuer": "GitHub",
       "username": "qa@example.com",
       "digits": 6,
       "period": 30,
       "algorithm": "SHA1"
     }'

Body

NameRequiredTypeDescription
base32SecretKeyRequiredStringBase32 shared secret key.
nameOptionalStringHuman-readable device name. If omitted, Mailisk derives one.
usernameOptionalStringAccount label associated with the device.
issuerOptionalStringIssuer or application label associated with the device.
digitsOptionalNumberOTP length. Supported values are 6 and 8. Defaults to 6.
periodOptionalNumberTOTP period in seconds. Must be between 10 and 300. Defaults to 30.
algorithmOptionalStringOne of SHA1, SHA256, or SHA512. Defaults to SHA1.
expiresAtOptionalStringFuture ISO timestamp after which the saved device expires.

Response

json
{
  "id": "9b1f6ec0-b90d-4bd8-8dd0-f6b2d5138273",
  "organisation_id": "7f0a9c32-66b2-4e25-a4cf-1f77db8f7f3b",
  "name": "GitHub staging",
  "username": "qa@example.com",
  "issuer": "GitHub",
  "digits": 6,
  "period": 30,
  "algorithm": "SHA1",
  "source": "base32_secret_key",
  "expiresAt": null,
  "created_at": "2026-05-18T12:00:00.000Z",
  "updated_at": "2026-05-18T12:00:00.000Z"
}

Create From otpauth URL

POST /api/devices/otpauth-url

Use this endpoint when your application or identity provider exposes an otpauth://totp/... setup URL.

sh
curl --request POST \
     --url https://api.mailisk.com/api/devices/otpauth-url \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'X-Api-Key: {Api Key}' \
     --data '{
       "otpAuthUrl": "otpauth://totp/GitHub:qa@example.com?secret=JBSWY3DPEHPK3PXP&issuer=GitHub"
     }'

Values inside the otpauth:// URL are treated as the source of truth for username, issuer, digits, period, and algorithm. The optional body name can override the derived display name.

Body

NameRequiredTypeDescription
otpAuthUrlRequiredStringotpauth://totp URL with a Base32 secret query parameter.
nameOptionalStringHuman-readable device name. If omitted, Mailisk derives one.
usernameOptionalStringAccount label used only when missing from the otpauth:// URL.
issuerOptionalStringIssuer used only when missing from the otpauth:// URL.
digitsOptionalNumberOTP length used only when missing from the URL. Supported values are 6 and 8.
periodOptionalNumberTOTP period used only when missing from the URL. Must be between 10 and 300.
algorithmOptionalStringAlgorithm used only when missing from the URL. One of SHA1, SHA256, or SHA512.
expiresAtOptionalStringFuture ISO timestamp after which the saved device expires.

Response

json
{
  "id": "9b1f6ec0-b90d-4bd8-8dd0-f6b2d5138273",
  "organisation_id": "7f0a9c32-66b2-4e25-a4cf-1f77db8f7f3b",
  "name": "GitHub:qa@example.com",
  "username": "qa@example.com",
  "issuer": "GitHub",
  "digits": 6,
  "period": 30,
  "algorithm": "SHA1",
  "source": "otpauth_url",
  "expiresAt": null,
  "created_at": "2026-05-18T12:00:00.000Z",
  "updated_at": "2026-05-18T12:00:00.000Z"
}

List Saved Devices

GET /api/devices

This endpoint returns active saved authenticator devices for the organisation API key. Expired devices are not returned.

sh
curl --request GET \
     --url 'https://api.mailisk.com/api/devices?limit=5&offset=0&issuer=GitHub' \
     --header 'Accept: application/json' \
     --header 'X-Api-Key: {Api Key}'

Query

NameRequiredTypeDescription
limitOptionalNumberMaximum number of devices to return. Must be between 1 and 100.
offsetOptionalNumberNumber of devices to skip for pagination.
usernameOptionalStringCase-insensitive partial username filter.
issuerOptionalStringCase-insensitive partial issuer filter.

Response

json
{
  "total_count": 1,
  "options": {
    "limit": 5,
    "offset": 0,
    "issuer": "GitHub"
  },
  "items": [
    {
      "id": "9b1f6ec0-b90d-4bd8-8dd0-f6b2d5138273",
      "organisation_id": "7f0a9c32-66b2-4e25-a4cf-1f77db8f7f3b",
      "name": "GitHub staging",
      "username": "qa@example.com",
      "issuer": "GitHub",
      "digits": 6,
      "period": 30,
      "algorithm": "SHA1",
      "source": "custom",
      "expiresAt": null,
      "created_at": "2026-05-18T12:00:00.000Z",
      "updated_at": "2026-05-18T12:00:00.000Z"
    }
  ]
}

Get OTP For Saved Device

GET /api/devices/{deviceId}/otp

Expired devices cannot generate OTPs and return an error.

sh
curl --request GET \
     --url https://api.mailisk.com/api/devices/9b1f6ec0-b90d-4bd8-8dd0-f6b2d5138273/otp \
     --header 'Accept: application/json' \
     --header 'X-Api-Key: {Api Key}'

Path Params

NameDescription
deviceIdSaved authenticator device UUID.

Response

json
{
  "code": "123456",
  "expires": "2026-05-18T12:00:30.000Z"
}

Delete Saved Device

DELETE /api/devices/{deviceId}

sh
curl --request DELETE \
     --url https://api.mailisk.com/api/devices/9b1f6ec0-b90d-4bd8-8dd0-f6b2d5138273 \
     --header 'X-Api-Key: {Api Key}'

Path Params

NameDescription
deviceIdSaved authenticator device UUID.

Successful deletion returns 204 No Content.

Typescript types

typescript
interface TotpDeviceListResponse {
  total_count: number;
  options: {
    limit: number;
    offset: number;
    username?: string;
    issuer?: string;
  };
  items: TotpDevice[];
}

interface TotpDevice {
  id: string;
  organisation_id: string;
  name: string;
  username?: string | null;
  issuer?: string | null;
  digits: 6 | 8;
  period: number;
  algorithm: 'SHA1' | 'SHA256' | 'SHA512';
  source: 'shared_secret' | 'custom' | 'base32_secret_key' | 'otpauth_url' | string;
  expiresAt?: string | null;
  created_at: string;
  updated_at: string;
}

interface TotpOtpResponse {
  code: string;
  expires: string;
}