Error Codes
HTTP status codes, error response format, and common error codes for the Axon API.
Error Codes
All Axon API error responses follow a consistent format and use standard HTTP status codes.
Error Response Format
{
"error": "Human-readable error message",
"code": "MACHINE_READABLE_CODE",
"details": "Optional additional context"
}| Field | Type | Description |
|---|---|---|
error | string | A human-readable description of the error. |
code | string | A machine-readable error code. Use this for programmatic error handling. |
details | string? | Optional additional context, such as which field failed validation or the expected format. |
HTTP Status Codes
400 Bad Request
The request is malformed or missing required fields.
| Code | Description |
|---|---|
INVALID_REQUEST | Request body is not valid JSON or is missing required fields. Check the details field for which field is invalid. |
INVALID_SIGNATURE | The EIP-712 signature does not match the provided intent data. The recovered signer address does not match the bot field. |
INVALID_ADDRESS | An address field is not a valid Ethereum address (not checksummed or wrong length). |
INVALID_AMOUNT | The amount field is not a valid non-negative integer string. |
INVALID_REF | The ref field is not a valid bytes32 hex string (must be 0x-prefixed, 66 characters). |
DEADLINE_EXPIRED | The intent's deadline timestamp has already passed. Generate a new intent with a future deadline. |
INVALID_CHAIN_ID | The chainId is not a supported chain. |
INVALID_IDEMPOTENCY_KEY | The idempotencyKey is not a valid UUID. |
MEMO_TOO_LONG | The memo field exceeds 1000 characters. |
METADATA_TOO_LARGE | The metadata object exceeds 10 keys or a value exceeds 500 characters. |
403 Forbidden
The request is well-formed but authorization checks failed.
| Code | Description |
|---|---|
BOT_NOT_ACTIVE | The bot address is not whitelisted on the specified vault. Register the bot via the dashboard or contract before submitting payments. |
EXCEEDS_PER_TX_LIMIT | The payment amount exceeds the bot's maxPerTxAmount configured on the vault. |
EXCEEDS_DAILY_LIMIT | The payment would cause the bot to exceed its rolling daily spending limit. |
EXCEEDS_VELOCITY_LIMIT | The payment would exceed a rolling spend window limit (e.g. more than $300 in 60 minutes). |
DESTINATION_NOT_ALLOWED | The recipient address is not on the vault's or bot's destination whitelist, and a whitelist is configured. |
VAULT_PAUSED | The vault is currently paused by the owner. No payments can be processed. |
404 Not Found
| Code | Description |
|---|---|
NOT_FOUND | The requested resource does not exist. For GET /v1/payments/:requestId, the request ID is not recognized. |
VAULT_NOT_FOUND | No vault exists at the specified vaultAddress on the given chain. |
409 Conflict
| Code | Description |
|---|---|
IDEMPOTENCY_CONFLICT | A payment with this idempotencyKey was already submitted with different intent data. Each unique payment must use a unique idempotency key. The original cached response is not returned in this case because the intent data does not match. |
DUPLICATE_INTENT | The exact intent hash has already been executed on-chain. The contract tracks all executed intent hashes and rejects duplicates. |
A 409 IDEMPOTENCY_CONFLICT means the same key was used with different data. If you re-submit the exact same
request with the same idempotency key, you will receive a 200 with the original cached response -- that is the
intended deduplication behavior.
422 Unprocessable Entity
The request is valid but cannot be executed.
| Code | Description |
|---|---|
INSUFFICIENT_BALANCE | The vault does not hold enough of the requested token to cover the payment amount. |
SIMULATION_FAILED | The transaction simulation (eth_call) reverted. The details field contains the revert reason. No gas was spent. |
SWAP_SLIPPAGE_EXCEEDED | For swap-and-pay transactions, the available swap route would result in slippage exceeding the vault's configured tolerance. |
NO_SWAP_ROUTE | For swap-and-pay transactions, no viable swap route was found for the requested token pair. |
429 Too Many Requests
| Code | Description |
|---|---|
RATE_LIMITED | The bot or vault has exceeded the rate limit. The response includes a Retry-After header with the number of seconds to wait before retrying. |
500 Internal Server Error
| Code | Description |
|---|---|
INTERNAL_ERROR | An unexpected error occurred in the relayer. The payment was not executed. Retry with the same idempotencyKey is safe. |
RPC_ERROR | The relayer was unable to communicate with the blockchain RPC node. Retry after a brief delay. |
AI_VERIFICATION_TIMEOUT | The AI verification agents did not respond within the expected window. The payment was not executed. You may retry. |
Handling Errors
Retryable Errors
The following errors are safe to retry with the same idempotencyKey:
429 RATE_LIMITED-- wait for the duration specified inRetry-After500 INTERNAL_ERROR-- retry after a brief backoff500 RPC_ERROR-- retry after a brief backoff500 AI_VERIFICATION_TIMEOUT-- retry after 30-60 seconds
Non-Retryable Errors
These errors require changes to the request before retrying:
400errors -- fix the request body403errors -- check vault configuration (bot whitelist, limits, pause state)409 IDEMPOTENCY_CONFLICT-- use a new idempotency key for the new intent422 INSUFFICIENT_BALANCE-- deposit funds to the vault
Example Error Handling
const response = await fetch('https://relay.axonfi.xyz/v1/payments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
const error = await response.json();
switch (error.code) {
case 'RATE_LIMITED':
const retryAfter = response.headers.get('Retry-After');
await sleep(Number(retryAfter) * 1000);
// Retry with same idempotencyKey
break;
case 'INSUFFICIENT_BALANCE':
console.error('Vault needs more funds:', error.details);
break;
case 'BOT_NOT_ACTIVE':
console.error('Bot not registered on vault');
break;
default:
console.error(`${error.code}: ${error.error}`);
}
}