# API error code catalogue

> Every error code the Anytime Capital Payments API returns: its HTTP status, what it means, how to fix it, whether to retry, and which endpoints return it.

Source: https://anytime-capital.com/docs/error-codes · Markdown: https://anytime-capital.com/docs/error-codes.md · All docs: https://anytime-capital.com/docs/llms-full.txt

Every error from `/api/v1` is `{ "error": { "code", "message" } }`. Branch on `code`, which never changes; show `message`, which is written for people and may be reworded. This page lists every code the API can return, taken from the API's own source; how to handle them in general is on [Errors and retries](https://anytime-capital.com/docs/errors.md).

## By status

| Status | Codes |
| --- | --- |
| 400 | `INVALID_JSON`, `INVALID_INPUT`, `INVALID_IDEMPOTENCY_KEY`, `NOT_EDITABLE`, `CUSTOMER_MISMATCH` |
| 401 | `UNAUTHORIZED`, `API_KEY_EXPIRED` |
| 403 | `NOT_ENROLLED`, `PERMISSION_DENIED`, `IP_NOT_ALLOWED`, `TEST_MODE_ONLY` |
| 404 | `NOT_FOUND`, `METER_NOT_FOUND`, `CUSTOMER_NOT_FOUND` |
| 409 | `IDEMPOTENCY_KEY_REUSED`, `NOTHING_TO_SEND`, `NOT_REFUNDABLE`, `REFUND_PENDING_APPROVAL`, `TERMS_URL_REQUIRED`, `INVOICE_CLOSED`, `DUPLICATE_CUSTOMER`, `CODE_TAKEN`, `LIMIT_REACHED`, `SUBSCRIPTION_ENDED`, `ALREADY_CANCELING`, `CANNOT_PAUSE`, `NOT_PAUSED`, `CHANGE_IN_PROGRESS`, `SWITCH_SCHEDULED` |
| 422 | `IDEMPOTENCY_KEY_REUSED`, `CUSTOMER_HAS_NO_EMAIL`, `TIMESTAMP_IN_FUTURE`, `TIMESTAMP_TOO_OLD`, `PERIOD_CLOSED` |
| 429 | `RATE_LIMITED`, `TOO_SOON` |
| 500 | `INTERNAL_ERROR`, `REFUND_NOT_RECORDED` |
| 502 | `PROCESSOR_ERROR`, `EMAIL_FAILED` |
| 503 | `NOT_CONFIGURED` |

## Every code

