diff --git a/mintlify/platform-overview/core-concepts/quote-system.mdx b/mintlify/platform-overview/core-concepts/quote-system.mdx index f6997418c..301405221 100644 --- a/mintlify/platform-overview/core-concepts/quote-system.mdx +++ b/mintlify/platform-overview/core-concepts/quote-system.mdx @@ -226,6 +226,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 c9ed40fa3..d73486dd1 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 @@ -190,6 +194,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 | @@ -270,6 +275,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;