Appearance
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
| Method | Endpoint | Description |
|---|---|---|
GET | /api/devices | List saved authenticator devices |
POST | /api/devices | Create a saved device from sharedSecret |
POST | /api/devices/custom | Create a saved device with custom TOTP settings |
POST | /api/devices/base32-secret-key | Create a saved device from base32SecretKey |
POST | /api/devices/otpauth-url | Create a saved device from an otpauth:// URL |
POST | /api/devices/otp | Generate an OTP from a shared secret |
GET | /api/devices/{deviceId}/otp | Generate 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
| Name | Required | Type | Description |
|---|---|---|---|
sharedSecret | Required | String | Base32 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
| Name | Required | Type | Description |
|---|---|---|---|
sharedSecret | Required | String | Base32 shared secret. |
name | Optional | String | Human-readable device name. If omitted, Mailisk derives one. |
expiresAt | Optional | String | Future 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
| Name | Required | Type | Description |
|---|---|---|---|
name | Optional | String | Human-readable device name. If omitted, Mailisk derives one. |
secret | Required | String | Base32 shared secret. |
username | Optional | String | Account label associated with the device. |
issuer | Optional | String | Issuer or application label associated with the device. |
digits | Optional | Number | OTP length. Supported values are 6 and 8. Defaults to 6. |
period | Optional | Number | TOTP period in seconds. Must be between 10 and 300. Defaults to 30. |
algorithm | Optional | String | One of SHA1, SHA256, or SHA512. Defaults to SHA1. |
expiresAt | Optional | String | Future 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
| Name | Required | Type | Description |
|---|---|---|---|
base32SecretKey | Required | String | Base32 shared secret key. |
name | Optional | String | Human-readable device name. If omitted, Mailisk derives one. |
username | Optional | String | Account label associated with the device. |
issuer | Optional | String | Issuer or application label associated with the device. |
digits | Optional | Number | OTP length. Supported values are 6 and 8. Defaults to 6. |
period | Optional | Number | TOTP period in seconds. Must be between 10 and 300. Defaults to 30. |
algorithm | Optional | String | One of SHA1, SHA256, or SHA512. Defaults to SHA1. |
expiresAt | Optional | String | Future 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
| Name | Required | Type | Description |
|---|---|---|---|
otpAuthUrl | Required | String | otpauth://totp URL with a Base32 secret query parameter. |
name | Optional | String | Human-readable device name. If omitted, Mailisk derives one. |
username | Optional | String | Account label used only when missing from the otpauth:// URL. |
issuer | Optional | String | Issuer used only when missing from the otpauth:// URL. |
digits | Optional | Number | OTP length used only when missing from the URL. Supported values are 6 and 8. |
period | Optional | Number | TOTP period used only when missing from the URL. Must be between 10 and 300. |
algorithm | Optional | String | Algorithm used only when missing from the URL. One of SHA1, SHA256, or SHA512. |
expiresAt | Optional | String | Future 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
| Name | Required | Type | Description |
|---|---|---|---|
limit | Optional | Number | Maximum number of devices to return. Must be between 1 and 100. |
offset | Optional | Number | Number of devices to skip for pagination. |
username | Optional | String | Case-insensitive partial username filter. |
issuer | Optional | String | Case-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
| Name | Description |
|---|---|
deviceId | Saved 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
| Name | Description |
|---|---|
deviceId | Saved 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;
}