AssureDeveloper Portal
Back to the developer portal

Assure verification API

Assure verifies a person over the channel they already use — interactive email, Apple Messages for Business, RCS or a passkey approval — and hands your backend one single-use result. Every call is plain HTTPS with a workspace API key; there is no vendor SDK to install.

  1. Your backend starts a verification request.
  2. Assure reaches the person and records what actually happened.
  3. Your backend spends the bound result once, then completes its own action once.
Base addresses
Every path below is relative to these roots.

Sandbox: https://app.assure.chat/api/public/v1

Production: https://app.assure.chat/api/public/v1

Not available yet
Accepted in shape, refused honestly.

Passkey step-up — The hosted authentication domain is not live yet.

Text-message delivery — The Twilio route is not activated in this environment.

Signed result webhooks — Signed customer callbacks arrive with the webhook stage.

Sandbox connectors, sends and events are simulations until real services are connected.

Recent changes

5 October 2026

Authentication and safety rules

Send your workspace key as Authorization: Bearer … from your own server. A key must never reach a browser. Each key carries only the permissions you grant it.

Permission scopes

challenges:write — Create, verify, resend and cancel verification requests

results:read — Read request state and whether a result is outstanding

results:consume — Spend a verification result once

connections:manage — Manage provider connections for this workspace

notifications:send — Deliver externally generated phone notifications from an identity provider

notifications:read — Read masked delivery status of external notifications

Idempotency and single use
  • Calls that create, send or spend take an `Idempotency-Key` header: creating a request or code, resend, the three consume calls, action completions and phone notifications. Verify, cancel and report do not. A repeat with the same key returns the first stored response.
  • Reusing a key with a different request body is refused with 409 `idempotency_conflict`; nothing new is created.
  • A result is single-use and bound to the application, subject, session, transaction and, for action approvals, the intent it was issued for. A call that does not match is refused.
  • Spending is atomic: the first consume wins. A later attempt without the original key returns 409 `conflict` with the original receipt; a repeat with the same key returns the receipt with `replayed: true`. Neither authorises anything again.
  • Responses never return a code, result token or other secret value a second time.
Signing keys
https://app.assure.chat/api/public/oauth/v1/jwks
  • Sign-in token signing keys (this JWKS) have no scheduled rotation today: one key per environment is published and stays until replaced.
  • Always resolve the key by the token's `kid`. Never pin a single key by value, so a future rotation needs no change on your side.
  • Refetch the JWKS when you see an unknown `kid` before rejecting a token.
  • Webhook signing secrets are a separate key system: after a rotation the previous secret stays valid for 24 hours.

Start a verification

Ask Assure to reach the person. A code is never in the response.

POST
/api/public/v1/otp/email
Start an email code
Queues an email carrying a one-time code. The code is never in the response.
scope: challenges:write
Idempotency-Key required

Example request body

{
  "email": "person@example.com",
  "subject_ref": "user_1042",
  "purpose": "email_confirmation",
  "application_ref": "checkout",
  "transaction_ref": "order_5567"
}

Response fields

request_id · status · expires_at · delivery_status · channel · destination_hint · simulated

  • Email confirmation proves control of an address. It is not multi-factor authentication.
POST
/api/public/v1/otp/messaging
Start a phone code
Apple Messages for Business first, RCS as the bounded fallback.
scope: challenges:write
Idempotency-Key required

Example request body

{
  "phone": "+15555550123",
  "subject_ref": "user_1042",
  "purpose": "phone_verification",
  "method_preference": "apple_first"
}

Response fields

request_id · status · expires_at · delivery_status · channel · destination_hint · simulated

  • rcs_only skips Apple Messages and exists for testing and demos.
  • With purpose step_up the destination comes from the subject's confirmed contact on the server; a phone in the body is ignored.
  • method_preference passkey is accepted and refused with not_available_yet.
POST
/api/public/v1/verification-requests
Create a verification request
One entry point for either channel when your backend already holds the policy decision.
scope: challenges:write
Idempotency-Key required

Example request body

{
  "channel": "email",
  "purpose": "email_confirmation",
  "subject_ref": "user_1042",
  "email": "person@example.com"
}

Response fields

request_id · status · expires_at · delivery_status · channel · destination_hint · simulated

  • channel sms and webhook_url are accepted and refused with not_available_yet.

Follow and control a request

