Verification Risk Tiers
Introduction
When a cardholder links a card, Astrada verifies that they own it. You can choose how strict that verification is per subaccount, using a risk tier. A higher tier adds stronger proof of ownership at the cost of more cardholder interaction; a lower tier optimizes for conversion.
This page explains the four tiers, how to set one, and how the tier changes the enrollment experience. For the strictest tier's two-hold flow see HIGHEST Verification; to test against sandbox cards see Test Cards & Sandbox Testing.
The tiers
| Tier | Strength | Cardholder experience | Who can set it |
|---|---|---|---|
LOW | Lenient | 3DS only when the issuer or regulation requires it; some issuer-unsupported declines are tolerated | Astrada (internal) |
MEDIUM | Standard | 3DS is always requested; cardholder may be challenged | You or Astrada |
HIGH | Strong | 3DS is always requested plus a transient authorization hold (no settled charge) | You or Astrada |
HIGHEST | Strongest | 3DS challenge; if the issuer approves without challenging, the cardholder confirms two small temporary holds | You or Astrada |
New subaccounts have no tier set, whether newly created or long-standing — there's no special case for new vs. existing. Verification for them runs the default, non-Stripe path (see "Account has no tier set" under Routing, below, and the "Default (no tier)" column in the validation table further down) — not MEDIUM. MEDIUM is a tier you opt into explicitly via PATCH, same as HIGH and HIGHEST. See Account & Subaccount Configuration for the full precedence rules, including what carries over from the account level. LOW is internal-only — it is a floor that Astrada manages; you can always raise a subaccount to a stricter tier, but you cannot self-assign LOW.
How each tier behaves
In every tier, the card's CVC must pass.
LOW— Astrada asks the network to run 3DS only when risk or regulation (for example EU SCA)
calls for it; for many cards 3DS is skipped. If the issuer declines for a "soft" reason such as
3DS not supported, the enrollment is still allowed; hard-decline reasons (lost/stolen/fraud,
expired, insufficient funds, and similar) always fail.MEDIUM— 3DS is always engaged and the issuer is asked to challenge the cardholder. There is
no soft-decline tolerance: a declined verification surfaces a specific reason to the cardholder.HIGH— same 3DS posture as MEDIUM, plus a transient authorization hold on the card
(requested at $0, retried at $1 if the issuer requires a non-zero amount). The hold is released
automatically — the cardholder never sees a settled charge — and Astrada gains additional
fraud-risk signals from the authorization.HIGHEST— Astrada attempts a 3DS challenge. If the issuer challenges the cardholder and
they pass it, that's sufficient and enrollment completes; if they fail it, enrollment fails outright
(same as HIGH — no second factor). If the issuer approves frictionlessly, or 3DS doesn't run at
all (the issuer or network can't support a challenge for this card), the cardholder must complete a
second factor instead: confirm two small temporary holds ($0.50–$0.99 each) read from their
banking app. See HIGHEST Verification.
3DS is a preference, not a guarantee. For MEDIUM/HIGH/HIGHEST, Astrada asks the issuer to
challenge, but the issuer decides the final authentication flow and may still approve
frictionlessly. The verification response reports what actually happened viaauthenticationFlow
(see below).
Card networks
The tier you set applies to both Visa and Mastercard, but the verification path differs by network:
- Visa — all tiers (
LOW/MEDIUM/HIGH/HIGHEST) run through Astrada's verification path. - Mastercard — 3DS always runs through Astrada's Mastercard network path at every tier. The
two-hold second factor is engaged only atHIGHEST;
LOW/MEDIUM/HIGHbehave as standard Mastercard 3DS.
How verification runs
For MEDIUM/HIGH/LOW the cardholder either sails through (frictionless) or completes a bank challenge — the SDK performs every step below for you; the diagram shows what happens under the hood. (HIGHEST adds a second factor — see HIGHEST Verification.)
sequenceDiagram
autonumber
participant Browser as Cardholder browser
participant Backend as Your app (via the SDK)
participant Astrada as Astrada API
participant SDK as Astrada SDK
participant ACS as Issuer (ACS)
Browser->>Backend: Cardholder submits card
Backend->>Astrada: Start verification
Astrada->>Astrada: Card + CVC checks, then a no-amount or small-hold card check (per tier)
alt Frictionless
Astrada-->>Backend: Verified, no challenge needed
Backend-->>Browser: Success
else 3DS challenge required
Astrada-->>Backend: Challenge required
Browser->>SDK: Run 3DS challenge
SDK->>ACS: Render bank challenge
ACS-->>SDK: Cardholder completes / fails
Browser->>Backend: Notify result
Backend->>Astrada: Confirm challenge result
Astrada-->>Backend: Verified or failed
Backend-->>Browser: Final outcome
end
Which path runs the verification depends on the network and the tier:
flowchart TD
A[Verification on a subaccount] --> B{Subaccount tier set?}
B -- no --> D[Default verification path<br/>per network]
B -- yes --> C{Network}
C -- Visa --> E[Tiered verification path]
C -- Mastercard --> F{Tier = HIGHEST?}
F -- yes --> G[Network 3DS + two-hold second factor]
F -- no --> H[Network 3DS only]
What each path validates
Every tier runs explicit card-existence, card-status, and CVC checks on each attempt — at every tier, including LOW — alongside the issuer's 3DS decision, rather than inferring them from it.
| Validation | Default (no tier) | LOW | MEDIUM | HIGH |
|---|---|---|---|---|
| Card exists / active | from 3DS response | explicit | explicit | explicit |
| CVC matches | inferred | strict | strict | strict |
| 3DS authentication | required (+ bypass rules) | automatic (engaged only when SCA/risk warrant) | challenge preferred | challenge preferred |
| Authorization hold | none | none | none | $0 → $1 fallback, voided (never settles) |
| Fraud-risk signals | n/a | n/a | n/a | captured |
- The floor is identical for LOW/MEDIUM/HIGH: card existence, active status, and CVC are always
validated. LOW is "lenient" only in that it permits a few issuer-side signals (the
contact-issuer family, and "issuer can't do 3DS for this card") to bypass to success — it does
not skip the card check, and it never bypasses a CVC mismatch. - HIGH is incrementally stricter than MEDIUM: the auth hold surfaces extra decline signals
(insufficient funds, velocity) plus an additional fraud-risk layer.
Setting a subaccount's tier
Set the tier on a subaccount's verificationPolicy using the Update Subaccount endpoint:
PATCH /subaccounts/{subaccountId} HTTP/1.1
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"verificationPolicy": {
"stripeValidationLevel": "HIGH"
}
}curl -X PATCH https://api.astrada.co/subaccounts/{subaccountId} \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"verificationPolicy": {
"stripeValidationLevel": "HIGH"
}
}'{
"verificationPolicy": {
"stripeValidationLevel": "HIGH"
}
}- Accepts
MEDIUM,HIGH, orHIGHEST. RequestingLOWreturns 403 — contact Astrada to set a
LOWfloor. - Set
stripeValidationLeveltonullto clear the override and return to Astrada's default path. - The policy is per subaccount. There is no inheritance from the parent account at request
time — set the tier on each subaccount you want to change.
An optional safeguard sits alongside the tiers: with verificationPolicy.failedAttemptLockout enabled, a card that fails verification too many times is throttled across all networks until it clears (or you clear it). It applies to LOW/MEDIUM/HIGH (HIGHEST has its own per-card lockout). See Verification Attempt Lockout.
Every completed verification reports how 3DS resolved:
authenticationFlow | Meaning |
|---|---|
challenge | The issuer challenged the cardholder (they interacted). |
frictionless | The issuer approved without challenging the cardholder. |
null | 3DS did not run (for example, a LOW skip, or at HIGHEST when the issuer/network can't support a challenge for this card at all). |
The raw verification resource carries this (relevant if you query the API directly for support triage — the SDK's onSuccess payload is shaped differently; see Quick Start: Card Enrollment):
{ "id": "…", "state": "completed", "currentStepId": null, "authenticationFlow": "frictionless" }At HIGHEST, a successful challenge completes immediately. A failed challenge fails the verification outright — same as any other tier, it does not fall through to a second factor. A frictionless approval or a null flow (3DS didn't run at all — the issuer/network couldn't support a challenge) both route into the two-hold confirmation described in HIGHEST Verification instead.
What the cardholder sees
The happy paths and routing are below; every rejection and error state lives in Error States & Remediation. Unless noted, the screen is the same across LOW / MEDIUM / HIGH; HIGHEST adds the two-hold second factor covered in HIGHEST Verification.
Happy paths
Frictionless success — most enrollments
The bank approves without a challenge; the cardholder reaches success in a few seconds and sees no charge. Same UI at every tier (HIGH may briefly show a $0/$1 pending hold that never settles).
| Pre-submit | Result |
|---|---|
![]() | ![]() |
3DS challenge success
The bank asks for an extra check — an OTP, an "approve in your app" prompt, or a password. The challenge UI is rendered by the bank, so copy and method vary by issuer.
| Pre-submit | Result |
|---|---|
![]() | ![]() |
Rejections, errors & recovery
Every failure path — CVC mismatch, bank declines, the contact-issuer family, 3DS authentication failures, hard-fraud rejections, infrastructure errors, and mid-challenge recovery — plus the structured error contract (errorCode / category / retryable) and remediation for each, is catalogued in Error States & Remediation.
Routing
Account has no tier set → default flow
Most existing customers have no tier set; their cardholders get the same default verification flow as before — no new iframe, no new copy. Setting a tier is the only way onto the tiered verification path.
| Pre-submit | Result |
|---|---|
![]() | ![]() |
Glossary
- 3DS (3-D Secure) — the protocol issuers use to confirm the cardholder (OTP, in-app approval, or
password). The challenge UI is rendered by the issuer's ACS (Access Control Server), not by us. - SCA (Strong Customer Authentication) — a regulatory requirement (PSD2 in the EU); when it
applies, 3DS engages even at LOW. - No-amount card check — confirms the card without authorizing any amount (MEDIUM/LOW).
- Small-hold card check — a brief authorization hold to confirm the card (HIGH; $0, or $1 if the
issuer rejects $0). Canceled before any funds settle. - Permitted-exception record — a successful enrollment despite a soft issuer signal (the issuer
couldn't run 3DS, or raised a soft contact-issuer reason). Only LOW produces these — see "How each
tier behaves" above for what each tier tolerates.
Next steps
- Quick Start: Card Enrollment — the end-to-end on-ramp.
- HIGHEST Verification — the two-hold second-factor flow.
- Test Cards & Sandbox Testing — exercise each tier deterministically before going live.
- Error States & Remediation — every failure path and how to handle it.
- Single Card Enrollment — what the SDK does under the hood, conceptually.





