This quickstart covers an example of sending a prefunded cross-border payout for a business customer on an unregulated platform.
Understanding Entity Mapping for B2B Payouts
In this guide, the entities map as follows:
Entity Type | Who They Are | In This Example |
---|
Platform | Your payouts platform | Your company providing AP automation |
Customer | Business sending payments | Your client company (e.g., Acme Corp) |
External Account | Vendors receiving payments | Maria Garcia (freelance contractor in Mexico) |
Flow: Your customer (a business) funds their internal account → uses your platform to send payments → to their vendors’ external bank accounts.
Get API credentials
Create Sandbox API credentials in the dashboard, then set environment variables for local use.
export GRID_BASE_URL="https://api.lightspark.com/grid/2025-10-13"
export GRID_CLIENT_ID="YOUR_SANDBOX_CLIENT_ID"
export GRID_CLIENT_SECRET="YOUR_SANDBOX_CLIENT_SECRET"
Use Basic Auth in cURL with -u "$GRID_CLIENT_ID:$GRID_API_SECRET"
.
Onboard a Customer
Onboard a customer using the hosted KYC/KYB link flow.
Generate KYC Link
Call the /customers/kyc-link
endpoint with your redirectUri
parameter to generate a hosted KYC URL for your customer.
The redirectUri
parameter is embedded in the generated KYC URL and will be used to automatically redirect the customer back to your application after they complete verification.
curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers/kyc-link?redirectUri=https://yourapp.com/onboarding-complete&platformCustomerId=019542f5-b3e7-1d02-0000-000000000001" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Response:
{
"kycUrl": "https://kyc.lightspark.com/onboard/abc123def456",
"platformCustomerId": "019542f5-b3e7-1d02-0000-000000000001"
}
Redirect Customer
Redirect your customer to the returned kycUrl
where they can complete their identity verification in the hosted interface.
The KYC link is single-use and expires after a limited time period for security.
Customer Completes Verification
The customer completes the identity verification process in the hosted KYC interface, providing required documents and information.
The hosted interface handles document collection, verification checks, and compliance requirements automatically.
After verification processing, you’ll receive a KYC status webhook notification indicating the final verification result.
Redirect back to your app
Upon successful KYC completion, the customer is automatically redirected to your specified redirectUri
URL.
On your redirect page, handle the completed KYC flow and integrate the new customer into your application.
The customer account will be automatically created by the system upon successful KYC completion. You can identify the new customer using your platformCustomerId
or other identifiers.
Get the Customer’s Internal Account
Once the customer is created, internal accounts will automatically be created on their behalf. Get their internal account in the desired currency for funding instructions.
curl -X GET "https://api.lightspark.com/grid/2025-10-13/internal-account?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USD" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Response:
{
"data": [
{
"id": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"balance": {
"amount": 0, // USD balance in cents
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
}
},
"fundingPaymentInstructions": [
{
"reference": "FUND-ABC123",
"instructionsNotes": "Include the reference code in your ACH transfer memo",
"bankAccountInfo": {
"accountType": "US_ACCOUNT",
"accountNumber": "9876543210",
"routingNumber": "021000021",
"accountHolderName": "Lightspark Payments FBO John Doe",
"bankName": "JP Morgan Chase"
}
}
],
"createdAt": "2025-10-03T12:00:00Z",
"updatedAt": "2025-10-03T12:00:00Z"
}
],
"hasMore": false,
"totalCount": 1
}
The fundingPaymentInstructions
provide the bank account details and reference code needed to fund
this internal account via ACH or wire transfer from the customer’s bank.
Fund the Internal Account
For this quickstart, we’ll fund the account using the /sandbox/internal-accounts/{accountId}/fund
endpoint to simulate receiving funds.
In production, your customer would initiate a transfer from their bank to the account details provided in the funding instructions,
making sure to include the reference code FUND-ABC123
in the transfer memo.
# Sandbox: fund internal account directly
curl -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/internal-accounts/InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965/fund" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"currencyCode": "USD",
"currencyAmount": 100000,
"reference": "FUND-ABC123"
}'
During the funding process, you’ll receive transaction status update webhooks.
Webhook Notification:
{
"transaction": {
"id": "Transaction:019542f5-b3e7-1d02-0000-000000000010",
"status": "COMPLETED",
"type": "INCOMING",
"receivedAmount": {
"amount": 100000,
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
}
},
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"settledAt": "2025-10-03T14:30:00Z",
"createdAt": "2025-10-03T14:25:00Z",
"description": "Internal account funding"
},
"timestamp": "2025-10-03T14:32:00Z",
"webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000020",
"type": "INCOMING_PAYMENT"
}
The internal account now has a balance of $1,000.00 (100000 cents).
Add the beneficiary as an External Account
Now add the beneficiary bank account where you want to send the funds. In this example, we’ll add a
Mexican CLABE account as the external account.
curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers/external-accounts" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"currency": "MXN",
"platformAccountId": "maria_garcia_account",
"accountInfo": {
"accountType": "CLABE",
"clabeNumber": "123456789012345678",
"bankName": "BBVA Mexico",
"beneficiary": {
"beneficiaryType": "INDIVIDUAL",
"fullName": "Maria Garcia",
"birthDate": "1990-01-01",
"nationality": "MX",
"address": {
"line1": "Av. Reforma 123",
"city": "Ciudad de México",
"state": "CDMX",
"postalCode": "06600",
"country": "MX"
}
}
}
}'
Response:
{
"id": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"status": "ACTIVE",
"currency": "MXN",
"platformAccountId": "maria_garcia_account",
"accountInfo": {
"accountType": "CLABE",
"clabeNumber": "123456789012345678",
"bankName": "BBVA Mexico",
"beneficiary": {
"beneficiaryType": "INDIVIDUAL",
"fullName": "Maria Garcia",
"birthDate": "1990-01-01",
"nationality": "MX",
"address": {
"line1": "Av. Reforma 123",
"city": "Ciudad de México",
"state": "CDMX",
"postalCode": "06600",
"country": "MX"
}
}
}
}
Create a quote
Create a quote to lock in the exchange rate, fees, and get the transfer details.
curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"lookupId": "LookupRequest:019542f5-b3e7-1d02-0000-000000000009", # ID from the lookup step
"source": {
"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" # USD internal account
},
"destination": {
"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
"currency": "MXN"
},
"lockedCurrencySide": "SENDING",
"lockedCurrencyAmount": 50000,
"description": "Payment to Maria Garcia for services"
}'
Amount Locking: You can lock either the sending amount (SENDING
) or receiving amount (RECEIVING
).
In this example, we’re locking the sending amount to exactly $500.00 USD (50000 cents). Alternatively,
you can lock the receiving amount to ensure that the receiver receives exactly some amount of the
destination currency.
Response:
{
"quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006",
"status": "PENDING",
"createdAt": "2025-10-03T15:00:00Z",
"expiresAt": "2025-10-03T15:05:00Z",
"source": {
"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
"currency": "USD"
},
"destination": {
"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
"currency": "MXN"
},
"sendingCurrency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
},
"receivingCurrency": {
"code": "MXN",
"name": "Mexican Peso",
"symbol": "$",
"decimals": 2
},
"totalSendingAmount": 50000,
"totalReceivingAmount": 861250,
"exchangeRate": 17.25,
"feesIncluded": 250,
"transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000015"
}
The quote shows:
- Sending: $500.00 USD (including $2.50 fee)
- Receiving: $8,612.50 MXN
- Exchange rate: 17.25 MXN per USD
- Quote expires: In 5 minutes
Quotes typically expire in 1-5 minutes. Make sure to execute the quote before the expiresAt
timestamp, or you’ll need to create a new quote.
Execute the quote
Execute the quote to initiate the transfer from the internal account to the external bank account.
curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes/Quote:019542f5-b3e7-1d02-0000-000000000006/execute" \
-H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Response:
{
"quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006",
"status": "PROCESSING",
"createdAt": "2025-10-03T15:00:00Z",
"expiresAt": "2025-10-03T15:05:00Z",
"source": {
"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
"currency": "USD"
},
"destination": {
"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123",
"currency": "MXN"
},
"sendingCurrency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
},
"receivingCurrency": {
"code": "MXN",
"name": "Mexican Peso",
"symbol": "$",
"decimals": 2
},
"totalSendingAmount": 50000,
"totalReceivingAmount": 861250,
"exchangeRate": 17.25,
"feesIncluded": 250,
"transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000015"
}
The quote status changes to PROCESSING
and the transfer is initiated. You can track the status by:
- Polling the quote endpoint:
GET /quotes/{quoteId}
- Waiting for webhook notifications
Completion Webhook:
{
"transaction": {
"id": "Transaction:019542f5-b3e7-1d02-0000-000000000015",
"status": "COMPLETED",
"type": "OUTGOING",
"sentAmount": {
"amount": 50000,
"currency": {
"code": "USD",
"name": "United States Dollar",
"symbol": "$",
"decimals": 2
}
},
"receivedAmount": {
"amount": 861250,
"currency": {
"code": "MXN",
"name": "Mexican Peso",
"symbol": "$",
"decimals": 2
}
},
"customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
"settledAt": "2025-10-03T15:02:30Z",
"createdAt": "2025-10-03T15:00:00Z",
"description": "Payment to Maria Garcia for services",
"exchangeRate": 17.25,
"quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006"
},
"timestamp": "2025-10-03T15:03:00Z",
"webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000025",
"type": "OUTGOING_PAYMENT"
}
Congrats, you’ve sent a real time cross-border payout to a Mexican bank account!