- Quick reference
- The response envelope
- Success responses
- Unified error object (
error_details) - Error responses
- Validation errors
- Errors that don’t use the envelope
- HTTP status code reference
- Authentication & access errors
- Account, customer & team restrictions
- Money movement errors
- Idempotency, duplicates & safe retries
- Rate limits
- Server & upstream errors
- Parsing responses: a reference implementation
- Best practices
Quick reference
Rule of thumb: treat a response as successful only when the HTTP status is2xxand the body says"status": "success". Never treat a request as successful just because the body containsdata.
The response envelope
Almost every endpoint wraps its result in the same four-field envelope.Note onstatus_codevs. HTTP status: on failed responses, the HTTP status always matchesstatus_code. On successful responses, the HTTP status is always200, even whenstatus_codein the body says201(created) or204(deleted). Read the HTTP status to tell success from failure, and usestatus_codeonly if you need the finer distinction.
Success responses
Single resource or action
"status_code": 201. The HTTP status is still 200.
A delete endpoint may return "status_code": 204 together with a body such as {"message": "Record deleted successfully"} inside data, again with HTTP 200. Unlike a true HTTP 204, these responses do have a body.
Paginated lists
List endpoints add apagination object next to data:
- Request later pages with
?page=N. Many list endpoints also acceptper_page. next_page_urlisnullon the last page, andprev_page_urlisnullon the first.- When a list endpoint returns everything in one go,
paginationisnull.
Computed fields (include_appends)
To keep responses fast, some computed/derived attributes are left out of resource objects by default. Add ?include_appends=1 to a request to include them. Only use it when you need those fields, because it makes large responses noticeably slower.
Unified error object (error_details)
Every failed response from the API carries a top-level error_details object with the same shape, whichever endpoint or platform layer produced it. That includes validation failures, authentication and permission errors, unknown routes, wrong methods, rate limits, server errors and maintenance. To handle errors, read this one object. You don’t need to handle each of the older shapes described below.
error_details is added next to the existing fields. It doesn’t replace or change any of them, so integrations that already read data.error, message or errors keep working unchanged.
Request ID and tracing (request_id)
Every error response returns a unique error.request_id string (e.g. req_8f1c2a3b4c5d6e7f8a9b0c1d). If you provide an X-Request-Id header in your request, the server uses that exact ID for correlation. Include this request_id whenever reporting issues to Yativo support for instant tracing in server access logs.
How to detect failure: if
error_details is present, the request failed. Successful responses never include it.
code values
On5xxresponses,error_details.messageis always a safe, generic explanation such as"Something went wrong, please try again later". It never contains internal details. Internal database errors are never returned, whatever the status code. If one occurs, the response is a standard failure envelope withdata.erroranderror_details.messageset to"Something went wrong, please try again later", and HTTP status500.
More examples
A business-rule failure:error_details.
Error responses
Failed responses use the same envelope, with"status": "failed" and a non-2xx HTTP status:
message is usually the generic "Request failed.". The useful, specific explanation is inside data. Depending on the endpoint, data takes one of these shapes:
You don’t need to handle these shapes yourself: the same message is always available in
error_details.message, and field errors in error_details.fields.
Important: build your logic on error_details.code (or the HTTP status) and treat message text as information for humans: show it to people or log it, but don’t branch on its exact wording, because messages can change.
Validation errors
Input validation can fail in three ways, depending on the endpoint. 1. Field map inside the envelope (most endpoints)400 instead of 422.
2. Field map under errors inside the envelope
status, status_code or data:
beneficiary.account_number), and values are arrays of messages.
Reserved words
Endpoints that create or update customers, businesses, employees, profiles and KYC/KYB data reject free-text values containing reserved words. These fail with HTTP422:
yativois rejected anywhere in a value, including inside a longer word such asMyYativoShop.testandadminare rejected as standalone words (Test,test123,test_user,admin@acme.com). Words that merely contain them, such asContestorAdministrative, are accepted.- URLs, data URIs and uploaded files are not checked.
Disallowed markup and HTML/XML tags
To protect against XSS and prompt/markup injection, API requests containing HTML, XML, or custom tags (such as<script>, <mention>, <iframe>, <div>, etc.) in request field keys or string values are strictly rejected across the API with HTTP 422:
password, password_confirmation, current_password, old_password) and binary file uploads are exempt.
Errors that don’t use the envelope
Some errors are produced before your request reaches an endpoint, by the routing and framework layers. These return a minimal{"message": "..."} body with no status field. They still include error_details:
A few endpoints use a
success boolean instead of the envelope. The bank-transfer deposit-instruction lookup endpoints under /payin/... are one example:
error_details too.
HTTP status code reference
Authentication & access errors
Server-to-server requests authenticate with anX-Api-Key / X-Api-Secret pair from a whitelisted IP. Bearer tokens (JWTs) are for the Yativo dashboard. See Authentication and IP whitelisting.
Account, customer & team restrictions
These checks run on every authenticated request, before the endpoint itself.
Team members always act on behalf of the team owner’s business. KYC and other account checks are evaluated against the owner’s account, not the member’s.
Money movement errors
Payouts & transfers
Payout and wallet-transfer endpoints check the quote, amount, limits and balance before any funds move. Every failure below happens before your wallet is debited.
See payin-payout-integration.md and payout-receive-amount.md for the full payout flow.
Cards
Card issuance, funding and withdrawal charge fees to your USDC balance first. If that charge fails, the request returns HTTP402:
Unable to charge withdrawal fee. (402) and Unable to confirm the card balance right now. Please try again. (502).
A successful response isn’t final settlement
A200 from a payout, deposit or card-funding endpoint means the instruction was accepted, not that the money has settled. The transaction’s own status field (for example pending, processing, completed or failed) and the corresponding webhook events tell you the final outcome. A transaction can be accepted and still fail later, so always confirm the final state before treating it as complete.
Idempotency, duplicates & safe retries
All authenticatedPOST, PUT, PATCH and DELETE requests to the core /v1 API are protected against duplicates.
How it works
- Send a unique
Idempotency-Keyheader (a UUID is ideal) with every write request. - The first request with that key runs normally, and its response is stored for 60 minutes.
- Any repeat with the same key and same body within that window returns the stored response unchanged, with the same status and body, without running the operation again.
- While the first request is still running, a repeat returns
409.
Requests without an Idempotency-Key
If you don’t send the header, the platform deduplicates on the exact method, path and body. Two identical requests within 60 minutes return the first one’s stored response, even if you meant them as two separate operations. To deliberately repeat an identical operation, such as two payouts of the same amount to the same beneficiary, send a different Idempotency-Key on each.
What gets replayed
- Responses with HTTP status below 500, both successes and
4xxerrors, are stored and replayed. 5xxresponses are not stored, so retrying after a5xxreally does run the request again.
Retrying after fixing aReplayed responses look identical to the original. No header marks them as replays.4xx: if a payout fails withInsufficient wallet balanceand you then top up, resending the identical request with the same key, or with no key, returns the stored400for up to 60 minutes. Use a newIdempotency-Keywhen you retry after fixing the cause.
Card endpoints
On card endpoints (/v1/spend/..., /v1/corporate-card/...), your Idempotency-Key is also used to prevent duplicate card issuance, funding and withdrawals. Always send one.
Concurrent wallet creation
Creating a crypto wallet while an identical creation is still in progress returns:Rate limits
Reads and writes use separate buckets, so polling a transaction’s status doesn’t use up your write allowance. Enterprise plan accounts are exempt from the core
/v1 throughput limits.
Rate-limited routes return these headers on every response:
A
429 from the rate limiter uses the framework shape:
429: Too many incorrect attempts. Please request a new One time login O.T.P.
Server & upstream errors
The generic error
The most common unexpected-failure response is:400 on most endpoints and 500 on some. It means the platform caught an unexpected problem while processing your request. Internal details are never exposed in this response.
Upstream failures
When a downstream banking, card or payment partner fails, you receive502 (or 503 when the service is temporarily unavailable) with a descriptive message, for example:
Unable to calculate the payout quote, please retry.(502)Exchange rates are temporarily unavailable, please try again shortly(503)Service temporarily unavailable.(503, top-levelmessage:Service Unavailable)
data.error. Show it to support staff, but don’t parse it.
Retrying money movement after an unexpected error
For a5xx, or the generic 400 above, on an endpoint that moves money:
- Don’t retry blindly. The operation may have partly gone through.
- Look up the transaction (by your reference or the transaction list) or wait for the webhook.
- Only if no transaction exists, retry with backoff (for example 1s, 2s, 4s, 8s, capped at about 30s). Use a new
Idempotency-Keyfor a4xx, or the same key for a5xx, since5xxresponses aren’t stored.
GET) are always safe to retry.
Parsing responses: a reference implementation
Because every failure carrieserror_details, a client needs only a few lines:
code:
Best practices
- Read errors from
error_details. It has the same shape on every failure, so one handler covers the whole API. Don’t read errors from the top-levelmessage, which is usually just"Request failed.". - Check the HTTP status as well as the body. Treat a response as failed if it has a non-
2xxstatus or includeserror_details. Don’t assume success just becausedatais present. - Branch on
error_details.code(or the HTTP status), not message text. Messages are written for people and may change. - Always send an
Idempotency-Keyon writes, and use a fresh one when you retry after fixing a4xx. - Never retry
401,403,404or422unchanged. They won’t succeed until the cause is fixed. - Respect
Retry-Afteron429, and poll status endpoints within the read limit. - Treat
200on money movement as “accepted”, and confirm the final state through the transaction status or webhooks. - Log the full response body (HTTP status,
error_detailsanddata) when you contact support. - Avoid reserved words (
test,admin,yativo) in customer, business and KYC fields, and never use test-named customers for live transactions.

