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:

GuideWhy
Getting StartedPlatform overview and setup
Api BasicsRequired headers and request configuration
AuthenticationHow to obtain the corporation token
Corridor TransfersCorridor 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/recipientsv2 — POST /api/v2/recipients
CreatesThe identity onlyThe identity and its first payment details
Payment detailsA second call to POST /api/v1/recipients/{recipientId}/payment_detailsIn the same request
Name checkNot performed — there are no account details to check againstPerformed against the details
Use forEverything

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"
  }
}
FieldTypeRequiredDescription
is_businessbooleanYestrue for a company, false for a person
first_namestringConditionalRequired when is_business is false
last_namestringConditionalRequired when is_business is false
company_namestringConditionalRequired when is_business is true. Must match ^[A-Za-z0-9&.,'’\-/() :+#]{2,100}$
nick_namestringNoDisplay label, at most 127 characters
typestringYesWhich rail these details are for — see the type table below
currenciesarray of stringYesCurrencies this recipient accepts, e.g. ["EUR"], ["USDT"]
<type object>objectYesThe 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": ""
  }
}
FieldDescription
idRecipient id — recipient_id on a corridor transfer
personal_infoThe stored identity
payment_details[]The details created. payment_details[].id is recipient_payment_details_id on a corridor transfer
name_check.name_matchedfalse means the name you supplied does not match the name the provider holds against the account
name_check.resolved_nameThe name the provider holds. Populated only on a partial match

name_check is a warning, not a rejection. A recipient with name_matched: false is 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 type is Crypto and
nick_name is 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

typeDetails objectRequired fields
Cryptocryptoaddress, network
Sepasepaiban, bic
Swiftswiftiban, bic; bank_name, bank_country, legal_address optional
FasterPaymentfaster_paymentsaccount_number, sort_code
Achachrouting_number, account_number; bank_name, legal_address optional
Speispeiclabe; recipient_id_number, legal_address optional
Cardcardcard_id, card_pan_last
Pixpixpix_key, pix_key_type, recipient_name; recipient_id_number, legal_address optional
FpsHkfps_hkfps_id, fps_id_type, bank_code, recipient_name
Impsimpsaccount_number, ifsc_code, recipient_name, destination_country
Instapayinstapayaccount_number, bank_code, recipient_name, destination_country
BiFastbi_fastaccount_number, bank_code, recipient_name, destination_country
Nipnipaccount_number, bank_code, recipient_name, destination_country
Ippippiban, recipient_name, destination_country
Psepseaccount_number, account_type, bank_code, recipient_name, document_type, document_number

The FasterPayment details object is named faster_payments on the way in and faster_payment on
the way out.
The request takes "faster_payments": { ... }; the response reports the same details
under "faster_payment". Map between them explicitly.

Field Formats

FieldValidation (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 — EVPUUID formpix
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_name accepts Unicode letters and digits — unlike employee names, which are restricted to
unaccented Latin. José Müller is a valid recipient name.

The PSE document_type regex accepts PP, while the field's own documentation names PASSPORT.
Send PP.


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
    pass
body, _ := 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 ReasonError DetailsDescriptionResolution
ErrorMissingFieldfield: first_namefirst name is requiredRequired when is_business is false
ErrorMissingFieldfield: last_namelast name is requiredRequired when is_business is false
ErrorMissingFieldfield: company_namecompany name is requiredRequired when is_business is true
ErrorInvalidFieldfield: company_nameInvalid format for company name2–100 characters from the allowed set
ErrorInvalidFieldfield: nick_name, max_length: 127nick name exceeds maximum lengthShorten the nickname
ErrorMissingFieldfield: typeType not providedSend one of the supported types
ErrorInvalidFieldfield: typeUnrecognised typeUse a value from the type table
ErrorMissingFieldfield: <type object>The details object for type is absentSend the object matching typesepa for Sepa, faster_payments for FasterPayment
ErrorInvalidFieldfield: <type>.<field>A details field failed its patternCheck the format table

Permission Errors (403)

Error ReasonDescriptionResolution
ErrorPermissionDeniedUser does not have required permissionsCreating a recipient needs TransactionManage or TransactionSu. Reading needs neither

Server Errors (500)

Error ReasonDescriptionResolution
ErrorGeneralRecipient creation failureDownstream rejection — most often a duplicate set of details for this corporation

Did this page help you?