SWIFT Payments on Behalf of Customers
This guide outlines how to make SWIFT payments on behalf of your business customers using the Keyrails API. These are POBO (Pay On Behalf Of) payments, where your customer's name appears as the sender.
Overview
To initiate a POBO SWIFT payment for a customer, follow these steps:
- Create a Customer
- Create a Wallet
- Check wallet balance
- Create a SWIFT Transfer
- Monitor Transfer Status
Step 1: Create a Customer
Onboard using KYB linkInstead of integrating the Customer API directly to onboard and verify (KYC/KYB) customer, you can generate onboarding Links for a faster setup. See Guide here
Note: customer onboarding functionality is currently unavailable for U.S. residents.
If withLivenessCheck=FALSE, then:
- On step 1b :
identityDocumentFrontmust be provided - On step 1d :
directorsLivenessCheckandshareHoldersLivenessCheckmust be provided.
curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/customers \
--header 'Authorization: Bearer {{AccessToken}}' \
--header 'accept: application/json' \
--header 'content-type: application/*+json' \
--data '
{
"businessLegalName": "string",
"businessTradeName": "string",
"taxIdentificationNumber": "string",
"website": "string",
"incorporationDate": "1999-03-21",
"companyRegistrationNumber": "string",
"businessIndustries": [
"AccountFunding"
],
"businessModel": "string",
"customerJurisdictions": [
"NG", "GB"
],
"regulatedEntity": "string",
"notRegulatedReason": "string",
"sourceOfFunds": "string",
"accountPurpose": "string",
"email": "string",
"transactionOriginCountries": [
"GB"
],
"transactionDestinationCountries": [
"GB", "NG"
],
"companySourceOfWealthExplanation": "string",
"shareholdersSourceOfWealthExplanation": "string",
"fundingSourceExplanation": "string",
"countryOfIncorporation": "GA",
"registeredAddress": {
"street1": "string",
"city": "string",
"country": "CA"
},
"operationalAddress": {
"street1": "string",
"city": "string",
"country": "CA"
},
"contactPerson": {
"firstName": "string",
"lastName": "string",
"phoneNumber": "+44791234560",
"email": "string"
},
"businessIndustryType": "CorporateOrMerchant",
"withLivenessCheck": true,
"termsOfServiceAcceptance": {
"date": "2025-07-28T20:00:44.191Z",
"ipAddress": "201.221.240.218"
},
"customerBaseBreakdown": "Retail",
"isBusinessRegulated": true,
"compliance": {
"hasFinancialCrimeHistoryLast5Years": false,
"isNegativeNewsAndSanctionsScreeningPerformed": false,
"isTransactionMonitoringOrBlockchainAnalyticsPerformed": true,
"isKYCPerformed": true,
"financialCrimeProceedingsDescription": "string",
"negativeNewsAndSanctionsVendor": "string",
"transactionMonitoringOrBlockchainAnalyticsVendor": "string",
"kycVendor": "string"
},
"transactionBreakdown": {
"stablecoinTxCountMonthly": "Range1To10",
"incomingStablecoinAvgUsdValue": "Usd15kTo50k",
"outgoingStablecoinTxCountMonthly": "Range1To10",
"outgoingStablecoinAvgUsdValue": "Usd15kTo50k",
"incomingAchTxCountMonthly": "Range1To10",
"incomingAchAvgUsdValue": "Usd15kTo50k",
"outgoingAchTxCountMonthly": "Range1To10",
"outgoingAchAvgUsdValue": "Usd15kTo50k",
"incomingDomesticWireTxCountMonthly": "Range1To10",
"incomingDomesticWireAvgUsdValue": "Usd15kTo50k",
"outgoingDomesticWireTxCountMonthly": "Range1To10",
"outgoingDomesticWireAvgUsdValue": "Usd15kTo50k",
"incomingInternationalWireTxCountMonthly": "Range1To10",
"incomingInternationalWireAvgUsdValue": "Usd15kTo50k",
"outgoingInternationalWireTxCountMonthly": "Range1To10",
"outgoingInternationalWireAvgUsdValue": "Usd15kTo50k",
"estimatedMonthlyVolumeUsd": "Usd500kTo1m",
"transactionTypes": ["StablecoinTransactions"],
"preferredSettlementCurrencies": ["USD"]
}
}
'Response
{
"customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}Step 1b: Add Associated Persons
If withLivenessCheck=FALSE when creating a customer in step 1, then identityDocumentFront must be provided.
curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/customers/{customerId}/associated-persons \
--header 'Authorization: Bearer {{AccessToken}}' \
--header 'accept: application/json' \
--header 'content-type: application/*+json' \
--data '
{
"firstName": "string",
"lastName": "string",
"email": "string",
"dateOfBirth": "1988-10-28",
"shareHolderPercentage": 25,
"isDirector": true,
"address": {
"street1": "string",
"city": "string",
"state": "string",
"country": "string"
},
"identity": {
"type": "Passport",
"countryCode": "JP",
"identityNumber": "string",
"identityDocumentFront": "data:application/pdf;base64,abdcxxxx",
"identityDocumentBack": "data:application/pdf;base64,abdcxxxx"
}
}
'Response
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"verificationUrl": "string" // when withVerification=TRUE
}Step 1c: Add Associated Entities
curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/customers/{customerId}/associated-entities \
--header 'Authorization: Bearer {{AccessToken}}' \
--header 'accept: application/json' \
--header 'content-type: application/*+json' \
--data '
{
"entityLegalName": "string",
"shareHolderPercentage": 25,
"businessTradeName": "string",
"incorporationNumber": "string",
"relationshipEstablishedAt": "1988-10-28",
"countryOfRegistration": "AL",
"registeredAddress": {
"street1": "string",
"city": "string",
"state": "string",
"country": "CA"
}
}
'Response
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}Step 1d: Upload documents
- Required file types:
shareholdersProofOfAddress,directorsProofOfAddress,shareholdersRegistry,directorsRegistry,companyProofOfAddress,businessRegistrationDocument,articlesOfAssociationOrMemorandum, - If
withLivenessCheck=FALSEwhen creating a customer in step 1, then bothdirectorsLivenessCheckandshareHoldersLivenessCheckmust be provided.
curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/customers/{customerId}/documents \
--header 'Authorization: Bearer {{AccessToken}}' \
--header 'accept: application/json' \
--header 'content-type: application/*+json' \
--data '
{
"fileType": "companyProofOfAddress",
"fileName": "CompanyProofOfAddress.pdf",
"fileBlob": "data:application/pdf;base64,abdcxxxx"
}
'Step 1e: Complete and submit onboarding
Finalise and submit the full customer onboarding package for review.
curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/customers/{customerId}/submit \
--header 'Authorization: Bearer {{AccessToken}}' \
--header 'accept: application/json'
Simulation - update customer statusNote you can update the customer status using the status simulation endpoint. See API Reference
Monitor Customer onboarding Status
curl --request GET \
--url https://api.sandbox.keyrails.com/api/v1/customers/{customerId} \
--header 'Authorization: Bearer {{AccessToken}}' \
--header 'accept: application/json'Customer Webhooks Examples
Before proceeding, ensure that your webhook configuration is set up. Refer to the setup guide for detailed instructions.
-
Webhook types:
Identity -
resourceId: References the customer IDApproved:{ "tenantId": "36a6deef-8d5a-4560-b17d-e73f1dd3cd88", "action": "Update", "id": "05dfa0a6-0e9a-4891-9b20-42b5b2c39dbd", "resourceId": "d0ffaf3b-a73b-461b-a82b-c2cff911cb13", "resourceType": "Identity", "createdAtUtc": "2025-07-29T13:58:56.8454302Z", "changes": { "complianceStatus": "Approved" } }Rejected:{ "tenantId": "36a6deef-8d5a-4560-b17d-e73f1dd3cd88", "action": "Update", "id": "c7f89968-d91f-4b2f-80c6-9ea2e1d6ad8a", "resourceId": "d0ffaf3b-a73b-461b-a82b-c2cff911cb13", "resourceType": "Identity", "createdAtUtc": "2025-07-29T14:00:26.1457208Z", "changes": { "complianceStatus": "Rejected", "rejectionReasons": [] } }
Step 2: Create a wallet
Create a wallet to hold funds for the customer.
curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/wallets \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{AccessToken}}' \
--data '{
"customerId": "f3675f2e-da61-42e9-a9db-eef8bd4fb4e2",
"network": "Sepolia" // Ethereum for production
}'Response:
{
"id": "d3a685f1-5803-4dc4-b63f-d9281879a686",
"createdAtUtc": "2025-08-21T09:03:35.356903+00:00",
"status": "Active",
"customerId": "6da60529-9ba5-4370-88f7-a23e96041707",
"cryptoDepositInstructions": [
{
"network": "Sepolia",
"address": "0xafabec37213042e92af9d88e81dd00ea6120ab7d"
}
]
}
Add some test funds - Top up wallet balancecurl --request POST \ --url https://api.sandbox.keyrails.com/api/v1/wallets/simulate-balance \ --header 'Authorization: Authorization: Bearer {{AccessToken}}' \ --header 'accept: application/json' \ --header 'content-type: application/*+json' \ --data ' { "walletAddress": "0x4d0280da2f2fDA5103914bCc5aad114743152A9c", "amount": "50000000", "tokenType": "USDC" // or USDT } '
Step 3: Check wallet balance
curl --request GET \
--url https://api.sandbox.keyrails.com/api/v1/wallets/{walletId}/balance \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{AccessToken}}'
Response:
[
{
"currency": "USDT",
"network": "Sepolia",
"total": 0
},
{
"currency": "USDC",
"network": "Sepolia",
"total": 0
}
]
Internal Wallet TransfersYou can transfer funds between a customer’s wallet and a tenant wallet.
Wallet Deposit Webhooks Example
Before proceeding, ensure that your webhook configuration is set up. Refer to the setup guide for detailed instructions.
-
Webhook types:
Transaction -
resourceId: References the transaction IdCrypto deposited:{ "tenantId": "2711ad4d-2c6b-4238-9f9d-2381d7a6d214", "action": "Update", "id": "d58d77b2-396e-44b1-a918-90ca655d498b", "resourceId": "58a8bedd-7bda-4471-936d-f9d32191524f", "resourceType": "Transaction", "createdAtUtc": "2025-07-29T14:15:25.7333739Z", "changes": { "transactionStatus": "Completed", "transactionType": "DepositCrypto" } }
Step 4: Initiate SWIFT Transfer
Send funds from the wallet to an external bank account via SWIFT.
Create an external account
Provide customerId when creating an external account on behalf of a customer.
curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/external-accounts \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{AccessToken}}' \
--data '{
"customerId": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "ABC LTD",
"phone": "+447934758009",
"website": "https://example.com", // company website or link business registry record
"address": {
"street1": "1 Main Road",
"street2": "string",
"postalCode": "string",
"city": "string",
"country": "GB"
},
"bankName": "asdsad",
"bankAddress": {
"street1": "5 Money Lane",
"street2": "string",
"postalCode": "10001",
"city": "Big City",
"country": "CN"
},
"swift": {
"accountNumber": "string", // optional when beneficiaryIban is provided
"iban": "string", // optional when beneficiaryAccountNumber is provided
"bic": "ABC123456",
"intermediaryBankId": "string" // optional - provide Intermidary bank BIC or Routing number
}
}
'Response:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"accountNumberLast4": "string",
"receiverName": "string",
"accountName": "string",
"swiftCode": "string",
"iban": "string",
"createdAtUtc": "2025-05-22T18:04:27.272Z"
}Create a SWIFT Transfer
To expedite the compliance check procedure, you need to upload documents that support the transaction. For instance, if the payment pertains to a trade, you can upload multiple files such as invoice, sender's company registration document and other supporting documents.
See API reference.curl --request POST \
--url https://api.sandbox.keyrails.com/api/v1/transfers \
--header 'accept: application/json' \
--header 'content-type: application/*+json' \
--header 'Authorization: Bearer {{AccessToken}}' \
--data '{
"source": {
"currency": "USDC",
"network": "ethereum",
"walletId": "497f6eca-6276-4993-bfeb-53cbbbba6f08"
},
"destination": {
"currency": "GBP",
"externalAccountId": "497f6eca-6276-4993-bfeb-53cbbbba6f08"
},
"purposeOfPayment": "PaymentForGoods",
"receiverAmount": 15000.00, // this is the amount in destination currency
"memo": "test payment",
"documents": [
"data:image/[type];base64,abdcxxxx",
"data:image/[type];base64,abdcxxxx"
]
}
'Response:
The API responds with a transactionId, which can be used to track the transfer.
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"transactionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "New",
...
}
Transfer QuoteUse the transfer quote endpoint to see the transfer quote before creating a transfer. See Transfer Quote API
Monitor Transfer Status
curl --request GET \
--url https://api.sandbox.keyrails.com/api/v1/transactions/{transactionId} \
--header 'accept: application/json' \
--header 'content-type: application/*+json' \
--header 'Authorization: Bearer {{AccessToken}}' \Response:
The API responds with a transactionId, which can be used to track the transfer.
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"transactionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "New",
"trackingStatus": "string",
"trackingReference": "string",
"currency": "USD",
"sourceCurrency": "USD",
"destinationCurrency": "USD",
"network": "string",
"amount": 0,
"cryptoAmount": 0,
"transactionFee": 0,
"appliedFee": 0,
"totalAmount": 0,
"comment": "string",
"memo": "string",
"purposeOfPayment": "string",
"country": "string",
...
}Transfer webhooks example
Before proceeding, ensure that your webhook configuration is set up. Refer to the setup guide for detailed instructions.
- Webhook types:
Transaction resourceId: References the transaction ID
Processing:
{
"tenantId": "2711ad4d-2c6b-4238-9f9d-2381d7a6d214",
"action": "Create",
"id": "e95503c4-f8bb-4f6b-89d3-42f10307a6bc",
"resourceId": "b5787a92-82ba-4f09-bacc-02f57ea601ed",
"resourceType": "Transaction",
"createdAtUtc": "2025-07-29T13:47:04.7710866Z",
"changes": {
"transactionStatus": "Processing",
"transactionType": "SwiftUsdc",
}
}Completed:
{
"tenantId": "36a6deef-8d5a-4560-b17d-e73f1dd3cd88",
"action": "Update",
"id": "4c01fdd9-923d-4ad9-9508-48a39cc85a3b",
"resourceId": "6470d2bd-89e3-4e10-a3ff-605d820f8da3",
"resourceType": "Transaction",
"createdAtUtc": "2025-07-29T14:06:04.1804263Z",
"changes": {
"transactionStatus": "Completed",
"transactionType": "SwiftUsdc",
}
}Failed:
{
"tenantId": "36a6deef-8d5a-4560-b17d-e73f1dd3cd88",
"action": "Update",
"id": "44110cf3-05c4-4652-b718-fc0d6475453f",
"resourceId": "6470d2bd-89e3-4e10-a3ff-605d820f8da3",
"resourceType": "Transaction",
"createdAtUtc": "2025-07-29T14:07:01.6713778Z",
"changes": {
"transactionStatus": "Failed",
"transactionType": "SwiftUsdc",
}
}