> ## Documentation Index
> Fetch the complete documentation index at: https://ramps-feat-striga-sca-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Strong Customer Authentication

> How Grid satisfies PSD2 SCA for EU customers

<Note>
  **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, the authentication endpoints return `409`, and you
  can skip this section.
</Note>

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 product to integrate, and the same request shapes work for every
customer whether or not SCA applies.

This section covers the whole surface:

<CardGroup cols={2}>
  <Card title="Per-transaction authorization" icon="https://mintcdn.com/ramps-feat-striga-sca-docs/GQlowb5Ei1EuAwEO/images/icons/lock.svg?fit=max&auto=format&n=GQlowb5Ei1EuAwEO&q=85&s=428bb6d40c785225062b67575ce8a971" href="/platform-overview/sca/per-transaction-authorization" width="20" height="20" data-path="images/icons/lock.svg">
    Authorize a money movement that came back `PENDING_AUTHORIZATION`. This is the flow you hit most often.
  </Card>

  <Card title="Factor enrollment" icon="https://mintcdn.com/ramps-feat-striga-sca-docs/GQlowb5Ei1EuAwEO/images/icons/key2.svg?fit=max&auto=format&n=GQlowb5Ei1EuAwEO&q=85&s=6d8175fe8ea8b13509626ec241f96401" href="/platform-overview/sca/factor-enrollment" width="20" height="20" data-path="images/icons/key2.svg">
    Enroll and manage a customer's TOTP and passkey factors.
  </Card>

  <Card title="Login & sessions" icon="https://mintcdn.com/ramps-feat-striga-sca-docs/qZwu2guKZj6TCUL8/images/icons/shield.svg?fit=max&auto=format&n=qZwu2guKZj6TCUL8&q=85&s=730cd997dc6b0a378bfdcb291014bfa0" href="/platform-overview/sca/login-and-sessions" width="20" height="20" data-path="images/icons/shield.svg">
    The end-user SCA login and the session it grants for reads.
  </Card>

  <Card title="Trusted beneficiaries" icon="https://mintcdn.com/ramps-feat-striga-sca-docs/GQlowb5Ei1EuAwEO/images/icons/checkmark1.svg?fit=max&auto=format&n=GQlowb5Ei1EuAwEO&q=85&s=2ca7cd84a7d4ca707e40ffb9259045da" href="/platform-overview/sca/trusted-beneficiaries" width="20" height="20" data-path="images/icons/checkmark1.svg">
    Whitelist a payee once so future sends to it skip the per-transaction ceremony.
  </Card>

  <Card title="Two-factor reset" icon="https://mintcdn.com/ramps-feat-striga-sca-docs/GQlowb5Ei1EuAwEO/images/icons/key2.svg?fit=max&auto=format&n=GQlowb5Ei1EuAwEO&q=85&s=6d8175fe8ea8b13509626ec241f96401" href="/platform-overview/sca/two-factor-reset" width="20" height="20" data-path="images/icons/key2.svg">
    Recover a customer who has lost their factors, gated by an identity (liveness) check.
  </Card>
</CardGroup>

## What SCA covers

