Web Rabbit MediaDocs
Messaging

OTP

Send and verify one-time passcodes with hashed storage and expiry.

Send and verify one-time passcodes. We generate the code, hash it before storage, deliver it over SMS and expire it for you — you never handle the plaintext code.

Send a code

POST/v1/messaging/otp
FieldTypeDescription
actionenumsend (default) or verify.
business_idstringOptional on API-key calls — taken from the key.
modeenumtest or live. Optional on API-key calls — taken from the key.
phonerequiredstringGhanaian number, local or international format.
purposeenumlogin, payment, pin_reset or custom. Picks the saved preset for that kind of code and is recorded on the request.
code_lengthinteger4–8 digits for this call only.
templatestringWording for this call only. Must contain {code}; {minutes} is replaced with the expiry.
iPer-call options win, then the purpose preset, then your settings
Configure them on the OTP page. Defaults: 6 digits, 5 minute expiry, template Your verification code is {code}. It expires in {minutes} minutes. A sender ID must be set before OTPs can be sent.
shell
curl -X POST https://api.webrabbitmedia.com/v1/messaging/otp \
  -H "Authorization: Bearer wr_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "action": "send",
    "business_id": "b0a1…",
    "mode": "live",
    "phone": "0240000000"
  }'
Response · 200
{
  "ok": true,
  "request_id": "9c71…",
  "expires_at": "2026-08-12T14:35:00.000Z",
  "cost": 0.035
}

Verify a code

Send action: "verify" with the request_id from the send call and the code the user entered. Verification is free.

There is also a dedicated route, POST /v1/messaging/otp/verify, which takes just request_id and code. Verifying a code twice is safe: the second call returns 200 with already_verified: true.

Response · 200
{ "ok": true, "status": "verified" }
Response · 400 — every failure is enumerated
{
  "error": "invalid_code",
  "message": "That code is not correct",
  "retryable": true,
  "attempts_remaining": 3,
  "request_id": "b3f1…"
}
FieldTypeDescription
400 invalid_codeerrorWrong code. retryable: true, with attempts_remaining. The attempt counter increments.
400 otp_expirederrorThe code passed its expiry window. retryable: false — send a new one.
429 too_many_attemptserrorFive failed attempts on one request id. retryable: false — issue a new code.
404 not_founderrorUnknown request_id. retryable: false.
›Test mode
In test mode no SMS leaves the platform, but the request row, expiry and verification flow behave exactly as they do live so you can build against the real contract.