Skip to main content
Errors are returned with a conventional HTTP status and a JSON body:
message is a string for most failures, but request validation returns an array of messages — one per invalid field. Handle both shapes:

Status codes

permanent
The request itself is wrong. On registration this covers an unknown bankCode and an accountNumber the bank could not confirm; on search it means neither address nor accountNumber was supplied. Retrying identical input will fail identically.
permanent
The x-api-key header is missing or does not match a business. See Authentication.
permanent
Nothing matched. For a search, the bank account has not been registered with Paj. For rates, no active rate exists for that currency — read this as “not supported yet”, not as a malformed request.
transient
A failure on Paj’s side or on a rail it depends on. Safe to retry with backoff.

Validation happens before your request is processed

Unknown fields are rejected rather than ignored: sending a body property that is not part of the schema fails the request outright. Send exactly the documented fields.

Retrying safely

Registration is idempotent per bank account, so retrying a POST /pub/v2/bank-account that timed out is safe — you get the existing record back rather than a duplicate. The two GET endpoints are read-only and always safe to retry.
Do not retry a 400 or a 401. Both indicate the request will never succeed as sent; a retry loop on a failing bank account confirmation puts avoidable load on the banking rail.

Timeouts

Registration makes a live call to the banking rail to confirm the account and performs on-chain work to assign the address, so it is meaningfully slower than the other endpoints. Allow a generous client timeout — 30 seconds is comfortable — and prefer registering accounts ahead of time rather than inside a latency-sensitive flow.