Read state, submit an answer, resend, cancel, or record a report.

GET
/api/public/v1/verification-requests/{requestId}
Read a request
Safe to poll: never creates a code, extends a deadline or triggers a send.
scope: results:read
Safe to poll

Response fields

status · purpose · expires_at · verified_at · consumed_at · channel · delivery_status · destination_hint · result_available · simulated · flagged_for_review · invitation_surface · invitation_path

POST
/api/public/v1/verification-requests/{requestId}/verify
Submit the code
A correct code returns the single-use result token once, here only.
scope: challenges:write

Example request body

{
  "code": "123456"
}

Response fields

request_id · status · result_token · result_expires_at · already_verified · idempotent (deprecated)

  • A wrong code returns the same neutral refusal whatever the reason, with the remaining attempts.
  • Call this from your backend. Never place a server key in a browser or in email markup.
POST
/api/public/v1/verification-requests/{requestId}/resend
Send the code again
Reuses the active code under the request's cooldown and resend cap.
scope: challenges:write
Idempotency-Key required

Response fields

request_id · status · expires_at · delivery_status · channel

POST
/api/public/v1/verification-requests/{requestId}/cancel
Cancel a request
The live code dies with the request; a finished request is left untouched.
scope: challenges:write

Response fields

request_id · status

POST
/api/public/v1/verification-requests/{requestId}/report
Report a request the person did not make
Same path as the Not me action in a message: an active request is canceled; a finished one keeps its result and the report is recorded.
scope: challenges:write

Response fields

request_id · report_kind · canceled · duplicate

  • Never locks an account, removes a passkey or reverses an action you already took.

Results

Read the outcome, then spend the single grant exactly once from your backend.

GET
/api/public/v1/verification-requests/{requestId}/result
Check the result state
Reports whether a single-use result is outstanding. It cannot hand back the token.
scope: results:read
Safe to poll

Response fields

status · purpose · verified_at · consumed_at · result_available · simulated

POST
/api/public/v1/results/{token}/consume
Deprecated
Spend the result
Tenant, application, subject, session and transaction must match the grant. Once only.

Puts the token in the URL. Use POST /results/consume, which takes it in the body.

scope: results:consume
Idempotency-Key required

Example request body

{
  "subject_ref": "user_1042",
  "transaction_ref": "order_5567"
}

Response fields

request_id · subject_ref · status · claims · receipt · replayed

  • The claims describe the evidence collected. There is no unconditional MFA flag.
  • claims always has purpose, method and simulated. Passkey results may also have user_verified and auth_time. Other keys may appear.
  • receipt always has all ten keys (receipt_ref, step_kind, method_evidence, policy_id, policy_version, requested_assurance, evaluated_assurance, assurance_reason_codes, verified_at, consumed_at); some values may be null.
  • method_evidence is {} when nothing was recorded. Otherwise it holds observed_method, user_verified, user_present and proof_provenance, and may hold issuer. For user_verified and user_present, null means not observed and false means observed false.
POST
/api/public/v1/verification-requests/{requestId}/result/consume
Spend the result by naming the request
For an approval that finished in the person's browser. The identifier is a locator, not a credential, and it spends the same single grant as the token call.
scope: results:consume
Idempotency-Key required

Example request body

{
  "subject_ref": "user_1042",
  "transaction_ref": "order_5567"
}

Response fields

request_id · subject_ref · status · claims · receipt · replayed

  • The browser never holds this authorisation; your backend calls it with its own key.
  • A second attempt is refused and returns the first receipt, so nothing is ever authorised twice.
  • A passkey that has since been removed is refused, replay included.
  • A second attempt returns 409 conflict with the original receipt next to the error.
POST
/api/public/v1/results/consume
Spend the result (token in the body)
Preferred consume call. The token travels in the body, so it never appears in a URL or log. Tenant, application, subject, session and transaction must match.
scope: results:consume
Idempotency-Key required

Example request body

{
  "result_token": "RESULT_TOKEN",
  "subject_ref": "user_1042",
  "transaction_ref": "order_5567"
}

Response fields

request_id · subject_ref · status · claims · receipt · replayed

  • Repeating the same Idempotency-Key returns the first receipt again with replayed: true; nothing is spent twice.

Completed actions

Tell Assure that your own business action finished, linked to the result you spent.

