---
title: "Authorize a quote's SCA challenge"
method: POST
path: "/quotes/{quoteId}/authorize"
tags: ["Strong Customer Authentication"]
---

# Authorize a quote's SCA challenge

`POST /quotes/{quoteId}/authorize`

## Request

- Base URL: `https://api.lightspark.com/grid/2025-10-13`
- URL: `https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}/authorize`
- Auth: HTTP basic

Satisfy the Strong Customer Authentication challenge carried by a quote in
`PENDING_AUTHORIZATION` status by submitting an `ScaAuthorization` proof.

This is used for realtime-funding quotes: the quote is returned with an
`scaChallenge` and **without** `paymentInstructions`; once authorized, the
quote advances and its `paymentInstructions` are populated so the customer
can fund the transfer.

As with all SCA, a quote may require more than one authorization: after
authorizing, if the quote is still `PENDING_AUTHORIZATION` it carries the
next `scaChallenge` — authorize that too, repeating until it advances (see
`ScaChallenge`).

This endpoint is only meaningful for customers in a region where SCA is required (e.g. EU). For customers outside SCA-regulated regions, this returns `409`.

In sandbox, the SMS code is always `000000`.

## Request body

- ScaAuthorization: Proof that satisfies an `ScaChallenge`. Provide exactly one of `code` (for `SMS_OTP` / `TOTP`) or `passkeyAssertion` (for `PASSKEY`). When supplying a `passkeyAssertion`, `origin` is **required** — the WebAuthn assertion is bound to the origin it was produced against, and a passkey confirmation is rejected without it.
  - `code` string, nullable: The one-time code the customer received by SMS, or read from their authenticator app. In sandbox, the code is always `000000`.
  - `passkeyAssertion` object, nullable: Opaque WebAuthn assertion produced by the device from the challenge's `passkeyAssertionOptions`. Required when satisfying a `PASSKEY` challenge.
  - `origin` string, nullable: The WebAuthn origin the `passkeyAssertion` was produced against. **Required** alongside `passkeyAssertion`; omit it for the `code` path. When the challenge lists `passkeyAllowedOrigins` (enrollment / login challenges), it must be one of those. A per-transaction passkey challenge carries `passkeyAssertionOptions` but may omit `passkeyAllowedOrigins`; in that case supply the origin your app invoked the WebAuthn API from, which must match the relying party in `passkeyAssertionOptions`.
  - `endUserIpAddress` string, nullable: The IP address of the end user's device authorizing this operation, forwarded to the SCA provider where it feeds risk assessment and any transaction-risk exemption. Supply the customer's address, not your server's. Optional: the provider records it for money-movement authorizations, and ignores it for beneficiary trust changes.

## Response `200`

Challenge authorized; the updated quote is returned.