SCA gates **debits** on EU-regulated balances. EUR / USDC reads are covered by
an active [SCA login session](/platform-overview/sca/login-and-sessions), while
non-EUR/USDC accounts do not require SCA. "Dynamic linking" means the authorization is cryptographically
bound to the transaction's amount and payee (PSD2 Article 97(2)); it forces a
fresh, transaction-specific challenge, and it's the reason some flows can't use
TOTP (see [Authentication factors](#authentication-factors)).

| Operation                                  | SCA required?                | Dynamically linked? |
| ------------------------------------------ | ---------------------------- | ------------------- |
| Send EUR / USDC (SEPA + intra-ledger)      | Yes                          | Yes                 |
| Convert **from** EUR / USDC (the swap leg) | Yes                          | Yes                 |
| On-chain / Lightning withdrawal            | Yes                          | No                  |
| Trust / untrust a beneficiary              | Yes                          | No                  |
| Send to an **already-trusted** beneficiary | Lighter, no dynamic linking  | No                  |
| Reading balances / history                 | Covered by the login session | n/a                 |
| Non-EUR/USDC accounts (e.g. USD)           | No                           | n/a                 |

## Authentication factors

The `scaChallenge.availableFactors` field tells you which factors a customer may
use; `scaChallenge.factor` is the one in use (default `SMS_OTP`).

| Factor    | Enrollment                                            | Per-transaction debit                                                                                             |
| --------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `SMS_OTP` | None; a code is sent to the customer's verified phone | ✅ Default                                                                                                         |
| `PASSKEY` | Required (WebAuthn credential)                        | ✅                                                                                                                 |
| `TOTP`    | Required (authenticator app)                          | Available only where the challenge is not dynamically linked; never assume it is available for a particular debit |

TOTP is barred from dynamically-linked debits because an authenticator code is
derived only from a clock and a shared secret, so it can't be bound to *this*
amount and payee. It stays valid for flows that don't require dynamic linking:
login, trusting a beneficiary, sends to an already-trusted beneficiary, and
eligible on-chain or Lightning withdrawals. Always use the factors in
`scaChallenge.availableFactors`; that field, not the operation name, is the
source of truth for the challenge Grid issued.

## The authorization flow

For an SCA-required customer, a money-movement call that would otherwise complete
instead returns the resource in status **`PENDING_AUTHORIZATION`** carrying an
**`scaChallenge`**, and the transfer is not released until the challenge is
satisfied. A single money movement can require **more than one** challenge in
sequence, so loop on status rather than assuming one authorization releases the
transfer.

```mermaid theme={null}
sequenceDiagram
  participant P as Your platform
  participant G as Grid
  participant U as End user

  P->>G: Initiate money movement (execute a quote)
  G-->>P: PENDING_AUTHORIZATION + scaChallenge
  G->>U: Deliver challenge (e.g. SMS OTP)
  loop while status == PENDING_AUTHORIZATION
    U-->>P: Provide proof (code / passkey assertion)
    P->>G: POST .../authorize (scaChallenge.id + proof)
    G-->>P: Updated resource + next scaChallenge (if any)
  end
  G-->>P: Resource leaves PENDING_AUTHORIZATION, transfer released
```

[Per-transaction authorization](/platform-overview/sca/per-transaction-authorization)
covers the mechanics: authorizing the quote, the multi-step loop, resending an
expired code, and the realtime-funding-quote nuance.

## Lifetimes & limits

| Aspect            | Behavior                                                                                                                                                                                                                            |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Challenge expiry  | Each challenge carries an absolute `scaChallenge.expiresAt` (UTC). SMS codes expire after 5 minutes; TOTP and passkey challenges expire after 10 minutes. After expiry, the challenge can no longer be authorized.                  |
| Resend            | `SMS_OTP` only. Resending reuses the existing challenge and does not extend `expiresAt`; `PASSKEY` (and `TOTP`) codes can't be resent.                                                                                              |
| Repeated failures | A challenge permits at most 5 attempts. Too many failed authorizations may invalidate it and return `429 RATE_LIMITED`, so honor `Retry-After`.                                                                                     |
| Login session     | A completed [SCA login](/platform-overview/sca/login-and-sessions) grants a 180-day session. EUR / USDC history older than 90 days requires fresh SCA even during that session.                                                     |
| Account lockout   | Repeated `FAILED_LOGIN_ATTEMPT` signals escalate a lockout (5 → 15 min, 6 → 30 min, 7 → 1 hour, 8 → 24 hours, 9+ → suspension). See [account-security signals](/platform-overview/sca/login-and-sessions#account-security-signals). |
| 2FA reset window  | A started reset carries its own `expiresAt`; complete it before then.                                                                                                                                                               |

## Errors you'll encounter

| Status | Meaning                                                                                                                                             | What to do                                                                  |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `400`  | Invalid or expired proof: wrong code, expired challenge, or a factor that can't satisfy this challenge (e.g. `TOTP` on a dynamically-linked debit). | Re-collect the proof. If the code lapsed, resend (`SMS_OTP`) or start over. |
| `409`  | SCA isn't required for this customer (non-EU), there's no pending challenge, or the factor's code can't be resent (e.g. `PASSKEY`).                 | Don't retry the same call. Treat a non-EU `409` as nothing to authorize.    |
| `429`  | `RATE_LIMITED`: too many attempts or resends, and the challenge may now be invalidated.                                                             | Honor `Retry-After`; you may need to restart the flow.                      |
| `404`  | The customer, transaction, quote, external account, or reset wasn't found.                                                                          | Check the id.                                                               |

## Calling a customer outside SCA-regulated regions

Every authentication endpoint returns **`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.
