Skip to main content
This guide explains how every Yativo API response is shaped, what each HTTP status code means, and how your integration should detect success, read error messages, retry safely and handle rate limits. This guide covers:

Quick reference

Rule of thumb: treat a response as successful only when the HTTP status is 2xx and the body says "status": "success". Never treat a request as successful just because the body contains data.

The response envelope

Almost every endpoint wraps its result in the same four-field envelope.
Note on status_code vs. HTTP status: on failed responses, the HTTP status always matches status_code. On successful responses, the HTTP status is always 200, even when status_code in the body says 201 (created) or 204 (deleted). Read the HTTP status to tell success from failure, and use status_code only if you need the finer distinction.

Success responses

Single resource or action

A create endpoint looks the same, but the body may carry "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 a pagination object next to data:
  • Request later pages with ?page=N. Many list endpoints also accept per_page.
  • next_page_url is null on the last page, and prev_page_url is null on the first.
  • When a list endpoint returns everything in one go, pagination is null.

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

On 5xx responses, error_details.message is 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 with data.error and error_details.message set to "Something went wrong, please try again later", and HTTP status 500.

More examples

A business-rule failure:
A rate limit, from the platform layer (no envelope):
The sections below describe the older error shapes, which are still sent alongside error_details.

Error responses

Failed responses use the same envelope, with "status": "failed" and a non-2xx HTTP status:
The top-level 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)
Some older endpoints return this exact body with HTTP 400 instead of 422. 2. Field map under errors inside the envelope
3. Standard validation response (no envelope) Some endpoints return the standard framework validation response, which has no status, status_code or data:
In every form, keys are field names (nested fields use dot notation, such as 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 HTTP 422:
  • yativo is rejected anywhere in a value, including inside a longer word such as MyYativoShop.
  • test and admin are rejected as standalone words (Test, test123, test_user, admin@acme.com). Words that merely contain them, such as Contest or Administrative, 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 fields (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:
These responses include error_details too.

HTTP status code reference


Authentication & access errors

Server-to-server requests authenticate with an X-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 HTTP 402:
Other card-specific messages include 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

A 200 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 authenticated POST, PUT, PATCH and DELETE requests to the core /v1 API are protected against duplicates.

How it works

  1. Send a unique Idempotency-Key header (a UUID is ideal) with every write request.
  2. The first request with that key runs normally, and its response is stored for 60 minutes.
  3. 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.
  4. 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 4xx errors, are stored and replayed.
  • 5xx responses are not stored, so retrying after a 5xx really does run the request again.
Retrying after fixing a 4xx: if a payout fails with Insufficient wallet balance and you then top up, resending the identical request with the same key, or with no key, returns the stored 400 for up to 60 minutes. Use a new Idempotency-Key when you retry after fixing the cause.
Replayed responses look identical to the original. No header marks them as replays.

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:
Repeated wrong magic-link codes return a separate, enveloped 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:
It appears with HTTP 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 receive 502 (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-level message: Service Unavailable)
Some endpoints pass on the partner’s own error description in data.error. Show it to support staff, but don’t parse it.

Retrying money movement after an unexpected error

For a 5xx, or the generic 400 above, on an endpoint that moves money:
  1. Don’t retry blindly. The operation may have partly gone through.
  2. Look up the transaction (by your reference or the transaction list) or wait for the webhook.
  3. Only if no transaction exists, retry with backoff (for example 1s, 2s, 4s, 8s, capped at about 30s). Use a new Idempotency-Key for a 4xx, or the same key for a 5xx, since 5xx responses aren’t stored.
Reads (GET) are always safe to retry.

Parsing responses: a reference implementation

Because every failure carries error_details, a client needs only a few lines:
The fallbacks only apply when no JSON body came back at all, for example when a network proxy in between returned an HTML error page. Then branch on 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-level message, 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-2xx status or includes error_details. Don’t assume success just because data is 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-Key on writes, and use a fresh one when you retry after fixing a 4xx.
  • Never retry 401, 403, 404 or 422 unchanged. They won’t succeed until the cause is fixed.
  • Respect Retry-After on 429, and poll status endpoints within the read limit.
  • Treat 200 on money movement as “accepted”, and confirm the final state through the transaction status or webhooks.
  • Log the full response body (HTTP status, error_details and data) 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.