| Code | Status | Meaning | Fix | Retry | Returned by |
| --- | --- | --- | --- | --- | --- |
| `UNAUTHORIZED` | 401 | The `Authorization` header is missing, or the key is malformed, unknown or revoked. A revoked key reads exactly like an unknown one. | Send `Authorization: Bearer ak_live_…` with an active key from **Settings → Developers**. | No: fix the request | Every request with a key |
| `API_KEY_EXPIRED` | 401 | The key passed its expiry date, or the overlap after it was rolled ended. The message says when it expired. | Use the key that replaced it, or create a new one in **Settings → Developers**. See [Authentication](https://anytime-capital.com/docs/authentication.md). | No: fix the request | Every request with a key |
| `NOT_ENROLLED` | 403 | The key is valid, but the business isn't verified and set up for card payments yet. | Finish verification in **Settings → Verification**; the API opens once card payments are set up. | No: fix the request | Every request with a key |
| `PERMISSION_DENIED` | 403 | The key doesn't have the permission this request needs. The message names it, such as `refunds:write`. | Use a key that has it: an owner or admin creates one in **Settings → Developers**. See [Authentication](https://anytime-capital.com/docs/authentication.md#restricted-keys-and-permissions). | No: fix the request | Every request with a key |
| `IP_NOT_ALLOWED` | 403 | The key has an IP allowlist and this request came from another address, which the message names. | Call from a server on the allowlist, or an owner or admin edits the key's allowed IPs in **Settings → Developers**. | No: fix the request | Every request with a key |
| `RATE_LIMITED` | 429 | The key made more than 100 requests this minute. | Wait the seconds in the `Retry-After` header, then retry. See [Rate limits](https://anytime-capital.com/docs/rate-limits.md). | After `Retry-After` | Every request with a key |
| `INTERNAL_ERROR` | 500 | Something failed on our side. | Retry with the same `Idempotency-Key`; you get the first result if the first attempt went through. Contact [Support](https://anytime-capital.com/docs/support.md) if it keeps happening. | Yes, same Idempotency-Key | Every request with a key |
| `INVALID_JSON` | 400 | The body isn't valid JSON, or couldn't be read. An empty body reads as `{}`. | Send `Content-Type: application/json` and a JSON object. | No: fix the request | Every request with a body |
| `INVALID_INPUT` | 400 | A field, filter, `limit` or `cursor` is missing or invalid. The message starts with the field's name, as in `amountCents: …`, or reads `Invalid cursor.` | Fix the named field. Don't retry the same request unchanged. | No: fix the request | Every list, every request with a body, and [`GET /meters/{id}/usage`](https://anytime-capital.com/docs/meters.md#get-meter-usage) |
| `INVALID_IDEMPOTENCY_KEY` | 400 | The `Idempotency-Key` header is empty, longer than 255 characters, or has characters other than printable ASCII. | Use a UUID. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). | No: fix the request | Every request with an Idempotency-Key |
| `IDEMPOTENCY_KEY_REUSED` | 422 or 409 | The `Idempotency-Key` was used in the last 24 hours with a different body or endpoint. Or: The usage event's `idempotencyKey` was already used for a different event. | Use a new key for a new request; reuse a key only to retry the same request. Send a new `idempotencyKey` for each event; repeat a key only to resend the same event. | No: fix the request | Every request with an Idempotency-Key, and [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |
| `NOT_FOUND` | 404 | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. | Check the id and the path. Ids are case-sensitive. | No: fix the request | Every request with an id in the path, and [`POST /refunds`](https://anytime-capital.com/docs/refunds.md#create-refund), [`POST /test-helpers/events`](https://anytime-capital.com/docs/test-helpers.md#create-test-event) |
| `TEST_MODE_ONLY` | 403 | A test helper was called with a live key. Test helpers never touch live data. | Use a test key (`ak_test_…`): turn on **Test mode** in the dashboard and create one in **Settings → Developers**. | No: fix the request | [`POST /test-helpers/events`](https://anytime-capital.com/docs/test-helpers.md#create-test-event) |
| `NOTHING_TO_SEND` | 409 | You have no test object of the kind this event is about yet (no paid test payment for `payment.succeeded`, no test invoice for `invoice.paid`…). | Make one in test mode first (pay a test link with a test card), or choose **Load sample data** in the test mode bar. | No: fix the request | [`POST /test-helpers/events`](https://anytime-capital.com/docs/test-helpers.md#create-test-event) |
| `NOT_CONFIGURED` | 503 | The part of Anytime Capital Payments this needs isn't available right now. | Retry later, with the same `Idempotency-Key` on a write. | Yes, same Idempotency-Key | [`POST /refunds`](https://anytime-capital.com/docs/refunds.md#create-refund), [`POST /subscriptions/{id}/cancel`](https://anytime-capital.com/docs/subscriptions.md#cancel-subscription), [`POST /subscriptions/{id}/pause`](https://anytime-capital.com/docs/subscriptions.md#pause-subscription), [`POST /subscriptions/{id}/resume`](https://anytime-capital.com/docs/subscriptions.md#resume-subscription), [`GET /payouts`](https://anytime-capital.com/docs/payouts-api.md#list-payouts), [`GET /payouts/{id}`](https://anytime-capital.com/docs/payouts-api.md#get-payout), [`GET /balance`](https://anytime-capital.com/docs/payouts-api.md#get-balance), [`GET /disputes`](https://anytime-capital.com/docs/disputes.md#list-disputes), [`GET /disputes/{id}`](https://anytime-capital.com/docs/disputes.md#get-dispute), [`POST /webhook-endpoints`](https://anytime-capital.com/docs/webhook-endpoints.md#create-webhook-endpoint), [`POST /test-helpers/events`](https://anytime-capital.com/docs/test-helpers.md#create-test-event) |
| `PROCESSOR_ERROR` | 502 | The payments network didn't answer in time. | Retry with the same `Idempotency-Key`. A refund is keyed at the network by that key too, so a retry never refunds twice. | Yes, same Idempotency-Key | [`POST /refunds`](https://anytime-capital.com/docs/refunds.md#create-refund), [`POST /subscriptions/{id}/cancel`](https://anytime-capital.com/docs/subscriptions.md#cancel-subscription), [`POST /subscriptions/{id}/pause`](https://anytime-capital.com/docs/subscriptions.md#pause-subscription), [`POST /subscriptions/{id}/resume`](https://anytime-capital.com/docs/subscriptions.md#resume-subscription), [`GET /payouts`](https://anytime-capital.com/docs/payouts-api.md#list-payouts), [`GET /payouts/{id}`](https://anytime-capital.com/docs/payouts-api.md#get-payout), [`GET /balance`](https://anytime-capital.com/docs/payouts-api.md#get-balance), [`GET /disputes`](https://anytime-capital.com/docs/disputes.md#list-disputes), [`GET /disputes/{id}`](https://anytime-capital.com/docs/disputes.md#get-dispute) |
| `NOT_REFUNDABLE` | 409 | The payment can't be refunded in its current state, or it was paid in stablecoins, which settle straight to your wallet. | Check the payment's `status` and `amountRefundedCents` with `GET /api/v1/payments/{id}`. Refund a stablecoin payment by sending the tokens back from your wallet. | No: fix the request | [`POST /refunds`](https://anytime-capital.com/docs/refunds.md#create-refund) |
| `REFUND_PENDING_APPROVAL` | 409 | Another refund on this payment is already waiting for an owner's approval. | Wait for the owner's decision: `GET /api/v1/refunds?paymentId=…&status=pending_approval` shows it. | No: fix the request | [`POST /refunds`](https://anytime-capital.com/docs/refunds.md#create-refund) |
| `REFUND_NOT_RECORDED` | 500 | The refund was sent, but our record of it isn't there yet. | Don't refund again. List `GET /api/v1/refunds?paymentId=…` in a minute to find it. | No: fix the request | [`POST /refunds`](https://anytime-capital.com/docs/refunds.md#create-refund) |
| `NOT_EDITABLE` | 400 | The update names a field that is fixed once a link exists: `amountCents`, `items`, `payWhatYouWant`, `minAmountCents`, `shippingCents`, `allowDiscountCodes` or `singleUse`. The message names it. | Create a new link at the new price and deactivate the old one. | No: fix the request | [`POST /payment-links/{id}`](https://anytime-capital.com/docs/payment-links.md#update-payment-link) |
| `TERMS_URL_REQUIRED` | 409 | `requireTermsConsent` needs a link to your terms, and the business hasn't set one. | Add your terms link in **Settings → Payments**, then retry. | No: fix the request | [`POST /payment-links`](https://anytime-capital.com/docs/payment-links.md#create-payment-link), [`POST /payment-links/{id}`](https://anytime-capital.com/docs/payment-links.md#update-payment-link) |
| `INVOICE_CLOSED` | 409 | The invoice is already paid, refunded or void, so there's nothing to send. | Read the invoice's `status`; nothing to do. | No: fix the request | [`POST /invoices/{id}/send`](https://anytime-capital.com/docs/invoices.md#send-invoice) |
| `TOO_SOON` | 429 | The invoice was already emailed in the last 24 hours. | Wait the seconds in `Retry-After`. | After `Retry-After` | [`POST /invoices/{id}/send`](https://anytime-capital.com/docs/invoices.md#send-invoice) |
| `EMAIL_FAILED` | 502 | We couldn't send the email. | Retry. Nothing was marked as sent. | Yes, same Idempotency-Key | [`POST /invoices/{id}/send`](https://anytime-capital.com/docs/invoices.md#send-invoice) |
| `DUPLICATE_CUSTOMER` | 409 | Another saved customer already has this email address, in any letter case. | Find them with `GET /api/v1/customers?email=…` and update that customer instead. | No: fix the request | [`POST /customers`](https://anytime-capital.com/docs/customers.md#create-customer), [`PATCH /customers/{id}`](https://anytime-capital.com/docs/customers.md#update-customer) |
| `CODE_TAKEN` | 409 | The business already has a discount code with this code. | Pick another code, or use the existing one. | No: fix the request | [`POST /discount-codes`](https://anytime-capital.com/docs/discount-codes.md#create-discount-code) |
| `LIMIT_REACHED` | 409 | The business already has 10 webhook endpoints. | Delete one you no longer use with `DELETE /api/v1/webhook-endpoints/{id}`. | No: fix the request | [`POST /webhook-endpoints`](https://anytime-capital.com/docs/webhook-endpoints.md#create-webhook-endpoint) |
| `SUBSCRIPTION_ENDED` | 409 | The subscription has already ended (canceled, expired or completed). | Nothing to do; a customer subscribes again at the plan's `url`. | No: fix the request | [`POST /subscriptions/{id}/cancel`](https://anytime-capital.com/docs/subscriptions.md#cancel-subscription), [`POST /subscriptions/{id}/pause`](https://anytime-capital.com/docs/subscriptions.md#pause-subscription), [`POST /subscriptions/{id}/resume`](https://anytime-capital.com/docs/subscriptions.md#resume-subscription) |
| `ALREADY_CANCELING` | 409 | The subscription is already set to cancel at the end of its period. | Send `atPeriodEnd: false` to end it now, or `POST …/resume` to keep it. | No: fix the request | [`POST /subscriptions/{id}/cancel`](https://anytime-capital.com/docs/subscriptions.md#cancel-subscription) |
| `CANNOT_PAUSE` | 409 | Only an active or trialing subscription that renews can be paused: this one is paused, past due or set to cancel. | Read the subscription's `status` and `cancelAtPeriodEnd` first. | No: fix the request | [`POST /subscriptions/{id}/pause`](https://anytime-capital.com/docs/subscriptions.md#pause-subscription) |
| `NOT_PAUSED` | 409 | The subscription is neither paused nor set to cancel, so there's nothing to resume. | Nothing to do. | No: fix the request | [`POST /subscriptions/{id}/resume`](https://anytime-capital.com/docs/subscriptions.md#resume-subscription) |
| `CHANGE_IN_PROGRESS` | 409 | The subscription is switching plans right now. | Retry in a minute. | Yes, same Idempotency-Key | [`POST /subscriptions/{id}/cancel`](https://anytime-capital.com/docs/subscriptions.md#cancel-subscription), [`POST /subscriptions/{id}/pause`](https://anytime-capital.com/docs/subscriptions.md#pause-subscription), [`POST /subscriptions/{id}/resume`](https://anytime-capital.com/docs/subscriptions.md#resume-subscription) |
| `SWITCH_SCHEDULED` | 409 | The subscription switches plans at its next renewal, so it can't be paused or resumed. | Undo the switch from the subscriber's details on the **Subscriptions** page first. | No: fix the request | [`POST /subscriptions/{id}/pause`](https://anytime-capital.com/docs/subscriptions.md#pause-subscription), [`POST /subscriptions/{id}/resume`](https://anytime-capital.com/docs/subscriptions.md#resume-subscription) |
| `METER_NOT_FOUND` | 404 | No active meter has this `eventName`. | Check the meter's event name on the **Meters** tab of **Subscriptions**, or with `GET /api/v1/meters`. | No: fix the request | [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |
| `CUSTOMER_NOT_FOUND` | 404 | No saved customer has this `customerId`. | Send the customer's `cus_…` id, or `customerEmail` instead. | No: fix the request | [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |
| `CUSTOMER_HAS_NO_EMAIL` | 422 | The customer has no email address, and usage is matched to subscriptions by email. | Add an email to the customer in the dashboard, or send `customerEmail`. | No: fix the request | [`GET /meters/{id}/usage`](https://anytime-capital.com/docs/meters.md#get-meter-usage), [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |
| `CUSTOMER_MISMATCH` | 400 | `customerId` and `customerEmail` name different customers. | Send one of them. | No: fix the request | [`GET /meters/{id}/usage`](https://anytime-capital.com/docs/meters.md#get-meter-usage), [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |
| `TIMESTAMP_IN_FUTURE` | 422 | The event's `timestamp` is more than five minutes ahead of our clock. | Send the time the usage happened, or leave `timestamp` out for now. | No: fix the request | [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |
| `TIMESTAMP_TOO_OLD` | 422 | The event's `timestamp` is more than 400 days ago. | Usage that old can't be billed; drop the event. | No: fix the request | [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |
| `PERIOD_CLOSED` | 422 | The `timestamp` falls in a period already billed for this customer and meter. | Send usage from the end of the billed period on. The message says when that is. | No: fix the request | [`POST /meter-events`](https://anytime-capital.com/docs/meters.md#create-meter-event) |

## Usage events in a batch

`POST /api/v1/meter-events/batch` answers `200` when the batch itself is valid, and reports each refused event in its result with the code a single event would get: `INVALID_INPUT`, `METER_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `CUSTOMER_HAS_NO_EMAIL`, `CUSTOMER_MISMATCH`, `TIMESTAMP_IN_FUTURE`, `TIMESTAMP_TOO_OLD`, `PERIOD_CLOSED` or `IDEMPOTENCY_KEY_REUSED`. See [Meters](https://anytime-capital.com/docs/meters.md#create-meter-event-batch).

## MCP server errors

A tool call that reaches the API returns the API's error as the tool result, with `isError: true`: `{ status, error: { code, message } }`, plus `retryAfterSeconds` when the API asked to wait. `UNEXPECTED_RESPONSE` means the API answered without a JSON body. Problems with the JSON-RPC message itself answer as JSON-RPC errors:

| Code | Meaning |
| --- | --- |
| -32700 | The body isn't valid JSON. |
| -32600 | Not a JSON-RPC 2.0 request, an empty batch, or an unsupported `MCP-Protocol-Version` header (HTTP `400`). `GET` and `DELETE` answer `405` with this code. |
| -32601 | A method other than `initialize`, `ping`, `tools/list` or `tools/call`. |
| -32602 | An unknown tool, no `params.name`, or arguments that aren't an object (or a missing `id`). |
| -32603 | Something failed on our side; try again. |

## Node SDK errors

The [Node SDK](https://anytime-capital.com/docs/node-sdk.md) throws a class per kind of error, each with the API's `code`, `message`, `status` and `retryAfterSeconds`, and adds codes of its own: `TIMEOUT` and `CONNECTION_ERROR` (no answer, after its retries), `UNEXPECTED_RESPONSE` (an answer without the error envelope) and `SIGNATURE_VERIFICATION_FAILED` (a webhook that didn't verify).

---

Anytime Capital is a fintech company, not a bank. Payment processing, banking and payout services are provided by Anytime Capital's licensed partners. Rates shown are for US card payments; international cards, currency conversion and disputes may carry additional fees shown in your dashboard before you accept a payment.
