---
title: "Authorize a rule-based account's SCA challenge"
method: POST
path: "/internal-accounts/{id}/authorize"
tags: ["Internal Accounts", "Strong Customer Authentication"]
---

# Authorize a rule-based account's SCA challenge

`POST /internal-accounts/{id}/authorize`

## Request

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

Satisfy the Strong Customer Authentication challenge carried by a
`RULE_BASED` internal account in `PENDING` status by submitting an
`ScaAuthorization` proof. The challenge authorizes the account's sweep
instruction once; later sweeps need no further authorization.

On success the account is returned `ACTIVE` with its
`fundingPaymentInstructions`, and an `INTERNAL_ACCOUNT.STATUS_UPDATED`
webhook is sent. As with all SCA, check the returned account: if it is still
`PENDING` it carries the next `scaChallenge` to authorize.

This endpoint is only meaningful for customers in a region where SCA is
required (e.g. EU). For other customers, and for accounts with no pending
challenge, this returns `409`.

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

## 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 account is returned.

- InternalAccount
  - `id` string, required: The ID of the internal account
  - `customerId` string: The ID of the customer associated with the internal account. If this field is empty, the internal account belongs to the platform.
  - `type` 'INTERNAL_FIAT' | 'INTERNAL_CRYPTO' | 'EMBEDDED_WALLET' | 'RULE_BASED' | 'BULK_SETTLEMENT', required: Classification of an internal account. - `INTERNAL_FIAT`: A Grid-managed fiat holding account (for example, the USD holding account used as the source for Payouts flows). - `INTERNAL_CRYPTO`: A Grid-managed crypto holding account denominated in a stablecoin such as USDC. - `EMBEDDED_WALLET`: A self-custodial Embedded Wallet provisioned for the customer. Outbound transfers require a session signature produced by the customer's device — see the Embedded Wallets guide. - `RULE_BASED`: An additional account number for an existing account holder, with a routing rule attached, so incoming payments can be attributed to a specific payer and swept automatically. Created with `POST /internal-accounts`. - `BULK_SETTLEMENT`: An account for receiving bulk payments (for example, a single settlement from a payment processor) that the platform then pays out to its customers. Only a platform can hold one, and funds in it can move only to the platform's customers.
  - `status` 'PENDING' | 'ACTIVE' | 'CLOSED' | 'FROZEN' | 'FAILED', required: Status of a Grid internal account. The status determines whether the account can send or receive payments. - `PENDING`: The account is under review and is being provisioned. The account cannot send or receive payments until provisioning completes. - `ACTIVE`: The account is ready to send and receive payments. - `CLOSED`: The account cannot send or receive payments. A customer can initiate the closing of an internal account, after which the account transitions to this status. - `FROZEN`: The account cannot send or receive payments. Grid may freeze an account in response to compliance or fraud signals; payments are blocked while the account remains frozen. - `FAILED`: The account could not be provisioned. Grid was unable to create the underlying account, so it cannot send or receive payments and requires remediation.
  - `balance` CurrencyAmount, required
    - `amount` integer, required: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC)
    - `currency` 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
  - `totalBalance` CurrencyAmount, required
    - `amount` integer, required: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC)
    - `currency` 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
  - `fundingPaymentInstructions` PaymentInstructions[], required: Payment instructions for funding the account. Empty while funding is withheld. Only bank (`USD_ACCOUNT`, `EUR_ACCOUNT`, `SWIFT_ACCOUNT`) and network wallet types appear here.
    - `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.
  - `onBehalfOfCustomerId` string: The customer whose payments this platform-owned account receives. Only set by regulated financial institutions. Omitted for accounts that name no such customer, which includes every account owned by a customer.
  - `label` string: The platform-supplied label recorded when the account was created. Null for accounts that carry none.
  - `sweepRule` SweepRule: The routing rule attached to a rule-based account. Returned on the account rather than as a resource of its own, because the rule has no lifecycle apart from the account.
    - `destination` SweepRuleDestination, required: Where a rule-based account's credits are swept.
      - `accountId` string, required: The account that receives the swept funds.
      - `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 used for the transfer. Payment rails represent the underlying payment network or system used to move funds between accounts. `ACH_SAME_DAY` requests same-business-day settlement for a USD payout and is priced separately. It is subject to the NACHA per-entry same-day limit, which the network applies to every originator: $1,000,000 per entry, rising to $10,000,000 on 2027-09-17. A payout above that limit is rejected rather than slowed — check the amount before requesting this rail, or send it over `ACH`, which settles on the standard schedule and has no such limit. `ACH` will settle on the standard next-business-day schedule. Until a date we announce in advance, `ACH` continues to settle same-business-day on production platforms; it already settles next-business-day on sandbox platforms. To guarantee same-day settlement after that date, request `ACH_SAME_DAY`.
    - `minimumAmount` CurrencyAmount
      - `amount` integer, required: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC)
      - `currency` 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
    - `maximumAmount` CurrencyAmount
      - `amount` integer, required: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC)
      - `currency` 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
    - `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.
    - `description` string: Free-form description recorded on each sweep. Not delivered to the recipient.
    - `remittanceInformation` string: Free-form information that travels with each sweep to the recipient.
    - `platformFeeOverride` PlatformFeeOverride: Overrides the platform-collected fee for this transaction. When present, it replaces any configured platform-collected fees that would otherwise apply to the transaction. The fixed fee must be denominated in the quote's source currency.
      - `platformFixedFee` FixedFee, required: Fixed fee charged per transaction.
        - `amount` integer, required: Fee amount in the smallest unit of the fixed fee's `currency` (e.g., cents for USD).
        - `currency` string, required: Three-letter currency code (ISO 4217) the fixed fee is denominated in. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.)
      - `platformVariableFeeBps` integer, required: Variable fee in basis points (1 bps = 0.01%) to apply to the transaction's source-currency amount.
  - `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`, `TRANSFER`, and `STANDING_INSTRUCTION` (authorizing a rule-based account's sweep instruction), 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.
  - `strandedBalances` CurrencyAmount[]: Funds a `RULE_BASED` account's sweep converted but could not deliver to the rule's destination, by currency. They are held for the account holder and can be moved to their own account in that currency with `POST /internal-accounts/{id}/stranded-funds/transfer`. Omitted when there are none, and for every account other than a `RULE_BASED` one.
    - `amount` integer, required: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC)
    - `currency` 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
  - `privateEnabled` boolean: Whether wallet privacy is enabled for the Embedded Wallet. Only present for `EMBEDDED_WALLET` internal accounts.
  - `cardCapabilities` CardCapabilities: Actions supported by the card program associated with this resource.
    - `supportsVelocityLimits` boolean, required: Whether this card program supports platform-managed limits through `/card-velocity-limits`. Supported where Grid makes authorization decisions; false where the issuer makes them. Amounts use the card's currency, regardless of its funding source.
    - `supports3dSecurePassword` boolean, required: Whether cards in this program accept a caller-supplied `threeDSecurePassword`.
    - `supportsPanReveal` boolean, required: Whether cards in this program can be revealed through `POST /cards/{id}/reveal`.
    - `supportsDigitalWalletTokenization` boolean, required: Whether cards in this program can be added to Apple Pay, Google Pay, or Samsung Pay from your app through `POST /cards/{id}/tokenize`. Manual entry into a wallet works regardless of this flag.
  - `createdAt` string, date-time, required: Timestamp when the internal account was created
  - `updatedAt` string, date-time, required: Timestamp when the internal account was last updated

## Other responses

- `400`: Invalid or expired authorization proof
- `401`: Unauthorized
- `404`: Internal account not found
- `409`: SCA is not required for this customer, or the account 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.
- `429`: Too many requests. Returned with `RATE_LIMITED` when authorization attempts for this challenge happen too frequently. Clients should back off and retry after the interval indicated by the `Retry-After` response header.
- `500`: Internal service error

## Changes

> 117 revisions in range; 2 not diffed.

- **2026-11-03** `7ef4b76d04a3`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/lightsparkdev/apis/grid-api/changes/internal-accounts/:id/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/7ef4b76d04a3?raw)
