Endpoint
Authentication
| Header | Required | Value |
|---|---|---|
X-API-Key | Yes | Your API key (bun_...) |
Content-Type | Yes | application/json |
Overview
Generate time-based one-time passwords (TOTP per RFC 6238) for two-factor authentication flows, or provision brand-new TOTP secrets ready to be enrolled in an authenticator app. Pass an existingsecret to get the current OTP code, or set new_secret: true to receive a fresh Base32 secret. Use the /verify endpoint to validate a user-submitted code against a known secret.
Use cases
- Generate the current OTP for a user’s registered secret server-side
- Provision new 2FA secrets during user enrollment
- Verify that an authenticator app is correctly configured
- Build custom 2FA flows without a third-party auth service
Details
OTPs default to 6 digits rotating every 30 seconds (standard TOTP window). Bothdigits and step are configurable to support non-standard authenticator setups. When provisioning a new secret with new_secret: true, the response includes only the Base32 secret, issuer, and account — no OTP is generated in that flow.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
secret | string | No | Existing Base32 TOTP secret to generate an OTP for |
new_secret | boolean | No | Set to true to generate a brand-new Base32 secret |
issuer | string | No | Issuer label for new secret provisioning (default: "Bunny") |
account | string | No | Account label for new secret provisioning (default: "user@example.com") |
digits | integer | No | Number of OTP digits: 6 or 8 (default: 6) |
step | integer | No | Time step in seconds (default: 30) |
secret or new_secret: true must be provided.
Example — generate OTP from existing secret
Example — provision new secret
Response
200 OK — generate OTP from existing secret
| Field | Type | Description |
|---|---|---|
code | string | Current OTP code (zero-padded to digits length) |
valid_for_seconds | integer | Seconds remaining until this OTP expires |
step | integer | Time step used in seconds |
digits | integer | Number of digits in the OTP |
200 OK — provision new secret (new_secret: true)
| Field | Type | Description |
|---|---|---|
secret | string | New Base32-encoded TOTP secret to store and enroll in an authenticator app |
issuer | string | Issuer label (echoed from request, or "Bunny" by default) |
account | string | Account label (echoed from request, or "user@example.com" by default) |
401 Unauthorized
402 Payment Required
422 Unprocessable Entity
429 Too Many Requests
cURL example
Verify endpoint
Request body
| Field | Type | Required | Description |
|---|---|---|---|
secret | string | Yes | Base32 TOTP secret for the user |
code | string | Yes | OTP code submitted by the user |
step | integer | No | Time step in seconds (default: 30) |
digits | integer | No | Number of OTP digits (default: 6) |
window | integer | No | Number of time steps to check on each side of the current time for clock drift tolerance (default: 1) |
Example
Response
200 OK — valid code
| Field | Type | Description |
|---|---|---|
valid | boolean | true if the code matched |
drift | integer | Number of time steps offset from current time where the match was found (0 = current window, -1 = previous, etc.) |

