Authentication flows
Every authentication starts the same way: the Directory Server sends the ACS an authentication request, and the ACS assesses risk. What happens next falls into one of three shapes.
Frictionless
The ACS approves without involving the cardholder. Your platform is never called — there is no /step_up, and nothing for you to implement.
This is the majority of traffic in a healthy portfolio, and it is the outcome to optimise for.
OTP challenge
The cardholder enters a one-time code. The ACS generates and validates the code; your platform only delivers it.
sequenceDiagram
participant DS as Directory Server
participant ACS as Cake ACS
participant You as Your platform
participant CH as Cardholder
DS->>ACS: AReq
ACS->>You: POST /step_up
You-->>ACS: SUCCESS, StepupType OTP
ACS-->>DS: ARes — challenge
DS->>ACS: CReq (challenge window opens)
ACS->>You: POST /initiate_action (VerificationToken)
You->>CH: Deliver the code
CH->>ACS: Enters the code
ACS->>DS: RReq — authenticated
The code arrives in /initiate_action as VerificationToken. Deliver that exact value. If your platform generates its own code instead, the cardholder receives one code while the ACS is checking against another, and every attempt fails.
Out-of-band challenge
The cardholder approves in your app. The ACS renders a waiting page and learns the outcome one of two ways.
sequenceDiagram
participant ACS as Cake ACS
participant You as Your platform
participant CH as Cardholder
ACS->>You: POST /initiate_action (OOBCallbackUrl)
You->>CH: Push notification
CH->>You: Approves in the app
You->>ACS: OOB callback — SUCCESS
Note over ACS,You: Or, if no callback arrives:
ACS->>You: POST /validate
You-->>ACS: SUCCESS / RETRY / REJECTED
The callback is a fast path, not a requirement. If it never arrives, the ACS polls /validate when the cardholder clicks Continue. Implementing the callback is still worth it — without it the cardholder waits for their own click to drive the check, instead of the page resolving as soon as they approve.
Resends
If the cardholder asks for a new code, the ACS calls /initiate_action again with the same StepupRequestId and StepupCounter incremented. Treat it as a redelivery of the same authentication session, not a new one.
Limits
All four end the authentication with transStatus N; they differ in the challenge cancellation indicator reported to the network.
| Limit | Default | Cancellation indicator |
|---|---|---|
| Failed attempts | 3 | 08 |
| Challenge window opened (authentication response → first challenge request) | 30s | 05, with transStatusReason 14 |
| Time between challenge rounds | 600s | 04 |
| Total challenge lifetime, from creation | 30 min | 04 |
The per-round timers come from EMV 3-D Secure. The total lifetime does not — it exists because each per-round timer resets on the next response, so a client that keeps polling would otherwise hold a challenge open indefinitely, outliving the authentication session on your side.
Your platform does not need to enforce any of these; the ACS does. But your own session lifetime should be at least 30 minutes, or your session will expire under a cardholder the ACS still considers within limits.