POST
/api/public/v1/actions/completions
Report that the protected action finished
Records that your own action finished, linked to a result this workspace consumed in the last 24 hours. Assure makes no claim beyond the report.
scope: results:consume
Idempotency-Key required

Example request body

{
  "receipt_ref": "rcp_8f2a91c04d",
  "action_ref": "order_5567",
  "outcome": "succeeded",
  "customer_event_id": "evt_10442",
  "occurred_at": "2026-10-05T02:41:00Z"
}

Response fields

report_id · recorded · duplicate · notice_queued · provenance

  • Send exactly one of result_token or receipt_ref. A receipt_ref looks like rcp_ followed by 8–64 letters, digits, - or _.
  • outcome is succeeded, failed, canceled or pending.
  • Only succeeded may cause a completion notice. The same Idempotency-Key with a different body is refused with 409.

Phone notifications (identity providers)

Delivery only. Your identity provider (for example Auth0) owns the code and the sign-in result; Assure only delivers the message.

POST
/api/public/v1/notifications/phone
Deliver an identity provider phone code
Delivery only, for identity providers such as the Auth0 Custom Phone Provider. The provider creates and checks the code; Assure never verifies it or issues a result.
scope: notifications:send
Idempotency-Key required

Response fields

notification_id · profile_ref · environment · status · simulated · replayed · created_at · updated_at · delivery_deadline · destination_hint · delivery_channel

  • Text only: voice returns 422 unsupported_delivery_method.
  • Production sending is still simulated, so a production live-only request returns 503 live_delivery_unavailable.
GET
/api/public/v1/notifications/phone/{notificationId}
Read a phone notification's delivery status
Masked delivery status. Delivery is never an authentication result.
scope: notifications:read
Safe to poll

Response fields

notification_id · profile_ref · environment · status · simulated · replayed · created_at · updated_at · delivery_deadline · destination_hint · delivery_channel · delivered_at · failure

Sign-in routes

Used by Apple Messages sign-in and passkey approval, not called from your backend directly. Listed for completeness; see the full specification for details.

GET
/oauth/authorizeOpen the Apple Messages sign-in session
POST
/oauth/authorizeContinue a sign-in session from the refusal or start page
GET
/oauth/cancelShow the stop page for one sign-in session
POST
/oauth/cancelStop one sign-in session
GET
/oauth/v1/authorizeStart an authorization for a registered application
POST
/oauth/v1/tokenExchange an authorization code, or renew access
POST
/oauth/v1/revokeRevoke a renewal token
POST
/oauth/v1/introspectInspect a renewal token
GET
/oauth/v1/jwksPublic signing keys
GET
/oauth/v1/discoveryDiscovery metadata for Assure's own OAuth service

Errors

Errors are neutral by design: nothing in a response reveals whether an account, address or number exists. Every endpoint uses the same body.

Error body
{
  "error": {
    "code": "verification_failed",
    "message": "That code is not valid. Request a new one if you need to.",
    "trace_id": "req_9f2c1a77d0e34b8a5c61",
    "retryable": false,
    "remaining_attempts": 2
  }
}
Reading it
  • trace_id identifies this call. Quote it to support.
  • retryable is true only for rate_limited, unavailable and destination_paused.
  • A rejected code (422 verification_failed) may include remaining_attempts in the same object.
  • Spending an already-used result by request returns 409 conflict with the original receipt next to error. It never authorises anything again.
  • New codes may be added. Treat an unknown code as a non-retryable failure.
CodeStatusMeaning
invalid_request400The body or a field is not acceptable.
unauthorized401Missing, unknown or revoked API key.
forbidden403Scope denied, or the record belongs to another caller.
destination_paused429New codes to this number are paused for 15 minutes after its owner tapped Not you?. Retry after the Retry-After header.
not_found404No such request, or no confirmed contact for the subject.
conflict409Idempotency or state conflict, or an attempt limit.
expired410The request, code or result has expired.
verification_failed422The code was not accepted. The wording is neutral.
rate_limited429Too many requests. Honour Retry-After.
unavailable503Temporarily unavailable. Retryable.
not_available_yet503Accepted in shape, but this environment cannot serve it.
intent_mismatch409The action you are spending the result for is not the one the person approved.
summary_missing400An action approval needs its masked summary.
idempotency_conflict
notifications
409The same Idempotency-Key was sent with a different body.
unsupported_delivery_method
notifications
422Only text delivery is supported; voice is refused.
no_eligible_channel
notifications
422No channel in the profile can reach this number.
content_not_supported
notifications
422The message content cannot be sent on the available channels.
live_delivery_unavailable
notifications
503Live sending is not enabled here. Not retryable.
undeliverable
notifications
—Appears only in a notification's failure field: the provider reported it could not deliver the message.

