Get Verification Detail
A single endpoint that returns the full lifecycle of a verification request — channel-by-channel send attempts, the fallback chain’s append-only history, the frozen config snapshot that drove the chain, the live expiration countdown, and a masked code-attempt log. The endpoint powers the verification-request drawer in the dashboard. It is also the canonical surface for self-serve dispute-resolution: when an end user claims they “never received the OTP”, an operator can reconstruct exactly what was tried, when, on which channel, with which provider, and what error came back. EndpointGET /api/v1/verify/{id}/detailAuthorization: Bearer <token>) or API key (X-API-Key). Any authenticated caller for the tenant may read this endpoint — no additional scope is required.
Rate limit: 60 requests/minute per API key.
Path parameter
Verification id (prefixed, e.g.
vrf_3f1c0b2a8e4d4f7a9c2b1e6d5a4c3b2a). Cross-tenant ids return 404 (the tenant boundary is enforced by schema isolation; the controller cannot leak existence of another tenant’s verification).Response
200 OKField reference
Top-level
channelAttempts[] — per-channel summary
One row per channel touched. Latest outcome wins when the same channel was used twice (e.g. an explicit /resend).
fallbackChain[] — append-only history
One row per fallback engine tick. This is the full timeline, not just the latest. Useful for support flows that need to reconstruct exactly what happened.
fallbackConfigSnapshot
Frozen JSON snapshot of the fallback config that was active at send time. Null when the verification didn’t use a profile (direct send). Reading the snapshot is the only reliable way to know what config drove this specific verification (the underlying profile may have been edited or deleted since).
codeAttempts[] — masked code-attempt log
Projected from audit_logs where action='verification.checked' AND resource='verification' AND resource_id=:id AND organization_id=:orgId, limited to the most recent 50 entries.
Errors
Performance
P95 ≤ 200ms. The row read is a single PK lookup on the tenant schema; theaudit_logs join is bounded to 50 rows and uses the (organization_id, resource, resource_id) composite index.
Edge-case handling
- Empty history. Just-sent verifications return
channelAttempts: []andfallbackChain: []. The drawer renders an empty-state copy. - Deleted profile.
profileName: nullwithprofileIdstill populated. The snapshot still hydrates the dashboard rendering. - Expired row.
expirationCountdownSec: 0— the FE renders the “expired” badge unconditionally. - No audit rows.
codeAttempts: []. Older verifications recorded before theverification.checkedaudit action shipped may legitimately have no entries.