WatificationDocs

WhatsApp OTP

Deliver your own verification codes and track their WhatsApp message status.

Send a code with POST /otp/messages, then read its status with GET /otp/messages/{messageId} or receive signed webhooks. Your application generates, expires, and verifies the code. There is no code verification endpoint.

Requirements

  • A sendable WhatsApp number in your workspace. Find its Sender ID under Developers > Overview > Senders.
  • A template on that sender's WhatsApp Business account with status APPROVED, category AUTHENTICATION, and authentication type.
  • Exactly one body code parameter and a valid template structure. The code fills the body and any OTP button parameters, including copy-code, one-tap, or zero-tap buttons.
  • Available monthly API OTP quota. Check API OTP messages this month on Developers > Overview.

Use the template's exact name and language code. A standard utility or marketing template cannot be used for OTP sends.

Send a code

Set WATIFICATION_API_KEY to your server-side key. Replace the fictional values with your recipient, sender, and template.

POST /otp/messages
curl --fail-with-body --request POST \
  'https://api.watification.com/api/v1/otp/messages' \
  --header "Authorization: Bearer $WATIFICATION_API_KEY" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: login-example-002' \
  --data '{
    "to": "+15555550123",
    "senderId": "11111111-1111-4111-8111-111111111111",
    "template": { "name": "login_code", "language": "en" },
    "code": "483920",
    "reference": "login-example-002"
  }'
FieldRequiredValue
toYesValid phone number with a leading +; normalized to E.164.
senderIdYesWatification sender UUID for a WhatsApp number in this workspace.
template.nameYesNon-empty template name.
template.languageYesNon-empty template language code.
codeYes1-15 ASCII letters or digits (A-Z, a-z, 0-9).
referenceNoYour correlation string, at most 128 characters.

Unknown body fields and query parameters are rejected with 422 DEVELOPER_INVALID_INPUT. Do not send additional fields inside template.

HTTP 202 returns:

Acceptance response
{
  "messageId": "22222222-2222-4222-8222-222222222222",
  "status": "queued",
  "reference": "login-example-002"
}

reference is null when omitted. It is returned in status responses and outcome webhooks, so use it to match a message to your login or verification attempt. It is not an idempotency key.

Get the status

GET /otp/messages/{messageId}
curl --fail-with-body \
  'https://api.watification.com/api/v1/otp/messages/22222222-2222-4222-8222-222222222222' \
  --header "Authorization: Bearer $WATIFICATION_API_KEY"

HTTP 200 returns:

Status response
{
  "messageId": "22222222-2222-4222-8222-222222222222",
  "status": "sent",
  "reference": "login-example-002",
  "to": "+15555550123",
  "createdAt": "2026-10-09T10:00:00.000Z",
  "sentAt": "2026-10-09T10:00:02.000Z",
  "failure": null
}

sentAt is null before a sent outcome is recorded. failure is null or { "code": "...", "message": "..." } with a safe description. The supplied code is never returned in status responses or webhooks. Send no GET body or query parameters.

Statuses

The status vocabulary includes the following values. Do not require every intermediate status to appear.

StatusMeaning
acceptedRequest reserved; message submission is not yet complete.
receivedMessage recorded before queueing.
queuedAccepted for sending.
sendingA send attempt is in progress.
sentA sent outcome has been reported.
deliveredWhatsApp reported delivery.
readWhatsApp reported the message as read.
playedPlayback status in the shared message vocabulary; not an expected OTP text outcome.
failedA send failure has been recorded. Inspect failure.

Status corrections can move a failed message to a sent, delivered, or read outcome and clear its failure details. No message status means the customer successfully verified your code.

Idempotency

Set the optional Idempotency-Key header to a string of at most 128 characters. Use a non-empty, unique value for each logical send and persist it before making the request.

Within a workspace, repeating a completed request with the same key returns the original 202 body, including status: "queued", even if the current status has advanced. It does not send another message or consume another quota unit.

The normalized recipient, sender UUID, template name, language, and reference must match. A mismatch returns 409 IDEMPOTENCY_KEY_REUSED. The code is excluded from this comparison: sending a different code with the same key does not replace the original message. Use a new key to deliver a new code. After a network failure, retry with the same key and unchanged request.

Quota and events

One accepted send consumes one monthly API OTP unit. Idempotent repeats do not consume more; later send failures are not refunded. A zero or exhausted plan allowance returns 429 OTP_QUOTA_EXCEEDED. Plan amounts vary; use the dashboard's current allowance.

  • message.sent: first reported sent, delivered, read, or played outcome. The webhook's data.status contains the actual reported status.
  • message.failed: first reported failure.

Each type is produced at most once per message, but webhook deliveries can repeat. Both types can occur for one message. There are no separate delivered or read events. See webhook handling.

Endpoint errors

All errors use Problem Details. These are the endpoint's stable error codes:

HTTPCodeMeaning
401API_KEY_INVALIDMissing, invalid, or revoked key.
404OTP_SENDER_NOT_FOUNDSender not found in this workspace.
404OTP_TEMPLATE_NOT_FOUNDTemplate name/language not found for the sender.
404NOT_FOUNDGET message not found in this workspace.
409OTP_SENDER_UNAVAILABLESender cannot send.
409OTP_TEMPLATE_NOT_USABLETemplate does not meet OTP requirements.
409IDEMPOTENCY_KEY_REUSEDKey was used for a different request.
422DEVELOPER_INVALID_INPUTInvalid fields, phone, UUID, or header.
422RECIPIENT_BLOCKEDRecipient is blocked.
429OTP_QUOTA_EXCEEDEDMonthly API OTP allowance unavailable or exhausted.
429RATE_LIMITEDPer-key request rate exceeded; use Retry-After.

On this page