Skip to main content
This is the flow you hit most often: an SCA-required customer initiates a money movement, it comes back PENDING_AUTHORIZATION with an scaChallenge, and you authorize it before the transfer is released. For where this sits in the wider SCA surface, see the overview.
This applies only to customers in a region where Strong Customer Authentication is required, in practice customers in the EU (EUR / USDC). For every other customer, none of this appears: money-movement calls complete as usual, no scaChallenge is returned, and the authorization endpoints are not used. If you don’t serve EU customers you can skip this section.
Under PSD2, EU e-money and e-money-token (EUR / USDC) money movement must be confirmed by the end user with Strong Customer Authentication (SCA). Grid wraps SCA so you satisfy it through the same resources you already use. There is no separate SCA product to integrate.

When you’ll encounter it

For an SCA-required customer, a money-movement call that would otherwise complete instead returns the transaction (or quote) in status PENDING_AUTHORIZATION with an scaChallenge object, and the transfer is not released until the challenge is satisfied. This affects debits such as:
  • Sending EUR / USDC (SEPA and intra-ledger transfers)
  • Cross-currency conversions from EUR / USDC (the swap leg)
  • On-chain and Lightning withdrawals
EUR / USDC reads are covered by an active SCA login session; non-EUR/USDC accounts do not require SCA.

Authentication factors

The scaChallenge.availableFactors field tells you which factors the customer may use. scaChallenge.factor is the one in use (default SMS_OTP). TOTP cannot satisfy a dynamically linked debit because its code cannot be bound to the amount and payee. For a non-dynamically-linked challenge, use TOTP only when it appears in scaChallenge.availableFactors; that field is authoritative. Request a specific factor per transaction with the optional top-level scaFactor field on execute (SMS_OTP default, or PASSKEY).

Satisfying a challenge

Submit an ScaAuthorization proof to POST /quotes/{quoteId}/authorize for the quote that carries the challenge. Provide exactly one of code (for SMS_OTP) or passkeyAssertion + origin (for PASSKEY):
Write your client to loop on status, not on a fixed challenge count. Treat scaChallenge as the challenge to satisfy now, not necessarily the only one: after authorizing, re-inspect the returned quote, and if it is still PENDING_AUTHORIZATION it carries the next scaChallenge (a new id) — authorize that one too and repeat until it leaves PENDING_AUTHORIZATION.
Once the quote is in PENDING_AUTHORIZATION, authorize it: POST /quotes/{quoteId}/authorize. This is the single authorize path for both execute (pre-funded) and realtime-funding quotes. The challenge — and the SMS code or passkey assertion that satisfies it — only exists after the challenge is issued, so the proof is always supplied on this follow-up call, never on the originating request.
For a realtime-funding quote, the 202 / PENDING_AUTHORIZATION response withholds paymentInstructions until the challenge is authorized. Authorize first, then read paymentInstructions from the returned (advanced) quote. If you read them off the initial pending response you’ll show the customer nothing to fund.
If an SMS code lapses before it’s used, re-send it. The existing challenge is reused, and its expiresAt is not extended. Use the quote resend endpoint: POST /quotes/{quoteId}/authorize/resend.
In sandbox, the SMS code is always 123456.

Reducing prompts for repeat payees

Trusting a beneficiary (a one-time SCA-gated whitelisting step) lets subsequent sends to that payee skip the per-transaction challenge. Use this for recurring payouts to known destinations rather than authorizing every send.

Calling a customer outside SCA-regulated regions

The authorization endpoints return 409 for customers outside SCA-regulated regions (non-EU), and no scaChallenge is ever attached to their transactions. You don’t need to branch on region. Handle scaChallenge when it’s present and treat its absence as nothing to do.

Walkthrough by flow

The mechanics above are the same everywhere: inspect for an scaChallenge, submit an ScaAuthorization, and repeat until the resource leaves PENDING_AUTHORIZATION. What differs between flows is which call first returns the challenge and which resource you authorize. Here is each one end to end.
The common case — lock a quote, execute it, authorize the quote.
1

Lock a quote

POST /quotes returns a quote as usual. A standard (prefunded) send carries no challenge at quote time.
2

Execute the quote

POST /quotes/{quoteId}/execute returns the quote in PENDING_AUTHORIZATION with an scaChallenge.
3

Authorize the quote

POST /quotes/{quoteId}/authorize with the proof. Re-inspect the returned quote: if it remains PENDING_AUTHORIZATION, authorize its next scaChallenge. Do not assume a fixed number of challenges for a cross-currency or other multi-step send.