
Overview
A phone report is produced every time a workflow node runs the Phone Verification feature. Each report represents one OTP challenge against one phone number and contains:- The phone number broken into prefix, national number and full E.164 form, plus ISO country code and country name.
- Carrier metadata (
name,type) resolved from the destination network. - Boolean risk flags (
is_disposable,is_virtual) derived from line-type analysis. - The actual delivery channel used (
verification_method) and the count of OTP send attempts. - A chronological
lifecycle[]log of every send, retry, delivery event, and code-check attempt. - A
matches[]array surfacing the same number on other sessions — any status, across KYC, KYB and standalone API verifications — or your blocklist. - A
warnings[]array — risk events emitted during the verification (see Phone Verification warnings). - A
node_idthat identifies which workflow graph node produced the report (V3 sessions only).
POST /v3/phone/send/ to deliver the code and POST /v3/phone/check/ to validate the user’s entry. A pending OTP is valid for 5 minutes from the first send (retries do not extend the window). In workflow sessions users get 2 OTP send attempts (phone_max_retries) and 2 code-entry attempts (phone_max_check_attempts) by default — both tunable per workflow node; exhausting either cap finalizes the step as Declined with VERIFICATION_CODE_ATTEMPTS_EXCEEDED. The standalone phone API allows 3 code-entry attempts per verification.
Where it appears in API responses
The decision endpoint (GET /v3/session/{sessionId}/decision/) returns phone reports under the plural array key phone_verifications. The array contains one entry per Phone Verification node in the workflow graph — typically one, but step-up flows may produce several.
null value means no Phone Verification step has run yet. Iterate the array (rather than reading phone_verifications[0]) when your workflow can collect more than one phone number.
Schema
The canonical field-by-field schema lives on the Data models page.Status values
| Status | Meaning |
|---|---|
Not Finished | The OTP send has been initiated but the verification has not been finalized yet. |
Approved | The user entered a valid OTP and no declining risk matched. |
Declined | An auto-decline warning fired (blocklist, high-risk, attempts exceeded) or a risk action configured to DECLINE matched. |
In Review | A risk action configured to REVIEW routed the step to manual review. |
Expired | The 5-minute OTP window elapsed with no valid code — applies to verifications created via the standalone phone API. |
Lifecycle event types
lifecycle[] is sorted chronologically. Each event has type, timestamp, details, and a billable fee (when applicable):
| Event type | Emitted when |
|---|---|
PHONE_VERIFICATION_MESSAGE_SENT | First OTP send attempt over the requested channel. |
PHONE_VERIFICATION_RETRY_MESSAGE_SENT | Subsequent OTP send after the user requested a resend. |
PHONE_VERIFICATION_BLOCKED | The send was blocked (spam, suspicious, repeated, invalid number). |
PHONE_DELIVERY_DELIVERED | Carrier confirmed delivery on the actual channel used. |
PHONE_DELIVERY_UNDELIVERABLE | Carrier reported the message could not be delivered. |
VALID_CODE_ENTERED | The user submitted the correct OTP code. |
INVALID_CODE_ENTERED | The user submitted an incorrect OTP code (counts toward the code-entry cap). |
PHONE_VERIFICATION_APPROVED | Feature-level status was set to Approved. |
PHONE_VERIFICATION_DECLINED | Feature-level status was set to Declined. |
PHONE_VERIFICATION_IN_REVIEW | Feature-level status was set to In Review. |
PHONE_VERIFICATION_EXPIRED | The OTP window elapsed without a valid code. |
details payload depends on the event family:
- Send events (
PHONE_VERIFICATION_MESSAGE_SENT,PHONE_VERIFICATION_RETRY_MESSAGE_SENT,PHONE_VERIFICATION_BLOCKED) —{ status, reason, channel, actual_channel }.statusisSuccess,RetryorBlocked;reasonisnullunless the send was blocked (invalid_phone_number,repeated_attempts,suspicious,spam,unknown);channelis the requested channel andactual_channelthe channel that actually delivered (e.g. a WhatsApp send that fell back to SMS). - Delivery events (
PHONE_DELIVERY_DELIVERED,PHONE_DELIVERY_UNDELIVERABLE) —{ channel, status }withstatusdeliveredorundeliverable. - Check events (
VALID_CODE_ENTERED,INVALID_CODE_ENTERED) —{ code_tried, status }withstatusApproved,Declined,FailedorExpired or Not Found. - Final status events —
null, exceptPHONE_VERIFICATION_DECLINED/PHONE_VERIFICATION_IN_REVIEW, which carry{ "reason": "<risk code>" }(e.g.PHONE_NUMBER_IN_BLOCKLIST).
fee on a send event is the estimated price until delivery is confirmed, then the actual billed amount — delivery is billed per delivered message when the verification finalizes, and only Blocked sends are guaranteed free. Delivery, check and status events always carry fee: 0.
Cross-session matches
matches[] records other sessions in the same application where the same number was seen — across KYC, KYB and standalone API verifications, regardless of their status — plus blocklist hits configured in the management-api lists. Sessions sharing the current session’s vendor_data are excluded (the same end-user does not match themselves), and the array is capped at 5 entries. When the number is on your blocklist but none of the matched sessions is blocklisted, a synthetic entry with source: "list_entry" (all session fields null) is prepended to the array.
Examples
Approved — WhatsApp OTP, mobile carrier
Declined — blocklisted VoIP number
The user entered a valid OTP, but finalization found the number on the application’s blocklist (auto-decline) and flagged its VoIP line type (voip_number_action left at NO_ACTION, so it logs as information). Because no matched session is blocklisted, the blocklist hit appears as a synthetic list_entry match.
Related
- Phone Verification overview — feature behavior, pricing, supported channels.
- Phone Verification warnings — every risk code, decline triggers and configurable actions.
- Data models — phone verification — canonical field-by-field schema.
- Standalone phone API —
POST /v3/phone/send/andPOST /v3/phone/check/endpoints. - Management API — Lists — manage the phone blocklist that feeds
matches[]withsource: list_entry.