From 562330255dd5cc8be4d07fbf5c38e9af99c7f532 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 08:42:15 +0000 Subject: [PATCH 1/2] docs: add PENDING_AUTHORIZATION status for SCA to quote and transaction docs Document the new Strong Customer Authentication (SCA) flow for EU customers: - Add PENDING_AUTHORIZATION status to transaction status table - Update state diagram with SCA authorization flow - Add OUTGOING_PAYMENT.PENDING_AUTHORIZATION webhook event - Add SCA section to quote system docs explaining the authorize flow - Update webhook handling example to show SCA case Co-Authored-By: Claude Opus 4.5 --- .../core-concepts/quote-system.mdx | 35 +++++++++++++++++++ .../core-concepts/transaction-lifecycle.mdx | 10 ++++++ 2 files changed, 45 insertions(+) diff --git a/mintlify/platform-overview/core-concepts/quote-system.mdx b/mintlify/platform-overview/core-concepts/quote-system.mdx index 13d7f665..9876e6d4 100644 --- a/mintlify/platform-overview/core-concepts/quote-system.mdx +++ b/mintlify/platform-overview/core-concepts/quote-system.mdx @@ -228,6 +228,41 @@ curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes/Quote:abc123/exec } ``` +### Strong Customer Authentication (EU Customers) + +For customers in regions where Strong Customer Authentication (SCA) is required (e.g., EU), quotes may enter a `PENDING_AUTHORIZATION` status with an `scaChallenge` object. The customer must authorize the challenge before the quote can be executed. + +**Quote response with SCA challenge:** + +```json +{ + "id": "Quote:019542f5-b3e7-1d02-0000-000000000050", + "status": "PENDING_AUTHORIZATION", + "scaChallenge": { + "id": "ScaChallenge:...", + "expiresAt": "2025-10-03T12:05:00Z", + "factor": "SMS_OTP", + "availableFactors": ["SMS_OTP", "PASSKEY"], + "purpose": "PAYOUT" + } +} +``` + +To authorize the quote, call `POST /quotes/{quoteId}/authorize` with the appropriate proof: + +```bash +curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes/Quote:abc123/authorize \ + -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ + -H "Content-Type: application/json" \ + -d '{"code": "123456"}' +``` + + + Multiple authorizations may be required in sequence (e.g., currency conversion + payout). After each authorization, check the quote status — if still `PENDING_AUTHORIZATION`, authorize the next challenge. + + +For JIT-funded quotes, `paymentInstructions` are withheld until all SCA challenges are satisfied. + ### Execution Timing diff --git a/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx b/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx index 37197e1a..340e75fb 100644 --- a/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx +++ b/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx @@ -121,6 +121,7 @@ A single `status` field represents whether the transaction reached its destinati | Status | Description | |--------|-------------| | **PENDING** | Quote is pending confirmation | +| **PENDING_AUTHORIZATION** | Awaiting Strong Customer Authentication. Only occurs for customers in regions where SCA is required (e.g., EU); authorize the transaction's `scaChallenge` to proceed. | | **EXPIRED** | Quote wasn't executed before the expiry window | | **PROCESSING** | Executing the quote after receiving funds (checking internal balances, or push/pull to/from external account) | | **COMPLETED** | Payout successfully reached the destination account | @@ -135,6 +136,9 @@ A single `status` field represents whether the transaction reached its destinati ```mermaid stateDiagram-v2 [*] --> PENDING : Quote created + PENDING --> PENDING_AUTHORIZATION : SCA required (EU) + PENDING_AUTHORIZATION --> PROCESSING : Authorized + PENDING_AUTHORIZATION --> EXPIRED : Challenge expired PENDING --> PROCESSING : Quote confirmed, funds received PENDING --> EXPIRED : Quote expired before execution PROCESSING --> COMPLETED : Payout reached destination @@ -202,6 +206,7 @@ Outgoing payment webhooks use the format `OUTGOING_PAYMENT.`. The webhoo | Event | Description | |-------|-------------| | `OUTGOING_PAYMENT.PENDING` | Transaction created, quote pending confirmation | +| `OUTGOING_PAYMENT.PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication (EU customers only) | | `OUTGOING_PAYMENT.PROCESSING` | Quote confirmed, payout in progress | | `OUTGOING_PAYMENT.COMPLETED` | Payout reached destination | | `OUTGOING_PAYMENT.FAILED` | Payout failed | @@ -282,6 +287,11 @@ app.post('/webhooks/grid', async (req, res) => { const { data, type } = req.body; switch (type) { + case 'OUTGOING_PAYMENT.PENDING_AUTHORIZATION': + // EU customers only: prompt user to complete SCA challenge + await notifyCustomer(data.customerId, 'Authorization required to complete payment.'); + break; + case 'OUTGOING_PAYMENT.COMPLETED': await notifyCustomer(data.customerId, 'Payment delivered!'); break; From d91e3a968419e123c5edd7b12dc5c7ed66bc09ed Mon Sep 17 00:00:00 2001 From: Bolt Agent Date: Thu, 30 Jul 2026 16:31:02 +0000 Subject: [PATCH 2/2] docs: point PENDING_AUTHORIZATION at the quote's scaChallenge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The transaction resource has no scaChallenge field — only Quote does. Route readers through the transaction's quoteId, add the missing webhook payload example and SCA scenario tab, and correct the abandoned-challenge terminal state to FAILED/SCA_NOT_COMPLETED. Co-Authored-By: jklein24 Co-Authored-By: Claude Opus 5 (1M context) --- mintlify/openapi.yaml | 5 +- .../core-concepts/quote-system.mdx | 9 ++-- .../core-concepts/transaction-lifecycle.mdx | 49 +++++++++++++++++-- openapi.yaml | 5 +- .../OutgoingTransactionStatus.yaml | 2 +- .../transactions/TransactionStatus.yaml | 2 +- .../schemas/webhooks/WebhookType.yaml | 1 + 7 files changed, 59 insertions(+), 14 deletions(-) diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index d13da97f..25844997 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -19954,7 +19954,7 @@ components: |--------|-------------| | `CREATED` | Initial lookup has been created | | `PENDING` | Quote has been created | - | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU); authorize the transaction's `scaChallenge` to proceed. | + | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU). The challenge is carried by the quote, not the transaction — fetch `GET /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its `scaChallenge` via `POST /quotes/{quoteId}/authorize`. | | `PROCESSING` | Funding has been received and payment initiated | | `COMPLETED` | Cross border payment has been received, converted and payment has been sent to the offramp network | | `REJECTED` | Receiving institution or wallet rejected payment, payment has been refunded | @@ -20377,7 +20377,7 @@ components: | Status | Description | |--------|-------------| | `PENDING` | Quote is pending confirmation | - | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU); authorize the transaction's `scaChallenge` to proceed. | + | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU). The challenge is carried by the quote, not the transaction — fetch `GET /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its `scaChallenge` via `POST /quotes/{quoteId}/authorize`. | | `EXPIRED` | Quote wasn't executed before expiry window | | `PROCESSING` | Executing the quote after receiving funds | | `COMPLETED` | Payout successfully reached the destination | @@ -23318,6 +23318,7 @@ components: type: string enum: - OUTGOING_PAYMENT.PENDING + - OUTGOING_PAYMENT.PENDING_AUTHORIZATION - OUTGOING_PAYMENT.PROCESSING - OUTGOING_PAYMENT.COMPLETED - OUTGOING_PAYMENT.FAILED diff --git a/mintlify/platform-overview/core-concepts/quote-system.mdx b/mintlify/platform-overview/core-concepts/quote-system.mdx index 9876e6d4..a85a5ac2 100644 --- a/mintlify/platform-overview/core-concepts/quote-system.mdx +++ b/mintlify/platform-overview/core-concepts/quote-system.mdx @@ -230,7 +230,12 @@ curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes/Quote:abc123/exec ### Strong Customer Authentication (EU Customers) -For customers in regions where Strong Customer Authentication (SCA) is required (e.g., EU), quotes may enter a `PENDING_AUTHORIZATION` status with an `scaChallenge` object. The customer must authorize the challenge before the quote can be executed. +For customers in regions where Strong Customer Authentication (SCA) is required (e.g., EU), quotes may enter a `PENDING_AUTHORIZATION` status with an `scaChallenge` object. The transfer is not released until the customer authorizes it. + +Where the challenge appears depends on how the quote is funded: + +- **Prefunded** — `POST /quotes/{quoteId}/execute` returns `PENDING_AUTHORIZATION` with the challenge instead of initiating the transfer. The challenge does not exist until `execute` is called, so the proof always goes on the follow-up authorize; re-calling `execute` returns `409`. +- **Realtime (JIT) funded** — the challenge arrives earlier and `paymentInstructions` are withheld until it is satisfied. **Quote response with SCA challenge:** @@ -261,8 +266,6 @@ curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes/Quote:abc123/auth Multiple authorizations may be required in sequence (e.g., currency conversion + payout). After each authorization, check the quote status — if still `PENDING_AUTHORIZATION`, authorize the next challenge. -For JIT-funded quotes, `paymentInstructions` are withheld until all SCA challenges are satisfied. - ### Execution Timing diff --git a/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx b/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx index 340e75fb..34421eed 100644 --- a/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx +++ b/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx @@ -121,7 +121,7 @@ A single `status` field represents whether the transaction reached its destinati | Status | Description | |--------|-------------| | **PENDING** | Quote is pending confirmation | -| **PENDING_AUTHORIZATION** | Awaiting Strong Customer Authentication. Only occurs for customers in regions where SCA is required (e.g., EU); authorize the transaction's `scaChallenge` to proceed. | +| **PENDING_AUTHORIZATION** | Awaiting Strong Customer Authentication. Only occurs for customers in regions where SCA is required (e.g., EU). The challenge lives on the quote, not the transaction — fetch `GET /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its `scaChallenge` with `POST /quotes/{quoteId}/authorize`. | | **EXPIRED** | Quote wasn't executed before the expiry window | | **PROCESSING** | Executing the quote after receiving funds (checking internal balances, or push/pull to/from external account) | | **COMPLETED** | Payout successfully reached the destination account | @@ -138,7 +138,7 @@ stateDiagram-v2 [*] --> PENDING : Quote created PENDING --> PENDING_AUTHORIZATION : SCA required (EU) PENDING_AUTHORIZATION --> PROCESSING : Authorized - PENDING_AUTHORIZATION --> EXPIRED : Challenge expired + PENDING_AUTHORIZATION --> FAILED : Challenge expired unsatisfied PENDING --> PROCESSING : Quote confirmed, funds received PENDING --> EXPIRED : Quote expired before execution PROCESSING --> COMPLETED : Payout reached destination @@ -234,6 +234,26 @@ Outgoing payment webhooks use the format `OUTGOING_PAYMENT.`. The webhoo ``` + +```json +{ + "type": "OUTGOING_PAYMENT.PENDING_AUTHORIZATION", + "data": { + "id": "Transaction:...", + "status": "PENDING_AUTHORIZATION", + "type": "OUTGOING", + "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", + "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", + "sentAmount": {"amount": 100000, "currency": {"code": "USD"}}, + "receivedAmount": {"amount": 92000, "currency": {"code": "EUR"}}, + "createdAt": "2025-10-03T15:00:00Z" + } +} +``` + +The payload carries no `scaChallenge` — that field lives on the quote. Use `quoteId` to fetch `GET /quotes/{quoteId}`, read its `scaChallenge`, and authorize with `POST /quotes/{quoteId}/authorize`. + + ```json { @@ -287,10 +307,12 @@ app.post('/webhooks/grid', async (req, res) => { const { data, type } = req.body; switch (type) { - case 'OUTGOING_PAYMENT.PENDING_AUTHORIZATION': - // EU customers only: prompt user to complete SCA challenge - await notifyCustomer(data.customerId, 'Authorization required to complete payment.'); + case 'OUTGOING_PAYMENT.PENDING_AUTHORIZATION': { + // EU customers only: the challenge is on the quote, not the transaction + const quote = await getQuote(data.quoteId); + await promptScaChallenge(data.customerId, data.quoteId, quote.scaChallenge); break; + } case 'OUTGOING_PAYMENT.COMPLETED': await notifyCustomer(data.customerId, 'Payment delivered!'); @@ -325,6 +347,21 @@ The standard successful payment flow: 3. `OUTGOING_PAYMENT.COMPLETED` + +The customer must satisfy a Strong Customer Authentication challenge before the payout proceeds: + +1. `OUTGOING_PAYMENT.PENDING` +2. `OUTGOING_PAYMENT.PENDING_AUTHORIZATION` +3. `OUTGOING_PAYMENT.PROCESSING` +4. `OUTGOING_PAYMENT.COMPLETED` + + +A single payment can require more than one authorization in sequence, so `OUTGOING_PAYMENT.PENDING_AUTHORIZATION` may arrive several times before `PROCESSING`. Loop on the quote's status rather than assuming one authorization releases the payment. + + +If the challenge expires before the customer satisfies it, the transaction lands on `OUTGOING_PAYMENT.FAILED` with `failureReason: SCA_NOT_COMPLETED` and no funds move. Create a new quote to try again. + + Payment fails and the refund succeeds: @@ -465,6 +502,8 @@ When a transaction fails, a refund is initiated automatically. Track the refund switch (webhookType) { case 'OUTGOING_PAYMENT.PENDING': return 'Payment processing...'; + case 'OUTGOING_PAYMENT.PENDING_AUTHORIZATION': + return 'Authorization required. Complete the security challenge to continue.'; case 'OUTGOING_PAYMENT.PROCESSING': return 'Payment in progress...'; case 'OUTGOING_PAYMENT.COMPLETED': diff --git a/openapi.yaml b/openapi.yaml index d13da97f..25844997 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -19954,7 +19954,7 @@ components: |--------|-------------| | `CREATED` | Initial lookup has been created | | `PENDING` | Quote has been created | - | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU); authorize the transaction's `scaChallenge` to proceed. | + | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU). The challenge is carried by the quote, not the transaction — fetch `GET /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its `scaChallenge` via `POST /quotes/{quoteId}/authorize`. | | `PROCESSING` | Funding has been received and payment initiated | | `COMPLETED` | Cross border payment has been received, converted and payment has been sent to the offramp network | | `REJECTED` | Receiving institution or wallet rejected payment, payment has been refunded | @@ -20377,7 +20377,7 @@ components: | Status | Description | |--------|-------------| | `PENDING` | Quote is pending confirmation | - | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU); authorize the transaction's `scaChallenge` to proceed. | + | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU). The challenge is carried by the quote, not the transaction — fetch `GET /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its `scaChallenge` via `POST /quotes/{quoteId}/authorize`. | | `EXPIRED` | Quote wasn't executed before expiry window | | `PROCESSING` | Executing the quote after receiving funds | | `COMPLETED` | Payout successfully reached the destination | @@ -23318,6 +23318,7 @@ components: type: string enum: - OUTGOING_PAYMENT.PENDING + - OUTGOING_PAYMENT.PENDING_AUTHORIZATION - OUTGOING_PAYMENT.PROCESSING - OUTGOING_PAYMENT.COMPLETED - OUTGOING_PAYMENT.FAILED diff --git a/openapi/components/schemas/transactions/OutgoingTransactionStatus.yaml b/openapi/components/schemas/transactions/OutgoingTransactionStatus.yaml index d49ffd37..e23f5e26 100644 --- a/openapi/components/schemas/transactions/OutgoingTransactionStatus.yaml +++ b/openapi/components/schemas/transactions/OutgoingTransactionStatus.yaml @@ -12,7 +12,7 @@ description: | | Status | Description | |--------|-------------| | `PENDING` | Quote is pending confirmation | - | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU); authorize the transaction's `scaChallenge` to proceed. | + | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU). The challenge is carried by the quote, not the transaction — fetch `GET /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its `scaChallenge` via `POST /quotes/{quoteId}/authorize`. | | `EXPIRED` | Quote wasn't executed before expiry window | | `PROCESSING` | Executing the quote after receiving funds | | `COMPLETED` | Payout successfully reached the destination | diff --git a/openapi/components/schemas/transactions/TransactionStatus.yaml b/openapi/components/schemas/transactions/TransactionStatus.yaml index dc5524c0..767c3176 100644 --- a/openapi/components/schemas/transactions/TransactionStatus.yaml +++ b/openapi/components/schemas/transactions/TransactionStatus.yaml @@ -16,7 +16,7 @@ description: | |--------|-------------| | `CREATED` | Initial lookup has been created | | `PENDING` | Quote has been created | - | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU); authorize the transaction's `scaChallenge` to proceed. | + | `PENDING_AUTHORIZATION` | Awaiting Strong Customer Authentication. Only occurs for customers in a region where SCA is required (e.g. EU). The challenge is carried by the quote, not the transaction — fetch `GET /quotes/{quoteId}` using the transaction's `quoteId`, then authorize its `scaChallenge` via `POST /quotes/{quoteId}/authorize`. | | `PROCESSING` | Funding has been received and payment initiated | | `COMPLETED` | Cross border payment has been received, converted and payment has been sent to the offramp network | | `REJECTED` | Receiving institution or wallet rejected payment, payment has been refunded | diff --git a/openapi/components/schemas/webhooks/WebhookType.yaml b/openapi/components/schemas/webhooks/WebhookType.yaml index fab72c52..da61ac7f 100644 --- a/openapi/components/schemas/webhooks/WebhookType.yaml +++ b/openapi/components/schemas/webhooks/WebhookType.yaml @@ -1,6 +1,7 @@ type: string enum: - OUTGOING_PAYMENT.PENDING + - OUTGOING_PAYMENT.PENDING_AUTHORIZATION - OUTGOING_PAYMENT.PROCESSING - OUTGOING_PAYMENT.COMPLETED - OUTGOING_PAYMENT.FAILED