Skip to main content

Out-of-band authentication

Out-of-band means the cardholder proves who they are somewhere other than the checkout page. Instead of reading a code out of an SMS and typing it into a merchant's form, they get a prompt in an app they are already signed in to and approve with a biometric.

Before you begin​

  • 3-D Secure enabled on your program, and out-of-band enabled on top of it.
  • A push channel to your own application that you trust to deliver in seconds.
  • The authentications:read and authentications:write scopes on your credential. Neither is granted by default.
  • A step-up your app can perform on the device: biometric, or the device passcode.

How it works​

The ACS decides to challenge and picks out-of-band. From there the cardholder stays in your app, and the merchant's checkout waits on the answer the same way it would wait for a passcode.

Two things about that sequence shape the implementation.

The authorization is separate. Approving authentication does not approve a payment. The authorization follows, and it can still decline for insufficient funds or a frozen card. Your app should say "confirmed", not "paid".

The authentication object​

{
"id": "authn_01HX8ZQ2K4M7P9RSTVWXYZABCD",
"object": "authentication",
"status": "pending",
"method": "out_of_band",
"card_id": "card_01HX8ZQ2K4M7P9RSTVWXYZABCD",
"cardholder_id": "chd_01HX8ZQ2K4M7P9RSTVWXYZABCD",
"merchant": { "name": "Kopi Roastery" },
"amount": { "amount": 4250, "currency": "SGD" },
"created_at": "2026-09-27T09:38:00Z",
"updated_at": "2026-09-27T09:38:00Z"
}
statusMeaning
pendingWaiting for an answer. The merchant's checkout is still open
approvedYou approved it. The authorization follows
declinedYou declined it, or the cardholder did
timed_outNobody answered in time. The purchase fails at the merchant

method is out_of_band on every object you will see. A passcode challenge produces no authentication object at all, because the ACS runs it end to end and your app is not involved.

Steps​

1. Subscribe to authentication.challenged​

The event carries the whole object, so your handler has what it needs to raise a prompt without a second call.

authentication.challenged a challenge is waiting; the window is minutes
authentication.approved answered yes
authentication.declined answered no, by the cardholder or by your step-up

Treat authentication.challenged as the highest-priority event your endpoint handles. It is the only one on this platform where a slow acknowledgement costs the cardholder a purchase.

2. Push to the device, then step up​

Show the cardholder what they are approving, from the object: the merchant name and the amount. Do not paraphrase the amount or round it.

Then step up before you send the answer. Biometric or device passcode, immediately before, not the session they already have open. An app that approves whenever it happens to be unlocked provides no authentication at all.

3. Answer​

curl -X POST "$ALETA_BASE_URL/v1/authentications/authn_01HX8ZQ2K4M7P9RSTVWXYZABCD/approve" \
-H "Authorization: Bearer $ALETA_ACCESS_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"

POST .../decline takes the same shape. Both return the updated object.

Answer once. The call is idempotent on the Idempotency-Key, and a second answer with a different key returns 422 authentication_not_pending.

4. Handle the ones nobody answers​

If your push channel is down, GET /v1/authentications?status=pending lists what is waiting, and GET /v1/authentications/{id} reads one back. Use them when your notification path breaks. They do not work as a polling loop: at the rate a challenge expires, polling often enough to be useful would cost you your rate limit.

5. Fall back to the passcode path​

The ACS picks the method. A cardholder without your app installed, or signed out, or on a device you cannot reach, gets a passcode instead, and your product has to work when that happens. See how a challenge works.

Verifying​

  • authentication.challenged reaches your endpoint within seconds of the cardholder submitting a purchase.
  • Your prompt shows the merchant and amount from the object, unrounded.
  • Approving returns the object with status: approved, and the authorization arrives separately.
  • A second approval on the same authentication returns authentication_not_pending.
  • Letting one expire clears your prompt on your own timeout, with no event and no expires_at to wait for.
  • A cardholder without your app still completes a purchase by passcode.

Common errors​

StatuscodeFix
403insufficient_scopeThe credential lacks authentications:write
404authentication_not_foundWrong id, or it belongs to another organization
422authentication_not_pendingAlready answered, or expired. Read the object for which
422operation_unsupportedOut-of-band is not enabled on this program