Skip to main content
Briq OTP codes are internally called flakes. In addition to the synchronous /v1/otp/verify response, you can register an optional per-request callback URL to receive async notifications when verification succeeds or fails. This is separate from registered app webhooks (SMS delivery events). Flake callbacks are one-off: you pass callback_url on a specific request, resend, or verify call — no webhook registration in the dashboard is required.

When to use callbacks

Use callbacks when…Rely on /verify only when…
Your backend needs a second system notified after verify (CRM, audit log, workflow engine)A single synchronous response is enough for your auth gate
You want failure signals (max attempts) pushed to your serverYou already handle failures from the verify envelope
You integrate with event-driven architectureYou poll or trust client-side state only
Callbacks are best-effort. Always use the synchronous /v1/otp/verify response as the source of truth for granting access. Treat callbacks as notifications, not as the primary auth decision.

How it works

  1. Request or resend — pass optional callback_url and callback_secret. Karibu associates them with this OTP for its lifetime (minutes_to_expire, default 10 minutes), scoped to your Developer App and phone number.
  2. Verify — you get the normal synchronous /verify response first. If a callback URL was set on request/resend (or passed again on verify), Karibu also sends an async webhook to that URL shortly after.
  3. Your endpoint — receives a JSON flake.verified or flake.failed envelope. Respond with 2xx; Karibu retries on failure.
Calling /v1/otp/invalidate, or issuing a new /request or /resend, replaces the callback association for that (phone, app) pair.

Request fields

Available on POST /v1/otp/request, POST /v1/otp/resend, and POST /v1/otp/verify:
FieldTypeRequiredNotes
callback_urlstringnoMust be HTTPS. Must not resolve to private, loopback, or link-local addresses. Rejected with 422 if invalid.
callback_secretstringnoWhen set, callbacks include X-Briq-Signature: sha256=<hex> (HMAC-SHA256 over the raw JSON body).
On verify, you may pass callback_url without callback_secret — Karibu reuses the secret from the original request/resend when available.

Events

EventWhen (Karibu OTP API)
flake.verified/v1/otp/verify returns success: true
flake.failed/v1/otp/verify hits max attempts (reason: "max_attempts")
The plaintext OTP code is never included in callback payloads.

flake.verified payload

{
  "id": "evt_a1b2c3d4e5f6...",
  "event": "flake.verified",
  "channel": "flake",
  "app_id": "550e8400-e29b-41d4-a716-446655440000",
  "created_at": "2026-06-07T12:00:00.000Z",
  "data": {
    "flake_id": "otp-uuid-here",
    "phone_number": "255712345678",
    "status": "verified",
    "verified_at": "2026-06-07T12:00:05.000000"
  }
}

flake.failed payload (max attempts)

{
  "id": "evt_f6e5d4c3b2a1...",
  "event": "flake.failed",
  "channel": "flake",
  "app_id": "550e8400-e29b-41d4-a716-446655440000",
  "created_at": "2026-06-07T12:02:00.000Z",
  "data": {
    "flake_id": "otp-uuid-here",
    "phone_number": "255712345678",
    "status": "failed",
    "reason": "max_attempts",
    "failed_at": "2026-06-07T12:02:00.000000",
    "remaining_attempts": 0
  }
}

Outbound request shape

AspectValue
MethodPOST
Content-Typeapplication/json
User-AgentBriq-Webhook/1.0
X-Briq-App-IDYour Developer App UUID
X-Briq-Service-Typeflake
X-Briq-SignaturePresent when callback_secret was provided
Signature verification matches registered webhooks: HMAC-SHA256 over the raw request body bytes.

Testing callbacks end-to-end

Use a public HTTPS request inspector (e.g. webhook.site) or your own staging endpoint.

Step 1 — capture a callback URL

  1. Open webhook.site (or similar) and copy your unique HTTPS URL.
  2. Optionally choose a test secret, e.g. test-callback-secret-123.

Step 2 — request an OTP with callback

curl -X POST https://karibu.briq.tz/v1/otp/request \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "255712345678",
    "app_key": "YOUR_APP_KEY",
    "delivery_method": "sms",
    "callback_url": "https://webhook.site/YOUR-UNIQUE-ID",
    "callback_secret": "test-callback-secret-123"
  }'
The synchronous response is unchanged — you still only get expires_at, not the code.

Step 3 — verify the code

Submit the code the recipient received:
curl -X POST https://karibu.briq.tz/v1/otp/verify \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "255712345678",
    "app_key": "YOUR_APP_KEY",
    "code": "123456"
  }'
Within a few seconds, your inspector should receive a POST with event: "flake.verified".

Step 4 — verify the signature (optional)

If you set callback_secret, confirm X-Briq-Signature matches HMAC-SHA256 of the raw body. See Webhooks → Verifying signatures.

Step 5 — test failure callback

Request a new OTP (same callback_url), then call /verify three times with wrong codes. On the third failure you should receive flake.failed with reason: "max_attempts".

API playground

The Karibu OTP API reference documents callback_url and callback_secret on request, verify, and resend. Use the interactive playground on those endpoints with your API key and a webhook.site URL.
422 on bad URLs. http:// URLs, localhost, and hostnames that resolve to private IPs are rejected at request validation time with a 422 body error — before any OTP is sent.

Best practices

  • Return 2xx quickly from your callback handler. Briq retries on non-2xx responses with exponential backoff.
  • Make handlers idempotent — use data.flake_id or envelope id to deduplicate.
  • Do not block login on callbacks — proceed from the synchronous verify response; process the webhook asynchronously.
  • Rotate callback_secret per environment (staging vs production) and store it server-side only.
  • Use resend to update the callback — a new callback_url on /resend replaces the callback target for the new OTP window.

What’s next