Skip to main content
Every OTP you create in OTP.ID is a transaction with its own unique identifier, status, and expiry window. Understanding the lifecycle of that transaction — from the moment you call the API to the moment the OTP is verified or expires — helps you build reliable, user-friendly authentication flows. This page walks through each stage in order, explains the status values you will encounter, and documents the clamping rules that govern TTL and OTP length.

OTP Transaction ID Format

Every OTP transaction is assigned a unique ID at creation time. The format is:
Example: OTP20260807ABCD000001 Store the otp_id returned in the API response — you need it to poll the transaction status and to verify the OTP.

Transaction Statuses

An OTP transaction passes through the following statuses during its lifetime.
A failed delivery still creates a transaction record and still returns HTTP 200 OK. Check the status field in the response body to distinguish a failed delivery from a network or authentication error.

Lifecycle Flow

The diagram below describes the full journey of an OTP transaction in plain text.
1

Create the transaction

Call POST /v3/request (with automatic OTP generation) or POST /v3/send (supplying your own OTP value). The API immediately returns a transaction object with status: pending and an otp_id.For whatsapp_inbound, the response also includes a verification block containing wa_number, message, wa_link, and expires_at. Display this information to your user right away.
2

OTP.ID attempts delivery

OTP.ID contacts the channel vendor (WhatsApp provider, SMS gateway, email relay, or telephony provider). If the vendor accepts the message, the status advances to sent or success and your balance is deducted. If the vendor rejects or cannot reach the destination, the status is set to failed and no charge is applied.
3

User receives the code

Your user reads the OTP from their WhatsApp message, SMS, email, missed-call log, or (for whatsapp_inbound) sends the pre-filled WhatsApp message. The OTP is valid until the TTL expires.
4

Your app submits the code for verification

When the user enters the code in your interface, call POST /v3/verify with the otp_id and the otp value the user provided.
5

OTP.ID returns the verification result

OTP.ID checks the submitted code and returns one of the following outcomes:
  • Correct code — HTTP 200, verified: true. Status advances to verified.
  • Wrong code (attempts remaining) — HTTP 200, verified: false, reason: "mismatch". The transaction remains active; the user can try again.
  • 5th wrong attempt — HTTP 422, error code TOO_MANY_ATTEMPTS. Transaction is permanently locked.
  • Expired — HTTP 422, error code OTP_EXPIRED.
  • Already used — HTTP 422, error code ALREADY_USED.

Polling Transaction Status

If you need to check the state of a transaction outside of the verification flow (for example, to confirm delivery or to build a status dashboard), use the status endpoint:
Example response:
Poll this endpoint periodically for whatsapp_inbound transactions to detect when the user has sent their reply and the status advances to verified.

TTL Clamping

The ttl field controls how many seconds the OTP remains valid after creation. OTP.ID applies the following clamping rules silently — values outside the valid range are adjusted rather than rejected.
OTP.ID never returns a validation error for an out-of-range ttl. The value is silently clamped. Always read the expires_at field in the response to know the exact expiry time.

OTP Length Clamping

The otp_length field controls how many digits the generated OTP contains. The same silent clamping logic applies.

Verification Behaviour Reference

A wrong-code response is not a server error — it is a successful API call that returns verified: false. Do not treat HTTP 200 with verified: false as an exception or retry it automatically. Show the user a “wrong code” message and let them try again (up to 5 attempts total).
Build your verification UI to count remaining attempts on the client side. After 4 failed attempts, warn the user that one attempt remains before the transaction is permanently locked. You can also request a new OTP at any time if the user wants to start fresh.