Webhooks into Assure

Assure receives provider callbacks on its own endpoints. Each signature is verified over the exact bytes received before anything is parsed, and delivery, seen, click and redirect events never establish a successful authentication.

SendGrid
/api/public/webhooks/sendgrid

Signed event payload, verified over the raw bytes before anything is parsed.

Delivery, open and click events describe the message, never the person.

Apple Messages for Business (1440)
/api/public/webhooks/amb1440/{configId}

Per-connection signed callback, verified over the raw bytes.

Conversation control proves phone control only with documented number binding.

Infobip RCS
/api/public/webhooks/infobip/{configId}

HMAC-SHA256 over the raw bytes using the per-connection secret, on the configured header (X-Hub-Signature by default).

A missing, invalid or ambiguous signature is refused. There is no alternate-header fallback.

Meta WhatsApp Business (WhatsApp connector events; not a verification channel)
/api/public/webhooks/meta/whatsapp

X-Hub-Signature-256 over the raw bytes, plus the subscription challenge on setup.

Events from an unknown business account are quarantined, not processed.

Webhooks Assure sends to you

An event only announces that something happened. It never carries a code, a result token or a full destination, and it never authorises anything: your backend still spends the bound result through the API. Schema version 1, timestamps accepted within 300 seconds.

Delivery headers

Assure-Webhook-Id — unique delivery identifier, for deduplication

Assure-Webhook-Timestamp — when it was signed

Assure-Webhook-Signature — signature over the raw body

Assure-Webhook-Key-Id — which signing secret was used

How to handle an event
  • Verify the signature over the exact bytes you received, before parsing.
  • Refuse a timestamp more than 300 seconds old, so an old delivery cannot be replayed.
  • Store Assure-Webhook-Id and ignore an identifier you have already handled: a retry is normal.
  • Treat the event as an announcement only. Spend the result through the API before you act.
  • Every event is saved in the same step as the change it announces, so a change never happens without its event.
  • If an event never arrives, ask Assure for the request's state instead of waiting. Nothing is lost.
  • During a secret rotation, accept either key until the old one is retired.
verification.created
Verification created
A verification request was accepted and is now in progress.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Nothing yet.

verification.delivered
Message delivered
A provider reported the message reached the destination. Proves nothing about the person.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Nothing. Delivery, opening and clicks never mean the person answered.

verification.verified
Verification succeeded
Every required step is satisfied. Read the single-use result through the API to act on it.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Spend the bound result through the API, then complete your own action once.

verification.failed
Verification failed
The request ended without success: the person declined it (the request is also flagged for review) or it ran out of code attempts.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Nothing.

verification.expired
Verification expired
The request passed its deadline before it was satisfied.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Nothing.

verification.canceled
Verification canceled
The request was canceled before it finished.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Stop waiting.

verification.denied
Person reported they did not request it
The person said the request was not theirs, so it was canceled and nothing can satisfy it. It does not identify them and does not lock anything.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Stop waiting. The person said it was not theirs; it identifies nobody and locks nothing.

verification.security_reported
Unexpected activity reported
The person reported the activity after the verification had already finished. The result stands and no business action was stopped or reversed.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Review. The earlier result still stands and nothing was reversed for you.

verification.consumed
Result consumed
The single-use result was read. It cannot be read again.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per verification request. Arrival order is not guaranteed.

What to do: Reconcile. The result cannot be spent again.

credential.enrolled
Passkey added
A passkey was committed to an account. It records that a credential now exists; it makes no claim about the device or about any biometric.

Data fields (CredentialEventData)

