> ## Documentation Index
> Fetch the complete documentation index at: https://docs.affixo.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors and limits

> Every error the API returns, what its body looks like, and the limits that can return 429.

Every error is JSON with an `error` code you can branch on. Many also carry a
`message` written for a person, which is the same wording the dashboard shows,
so you can display it as is.

```json theme={null}
{ "error": "move_refused", "message": "1 of these commissions is in a payout run that hasn't been paid yet. Remove it from that run first, then move the referral.", "hint": "..." }
```

A refused request changes nothing. When a call does several things at once
(for example moving a sale and changing its status), either all of it is
applied or none of it is.

## Status codes

| Status | `error` | What it means |
| - | - | - |
| 400 | `invalid_json`, `…_required`, `invalid_status`, `invalid_request`, `no_updatable_fields` | The request is missing something or has a value that isn't allowed. `invalid_status` lists the allowed values in `valid` |
| 401 | `unauthorized` | No token, or one Affixo doesn't recognise |
| 402 | `plan_restricted` | Your usage is over your plan and management access is locked. Details below |
| 403 | `insufficient_scope` | The key lacks the scope in `need`, or an MCP connector token tried a money-bearing call (`need: "financial"`) |
| 403 | `not_authorized` | The key's workspace isn't allowed to do this, the same refusal the dashboard would give |
| 404 | `unknown_resource`, `…_not_found` | No such thing in your workspace |
| 409 | varies | The change conflicts with the record as it stands. Details below |
| 422 | `affiliate_not_eligible` | The affiliate you named can't take this. Details below |
| 429 | `rate_limited` | You hit one of the limits below |
| 500 | varies | Something went wrong on our side. The body carries a `request_id` to quote if you contact support |

## 402: plan locked

When your tracked revenue goes past your plan's allowance, dashboard and
management access lock until you choose a plan that covers it. API calls that
manage your program (writes, plus the commission quote and the move preview)
then return:

```json theme={null}
{
  "error": "plan_restricted",
  "message": "Dashboard and management access are locked until you upgrade. Your tracking has not stopped. ...",
  "docs": "https://www.affixo.dev/pricing"
}
```

**Your tracking keeps working.** Links still resolve, clicks still attribute,
`/v1/track` still records sales and commissions still accrue. `GET` reads keep
working too. Choosing a plan that covers your usage restores access.

## 409: conflicts

A 409 means the request was understood but the record's current state doesn't
allow it. Nothing changed. The common ones:

| `error` | Where | Why |
| - | - | - |
| `transition_not_allowed` | `PATCH /v1/commissions/{id}` | The status change isn't allowed, for example asking for `pending` |
| `commission_locked` | `PATCH /v1/commissions/{id}` | The commission is paid or was reversed by a refund or chargeback. `status` says which |
| `reassign_refused` | `PATCH /v1/conversions/{id}` | The sale can't move: its commission is paid or in a payout run, it's one payment of a subscription, or the customer belongs to another affiliate |
| `move_refused` | `POST /v1/referrals/move` | A commission is in an unpaid payout run, the customer has no email or Stripe customer id, or the referral already belongs to that affiliate |
| `update_failed` | `PATCH /v1/campaigns/{id}` | For example pausing the default campaign, making it invite only, or turning on auto-add for an archived campaign. Carries a `request_id` |
| `no_pending_membership` | `POST /v1/campaigns/{id}/memberships/{affiliate_id}` | That affiliate has no pending request on this campaign |
| `product_not_importable` | product and commission-rule writes | The Stripe product is archived or has no price. `reason` says which |

Refusals that come from the same rules as the dashboard carry `message` (what
went wrong, in plain words) and often a `hint` (where to do it instead).

## 422: the affiliate can't take it

Moving a referral or a sale to another affiliate returns `422
affiliate_not_eligible` when the new affiliate can't take it: they're
suspended, banned, archived or left the program, they're a test affiliate and
the customer is live (or the other way round), or they're the customer
themself.

```json theme={null}
{
  "error": "affiliate_not_eligible",
  "message": "Ray isn't an active affiliate, so this sale can't move to them.",
  "hint": "To move the customer and their future payments, use the move in Referrals."
}
```

## Limits

When you hit one of these you get `429` with `error: "rate_limited"`. Wait and
try again.

| What | Limit |
| - | - |
| `POST /v1/track/*` | 6,000 calls a minute per workspace |
| `POST /v1/commissions/quote` | 60 a minute per workspace, 500 a day per user, and 2,000 a day per workspace (dashboard and API together). API keys count as one user per workspace |
| `POST /v1/test-referrals` | 30 an hour per workspace |
| `POST /v1/products/sync` | Once a minute per workspace. The response has a `Retry-After` header and `retry_after` in seconds |

The quote limits return a `message` saying which limit you hit and when to try
again. In the dashboard, resending a failed webhook delivery is limited to once
every 10 minutes per delivery.

## MCP connector tokens

A token minted for an AI assistant through the [MCP server](https://docs.affixo.dev/mcp-server.md) can
read your program and change its configuration, but it can never move money or
change who is owed it, even with the `write` scope. These calls refuse it with
`403 insufficient_scope` and `need: "financial"`:

* `POST /v1/track/click`, `/lead`, `/sale` and `/refund`
* `PATCH /v1/commissions/{id}`
* `PATCH /v1/conversions/{id}`
* `POST /v1/referrals/move`

The connector can still preview a referral move. To apply it, use a REST API
key from **Settings → Developer & API**, or the dashboard.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.