Creating a Recipient
Create a payee with its payment details in one call, across every supported rail.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Getting Started | Platform overview and setup |
| Api Basics | Required headers and request configuration |
| Authentication | How to obtain the corporation token |
| Corridor Transfers | Corridor payouts name a recipient by id |
Overview
A recipient is a stored payee: an identity — a person or a business — plus one or more sets of payment
details, each covering a rail. Corridor transfers and card transfers pay a recipient by id rather than
carrying beneficiary data in the request.
Two ways to create one:
v1 — POST /api/v1/recipients | v2 — POST /api/v2/recipients | |
|---|---|---|
| Creates | The identity only | The identity and its first payment details |
| Payment details | A second call to POST /api/v1/recipients/{recipientId}/payment_details | In the same request |
| Name check | Not performed — there are no account details to check against | Performed against the details |
| Use for | — | Everything |
Use v2. The v1 pair is documented in Managing Recipients for the
case where you are adding a second rail to an existing recipient.
All create and update calls require TransactionSu or TransactionManage.
Create a Recipient with Payment Details
POST /api/v2/recipients
Request body:
{
"first_name": "Alex",
"last_name": "Grey",
"is_business": false,
"type": "Sepa",
"currencies": ["EUR"],
"sepa": {
"iban": "DE89370400440532013000",
"bic": "COBADEFF"
}
}| Field | Type | Required | Description |
|---|---|---|---|
is_business | boolean | Yes | true for a company, false for a person |
first_name | string | Conditional | Required when is_business is false |
last_name | string | Conditional | Required when is_business is false |
company_name | string | Conditional | Required when is_business is true. Must match ^[A-Za-z0-9&.,'’\-/() :+#]{2,100}$ |
nick_name | string | No | Display label, at most 127 characters |
type | string | Yes | Which rail these details are for — see the type table below |
currencies | array of string | Yes | Currencies this recipient accepts, e.g. ["EUR"], ["USDT"] |
<type object> | object | Yes | The details object matching type. See the per-rail tables below |
Response:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"personal_info": {
"first_name": "Alex",
"last_name": "Grey",
"is_business": false,
"nick_name": ""
},
"payment_details": [
{
"id": "64120850-73a1-4df5-a074-d463258c9deb",
"type": "Sepa",
"currencies": ["EUR"],
"sepa": {
"iban": "DE89370400440532013000",
"bic": "COBADEFF"
}
}
],
"name_check": {
"name_matched": true,
"resolved_name": ""
}
}| Field | Description |
|---|---|
id | Recipient id — recipient_id on a corridor transfer |
personal_info | The stored identity |
payment_details[] | The details created. payment_details[].id is recipient_payment_details_id on a corridor transfer |
name_check.name_matched | false means the name you supplied does not match the name the provider holds against the account |
name_check.resolved_name | The name the provider holds. Populated only on a partial match |
name_checkis a warning, not a rejection. A recipient withname_matched: falseis created and
is payable. Surface the mismatch before the first payout — a name mismatch is what a rail rejects
later, and the transfer will have been debited by then.
A crypto recipient can be created with a nickname instead of a name. When
typeisCryptoand
nick_nameis set, the first-name / last-name / company-name requirement is skipped entirely. For
every other type, and for crypto without a nickname, the identity fields are mandatory.
Payment Details by Type
type | Details object | Required fields |
|---|---|---|
Crypto | crypto | address, network |
Sepa | sepa | iban, bic |
Swift | swift | iban, bic; bank_name, bank_country, legal_address optional |
FasterPayment | faster_payments | account_number, sort_code |
Ach | ach | routing_number, account_number; bank_name, legal_address optional |
Spei | spei | clabe; recipient_id_number, legal_address optional |
Card | card | card_id, card_pan_last |
Pix | pix | pix_key, pix_key_type, recipient_name; recipient_id_number, legal_address optional |
FpsHk | fps_hk | fps_id, fps_id_type, bank_code, recipient_name |
Imps | imps | account_number, ifsc_code, recipient_name, destination_country |
Instapay | instapay | account_number, bank_code, recipient_name, destination_country |
BiFast | bi_fast | account_number, bank_code, recipient_name, destination_country |
Nip | nip | account_number, bank_code, recipient_name, destination_country |
Ipp | ipp | iban, recipient_name, destination_country |
Pse | pse | account_number, account_type, bank_code, recipient_name, document_type, document_number |
The
FasterPaymentdetails object is namedfaster_paymentson the way in andfaster_paymenton
the way out. The request takes"faster_payments": { ... }; the response reports the same details
under"faster_payment". Map between them explicitly.
Field Formats
| Field | Validation (regex) | Applies to |
|---|---|---|
iban | ^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$ | sepa, swift |
iban (UAE) | ^AE\d{21}$ | ipp |
bic | ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ | sepa, swift |
account_number | ^\d{8,18}$ | faster_payments, ach |
account_number | ^\d{9,18}$ | imps |
account_number | ^\d{10}$ | nip |
sort_code | ^\d{6}$ | faster_payments |
routing_number | ^\d{9}$ | ach |
clabe | ^\d{18}$ | spei |
recipient_name | ^[\p{L}\p{N}\s.'\-]{1,100}$ | every corridor type |
pix_key_type | ^(CPF|CNPJ|EMAIL|PHONE|EVP)$ | pix |
pix_key — CPF | ^\d{11}$ | pix |
pix_key — CNPJ | ^\d{14}$ | pix |
pix_key — PHONE | ^\+55\d{10,11}$ | pix |
pix_key — EVP | UUID form | pix |
fps_id_type | ^(FPS_ID|MOBILE|EMAIL)$ | fps_hk |
bank_code | ^\d{3}$ | fps_hk, nip, pse |
bank_code | ^([A-Z]{3,4}|[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?)$ | instapay |
bank_code | ^(\d{3}|[A-Z]{4}ID[A-Z]{2})$ | bi_fast |
ifsc_code | ^[A-Z]{4}0[A-Z0-9]{6}$ | imps |
account_type | ^(SAVINGS|CHECKING)$ | pse |
document_type | ^(CC|NIT|CE|PP)$ | pse |
document_number | ^[A-Z0-9]{5,20}$ | pse |
recipient_nameaccepts Unicode letters and digits — unlike employee names, which are restricted to
unaccented Latin.José Mülleris a valid recipient name.
The PSE
document_typeregex acceptsPP, while the field's own documentation namesPASSPORT.
SendPP.
Examples by Rail
Crypto
{
"is_business": false,
"nick_name": "Treasury cold wallet",
"type": "Crypto",
"currencies": ["USDT"],
"crypto": {
"address": "0xAAFF0821A09A1Aac28B72dD3Ff410A7ea5FEb874",
"network": "Base"
}
}ACH
{
"company_name": "Acme Supplies Inc.",
"is_business": true,
"type": "Ach",
"currencies": ["USD"],
"ach": {
"routing_number": "123456789",
"account_number": "12345678",
"bank_name": "The Bank of New York",
"legal_address": {
"line1": "10 Downing Street",
"city": "London",
"zip_code": "SW1A 2AA",
"country": "GB"
}
}
}PIX
{
"company_name": "Acme Brasil Ltda",
"is_business": true,
"type": "Pix",
"currencies": ["BRL"],
"pix": {
"pix_key": "12345678901",
"pix_key_type": "CPF",
"recipient_name": "Acme Brasil Ltda",
"recipient_id_number": "12345678901"
}
}Card
{
"first_name": "Alex",
"last_name": "Grey",
"is_business": false,
"type": "Card",
"currencies": ["USD"],
"card": {
"card_id": "wirex_123456789",
"card_pan_last": "4242"
}
}card.card_id is the tokenized card identifier and is what
Card Transfer takes as external_card_id.
Code
const response = await fetch(`${baseUrl}/api/v2/recipients`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${corporationToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
first_name: 'Alex',
last_name: 'Grey',
is_business: false,
type: 'Sepa',
currencies: ['EUR'],
sepa: { iban: 'DE89370400440532013000', bic: 'COBADEFF' }
})
});
const recipient = await response.json();
if (recipient.name_check && !recipient.name_check.name_matched) {
// The provider holds a different name — resolve before paying out
}response = requests.post(
f"{base_url}/api/v2/recipients",
headers={
"Authorization": f"Bearer {corporation_token}",
"Content-Type": "application/json",
},
json={
"first_name": "Alex",
"last_name": "Grey",
"is_business": False,
"type": "Sepa",
"currencies": ["EUR"],
"sepa": {"iban": "DE89370400440532013000", "bic": "COBADEFF"},
},
)
recipient = response.json()
name_check = recipient.get("name_check")
if name_check and not name_check["name_matched"]:
# The provider holds a different name — resolve before paying out
passbody, _ := json.Marshal(map[string]interface{}{
"first_name": "Alex",
"last_name": "Grey",
"is_business": false,
"type": "Sepa",
"currencies": []string{"EUR"},
"sepa": map[string]string{"iban": "DE89370400440532013000", "bic": "COBADEFF"},
})
req, _ := http.NewRequest("POST", baseURL+"/api/v2/recipients", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+corporationToken)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var recipient RecipientResponse
json.NewDecoder(resp.Body).Decode(&recipient)Error Handling
{
"error_reason": "ErrorMissingField",
"error_description": "company name is required",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "company_name" },
{ "key": "issue", "details": "missing" }
]
}Validation Errors (400)
| Error Reason | Error Details | Description | Resolution |
|---|---|---|---|
ErrorMissingField | field: first_name | first name is required | Required when is_business is false |
ErrorMissingField | field: last_name | last name is required | Required when is_business is false |
ErrorMissingField | field: company_name | company name is required | Required when is_business is true |
ErrorInvalidField | field: company_name | Invalid format for company name | 2–100 characters from the allowed set |
ErrorInvalidField | field: nick_name, max_length: 127 | nick name exceeds maximum length | Shorten the nickname |
ErrorMissingField | field: type | Type not provided | Send one of the supported types |
ErrorInvalidField | field: type | Unrecognised type | Use a value from the type table |
ErrorMissingField | field: <type object> | The details object for type is absent | Send the object matching type — sepa for Sepa, faster_payments for FasterPayment |
ErrorInvalidField | field: <type>.<field> | A details field failed its pattern | Check the format table |
Permission Errors (403)
| Error Reason | Description | Resolution |
|---|---|---|
ErrorPermissionDenied | User does not have required permissions | Creating a recipient needs TransactionManage or TransactionSu. Reading needs neither |
Server Errors (500)
| Error Reason | Description | Resolution |
|---|---|---|
ErrorGeneral | Recipient creation failure | Downstream rejection — most often a duplicate set of details for this corporation |
Updated 20 days ago

