Field Validation Rules

Every regex the retail API enforces, and where the published rules differ from them.

Before You Start

Read the following guides before proceeding:

GuideWhy
Api BasicsRequest configuration

Overview

The API validates most string fields against a regular expression before the request reaches a handler.
The patterns are published at runtime so a client can validate locally and avoid a round trip.

GET /api/v1/validation/rules

No user identity headers are required — see Authentication.

Response:

{
  "rules": {
    "firstName": "^[a-zA-Z][a-zA-Z\\-' ]{1,50}$",
    "email": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
    "iban": "^[A-Z]{2}\\d{2}[A-Z0-9]{11,30}$"
  }
}
FieldDescription
rulesMap of rule name to regular expression

The published rules are not identical to the enforced ones. Two are stricter than the API, two
are published for fields the API does not validate, and four enforced patterns are not published at
all. The differences are listed below. Where they disagree, the enforced pattern is what decides
whether a request succeeds.


Enforced Patterns

These are the patterns the API applies.

Identity

RulePatternApplies to
First name^[a-zA-Z][a-zA-Z\-' ]{1,50}$first_name
Last name^[a-zA-Z][a-zA-Z\-' ]{1,50}$last_name
Email^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$email
Phone^\+?\d{1,15}$phone_number on user creation, KYC data update and phone-number change
Company name^[A-Za-z0-9&.,'’\-/() :+#]{2,100}$company_name
Recipient name^[\p{L}\p{N}\s.'\-]{1,100}$recipient_name on corridor payment details

Names accept only unaccented Latin letters, apostrophes, hyphens and spaces, and must be at least
2 characters — the pattern is a leading letter plus 1–50 more. Recipient names accept any Unicode
letter or digit, so José Müller is a valid recipient name and an invalid user name. Transliterate
before sending.

Addresses

RulePatternApplies to
Address line 1^[A-Za-z0-9&.,'’\-/() :+#]{2,100}$line1
Address line 2^[A-Za-z0-9&.,'’\-/() :+#]{2,100}$line2
City^[A-Za-z\s\-'.]{2,100}$city
State^[A-Za-z][A-Za-z\s\-']{1,99}$state
Postal code^[A-Za-z0-9\s\-]{3,12}$zip_code
Country^[A-Z]{2}$country

City accepts no digits. An address in a city whose name contains a numeral fails validation.

zip_code is always required on card delivery, including for countries with no postal code. Send
a placeholder that matches the pattern, e.g. "00000" — only the format is validated. See
Card Issuance.

Blockchain

RulePatternApplies to
Address^(0x[a-fA-F0-9]{40}|C[A-Za-z2-7]{55}|G[A-Za-z2-7]{55}|T[1-9A-HJ-NP-Za-km-z]{33}|r[1-9A-HJ-NP-Za-km-z]{24,34})$Wallet, token and account addresses
XRPL public key^[0-9A-Fa-f]{2,200}$user_public_key on POST /api/v1/accounts, and the action-confirmation token request

The address pattern covers five chains in one alternation: EVM 0x, Stellar C and G, Tron T, and
XRPL r-addresses. See Ripple (XRPL).

Cards

RulePatternApplies to
Card name^[a-zA-Z0-9\-':+# @]{1,50}$card_name on card issuance
Expiry date^(0[1-9]|1[012])/\d{4}$expiry_date on card activation
Card number last 4^\d{4}$card_number_last4

expiry_date is a four-digit year — 01/2028, not 01/28.

name_on_card is accepted without any pattern check. It is bound on every card-issuance request
and never matched against a regex, so whatever you send goes to the card processor, which may reject
the order with a synchronous 400. Do not rely on the published nameOnCard rule — see below.

Bank Details

RulePatternApplies to
IBAN^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$iban
UAE IBAN^AE\d{21}$iban on IPP details
BIC^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$bic
Account number^\d{8,18}$account_number on Faster Payments and ACH
Sort code^\d{6}$sort_code
Routing number^\d{9}$routing_number
CLABE^\d{18}$clabe
Currency^[A-Za-z0-9]{3,10}$currency on the recipient currency filter
Catalog index^[a-zA-Z]$catalog_index on the recipient catalog filter

IBAN and card number checksums are not validated. Only the format is checked, so a format-valid
IBAN with a bad check digit passes here and is rejected by the rail after the transfer is accepted.

Cross-Chain Recipients

RulePatternApplies to
Cross-chain address^[A-Za-z0-9:._\-]{1,128}$cross_chain_crypto.address
Cross-chain network^[A-Za-z0-9 \-_]{1,32}$cross_chain_crypto.network
Cross-chain tag^[A-Za-z0-9 \-_:.]{1,64}$cross_chain_crypto.tag

These are deliberately permissive — a cross-chain destination is validated by the receiving network, not
by this API.

Corridor Details

RulePatternApplies to
PIX key type^(CPF|CNPJ|EMAIL|PHONE|EVP)$pix_key_type
PIX key — CPF^\d{11}$pix_key
PIX key — CNPJ^\d{14}$pix_key
PIX key — phone^\+55\d{10,11}$pix_key
PIX key — EVP^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$pix_key
FPS ID type^(FPS_ID|MOBILE|EMAIL)$fps_id_type
FPS-HK bank code^\d{3}$bank_code on FPS-HK
IMPS account number^\d{9,18}$account_number on IMPS
IFSC code^[A-Z]{4}0[A-Z0-9]{6}$ifsc_code
InstaPay bank code^([A-Z]{3,4}|[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?)$bank_code on InstaPay
BI-FAST bank code^(\d{3}|[A-Z]{4}ID[A-Z]{2})$bank_code on BI-FAST
NIP account number^\d{10}$account_number on NIP
NIP bank code^\d{3}$bank_code on NIP
PSE account type^(SAVINGS|CHECKING)$account_type on PSE
PSE bank code^\d{3}$bank_code on PSE
PSE document type^(CC|NIT|CE|PP)$document_type on PSE
PSE document number^[A-Z0-9]{5,20}$document_number on PSE

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

Savings

RulePatternApplies to
Plan type^(Flexible|FixedTerms)$plan_type on savings account creation
Account name^[a-zA-Z0-9\-' ]{1,50}$name on savings account creation and rename
Term^\d+:\d+:\d+:\d+:\d+:\d+$term on savings account creation and upgrade

See Earn.

Rates

RulePatternApplies to
Tickers^[A-Za-z0-9]{2,10}/[A-Za-z0-9]{2,10}$tickers on GET /api/v1/rates

Where the Published Rules Differ

RulePublished by GET /api/v1/validation/rulesActually enforcedConsequence
address^(0x…|C…|G…|T…)$ — four chainsFive chains, adding r[1-9A-HJ-NP-Za-km-z]{24,34}The published rule rejects every XRPL address. A client validating locally against it cannot submit a valid XRPL account or recipient
phoneNumber^\+\d{1,15}$ — leading + required^\+?\d{1,15}$ — leading + optionalThe published rule rejects a bare 447700900123, which the API accepts
nameOnCard^[A-Za-z][A-Za-z .'-]{1,25}$Not enforcedname_on_card is accepted unvalidated. Validating locally against the published rule rejects input the API would take — though the card processor may still refuse it
cardNumber^\d{13,19}$Not usedNo request validated by this rule accepts a full card number

Four enforced patterns are not published at all — validate these from the table above rather than
from the endpoint:

crossChainAddress · crossChainNetwork · crossChainTag · xrplPublicKey

One naming quirk: the CLABE rule is published under the key clabel, not clabe. A client
looking the rule up by name will miss it.


Error Shape

Validation failures come back in two shapes, depending on the endpoint.

Structured — the field is in error_details under the key field, usually with the offending
pattern:

{
  "error_reason": "ErrorInvalidField",
  "error_description": "Invalid format for card name",
  "error_category": {
    "category": "CategoryValidationFailure",
    "http_status_code": 400
  },
  "error_details": [
    { "key": "field", "details": "card_name" },
    { "key": "issue", "details": "invalid_format" },
    { "key": "pattern", "details": "^[a-zA-Z0-9\\-':+# @]{1,50}$" }
  ]
}

Keyed — the field is the key, and error_description is generic:

{
  "error_reason": "ErrorGeneral",
  "error_description": "Request failed validation",
  "error_category": {
    "category": "CategoryValidationFailure",
    "http_status_code": 400
  },
  "error_details": [
    { "key": "first_name", "details": "First name is invalid" }
  ]
}

Handle both: read error_details[].key, and where it is field, read the field name from details.

The structured shape carries the offending pattern, which is the most reliable source of the enforced
regex for a specific field — more reliable than the published rules map.


Did this page help you?