Skip to main content
Use POST /v1/otp/verify to check a plaintext code submitted by the end user. The endpoint is keyed on (phone_number, app_key, code) — there is no separate otp_id to track, and the call is the same regardless of whether the code was originally delivered via SMS, voice call, or WhatsApp. The server looks up the single active OTP for that phone within your Developer App and compares the bcrypt hash. The API enforces a hard cap of 3 attempts per active OTP. After the third wrong code the OTP is auto-locked and the user must request a new one. Drive your UI off data.remaining_attempts so the user always knows where they stand.

Endpoint

POST https://karibu.briq.tz/v1/otp/verify
Headers: X-API-Key (required), Content-Type: application/json (required).

Request body

FieldTypeRequiredNotes
phone_numberstring (E.164 digits)yesDigits only, no +. e.g. 255712345678.
app_keystringyesMust match the app the OTP was issued under.
codestringyesPlaintext code submitted by the user.
Trim whitespace and strip non-digits from the user’s input before submitting.

Success response

{
  "success": true,
  "message": "OTP verified successfully.",
  "data": { "verified_at": "2026-05-08T12:35:21.000000" },
  "status_code": 200
}
The OTP is marked used; subsequent verifies for the same code return success: false.

Handled errors

These are returned in the standard envelope with success: false. Branch on body.message and body.data.remaining_attempts:
messagedata.remaining_attemptsWhenRecommended UX
"No valid OTP found"0No active OTP for this (phone, app), expired, or already usedPrompt the user to request a new code.
"Invalid OTP code"2, 1, 0Wrong code; counter decrements with each failed attempt.Show “X attempts remaining”; allow retry until 0.
"Max verification attempts reached"03rd wrong attempt — OTP is now locked.Disable the verify button; force the user to resend.
"Invalid phone number"Malformed phone (non-digit, wrong shape).Re-validate digits-only E.164 client-side before retrying.
Auth (401), host/scoping (403), and validation (422) errors share the same shape as on /v1/otp/request. See Error scenarios for the full matrix.

Code samples

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"
  }'

Best practices

  • Branch on success first, then on data.remaining_attempts. Treat 0 as a hard lockout — the user must resend.
  • Never log the plaintext code. Don’t echo it from your verify form, store it in analytics, or include it in error reports.
  • Strip whitespace and non-digits from the user’s input before submitting to avoid spurious “Invalid OTP code” responses.
  • Use the same Developer App that issued the OTP. Cross-app verifies always fail with "No valid OTP found".

What’s next