Skip to main content
Every Yuno API error returns the same compact JSON envelope. Parse the code for programmatic handling and surface the messages array to your support tools.

Error envelope

{
  "code": "CUSTOMER_ID_DUPLICATED",
  "messages": [
    "A customer with this merchant_customer_id already exists for this account."
  ]
}
FieldTypeDescription
codestringStable, machine readable identifier in SCREAMING_SNAKE_CASE. Branch on this in your code.
messagesarray of stringHuman readable details. Always an array, even when there is only one entry. Validation errors put one entry per failed field, formatted "fieldName message".
There is no error wrapper, no singular message, no type, no details. The body is exactly the two top level fields above.
Yuno sets x-trace-id on every response. When you open a support ticket, attach the response body and the x-trace-id header so the team can pull the trace.

HTTP status to error code

The shared utility libraries map common HTTP statuses to a default code. Service code may return a more specific business code for the same status (see the next table).
HTTPDefault codeMeaning
400BAD_REQUESTGeneric bad request fallback when no business code applies.
400VALIDATION_ERRORRequest payload failed schema or constraint validation.
400INVALID_REQUESTRequest was rejected by the resource’s business rules.
401UNAUTHORIZEDMissing or invalid public-api-key / private-secret-key.
403FORBIDDENCaller authenticated but is not allowed to perform the action.
404NOT_FOUNDThe route does not exist, or the framework catch all could not match the resource.
405METHOD_NOT_ALLOWEDThe HTTP verb is not supported on this path. (Kotlin services emit UNSUPPORTED_METHOD.)
408REQUEST_TIMEOUTRequest timed out before the service could respond.
413REQUEST_ENTITY_TOO_LARGERequest body exceeds the service limit.
415UNSUPPORTED_MEDIA_TYPEContent-Type is missing or not JSON.
429TOO_MANY_REQUESTSEdge throttling kicked in. Back off and retry.
500INTERNAL_ERRORUnhandled server side error. Safe to retry idempotent operations.
502BAD_GATEWAYUpstream provider was unreachable or returned a malformed response.
503SERVICE_UNAVAILABLEYuno service is overloaded or in maintenance.
Yuno does not currently use HTTP 422. Schema and constraint failures arrive as 400 with code VALIDATION_ERROR (Kotlin services) or BAD_REQUEST (Go services). Most “not found” business cases (such as CUSTOMER_NOT_FOUND, RECIPIENT_NOT_FOUND) also arrive as 400 with a descriptive code, not as 404.

Resource specific business codes

Services emit their own code values for domain rules. The most common ones you will see in production:

Customers

codeHTTPMeaning
CUSTOMER_ID_DUPLICATED400A customer already exists with the same merchant_customer_id. Use Retrieve Customer by External ID to fetch the existing record.
CUSTOMER_NOT_FOUND400No customer matches the supplied identifier.
INVALID_PARAMETERS400One or more body parameters are invalid. The messages array names each failure.
CONCURRENT_MODIFICATION409The record was updated by another caller while your request was in flight. Re fetch and retry.

Recipients (marketplace)

codeHTTPMeaning
RECIPIENT_NOT_FOUND400No recipient matches the supplied identifier.
EXTERNAL_ID_EXIST400A recipient already exists with the same merchant_recipient_id.
INVALID_STATE400The recipient cannot accept this action in its current state.
ONBOARDING_ALREADY_EXISTS400An onboarding flow has already started for this recipient.
ONBOARDING_NOT_FOUND400No onboarding record matches the supplied identifier.
TRANSFER_NOT_FOUND400No transfer matches the supplied identifier.
INVALID_TRANSFER_STATUS400The transfer cannot accept this action in its current state.
ONBOARDING_RECIPIENT_MISMATCH400The onboarding does not belong to the recipient supplied in the request.
REQUEST_CANNOT_BE_PROCESSED400The request is structurally valid but cannot be processed against the current recipient state.

Validation framework codes (Kotlin services)

