--- title: TOTP (Authenticator) | Sendblue Docs description: Store TOTP secrets and generate 2FA codes via API — no phone-based authenticator app required for your agents --- The TOTP API lets you store encrypted TOTP secrets and generate 2FA codes programmatically. Instead of requiring an AI agent or automation to use a phone-based authenticator app, you can store the secret once and call the API whenever a 6-digit code is needed. Secrets are encrypted at rest with AES-256-GCM. The plaintext secret is returned once at registration and never again. ## Register a TOTP Secret Store a new TOTP secret. You can provide either a raw `secret` (base32) or a full `otpauth://` URI from a QR code scan. ``` POST https://api.sendblue.com/api/v2/totp/secrets ``` ### Request Body | Parameter | Type | Required | Description | | ----------- | ------- | -------- | ---------------------------------------------------------------------------------------------- | | `label` | string | Yes\* | Human-readable label (e.g. `"GitHub - agent@example.com"`). Required unless `uri` is provided. | | `uri` | string | No | Full `otpauth://totp/...` URI from a QR code. Overrides all other fields. | | `secret` | string | No | Base32-encoded TOTP secret. Omit to auto-generate. | | `issuer` | string | No | Service name (e.g. `"GitHub"`, `"Google"`) | | `algorithm` | string | No | `SHA1` (default), `SHA256`, or `SHA512` | | `digits` | integer | No | Code length: `6` (default) or `8` | | `period` | integer | No | Rotation period in seconds (default: `30`) | ### Example — from a QR code URI Terminal window ``` curl -X POST 'https://api.sendblue.com/api/v2/totp/secrets' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "uri": "otpauth://totp/GitHub:agent%40example.com?secret=JBSWY3DPEHPK3PXP&issuer=GitHub" }' ``` ### Example — from a raw secret Terminal window ``` curl -X POST 'https://api.sendblue.com/api/v2/totp/secrets' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "label": "GitHub - agent@example.com", "secret": "JBSWY3DPEHPK3PXP", "issuer": "GitHub" }' ``` ### Success Response (200) The plaintext `secret` is returned **only on creation**. Save it if you need it — it is never returned again. ``` { "status": "OK", "totp_secret": { "id": "550e8400-e29b-41d4-a716-446655440000", "label": "GitHub - agent@example.com", "issuer": "GitHub", "algorithm": "SHA1", "digits": 6, "period": 30, "secret": "JBSWY3DPEHPK3PXP", "created_at": "2026-04-05T12:00:00Z" } } ``` --- ## Get Current TOTP Code Generate the current code for a stored secret. Also returns `expires_in` — seconds until the code rotates — so your agent knows when to retry if a code is rejected. ``` GET https://api.sendblue.com/api/v2/totp/code/:secret_id ``` ### Path Parameters | Parameter | Type | Description | | ----------- | ------------- | ---------------------------- | | `secret_id` | string (UUID) | ID of the stored TOTP secret | ### Example Request Terminal window ``` curl 'https://api.sendblue.com/api/v2/totp/code/550e8400-e29b-41d4-a716-446655440000' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' ``` ### Success Response (200) ``` { "status": "OK", "code": "482031", "expires_in": 14 } ``` `expires_in` is the number of seconds remaining in the current time window. If a 2FA form rejects the code, wait until `expires_in` reaches 0 and fetch a new one. --- ## List TOTP Secrets List all stored secrets for your account. Secret values are never included. ``` GET https://api.sendblue.com/api/v2/totp/secrets ``` ### Example Request Terminal window ``` curl 'https://api.sendblue.com/api/v2/totp/secrets' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' ``` ### Success Response (200) ``` { "status": "OK", "totp_secrets": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "label": "GitHub - agent@example.com", "issuer": "GitHub", "algorithm": "SHA1", "digits": 6, "period": 30, "created_at": "2026-04-05T12:00:00Z" } ] } ``` --- ## Delete a TOTP Secret Permanently delete a stored secret. ``` DELETE https://api.sendblue.com/api/v2/totp/secrets/:secret_id ``` ### Path Parameters | Parameter | Type | Description | | ----------- | ------------- | ------------------------------- | | `secret_id` | string (UUID) | ID of the TOTP secret to delete | ### Example Request Terminal window ``` curl -X DELETE 'https://api.sendblue.com/api/v2/totp/secrets/550e8400-e29b-41d4-a716-446655440000' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' ``` ### Success Response (200) ``` { "status": "OK" } ``` --- ## Error Responses ### Bad Request (400) ``` { "status": "ERROR", "message": "label is required (or provide a uri)" } ``` ### Unauthorized (401) ``` { "status": "ERROR", "message": "Unauthorized" } ``` ### Not Found (404) ``` { "status": "ERROR", "message": "TOTP secret not found" } ``` ### Conflict (409) ``` { "status": "ERROR", "message": "A secret with this label already exists" } ``` --- ## Notes 1. **Security**: Secrets are encrypted at rest with AES-256-GCM. The plaintext secret is only returned at registration time. 2. **URI format**: Most services display a QR code during 2FA setup. If your setup flow shows a “can’t scan?” fallback, that link is the `otpauth://` URI — pass it directly as `uri`. 3. **Timing**: TOTP codes are time-based. If a code is rejected, check `expires_in` and wait for the next window before retrying. 4. **Label uniqueness**: Labels must be unique within your account. Use descriptive labels like `"service - account@example.com"` to avoid conflicts.