Skip to main content
OTP (one-time passwords) are short, time-limited numeric codes sent to a user’s phone to confirm identity for sign-in, registration, or sensitive actions. Briq’s OTP API generates the code, delivers it via SMS, voice call, or WhatsApp, and then verifies what the user submits — all scoped to a Developer App resolved from your bound API key. This page is the integrator’s overview: what to set up, the rules the API enforces, and how the pieces fit together. For payload-level detail, see the Karibu OTP API reference.

Prerequisites

To use OTP services you need:
  1. A workspace with developer_access = true. Toggle this in the Briq UI under workspace settings, or see Workspaces.
  2. A Developer App linked to that workspace. See Developer Apps.
  3. An active developer API key (X-API-Key header) bound to that Developer App — at key creation or via the attach endpoint. Karibu resolves the app from the key; bound keys need only X-API-Key on every OTP call.
  4. Recipient phone numbers in E.164 digits-only format (no +, no spaces) — e.g. 255712345678.
Bind every OTP API key to a Developer App and keep it server-side. Never expose your API key to client apps or browsers — have your frontend call your backend, which then calls Karibu with X-API-Key only.Legacy integrations with an unbound API key may scope requests via the optional X-App-ID header or an app_key field in the body. New integrations should bind the key instead.

Behavioral rules to internalize

These rules drive correct UX and prevent the most common integration bugs. Read them once before writing code.

End-to-end flow

A typical phone-verification flow:
  1. User enters phone in your UI → your backend calls POST /v1/otp/request with the appropriate delivery_method. → see Requesting OTP codes.
  2. User receives SMS, voice call, or WhatsApp message with the plaintext code.
  3. User submits code in your UI → your backend calls POST /v1/otp/verify. → see Validating OTP codes.
    • On success: true → mark phone verified and continue.
    • On success: false → drive the UX off data.remaining_attempts.
  4. User clicks “Resend” → your backend calls POST /v1/otp/resend (which invalidates the prior code automatically). You may switch channels here — e.g. retry on WhatsApp after an SMS didn’t arrive. → see Managing OTP lifecycle.
  5. User logs out / changes phone → your backend calls POST /v1/otp/invalidate.
  6. Optional: poll GET /v1/otp/status to drive countdown timers or detect that an OTP is already in-flight before issuing a new one.
  7. Optional: pass callback_url on request or resend if you want async flake.verified / flake.failed notifications at your HTTPS endpoint when the user verifies. → see Flake lifecycle callbacks.

Standard response envelope

Every OTP endpoint returns the same JSON envelope:
Inspect body.success and body.status_code rather than the raw HTTP status — /v1/otp/status returns HTTP 200 wrapping status_code: 404 when no active OTP exists. Auth, host, and validation errors (401/403/422) follow FastAPI’s {"detail": "..."} shape instead — they bypass the envelope.
For complete payload tables, error matrices, and copy-pasteable client snippets in cURL, Python, Node.js, and PHP, see the Karibu OTP API reference.
Need to send WhatsApp messages beyond OTP, such as notifications, templates, media, or interactive flows? See the WhatsApp guide.