Utopia PaymentsDocs

Errors

HTTP statuses, machine-readable error codes and safe retry behaviour.

Failed requests return an HTTP status and a JSON body with a stable code and a human-readable message.

HTTP/1.1 400 Bad Request

{
  "code": "INVALID_REQUEST",
  "message": "currency: Invalid input: expected \"AED\""
}

Codes raised by the API

StatusCodeMeaning
400INVALID_REQUESTA body, query parameter or field combination is invalid.
400INVALID_REQUEST_BODYA webhook URL is malformed, non-HTTPS, private or otherwise unsafe.
400INVALID_CURSORThe pagination cursor is malformed or no longer usable.
400INVALID_IDEMPOTENCY_KEYThe key is not 1–255 printable ASCII characters.
400PRODUCT_NOT_ACTIVEA checkout contains a draft or archived product.
400INVALID_PRICEA price is zero or below the smallest chargeable amount.
400INVALID_CARTA recurring checkout does not contain exactly one recurring product.
400MIXED_CURRENCIESProducts in one checkout use different currencies.
400CURRENCY_NOT_SUPPORTEDA checkout's currency is one the API accepts but the card processor has not enabled in this mode.
400INVALID_AMOUNTA refund's amount is not a whole number above zero.
400AMOUNT_TOO_LARGEA refund is more than what is left of the payment.
401UNAUTHORIZEDThe API key is missing, wrong or revoked.
403MERCHANT_NOT_APPROVEDThe account is not approved to use the API.
403FORBIDDENThe authenticated key cannot perform the operation.
404NOT_FOUNDThe endpoint or object does not exist for this merchant and mode.
405METHOD_NOT_ALLOWEDThe path exists but not for that HTTP method.
409CUSTOMER_EXISTSA customer with this email already exists in this merchant and mode.
409IDEMPOTENCY_KEY_IN_USEAnother request with the same key is still running; retry the same request.
409TEST_MODE_UNAVAILABLESandbox payments are not available on the platform yet; it is not a per-account setting.
409SUBSCRIPTION_CANCELLEDA cancelled subscription cannot be changed.
409CANCELLATION_FINALRenewal was already stopped at the processor and cannot be resumed.
409CHARGE_IN_PROGRESSA renewal is being charged right now; cancel again in a few minutes.
409LIMIT_REACHEDThe account reached a resource limit, such as API keys or webhooks.
409NOT_REFUNDABLEThe payment has not succeeded, so there is nothing to refund.
409ALREADY_REFUNDEDThe payment has been refunded in full already.
409FULL_REFUND_ONLYThe payment was charged in the customer's own currency and can be refunded only in full.
409NOT_SETTLEDThe payment was made today and has not settled yet: until tomorrow it can be refunded only in full.
409INSUFFICIENT_BALANCEYour balance in the payment's currency does not cover the refund.
409CONFLICTThe request conflicts with the object's current state.
410GONEReserved. The hosted checkout pages answer it for a checkout already paid or expired; no /api/v1 endpoint raises it today.
413PAYLOAD_TOO_LARGEThe request exceeds the body-size limit.
415UNSUPPORTED_MEDIA_TYPEThe request content type is unsupported.
422IDEMPOTENCY_KEY_REUSEDThe key was previously used with different request parameters.
422UNPROCESSABLE_ENTITYThe request is syntactically valid but cannot be processed.
429RATE_LIMITEDThe rate limit was reached; follow Retry-After.
502CANCELLATION_FAILEDThe card processor did not confirm the cancellation; nothing changed, so try again.
502REFUND_FAILEDThe card processor did not make the refund; nothing was refunded, so try again later.
503PAYMENTS_UNAVAILABLELive payment configuration is temporarily unavailable.
503REFUNDS_UNAVAILABLERefunds are not available for this account right now; contact support.
5xxINTERNAL_ERRORAn unexpected server error occurred.

Validation errors produced from a schema use INVALID_REQUEST. The message names the first invalid field, but integrations should branch on code, not the English message.

The schema is checked first, so a currency outside the accepted list never reaches the CURRENCY_NOT_SUPPORTED check: creating a product, or pricing a cart line inline, in another currency answers INVALID_REQUEST. What is left for CURRENCY_NOT_SUPPORTED is a checkout whose currency the API accepts while the card processor has not enabled it — a product saved before the accepted list narrowed, for instance. Read the enabled list from GET /account instead of assuming it. See Currencies and amounts.

Limits

WhatLimitAnswer when exceeded
Request body64 KiB413 PAYLOAD_TOO_LARGE
Webhook endpoints16 per mode409 LIMIT_REACHED
API keys10 active per mode; revoked ones don't count409 LIMIT_REACHED
metadata50 keys, each name 1–40 and each value up to 500 characters400 INVALID_REQUEST
product_cart1–50 lines, quantity 1–100 per line400 INVALID_REQUEST
List limit1–100, default 20400 INVALID_REQUEST
Idempotency-Key1–255 printable ASCII characters400 INVALID_IDEMPOTENCY_KEY

The two 409 LIMIT_REACHED limits are counted per mode, so live and test have their own allowance. POST /webhooks and the dashboard share the endpoint limit; keys are only created in the dashboard. Neither limit lifts on its own — delete an endpoint or revoke a key to make room.

Retrying

Retry network errors, 429, IDEMPOTENCY_KEY_IN_USE and 5xx with backoff. For a POST, reuse the same Idempotency-Key and identical body. Do not retry other 4xx responses without changing the request or account state.

The libraries map statuses to typed errors. See Node.js and PHP.

On this page