Data for credential.enrolled, credential.revoked and passkey.enrolled_during_sign_in. These events carry no request, so the request fields are null; the account and passkey are identified from the event itself. Derived from the emitter code only (no stored examples).

  • request_id: null
  • subject_ref: string | null — Your reference for the account. Set on credential.enrolled and credential.revoked; null on passkey.enrolled_during_sign_in.
  • subject_hint (optional): string | null — Masked account reference. passkey.enrolled_during_sign_in only.
  • credential_reference (optional): string | null — Last six characters of the passkey's credential ID. passkey.enrolled_during_sign_in only.
  • operation (optional): string | null (known values, may grow: first_credential, add_credential, recovery_reenrollment) — credential.enrolled only. What kind of enrollment this was.
  • bootstrap_strength (optional): string | null (known values, may grow: phishing_resistant, possession_of_channel, operator_authorized, unverified) — credential.enrolled only. The evidence that authorized the enrollment.
  • transaction_ref: null
  • purpose: null
  • status (optional): string | null
  • channel (optional): string | null
  • delivery_channel (optional): string | null
  • authentication_method (optional): string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean

Ordering: Rises per subject. It is not tied to any one verification request.

What to do: Record that a passkey exists. It claims nothing about the device or a biometric.

passkey.enrolled_during_sign_in
Passkey created during sign-in
A person created their first passkey while answering a sign-in request. It records that a passkey now exists; it does not mean the request was approved.

Data fields (CredentialEventData)

Data for credential.enrolled, credential.revoked and passkey.enrolled_during_sign_in. These events carry no request, so the request fields are null; the account and passkey are identified from the event itself. Derived from the emitter code only (no stored examples).

  • request_id: null
  • subject_ref: string | null — Your reference for the account. Set on credential.enrolled and credential.revoked; null on passkey.enrolled_during_sign_in.
  • subject_hint (optional): string | null — Masked account reference. passkey.enrolled_during_sign_in only.
  • credential_reference (optional): string | null — Last six characters of the passkey's credential ID. passkey.enrolled_during_sign_in only.
  • operation (optional): string | null (known values, may grow: first_credential, add_credential, recovery_reenrollment) — credential.enrolled only. What kind of enrollment this was.
  • bootstrap_strength (optional): string | null (known values, may grow: phishing_resistant, possession_of_channel, operator_authorized, unverified) — credential.enrolled only. The evidence that authorized the enrollment.
  • transaction_ref: null
  • purpose: null
  • status (optional): string | null
  • channel (optional): string | null
  • delivery_channel (optional): string | null
  • authentication_method (optional): string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean

Ordering: Rises per subject. It is not tied to any one verification request.

What to do: Record that a passkey now exists. It is not an approval: the request still has to be answered with that passkey.

credential.revoked
Passkey removed
A passkey was removed and can no longer authenticate. Nothing that it previously proved is changed or reversed.

Data fields (CredentialEventData)

Data for credential.enrolled, credential.revoked and passkey.enrolled_during_sign_in. These events carry no request, so the request fields are null; the account and passkey are identified from the event itself. Derived from the emitter code only (no stored examples).

  • request_id: null
  • subject_ref: string | null — Your reference for the account. Set on credential.enrolled and credential.revoked; null on passkey.enrolled_during_sign_in.
  • subject_hint (optional): string | null — Masked account reference. passkey.enrolled_during_sign_in only.
  • credential_reference (optional): string | null — Last six characters of the passkey's credential ID. passkey.enrolled_during_sign_in only.
  • operation (optional): string | null (known values, may grow: first_credential, add_credential, recovery_reenrollment) — credential.enrolled only. What kind of enrollment this was.
  • bootstrap_strength (optional): string | null (known values, may grow: phishing_resistant, possession_of_channel, operator_authorized, unverified) — credential.enrolled only. The evidence that authorized the enrollment.
  • transaction_ref: null
  • purpose: null
  • status (optional): string | null
  • channel (optional): string | null
  • delivery_channel (optional): string | null
  • authentication_method (optional): string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean

Ordering: Rises per subject. It is not tied to any one verification request.

What to do: Stop treating that passkey as usable. Nothing it already proved changes.

consent.revoked
Consent withdrawn
A person withdrew consent for a destination.

Data fields (VerificationEventData)

Data for verification.* and consent.revoked. Every emitter of these types attaches a request, so the request fields are always present and set. Stored examples exist for created, verified, expired, consumed, canceled and denied; delivered, failed, security_reported and consent.revoked are derived from the emitter code only (no stored examples).

  • request_id: string
  • subject_ref: string — Your own reference for the person.
  • transaction_ref: string | null
  • purpose: string (known values, may grow: phone_verification, email_confirmation, email_phone_enrollment, step_up, password_recovery, account_password_reset, shipping_address_change)
  • status: string — The request's state when the event was published, not necessarily the state named by the event (for example a late-published verification.created can carry verified). Use the event type to know what happened. A future version will make status match the event; until then, do not branch on status alone.
  • channel: string | null
  • delivery_channel: string | null
  • authentication_method: string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean
  • reason (optional): string | null (known values, may grow: amb_declined_by_subject, attempts_exhausted) — Why a verification.failed request ended. Null on every other verification and consent event.