- Quote
  - `id` string, required: Unique identifier for this quote
  - `status` 'PENDING' | 'PENDING_AUTHORIZATION' | 'PROCESSING' | 'COMPLETED' | 'FAILED' | 'EXPIRED', required: Current status of the quote. `PENDING_AUTHORIZATION` occurs only for customers in a region where Strong Customer Authentication is required (e.g. EU): the quote carries an `scaChallenge` that must be authorized before execution, and for realtime-funding sources `paymentInstructions` are withheld until it is satisfied.
  - `createdAt` string, date-time, required: When this quote was created
  - `expiresAt` string, date-time, required: Absolute UTC timestamp when the rate locked in this quote becomes invalid and the quote can no longer be executed. The window depends on the rail and corridor: instant rails (Lightning, Spark, USDC on Solana/Base/Polygon, RTP, SEPA Instant) typically expire in 1–5 minutes; corridors with longer settlement guarantees may have longer windows. Always rely on this timestamp rather than assuming a fixed window.
  - `source` union, required
    - AccountQuoteSource: Source account details
      - `sourceType` 'ACCOUNT', required: Type of quote funding source
      - `accountId` string, required: Source account identifier
      - `customerId` string: The customer on whose behalf the transaction is being initiated. Required when funding from a `BULK_SETTLEMENT` account. The customer is never the account owner, because the platform owns the account. When funding from an FBO account, this field is optional. If you omit it, the transaction is initiated on behalf of the platform. For any other account, it defaults to the account owner's customerId.
    - RealtimeFundingQuoteSource: Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, etc.). This will require manual just-in-time funding using `paymentInstructions` in the response. Because quotes expire quickly, this option is only valid for instant payment methods. Do not try to fund a quote with a non-instant payment method (ACH, etc.).
      - `sourceType` 'REALTIME_FUNDING', required: Type of quote funding source
      - `customerId` string: Source customer ID. If this transaction is being initiated on behalf of a customer, this is required. If customerId is not provided, the quote will be created on behalf of the platform itself.
      - `currency` string, required: Currency code for the funding source. See [Supported Currencies](https://docs.lightspark.com/platform-overview/core-concepts/currencies-and-rails) for the full list of supported fiat and crypto currencies.
      - `cryptoNetwork` string: The crypto network to use for the funding source. Required when `currency` is a stablecoin (e.g. USDC, USDT). Specifies which network the customer will deposit on, so the correct deposit address can be generated. Example values: `SOLANA`, `ETHEREUM`, `BASE`, `POLYGON`, `TRON`, `PLASMA`, `ARBITRUM`, `SPARK`, `LIGHTNING`, `BITCOIN`.
  - `destination` union, required
    - AccountDestination: Destination account details
      - `destinationType` 'ACCOUNT', required: Type of payment destination
      - `accountId` string, required: Destination account identifier
      - `paymentRail` 'ACH' | 'ACH_COLOMBIA' | 'ACH_SAME_DAY' | 'BANK_TRANSFER' | 'BRE_B' | 'CIPS' | 'FAST' | 'FASTER_PAYMENTS' | 'FEDNOW' | 'INSTAPAY' | 'MOBILE_MONEY' | 'NEFT' | 'PAYNOW' | 'PESONET' | 'PIX' | 'RTGS' | 'RTP' | 'SEPA' | 'SEPA_INSTANT' | 'SPEI' | 'SWIFT' | 'UNIONPAY' | 'UPI' | 'WIRE': The payment rail to use for the transfer. Must be one of the rails supported by the destination account. If not specified, the system will select a default rail.
    - UmaAddressDestination: UMA address destination details
      - `destinationType` 'UMA_ADDRESS', required: Type of payment destination
      - `umaAddress` string, required: UMA address of the recipient
      - `currency` string, required: Currency code for the destination. See [Supported Currencies](https://docs.lightspark.com/platform-overview/core-concepts/currencies-and-rails) for the full list of supported fiat and crypto currencies.
  - `sendingCurrency` Currency, required
    - `code` string: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.)
    - `name` string: Full name of the currency
    - `symbol` string: Symbol of the currency
    - `decimals` integer: Number of decimal places for the currency
  - `receivingCurrency` Currency, required
    - `code` string: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.)
    - `name` string: Full name of the currency
    - `symbol` string: Symbol of the currency
    - `decimals` integer: Number of decimal places for the currency
  - `totalSendingAmount` integer, required: The total amount that will be sent in the smallest unit of the sending currency (eg. cents).
  - `totalReceivingAmount` integer, required: The total amount that will be received in the smallest unit of the receiving currency (eg. cents).
  - `exchangeRate` number, required: Number of sending currency units per receiving currency unit. The rate is fee-exclusive: Grid deducts `feesIncluded` from `totalSendingAmount` first, then converts the remainder at this rate to reach `totalReceivingAmount`.
  - `feesIncluded` integer, required: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). Note: this value may fluctuate between quotes — some underlying fee components are defined in the receiving currency, so their equivalent in the sending currency moves with the FX rate. The fees shown here are locked only for the lifetime of this quote.
  - `platformFeesIncluded` integer: The portion of `feesIncluded` collected by the platform (platform-configured transaction fees), in the smallest unit of the sending currency. 0 when the platform has no applicable fee configured. Already included in `feesIncluded`. May be omitted from payloads produced before platform fees existed.
  - `paymentInstructions` PaymentInstructions[]: Payment instructions for executing the payment. This is not required when using an internal account source.
    - `instructionsNotes` string: Additional human-readable instructions for making the payment
    - `isPlatformAccount` boolean: Indicates whether the account is a platform account or a customer account.
    - `accountOrWalletInfo` union, required: Where to send the funds. Quotes can carry any of these types. Internal accounts return only `USD_ACCOUNT`, `EUR_ACCOUNT`, `SWIFT_ACCOUNT`, and the network wallet types; MXN, BRL, COP, ARS, Lightning, and Bitcoin L1 instructions appear only on real-time funded quotes.
      - PaymentUsdAccountInfo
        - `accountType` 'USD_ACCOUNT', required: Type of payment account or wallet
        - `accountNumber` string, required: The account number of the bank
        - `routingNumber` string, required: The ABA routing number
        - `bankName` string: The name of the financial institution holding the account. Optional on every rail, and recommended for wires, where it identifies the beneficiary's institution on the payment message. Not checked against `GET /discoveries`.
        - `intermediaryBankName` string: The name of the intermediary financial institution, for accounts reachable only through a correspondent bank. Used on the WIRE rail; ignored on ACH, RTP and FEDNOW.
        - `intermediaryRoutingNumber` string: The ABA routing number of the intermediary financial institution. Used on the WIRE rail; ignored on ACH, RTP and FEDNOW.
        - `fiToFiInformation` string: Bank-to-bank instructions carried alongside the payment. Used on the WIRE rail; ignored on ACH, RTP and FEDNOW.
        - `paymentRails` string[], required
        - `bankAccountType` 'CHECKING' | 'SAVINGS': Whether the account is a checking or a savings account. Grid uses this to set the ACH transaction code, so a value that does not match the account causes the receiving bank to return a notification of change.
        - `reference` string, required: Unique reference code that must be included with the payment to properly credit it
        - `bankAddress` string: The postal address of the financial institution holding the account, on a single line. Optional on every rail, and recommended for wires, where some originating banks require the beneficiary institution's address before they will send.
      - PaymentEurAccountInfo
        - `accountType` 'EUR_ACCOUNT', required: Type of payment account or wallet
        - `iban` string, required: The IBAN of the bank account
        - `swiftCode` string: The SWIFT/BIC code of the bank. When omitted, Grid derives it from the IBAN when possible. Provide it when automatic derivation is unavailable.
        - `paymentRails` string[], required
        - `reference` string, required: Unique reference code that must be included with the payment to properly credit it
      - PaymentSwiftAccountInfo: At least one of accountNumber or iban is always present: IBAN-only corridors (e.g. BR, GB) use iban, other corridors use accountNumber, and both appear when the bank exposes both identifiers for the same account.
        - `accountType` 'SWIFT_ACCOUNT', required: Type of payment account or wallet
        - `country` string, required: The ISO 3166-1 alpha-2 country code of the bank account
        - `swiftCode` string, required: The SWIFT/BIC code of the bank
        - `bankName` string, required: The name of the bank
        - `accountNumber` string: The bank account number. Required for most corridors. Use iban instead for IBAN-only corridors (e.g. BR, GB).
        - `iban` string: The IBAN of the bank account. Required for IBAN-only corridors (e.g. BR, GB). Use accountNumber for all other corridors.
        - `paymentRails` string[], required
        - `accountHolderName` string, required: The name of the account holder as it must appear on the wire. Remitting banks match this against the beneficiary name field, so payers should copy it exactly.
        - `bankAddress` string: The address of the bank holding the account, when known.
        - `reference` string: Reference code to include with the payment when present. SWIFT payments are attributed by the destination account number/IBAN, so this account type typically requires no reference.
      - PaymentMxnAccountInfo
        - `accountType` 'MXN_ACCOUNT', required: Type of payment account or wallet
        - `clabeNumber` string, required: The CLABE number of the bank
        - `paymentRails` string[], required
        - `reference` string, required: Unique reference code that must be included with the payment to properly credit it
      - PaymentBrlAccountInfo
        - `accountType` 'BRL_ACCOUNT', required: Type of payment account or wallet
        - `qrCode` string, required: A PIX QR code payload that can be used to fund the transaction. This can be rendered as a QR code image or pasted into a PIX-compatible banking app.
      - PaymentCopAccountInfo
        - `accountType` 'COP_ACCOUNT', required: Type of payment account or wallet
        - `paymentUrl` string, uri, required: A payment URL where the customer can complete their COP deposit.
      - PaymentArsAccountInfo
        - `accountType` 'ARS_ACCOUNT', required: Type of payment account or wallet
        - `accountNumber` string, required: The static CVU (Clave Virtual Uniforme) bank account number to pay to.
      - PaymentSparkWalletInfo
        - `accountType` 'SPARK_WALLET', required: Type of payment account or wallet
        - `address` string, required: Spark wallet address
        - `assetType` string, required: Type of asset or configured Spark token currency code
        - `invoice` string: Invoice for the payment
      - PaymentLightningInvoiceInfo
        - `accountType` 'LIGHTNING', required: Type of payment account or wallet
        - `invoice` string, required: Invoice for the payment
      - PaymentBitcoinDepositAddressInfo
        - `accountType` 'BITCOIN_L1', required: Type of payment account or wallet
        - `address` string, required: On-chain Bitcoin (L1) deposit address to send funds to
        - `network` 'BITCOIN': The blockchain network for the deposit address.
      - PaymentSolanaWalletInfo
        - `accountType` 'SOLANA_WALLET', required: Type of payment account or wallet
        - `address` string, required: Solana wallet address
        - `assetType` 'USDC' | 'USDT': Type of asset
      - PaymentEthereumWalletInfo
        - `accountType` 'ETHEREUM_WALLET', required: Type of payment account or wallet
        - `address` string, required: Ethereum L1 wallet address
        - `assetType` 'USDC' | 'USDT': Type of asset
      - PaymentBaseWalletInfo
        - `accountType` 'BASE_WALLET', required: Type of payment account or wallet
        - `address` string, required: Base eth wallet address
        - `assetType` 'USDC': Type of asset
      - PaymentPolygonWalletInfo
        - `accountType` 'POLYGON_WALLET', required: Type of payment account or wallet
        - `address` string, required: Polygon eth wallet address
        - `assetType` 'USDC': Type of asset
      - PaymentArbitrumWalletInfo
        - `accountType` 'ARBITRUM_WALLET', required: Type of payment account or wallet
        - `address` string, required: Arbitrum wallet address
        - `assetType` 'USDC' | 'USDT': Type of asset
      - PaymentTronWalletInfo
        - `accountType` 'TRON_WALLET', required: Type of payment account or wallet
        - `address` string, required: Tron wallet address
        - `assetType` 'USDT': Type of asset
      - PaymentPlasmaWalletInfo
        - `accountType` 'PLASMA_WALLET', required: Type of payment account or wallet
        - `address` string, required: Plasma wallet address
        - `assetType` 'USDT': Type of asset
      - PaymentEmbeddedWalletInfo
        - `accountType` 'EMBEDDED_WALLET', required: Discriminator value identifying this as Embedded Wallet payment instructions.
        - `payloadToSign` string, required: JSON-encoded transaction signing payload that must be stamped, as-is (byte-for-byte, without re-serialization), with the session private key of a verified authentication credential on the source Embedded Wallet. The resulting Grid wallet signature is passed as the `Grid-Wallet-Signature` header on `POST /quotes/{quoteId}/execute` to authorize the outbound transfer from the wallet.
  - `transactionId` string, required: The ID of the transaction created from this quote.
  - `counterpartyInformation` CounterpartyInformation: Additional information about the counterparty, if available and relevant to the transaction and platform.
  - `rateDetails` OutgoingRateDetails: Details about the rate and fees for an outgoing transaction or quote. Note: `counterpartyFixedFee` is denominated in the receiving currency, so its equivalent value in the sending currency fluctuates with the FX rate. As a result, the total fee on a subsequent quote for the same transfer may differ even if the underlying fee structure is unchanged.
    - `counterpartyMultiplier` number, double, required: The underlying multiplier from mSATs to the receiving currency as returned by the counterparty institution.
    - `counterpartyFixedFee` integer, required: The fixed fee charged by the counterparty institution to execute the quote in the smallest unit of the receiving currency (eg. cents).
    - `gridApiMultiplier` number, double, required: The underlying multiplier from the sending currency to mSATS, including variable fees.
    - `gridApiFixedFee` integer, required: The fixed fee charged by the Grid product to execute the quote in the smallest unit of the sending currency (eg. cents).
    - `gridApiVariableFeeRate` number, double, required: The variable fee rate charged by the Grid product to execute the quote as a percentage of the sending currency amount.
    - `gridApiVariableFeeAmount` number, required: The variable fee amount charged by the Grid product to execute the quote in the smallest unit of the sending currency (eg. cents). This is the sending amount times gridApiVariableFeeRate.
  - `purposeOfPayment` 'GIFT' | 'SELF' | 'GOODS_OR_SERVICES' | 'EDUCATION' | 'HEALTH_OR_MEDICAL' | 'REAL_ESTATE_PURCHASE' | 'TAX_PAYMENT' | 'LOAN_PAYMENT' | 'UTILITY_BILL' | 'DONATION' | 'TRAVEL' | 'FAMILY_SUPPORT' | 'SALARY_PAYMENT' | 'EXPORTED_GOODS_PREPAYMENT' | 'EXPORTED_GOODS_POSTPAYMENT' | 'SERVICE_CHARGES' | 'OFFICE_EXPENSES' | 'DELIVERY_FEES' | 'HOTEL_ACCOMMODATION' | 'COMMISSION_ON_GOODS' | 'COMMISSION_ON_SERVICES' | 'ACCOUNTING_SERVICES' | 'EXHIBITION_SERVICES' | 'OTHER': The purpose of the payment. This may be required when sending to certain geographies (e.g. India). Some destinations accept only certain purposes. A business payout to China must use one of the purposes listed in [Supporting Documents](https://docs.lightspark.com/payouts-and-b2b/payment-flow/send-payment#supporting-documents), and each needs its own supporting documents.
  - `documentIds` string[]: IDs of the supporting documents attached to the payment behind this quote, as passed in `documentIds` on `POST /quotes`. Present only when the quote was created with `documentIds`. Grid returns such a quote only after it has attached every document. Call `GET /payment-documents/{paymentDocumentId}` for each document's details, such as its `documentType` and its `ATTACHED` status.
  - `scaChallenge` ScaChallenge: A Strong Customer Authentication challenge that must be satisfied before a money-movement operation can complete. This object is **only present when the customer is in a region where SCA is required** (the EU); for customers outside SCA-regulated regions it is omitted entirely and no action is needed. When present on a quote, authorize it by submitting an `ScaAuthorization` proof to `POST /quotes/{quoteId}/authorize`. **A single operation may require more than one authorization, in sequence.** Treat `scaChallenge` as *the challenge to satisfy now*, not "the only one". After each authorize, re-inspect the returned quote/transaction: if it is still `PENDING_AUTHORIZATION`, it carries the **next** `scaChallenge` (a new `id`) — authorize that too, and repeat until it leaves `PENDING_AUTHORIZATION`. Do not assume one authorization releases the transfer. The number of authorizations is flow-dependent and **may decrease in future**: for example, a cross-currency send today authorizes the currency conversion and the payout as two separate challenges; a future update may collapse them into one. A client written to loop on status handles any count unchanged.
    - `id` string, required: Unique identifier for this challenge. The server resolves the active challenge from the quote or transaction being authorized, so this field need not be supplied back; it is informational (e.g. for logging or correlation).
    - `expiresAt` string, date-time, required: Absolute UTC timestamp after which this challenge can no longer be authorized.
    - `factor` 'SMS_OTP' | 'TOTP' | 'PASSKEY', required: A Strong Customer Authentication factor. | Factor | Description | |--------|-------------| | `SMS_OTP` | One-time code sent by SMS to the customer's verified phone. Requires no prior enrollment. | | `TOTP` | Time-based one-time code from an authenticator app. Requires enrollment. Not valid for per-transaction challenges (cannot carry dynamic linking). | | `PASSKEY` | WebAuthn passkey assertion. Requires enrollment. |
    - `availableFactors` ScaFactor[], required: The factors the customer may use to satisfy this challenge.
    - `purpose` string, nullable: Optional, informational label for what this particular challenge in the sequence authorizes — useful for step UX (e.g. "Authorize the currency conversion" vs "Authorize the payout"). Known values include `CURRENCY_CONVERSION`, `PAYOUT`, and `TRANSFER`, but the set is **non-exhaustive and may grow** — treat unrecognized values as a generic authorization step and do not branch program logic on it. Omitted when steps are not distinguished (e.g. a single-authorization flow).
    - `passkeyAssertionOptions` object, nullable: Opaque WebAuthn assertion request options (including the relying-party id, challenge, and allowed credentials), present only when `factor` is `PASSKEY`. Pass to the device's WebAuthn API to produce the assertion submitted back in `ScaAuthorization.passkeyAssertion`.
    - `passkeyAllowedOrigins` string[], nullable: The origins the WebAuthn ceremony may run against. Populated for enrollment and login passkey challenges; the origin the assertion is produced against must be one of these and echoed back as `ScaAuthorization.origin`. Per-transaction passkey challenges omit this (they carry `passkeyAssertionOptions` only) — see `ScaAuthorization.origin` for how to source the origin in that case.

## Other responses

- `400`: Invalid or expired authorization proof
- `401`: Unauthorized
- `404`: Quote not found
- `409`: SCA is not required for this customer, or the quote has no pending challenge to authorize. Also returned with `SCA_SESSION_REQUIRED` when the customer's SCA login session is missing or expired — re-authenticate the customer, then authorize again.
- `423`: Returned with `ACCOUNT_LOCKED` when the customer's login is temporarily locked after too many failed authorization attempts. The customer must wait for the lock to expire before authorizing again.
- `429`: Too many requests. Returned with `RATE_LIMITED` when authorization attempts for this challenge happen too frequently (for example, repeated bad codes brute-forcing the OTP). The challenge may be invalidated after too many failed attempts. Clients should back off and retry after the interval indicated by the `Retry-After` response header.
- `500`: Internal service error

## Changes

> 115 revisions in range; 2 not diffed.

- **2026-10-28** `d996ddfb9d2b`: 2 info
  - added the optional property `details/missingFields` to the response with the `400` status
  - added the optional property `details/refusalReason` to the response with the `400` status
- **2026-10-26** `f11f1e76bdd7`: 1 warning
  - added the new `PURCHASE_CARD_LIMIT_REACHED` enum value to the `code` response property for the response status `409`
- **2026-10-26** `0a6dad5815c7`: 1 warning
  - added the new `INVOICE_ALREADY_USED` enum value to the `code` response property for the response status `409`
- **2026-10-24** `715332dcb111`: 1 warning
  - added the new `EXACT_AMOUNT_UNAVAILABLE` enum value to the `code` response property for the response status `400`
- **2026-10-20** `761d1d030cb5`: 2 warning
  - added the new `AGENT_CARD_EXISTS` enum value to the `code` response property for the response status `409`
  - added the new `CARDHOLDER_AGREEMENT_NOT_ACCEPTED` enum value to the `code` response property for the response status `400`

[Operation history](https://skmtc.dev/lightsparkdev/apis/grid-api/changes/quotes/:quoteId/authorize/post.md)

---

[API](https://skmtc.dev/lightsparkdev/apis/grid-api.md) - [All operations](https://skmtc.dev/lightsparkdev/apis/grid-api/llms.txt) - [OpenAPI document](https://skmtc.dev/lightsparkdev/apis/grid-api/revisions/7a1db46ec552?raw)
