Skip to main content

TOTP (Time-based One-Time Password)

TOTP provides two-factor authentication using authenticator apps like Google Authenticator, Authy, or 1Password.
TOTP must be enabled by the server administrator. If disabled, these endpoints will return 400 Bad Request.

Get TOTP Status

GET /api/user/mfa/totp

Check if TOTP is enabled and generate setup credentials if needed.

Request

curl -X GET https://your-zipline.com/api/user/mfa/totp \
  -H "Authorization: YOUR_TOKEN"

Response (TOTP Not Enabled)

If you haven’t set up TOTP yet:
secret
string
Base32-encoded secret key to enter manually
qrcode
string
Data URL containing QR code image for scanning
Example Response
{
  "secret": "JBSWY3DPEHPK3PXP",
  "qrcode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..."
}

Response (TOTP Already Enabled)

If TOTP is already configured:
{
  "secret": "JBSWY3DPEHPK3PXP"
}

Enable TOTP

POST /api/user/mfa/totp

Verify and activate TOTP authentication.

Request

code
string
required
6-digit verification code from your authenticator app
secret
string
required
The secret key returned from GET /api/user/mfa/totp
curl -X POST https://your-zipline.com/api/user/mfa/totp \
  -H "Authorization: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "123456",
    "secret": "JBSWY3DPEHPK3PXP"
  }'

Response

Returns the updated user object with TOTP enabled.

Errors

  • 400 Bad Request: Invalid verification code or TOTP disabled on server

Disable TOTP

DELETE /api/user/mfa/totp

Remove TOTP authentication from your account.

Request

code
string
required
Current 6-digit TOTP code to confirm removal
curl -X DELETE https://your-zipline.com/api/user/mfa/totp \
  -H "Authorization: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "123456"
  }'

Response

Returns the updated user object with TOTP disabled.

Errors

  • 400 Bad Request: Invalid code, TOTP not enabled, or TOTP disabled on server

Passkeys (WebAuthn)

Passkeys provide passwordless authentication using biometrics, security keys, or device authentication.
Passkeys must be enabled and configured by the server administrator (requires RP ID and Origin). If disabled or misconfigured, these endpoints will return 404 Not Found.

List Passkeys

GET /api/user/mfa/passkey

Get all registered passkeys for your account.

Request

curl -X GET https://your-zipline.com/api/user/mfa/passkey \
  -H "Authorization: YOUR_TOKEN"

Response

Returns an array of passkey objects (without sensitive registration data).
Example Response
[
  {
    "id": "pk_abc123",
    "createdAt": "2024-03-10T14:22:00.000Z",
    "updatedAt": "2024-03-15T09:30:00.000Z",
    "lastUsed": "2024-03-15T09:30:00.000Z",
    "name": "YubiKey 5C",
    "userId": "clxxx123456789"
  },
  {
    "id": "pk_def456",
    "createdAt": "2024-02-18T11:45:00.000Z",
    "updatedAt": "2024-03-14T16:20:00.000Z",
    "lastUsed": "2024-03-14T16:20:00.000Z",
    "name": "MacBook Touch ID",
    "userId": "clxxx123456789"
  }
]

Get Registration Options

GET /api/user/mfa/passkey/options

Generate WebAuthn registration options for creating a new passkey.

Request

curl -X GET https://your-zipline.com/api/user/mfa/passkey/options \
  -H "Authorization: YOUR_TOKEN"

Response

Returns WebAuthn PublicKeyCredentialCreationOptions for the browser’s navigator.credentials.create() API.
Example Response
{
  "rp": {
    "name": "Zipline",
    "id": "zipline.example.com"
  },
  "user": {
    "id": "Y2x4eHgxMjM0NTY3ODk=",
    "name": "john_doe",
    "displayName": "john_doe"
  },
  "challenge": "random-challenge-string",
  "pubKeyCredParams": [
    { "type": "public-key", "alg": -7 },
    { "type": "public-key", "alg": -257 }
  ],
  "timeout": 60000,
  "excludeCredentials": [],
  "authenticatorSelection": {
    "userVerification": "preferred",
    "residentKey": "preferred"
  }
}
Registration options are cached for 3 minutes. Requesting options again within this period returns the same challenge.

Rate Limiting

This endpoint is rate-limited to 1 request per second.

Register Passkey

POST /api/user/mfa/passkey

Complete passkey registration with the WebAuthn credential.

Request

response
object
required
WebAuthn RegistrationResponseJSON from navigator.credentials.create()
name
string
required
Friendly name for this passkey (e.g., “YubiKey 5C”, “iPhone 15”)
curl -X POST https://your-zipline.com/api/user/mfa/passkey \
  -H "Authorization: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "YubiKey 5C",
    "response": {
      "id": "credential-id",
      "rawId": "credential-id",
      "type": "public-key",
      "response": {
        "clientDataJSON": "...",
        "attestationObject": "..."
      }
    }
  }'

Response

Returns the updated user object.

Errors

  • 400 Bad Request: Invalid registration response, expired challenge, or verification failed
  • 429 Too Many Requests: Rate limit exceeded (1 request per second)

Delete Passkey

DELETE /api/user/mfa/passkey

Remove a registered passkey from your account.

Request

id
string
required
ID of the passkey to delete
curl -X DELETE https://your-zipline.com/api/user/mfa/passkey \
  -H "Authorization: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "pk_abc123"
  }'

Response

Returns the updated user object.

Security Best Practices

  • Use TOTP and passkeys together for defense in depth
  • Register multiple passkeys as backups in case you lose a device
  • Name your passkeys clearly to identify them later (e.g., “Work YubiKey”, “Personal iPhone”)
  • Review active sessions regularly in /api/user/sessions
If you lose access to all MFA methods and are locked out, contact your server administrator for account recovery.

Build docs developers (and LLMs) love