Ordering: Rises per destination, not per request. A consent event may belong to no verification at all, so never order it against one.

What to do: Stop sending to that destination.

notification.accepted
Phone notification accepted
Assure accepted a delivery-only phone notification (for example from Auth0). It is not a verification and proves nothing about the person.

Data fields (NotificationEventData)

Data for notification.* events. These have no verification request by design. The request fields are still emitted, always null; do not read them.

  • request_id: null — Always null on notification events and will be removed in a future version.
  • subject_ref: null — Always null on notification events and will be removed in a future version.
  • transaction_ref: null — Always null on notification events and will be removed in a future version.
  • purpose: null — Always null on notification events and will be removed in a future version.
  • status: string (known values, may grow: accepted, sent, delivered, failed)
  • channel (optional): string | null
  • delivery_channel (optional): string | null
  • authentication_method (optional): string | null
  • notification: object (NotificationDetail) — Present only on notification.* events that carry a payload, so it may be absent even on those. Delivery facts only; never an authentication result.
  • notification.notification_id (optional): string — The notification's id. An empty string when the source omitted it.
  • notification.destination_hint (optional): string | null — Masked destination. Never the full number.
  • notification.delivery_channel (optional): string | null (known values, may grow: amb, rcs) — Known values, not exhaustive: amb, rcs. These are short provider names and differ from NotificationReceipt.delivery_channel's spellings (e.g. apple_messages_for_business), so do not compare the two directly. Null on accepted events.
  • notification.status (optional): string (known values, may grow: accepted, sent, delivered, failed) — Known values, not exhaustive: accepted, sent, delivered, failed. Falls back to the event-name suffix, so it is not NotificationReceipt.status.
  • notification.failure_code (optional): string | null (known values, may grow: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable) — Known values, not exhaustive: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable.
  • notification.occurred_at (optional): string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean

Ordering: 1 accepted, 2 sent, 3 delivered or failed, per notification. Arrival order is not guaranteed.

What to do: Nothing. Auth0 (or your IdP) still owns the code and the sign-in result.

notification.sent
Phone notification sent
A provider accepted the phone notification for delivery.

Data fields (NotificationEventData)

Data for notification.* events. These have no verification request by design. The request fields are still emitted, always null; do not read them.

  • request_id: null — Always null on notification events and will be removed in a future version.
  • subject_ref: null — Always null on notification events and will be removed in a future version.
  • transaction_ref: null — Always null on notification events and will be removed in a future version.
  • purpose: null — Always null on notification events and will be removed in a future version.
  • status: string (known values, may grow: accepted, sent, delivered, failed)
  • channel (optional): string | null
  • delivery_channel (optional): string | null
  • authentication_method (optional): string | null
  • notification: object (NotificationDetail) — Present only on notification.* events that carry a payload, so it may be absent even on those. Delivery facts only; never an authentication result.
  • notification.notification_id (optional): string — The notification's id. An empty string when the source omitted it.
  • notification.destination_hint (optional): string | null — Masked destination. Never the full number.
  • notification.delivery_channel (optional): string | null (known values, may grow: amb, rcs) — Known values, not exhaustive: amb, rcs. These are short provider names and differ from NotificationReceipt.delivery_channel's spellings (e.g. apple_messages_for_business), so do not compare the two directly. Null on accepted events.
  • notification.status (optional): string (known values, may grow: accepted, sent, delivered, failed) — Known values, not exhaustive: accepted, sent, delivered, failed. Falls back to the event-name suffix, so it is not NotificationReceipt.status.
  • notification.failure_code (optional): string | null (known values, may grow: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable) — Known values, not exhaustive: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable.
  • notification.occurred_at (optional): string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean

Ordering: 1 accepted, 2 sent, 3 delivered or failed, per notification. Arrival order is not guaranteed.

What to do: Nothing. A provider accepting the message is not delivery.

notification.delivered
Phone notification delivered
An authenticated provider report says the message reached the phone. It does not mean anyone read it or signed in.

