<Intro>

<EndpointHeader />

<Description>

Checks a passcode for a recipient and returns the outcome together with the verification's current state. Identify the verification by the same `to` used to create it; you do not need to store a verification ID.

A wrong or expired passcode is a normal outcome, not an HTTP error: the response is `200` with `success` set to `false` and a `reason` such as `incorrect_code` or `expired`. `success: true` means the verification is complete. Each verification reports its final outcome exactly once and is no longer checkable afterwards.

An error status is returned only when the check cannot be evaluated: `404` when no verification matches the recipient or the matching one already reached its final state, `422` for an invalid recipient, and `429` when passcodes for a recipient are checked too quickly.

</Description>

</Intro>

<Payload kind="request">

<Field name="to" type="object" required>

<Description>

The recipient to verify. Provide an `email_address`, a `phone_number`, or both; at least one is required. The addresses also identify the verification: a check must supply exactly the set used on the create call, so a verification created with both addresses is not found by either one alone.

</Description>

<FieldChildren kind="request">

<Field name="email_address" type="string" prefix="to.">

<Description>

The recipient's email address. Case does not matter; the address is lowercased before use.

</Description>

</Field>

<Field name="phone_number" type="string" prefix="to.">

<Description>

The recipient's phone number in E.164 format, with the leading `+` and country code (for example `+15551234567`). A number in any other format is rejected as an invalid recipient (`422`).

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="code" type="string" required>

<Description>

The passcode the recipient received. Passcodes are numeric; submit the digits exactly as delivered. An incorrect value is a normal `200` outcome with `success: false`, not an error.

</Description>

</Field>

</Payload>

<Payload kind="response">

<Field name="success" type="boolean" required>

<Description>

Whether the submitted passcode verified this verification. `true` means the passcode was correct and the verification is now complete; `false` means it did not verify, and `reason` says why. A verification that has already reached a final state is no longer checkable and returns `404`.

</Description>

</Field>

<Field name="reason" type="nullable string">

<Description>

Why the check did not succeed, or null when `success` is true: `incorrect_code` means the passcode was wrong and attempts remain; `expired` means the time window elapsed; `attempts_exhausted` means too many incorrect attempts. Open enum; treat any unrecognized value as a future reason.

</Description>

<Description>

Possible values (may grow over time): `incorrect_code`, `expired`, `attempts_exhausted`

</Description>

</Field>

<Field name="verification" type="object" required>

<FieldChildren kind="response">

<Field name="id" type="string" prefix="verification." required />

<Field name="status" type="string" prefix="verification." required>

<Description>

The verification's current state: `pending` (the initial state, awaiting a correct passcode), `verified` (a correct passcode was submitted), `failed` (too many incorrect attempts), `expired` (the time window elapsed before a correct passcode), `canceled` (the verification was canceled before completing), or `blocked` (it was stopped by a fraud or abuse control).

</Description>

<Description>

Possible values: `pending`, `verified`, `failed`, `expired`, `canceled`, `blocked`

</Description>

</Field>

<Field name="reason" type="nullable string" prefix="verification.">

<Description>

Why the verification reached its final state: `attempts_exhausted` (too many incorrect passcodes) or `ttl_elapsed` (the time window elapsed before a correct passcode). Null while `pending` and once `verified`. Open enum; treat any unrecognized value as a future reason.

</Description>

<Description>

Possible values (may grow over time): `attempts_exhausted`, `ttl_elapsed`

</Description>

</Field>

<Field name="to" type="object" prefix="verification." required>

<Description>

The recipient to verify. Provide an `email_address`, a `phone_number`, or both; at least one is required. The addresses also identify the verification: a check must supply exactly the set used on the create call, so a verification created with both addresses is not found by either one alone.

</Description>

<FieldChildren kind="response">

<Field name="email_address" type="string" prefix="verification.to.">

<Description>

The recipient's email address. Case does not matter; the address is lowercased before use.

</Description>

</Field>

<Field name="phone_number" type="string" prefix="verification.to.">

<Description>

The recipient's phone number in E.164 format, with the leading `+` and country code (for example `+15551234567`). A number in any other format is rejected as an invalid recipient (`422`).

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="channels" type="array of object" prefix="verification." required>

<Description>

The channels this verification uses to deliver the passcode, in attempt order: the first entry is tried first and later entries are fallbacks. An email recipient is verified over email; a phone recipient is verified over SMS.

</Description>

<FieldChildren kind="response">

<Field name="channel" type="string" prefix="verification.channels." required>

<Description>

The channel a passcode is delivered over. Open enum — new channels may be added over time, so treat any unrecognized value as a future channel rather than an error.

</Description>

<Description>

Possible values (may grow over time): `email`, `sms`, `whatsapp`

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="last_channel" type="nullable string" prefix="verification.">

<Description>

The channel the most recent passcode was sent on, or null before the first send. Open enum; new channels may be added over time, so treat any unrecognized value as a future channel rather than an error.

</Description>

<Description>

Possible values (may grow over time): `email`, `sms`, `whatsapp`

</Description>

</Field>

<Field name="metadata" type="object" prefix="verification.">

<Description>

The key/value pairs attached when the verification was created.

</Description>

</Field>

<Field name="expires_at" type="string" prefix="verification." required>

<Description>

When the verification expires if no correct passcode is submitted first. After this time its status reports `expired`.

</Description>

</Field>

<Field name="verified_at" type="nullable string" prefix="verification.">

<Description>

When the verification was completed, or null if it is not yet verified.

</Description>

</Field>

<Field name="created_at" type="string" prefix="verification." required />

<Field name="updated_at" type="string" prefix="verification." required />

</FieldChildren>

</Field>

<Field name="attempts_remaining" type="nullable integer">

<Description>

The number of check attempts left while the verification is still pending, or null once it has reached a final state.

</Description>

</Field>

</Payload>