These come from the shared exception handler in the Kotlin services and apply to checkout sessions, recipients, payouts, and any service that uses the shared library.
codeHTTPTrigger
VALIDATION_ERROR400A required field is missing, an enum value is wrong, or a JSON field has the wrong type. The messages array lists each field failure.
MISSING_HEADER400A required header (e.g., public-api-key or private-secret-key) was not sent.
MISSING_PARAMETER400A required query parameter was not sent.
INVALID_DATE_TYPE400A date field could not be parsed in the expected ISO format.
ILLEGAL_ARGUMENT400An argument violates a business invariant outside JSON validation.

Provider errors

Errors that originate at a downstream payment provider are surfaced under codes prefixed with PROVIDER_. Examples include PROVIDER_INVALID_CREDENTIALS, PROVIDER_INVALID_REQUEST, PROVIDER_PAYMENT_NOT_FOUND, PROVIDER_INVALID_AMOUNT, PROVIDER_COUNTRY_NOT_SUPPORTED, and PROVIDER_CURRENCY_NOT_ALLOWED. The provider’s raw response is reflected through messages. See Provider errors for the full mapping.

Validation errors in practice

Validation failures from Kotlin services package every failed field into the messages array. The shape is always { code, messages }, never a structured details object.
{
  "code": "VALIDATION_ERROR",
  "messages": [
    "amount must be greater than 0",
    "country must not be blank",
    "merchant_order_id must not be blank"
  ]
}
When you display these to internal users (operators, support), surface every entry in messages so they can act on all failures at once.

Retry strategy

For non idempotent writes, the safe pattern is to set a unique business key in the request body (merchant_customer_id, merchant_order_id, merchant_recipient_id). Yuno’s database constraints reject duplicates, so a retry that finds the same key returns a clear 400 rather than creating a duplicate. The one endpoint that supports X-Idempotency-Key directly is POST /v1/subscriptions. See Avoiding duplicates.
Retry transient failures only. Do not retry codes that mean “your request is wrong”. Automated retries on those waste budget and add noise.
RetryDo not retry
429 TOO_MANY_REQUESTS400 BAD_REQUEST and 400 VALIDATION_ERROR
500 INTERNAL_ERROR400 INVALID_REQUEST
502 BAD_GATEWAY401 UNAUTHORIZED
503 SERVICE_UNAVAILABLE403 FORBIDDEN
504 Gateway Timeout404 NOT_FOUND
408 REQUEST_TIMEOUT409 CONCURRENT_MODIFICATION (re fetch first, then retry)
Use exponential backoff with full jitter, starting at 1 s and capped at 30 s. When Retry-After is present on a 429, honor it as the floor for the wait.
const RETRYABLE = new Set([408, 429, 500, 502, 503, 504]);

async function retryWithBackoff(call, { maxRetries = 4 } = {}) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await call();
    } catch (err) {
      if (!RETRYABLE.has(err.status) || attempt === maxRetries) throw err;

      const cap = Math.min(1000 * 2 ** attempt, 30_000);
      const jittered = Math.floor(Math.random() * cap);
      const retryAfterMs = Number(err.headers?.["retry-after"]) * 1000 || 0;
      const delay = Math.max(jittered, retryAfterMs);

      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

Best practices

  1. Branch on code, never on messages. The strings in messages are subject to copy edits and localization. The code is the contract.
  2. Set a unique business key on every non idempotent write (merchant_customer_id, merchant_order_id, merchant_recipient_id). On POST /v1/subscriptions you can also pass X-Idempotency-Key.
  3. Log the full envelope plus the x-trace-id response header for every failed request. Without x-trace-id, support cannot find your request in our traces.
  4. Treat PROVIDER_* codes as upstream signals. They indicate the failure happened at the provider, not at Yuno. Inspect messages for the provider’s raw text.
  5. Alert on 5xx rate, not on individual 5xx events. Provider blips are normal. Sustained spikes are not.

What next

Avoiding duplicates

Use unique business keys to make retries safe across customers, payments, and subscriptions.

Error codes catalog

Full reference of every error code Yuno can return.

Provider errors

PROVIDER_* codes mapped to each provider’s raw responses.