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 — Visa/Stripe and Mastercard/TNS share one ledger — so it can't be sidestepped by retrying on a different scheme. It protects issuers (and your account standing) from card-enumeration and brute-force abuse.

📘

This is not the HIGHEST lockout

This page covers the cross-network attempt lockout for the LOW/MEDIUM/HIGH tiers. The HIGHEST tier 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}
Authorization: Bearer <token with subaccounts:write>

{
  "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 failedAttemptLockout to false (or null) 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 lock5 hard failures inside a rolling 60-minute window block the card for 60 minutes, then it auto-clears.
  • Permanent lock15 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:

errorCodecategoryretryableHTTPCardholder sees (SDK)
verification.attempts_lockedverification-lockedno400"Verification temporarily blocked"
verification.attempts_locked_permanentverification-lockedno400"Verification blocked"
  • The temporary code carries metadata.lockedUntil (ISO-8601) — the moment the lock auto-clears.
  • The permanent code has no lockedUntil.

See Error States & Remediation for the full error catalog.

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_lockedPermanent lock — verification.attempts_locked_permanent
  • Temporary — "Verification temporarily blocked." Tells the cardholder to wait and try again later; the lock auto-clears at metadata.lockedUntil.
  • Permanent — "Verification blocked." Directs the cardholder to contact their spend-management provider (you); clear it with POST /card-verifications/unlock below.

Unlocking a card

Clear a card's lockout — temporary or permanent — with the Unlock Card Verification endpoint:

POST /card-verifications/unlock
Authorization: Bearer <token with subaccounts:write>

{
  "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 scope

Unlocking requires subaccounts:write — the same scope used to manage a subaccount's policy — not card-verifications:write (which creates verifications). A token without subaccounts:write is 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 clears only the cross-network attempt lockout — the HIGHEST per-card lockout is separate.
❗️

Two different lockouts

The attempt lockout (this page) is cross-network, opt-in via failedAttemptLockout, and you clear it with POST /card-verifications/unlock (subaccounts:write). The HIGHEST lockout (HIGHEST Verification) is the two-hold amount-confirm lockout (stripe.amount_confirm_locked), specific to the HIGHEST tier, and clears only by contacting Astrada.