Data fields (NotificationEventData)

Data for notification.* events. These have no verification request by design. The request fields are still emitted, always null; do not read them.

  • request_id: null — Always null on notification events and will be removed in a future version.
  • subject_ref: null — Always null on notification events and will be removed in a future version.
  • transaction_ref: null — Always null on notification events and will be removed in a future version.
  • purpose: null — Always null on notification events and will be removed in a future version.
  • status: string (known values, may grow: accepted, sent, delivered, failed)
  • channel (optional): string | null
  • delivery_channel (optional): string | null
  • authentication_method (optional): string | null
  • notification: object (NotificationDetail) — Present only on notification.* events that carry a payload, so it may be absent even on those. Delivery facts only; never an authentication result.
  • notification.notification_id (optional): string — The notification's id. An empty string when the source omitted it.
  • notification.destination_hint (optional): string | null — Masked destination. Never the full number.
  • notification.delivery_channel (optional): string | null (known values, may grow: amb, rcs) — Known values, not exhaustive: amb, rcs. These are short provider names and differ from NotificationReceipt.delivery_channel's spellings (e.g. apple_messages_for_business), so do not compare the two directly. Null on accepted events.
  • notification.status (optional): string (known values, may grow: accepted, sent, delivered, failed) — Known values, not exhaustive: accepted, sent, delivered, failed. Falls back to the event-name suffix, so it is not NotificationReceipt.status.
  • notification.failure_code (optional): string | null (known values, may grow: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable) — Known values, not exhaustive: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable.
  • notification.occurred_at (optional): string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean

Ordering: 1 accepted, 2 sent, 3 delivered or failed, per notification. Arrival order is not guaranteed.

What to do: Nothing. Delivery never means the person read the message or signed in.

notification.failed
Phone notification failed
The notification ended without delivery: refused, undeliverable, expired, or an unclear provider outcome that Assure did not resend.

Data fields (NotificationEventData)

Data for notification.* events. These have no verification request by design. The request fields are still emitted, always null; do not read them.

  • request_id: null — Always null on notification events and will be removed in a future version.
  • subject_ref: null — Always null on notification events and will be removed in a future version.
  • transaction_ref: null — Always null on notification events and will be removed in a future version.
  • purpose: null — Always null on notification events and will be removed in a future version.
  • status: string (known values, may grow: accepted, sent, delivered, failed)
  • channel (optional): string | null
  • delivery_channel (optional): string | null
  • authentication_method (optional): string | null
  • notification: object (NotificationDetail) — Present only on notification.* events that carry a payload, so it may be absent even on those. Delivery facts only; never an authentication result.
  • notification.notification_id (optional): string — The notification's id. An empty string when the source omitted it.
  • notification.destination_hint (optional): string | null — Masked destination. Never the full number.
  • notification.delivery_channel (optional): string | null (known values, may grow: amb, rcs) — Known values, not exhaustive: amb, rcs. These are short provider names and differ from NotificationReceipt.delivery_channel's spellings (e.g. apple_messages_for_business), so do not compare the two directly. Null on accepted events.
  • notification.status (optional): string (known values, may grow: accepted, sent, delivered, failed) — Known values, not exhaustive: accepted, sent, delivered, failed. Falls back to the event-name suffix, so it is not NotificationReceipt.status.
  • notification.failure_code (optional): string | null (known values, may grow: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable) — Known values, not exhaustive: expired, no_eligible_channel, unavailable, ambiguous_provider_outcome, undeliverable.
  • notification.occurred_at (optional): string | null
  • signals: object (EventSignals) — Indicators about the exchange, copied onto an event when present. An event authorises nothing, so these are signals, not claims; only a consumed result (ResultClaims) is a statement you act on. Today the only signal is reason_code, so expect {} or a reason_code alone. New keys may be added.
  • assurance: object (EventAssurance) — The request's assurance decision as stored. Both keys are always emitted; with no request, decision is null and reason_codes is empty.
  • simulated: boolean

Ordering: 1 accepted, 2 sent, 3 delivered or failed, per notification. Arrival order is not guaranteed.

What to do: Optional: let your IdP offer another method. Never treat it as a sign-in failure by itself.

Full specification

docs/assure-verification-api.yaml

The JSON is converted from the document below as you download it, so the two can never disagree.

Create a free developer accountSign inVisit assure.chat