API docs
The verification object
A single verification represents one OTP session - its destination, delivery attempts and lifecycle status - and is the object OTPRelay returns from every verification endpoint.
The same verification object is returned when you send a verification (POST /v1/verifications), check one (POST /v1/verifications/check), and when you retrieve it (GET /v1/verifications/{id}). Check responses are a compact view of the same resource - they always include status and the relevant convenience boolean, and a full retrieve returns every attribute below.
The attempts[] array records every SMS send OTPRelay made for this verification, in order. In the example above the first attempt over stc went undelivered, so SMS failover automatically retried the same code over an alternate route - handled here by mobily - which was delivered. Every attempt is SMS; they differ only by carrier.
Attributes
idstringoptional- Unique identifier for the verification, prefixed
ver_. objectstringoptional- Always
"verification". statusstringoptional- The lifecycle state - one of
pending,approved,expired,canceled,max_attempts_reachedorfailed. The source of truth for whether the user is verified. See Statuses. tostringoptional- The destination phone number in E.164 format, e.g.
+966512345678. countrystringoptional- ISO 3166-1 alpha-2 country code inferred from
to, e.g.SA. senderstringoptional- The alphanumeric sender every code is delivered from. Always
"OTPRelay"- a single pre-cleared shared sender, with nothing for you to register. See Sender & message. code_lengthintegeroptional- Number of digits in the generated code, 4-8.
verifiedbooleanoptional- Convenience flag returned by check:
truewhen the submitted code was correct. Mirrorsstatus: "approved". attemptsarrayoptional- Each send OTPRelay made for this verification, in order. See The attempt object.
webhook_urlstring | nulloptional- The HTTPS URL this verification's events are POSTed to, if one was set on the request. See Webhooks.
metadataobjectoptional- The key/value pairs you attached on send, echoed back here and on webhook events.
created_atstringoptional- ISO 8601 timestamp when the verification was created.
expires_atstringoptional- ISO 8601 timestamp when the code expires. After this,
statusbecomesexpired. checked_atstringoptional- ISO 8601 timestamp of the most recent check. Present once the verification has been checked at least once.
The attempt object
Each entry in attempts[] describes one SMS delivery attempt. Route and operator retries each add an entry, so the array is your audit trail of exactly what OTPRelay did to deliver the code.
idstringoptional- Unique identifier for the attempt, prefixed
att_. statusstringoptional- Delivery state of the attempt -
sent,delivered,undeliveredorfailed. A status ofundeliveredorfailedis what triggers an automatic retry over an alternate route/operator. carrierstringoptional- The mobile operator that handled the attempt, e.g.
stc,mobily,duorooredoo. atstringoptional- ISO 8601 timestamp when the attempt was made.
Statuses
A verification starts as pending and moves to exactly one terminal state. Once it leaves pending it is closed: further checks return 404 and you must start a new verification to retry.
| status | meaning |
|---|---|
pending | The code has been sent and is awaiting a correct check. The only non-terminal state. |
approved | The user submitted the correct code. verified is true. Terminal. |
expired | The code reached expires_at before a correct check. Terminal. |
canceled | You canceled the verification before it was approved. Terminal. |
max_attempts_reached | Five incorrect checks were made. The verification is closed. Terminal. |
failed | SMS delivery failed across every route and operator OTPRelay tried. Terminal. |
status is the source of truth
status. The verified boolean is a convenience flag that mirrors status: "approved" - it exists so a simple if (check.verified) reads cleanly, but it never carries information statusdoesn't.Retrieve, cancel and list
Retrieve a verification at any time to read its current status and the full attempts[] history - useful for reconciling state outside the send/check flow or alongside webhooks.
/v1/verifications/{id}Cancel an in-flight verification to close it early - for example when the user requests a fresh code. This sets status to canceled; the old code stops being accepted immediately.
/v1/verifications/{id}/cancelTo page through past verifications, list them and filter by status, to or created_at.
/v1/verificationsTo create one, see Send a verification. To understand how the attempts[] array gets populated by SMS route and operator failover, see Delivery & failover.