Errors

Error response format, status codes, and error handling guidance.


All API errors return a consistent JSON body regardless of the HTTP status code. This document describes the shape, the error codes you may receive, and how to handle errors programmatically.


Error response format

{
  "message": "Human-readable description of what went wrong.",
  "error_code": "PAYMENT_ALREADY_REFUNDED"
}
FieldTypeAlways presentDescription
messagestringYesHuman-readable explanation of the error.
error_codestringYesMachine-readable code. Use this for programmatic error handling.

Correlation ID (request tracing)

Every response — success or error — includes an x-correlation-id header containing a unique ID for the request:

x-correlation-id: 9f3a2b1c-4d5e-6f7a-8b9c-0d1e2f3a4b5c

Log this value alongside every error. When contacting support, include the correlation ID and the Advance team can locate the full request trace immediately.

You can also supply your own correlation ID via the x-correlation-id request header and we'll echo it back:

POST /v1/plans
x-correlation-id: my-system-request-id-abc123

Bulk operation errors

When an operation fails because specific items are in an invalid state, a context object is included with the IDs of the affected items:

{
  "message": "Some payables can't be submitted — they must be in NEW or REVISION_REQUIRED status.",
  "error_code": "BAD_REQUEST",
  "context": {
    "invalid_ids": ["pay_aaa111", "pay_bbb222"]
  }
}

HTTP status codes

StatusMeaning
200Success
400Bad request — the request was invalid or the operation is not allowed
401Unauthorized — missing or invalid API key
403Forbidden — your API key does not have permission for this action
404Not found — the requested resource does not exist
422Unprocessable — validation failed or a database constraint was violated
500Internal server error — something went wrong on our end

Error codes

Payment errors

error_codeStatusDescription
PAYMENT_ALREADY_REFUNDED400The payment has already been fully refunded.
PAYMENT_STATUS_NOT_REFUNDABLE400Only Captured or Settled payments can be refunded.
PAYMENT_MISSING_PAYRIX_TRANSACTION_ID400The payment cannot be refunded through the API. Contact support.
PAYMENT_REQUEST_EXPIRED400The payment request has expired.
PAYMENT_REQUEST_ALREADY_PAID400This payment request has already been paid.
PAYMENT_NOT_FOUND404No payment found with the given ID.
PAYMENT_REQUEST_NOT_FOUND404No payment request found with the given ID.

Plan and invoice errors

error_codeStatusDescription
CHECKOUT_DETAILS_ALREADY_PAID400This invoice has already been paid.
CHECKOUT_DETAILS_EXPIRED400This payment link has expired.
CHECKOUT_DETAILS_CANCELLED400This payment link has been cancelled.
INVOICE_NOT_FOUND404No invoice found with the given ID.

Resource errors

error_codeStatusDescription
NOT_FOUND404Generic not-found for HTTPException-based routes.
ITEM_NOT_FOUND404A specific resource was not found.
MISSING_REQUIRED_FIELDS400The resource is missing required fields.
BAD_REQUEST400The request or operation is not valid.

Authentication and authorization errors

error_codeStatusDescription
UNAUTHORIZED401Missing or invalid API key.
FORBIDDEN403The API key does not have permission for this resource or action.

Validation errors

error_codeStatusDescription
VALIDATION_ERROR422Request body or query parameter failed validation.
FOREIGN_KEY_VIOLATION422A referenced resource (e.g., insured_id) does not exist.
UNIQUE_CONSTRAINT_VIOLATION422A record with these values already exists.

Server errors

error_codeStatusDescription
INTERNAL_SERVER_ERROR500An unexpected error occurred. Include the x-correlation-id response header value in your support request.

Handling errors in code

Check error_code first

Never string-match on message — it may change. Use error_code for all programmatic decisions:

import requests

response = requests.post("/v1/payments/pay_xyz/refund", ...)

if not response.ok:
    error = response.json()
    code = error.get("error_code")

    if code == "PAYMENT_ALREADY_REFUNDED":
        # Payment was already refunded — treat as success
        pass
    elif code == "PAYMENT_STATUS_NOT_REFUNDABLE":
        # Notify user the payment status doesn't allow refunds
        raise ValueError(error["message"])
    else:
        # Unexpected error — log the x-correlation-id header for support
        ref = response.headers.get("x-correlation-id", "unavailable")
        raise RuntimeError(f"Refund failed [{code}]. Correlation ID: {ref}")
const response = await fetch('/v1/payments/pay_xyz/refund', { method: 'POST', ... });

if (!response.ok) {
  const error = await response.json();

  if (error.error_code === 'PAYMENT_ALREADY_REFUNDED') {
    // already done — no action needed
  } else {
    const ref = response.headers.get('x-correlation-id') ?? 'unavailable';
    console.error(`Error ${error.error_code}: ${error.message} (ref: ${ref})`);
    throw new Error(error.message);
  }
}

Bulk operations

When submitting multiple items and receiving a context.invalid_ids response, retry with only the valid subset:

error = response.json()
invalid = set(error.get("context", {}).get("invalid_ids", []))
valid_ids = [id for id in submitted_ids if id not in invalid]
# retry with valid_ids

Contacting support

Include the x-correlation-id response header value in your support request or API integration error logs. This is the single most useful piece of information for diagnosing issues.


Retry guidance

StatusRetry?
400No — the request itself is invalid. Fix the request before retrying.
401 / 403No — check your API key and permissions.
404No — the resource does not exist.
422No — validation or constraint failure. Fix the data.
429Yes, with backoff — you are being rate limited.
500Yes, with exponential backoff — transient server error. If it persists, contact support with the x-correlation-id header value.