Verification Attempt Lockout
Introduction
The verification attempt lockout is an opt-in, account-scoped safeguard that throttles a card after repeated hard verification failures. It applies across every network.
This is not the HIGHEST lockoutThis page covers the cross-network attempt lockout for the
LOW/MEDIUM/HIGHtiers. The
HIGHESTtier has its own, separate per-card lockout (the two-hold amount-confirm) — see
HIGHEST Verification. The two never cross-feed.
Enabling it
The lockout is off by default. Turn it on per subaccount with the failedAttemptLockout flag on verificationPolicy, using the Update Subaccount endpoint:
PATCH /subaccounts/{subaccountId} HTTP/1.1
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"verificationPolicy": {
"failedAttemptLockout": true
}
}curl -X PATCH https://api.astrada.co/subaccounts/{subaccountId} \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"verificationPolicy": {
"failedAttemptLockout": true
}
}'{
"verificationPolicy": {
"failedAttemptLockout": true
}
}- Failures are always recorded — the flag only controls enforcement. So you can flip it on
and it is immediately protective, with no warm-up window. - The policy is per subaccount; set it on each subaccount you want protected. Set
failedAttemptLockouttofalse(ornull) to turn it off.
How it works
The lockout is keyed to the card, account-scoped, and counts countable hard failures across all networks. There are two tiers:
- Temporary lock — 5 hard failures inside a 60-minute window block the card for
60 minutes, then it auto-clears. The window is fixed, not rolling: it starts at the first
failure and resets only once a failure arrives 60+ minutes after that start — it doesn't
continuously slide with each new failure. - Permanent lock — 15 cumulative hard failures (over any span of time) block the card with
no auto-expiry; it stays locked until you clear it.
What counts: hard declines (insufficient funds, stolen / lost / restricted card, contact-issuer), incorrect CVC, and 3DS authentication rejected by the issuer.
What doesn't: transient errors ("try again later"), cardholder-abandoned or canceled attempts, and the HIGHEST second-factor steps.
flowchart LR A[Active] -->|5 hard fails in 60 min| B[Temporary lock<br/>60 min] B -->|auto-expires| A B -->|POST /card-verifications/unlock| A A -->|15 cumulative hard fails| C[Permanent lock] C -->|POST /card-verifications/unlock| A
Error codes
When a locked card attempts verification, the request returns 400 with a verification.* errorCode — verification.attempts_locked (temporary; carries metadata.lockedUntil) or verification.attempts_locked_permanent (no lockedUntil). Full table (category, retryable, the cardholder-facing screen for each) in Error States & Remediation — that page is the canonical error catalog; this one owns the mechanism.
What the cardholder sees
The SDK renders both lockout screens for you — there's no UI to build. Each lock tier shows its own screen so the cardholder knows whether to wait or to get help.
Temporary lock — verification.attempts_locked | Permanent lock — verification.attempts_locked_permanent |
|---|---|
![]() | ![]() |
- Temporary — "Verification temporarily blocked." Tells the cardholder to wait and try again
later; the lock auto-clears atmetadata.lockedUntil. - Permanent — "Verification blocked." Directs the cardholder to contact their spend-management
provider (you); clear it withPOST /card-verifications/unlockbelow.
Unlocking a card
Clear a card's lockout — temporary or permanent — with the Unlock Card Verification endpoint:
POST /card-verifications/unlock HTTP/1.1
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"cardId": "00cdba2d-01f0-46bb-b34a-c76d9699e991"
}curl -X POST https://api.astrada.co/card-verifications/unlock \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cardId": "00cdba2d-01f0-46bb-b34a-c76d9699e991"
}'{
"cardId": "00cdba2d-01f0-46bb-b34a-c76d9699e991"
}Returns 200 with { "unlocked": true } (plus vaultCardFingerprint when a lock was actually cleared). It is idempotent — unlocking a card with no active lock succeeds and clears nothing.
Token scopeUnlocking requires
subaccounts:write— the same scope used to manage a subaccount's policy —
notcard-verifications:write(which creates verifications). A token without
subaccounts:writeis rejected with 403.
- The endpoint clears both the temporary and the permanent lock. (The temporary lock also clears
on its own once the 60-minute window passes.) - It doesn't touch the HIGHEST lockout — see the note at the top of this page.

