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 :
identityDocumentFront
must be provided - On step 1d :
directorsLivenessCheck
andshareHoldersLivenessCheck
must 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",
"companySourceOfWealthEvidence": "Base64String",
"shareholdersSourceOfWealthExplanation": "string",
"shareholdersSourceOfWealthEvidence": "Base64String",
"fundingSourceEvidence": "Base64String",
"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,
"address": {
"street1": "string",
"city": "string",
"state": "string",
"country": "string"
},
"identity": {
"type": "Passport",
"countryCode": "JP",
"identityNumber": "string",
"identityDocumentFront": "data:image/pdf;base64,abdcxxxx",
"identityDocumentBack": "data:image/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:
organizationChart
,shareholdersProofOfAddress
,directorsProofOfAddress
,ownershipChart
,shareholdersRegistry
,directorsRegistry
,companyProofOfAddress
,businessRegistrationDocument
,articlesOfAssociationOrMemorandum
, - If
withLivenessCheck=FALSE
when creating a customer in step 1, then bothdirectorsLivenessCheck
andshareHoldersLivenessCheck
must 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 '
{
"files": [
// ---------- REQUIRED FILES ----------
{
"fileType": "organizationChart",
"fileName": "OrganizationChart.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "shareholdersProofOfAddress",
"fileName": "ShareholdersProofOfAddress.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "directorsProofOfAddress",
"fileName": "DirectorsProofOfAddress.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "ownershipChart",
"fileName": "OwnershipChart.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "shareholdersRegistry",
"fileName": "ShareholdersRegistry.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "directorsRegistry",
"fileName": "DirectorsRegistry.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "companyProofOfAddress",
"fileName": "CompanyProofOfAddress.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "businessRegistrationDocument",
"fileName": "BusinessRegistrationDocument.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "articlesOfAssociationOrMemorandum",
"fileName": "ArticlesOfAssociationOrMemorandum.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
// ---------- OPTIONAL FILES ----------
{
"fileType": "other",
"fileName": "Other.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "parentCompanyBusinessRegistrationDocument",
"fileName": "ParentCompanyBusinessRegistrationDocument.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "parentCompanyShareholdersRegistry",
"fileName": "ParentCompanyShareholdersRegistry.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "parentCompanyDirectorsRegistry",
"fileName": "ParentCompanyDirectorsRegistry.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "parentCompanyShareholdersProofOfAddress",
"fileName": "ParentCompanyShareholdersProofOfAddress.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "parentCompanyDirectorsProofOfAddress",
"fileName": "ParentCompanyDirectorsProofOfAddress.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "parentCompanyShareholdersId",
"fileName": "ParentCompanyShareholdersId.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "parentCompanyDirectorId",
"fileName": "ParentCompanyDirectorId.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "complianceOfficerCV",
"fileName": "ComplianceOfficerCV.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "independentAmlAudit",
"fileName": "IndependentAmlAudit.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "amlPolicy",
"fileName": "AmlPolicy.pdf",
"fileBlob": "data:image/pdf;base64,abdcxxxx"
},
{
"fileType": "regulatoryLicenses",
"fileName": "RegulatoryLicenses.pdf",
"fileBlob": "data:image/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.
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 '{
"customerId": "string",
"source": {
"currency": "USDC",
"network": "Sepolia" // Ethereum for production
"walletId": "string"
},
"destination": {
"currency": "GBP",
"receiver": {
"name": "string",
"phone": "+447912345678",
"address": {
"street1": "string",
"street2": "string", // optional
"postalCode": "string", // optional
"city": "string",
"state": "string", // optional
"country": "string"
}
},
"bank": {
"name": "string",
"address": {
"street1": "string",
"street2": "string", // optional
"postalCode": "string", // optional
"city": "string",
"state": "string", // optional
"country": "string"
},
"accountNumber": "string", // optional - when iban is provided
"iban": "string", // optional - when accountNumber is provided
"bic": "string"
}
},
"purposeOfPayment": "PaymentForGoods",
"receiverAmount": 5000.00, // Or receiverAmount in destination currency eg. GBP
"memo": "INV-001",
"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": "SettlementGlobalConnectUsdc",
}
}
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": "SettlementGlobalConnectUsdc",
}
}
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": "SettlementGlobalConnectUsdc",
}
}
Updated 14 days ago