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

# Errors

> Every status the API answers, every refusal code it publishes, and what to do about each.

The API uses conventional HTTP status codes. A `2xx` means the request did what it said. A `4xx` means something about the request or the record stands in the way, and the body says what. A `5xx` means the Platform failed, and the body says nothing about your request.

Every refusal is one envelope. Branch on `code`, `retry` and `details`. The `error` sentence is written for a person and may be reworded without notice; nothing else in the envelope ever is.

```json Error shape theme={null}
{
  "error": "loan: the reference names a different record: order-0042 already records a loan whose principal is 5000.00 MXN, and this issuance states 6000.00 MXN — a loan issued in error is corrected by cancelling it and issuing the correction under a new reference",
  "code": "reference_reused",
  "retry": "after_change",
  "details": {
    "field": "principal",
    "recorded_id": "c68c7355-0598-4cf4-a69a-b6b4ea10a5c4",
    "recorded_value": "5000.00 MXN",
    "stated_value": "6000.00 MXN"
  },
  "request_id": "0190f6a2-3c4d-7e8f-9a0b-1c2d3e4f5a6b"
}
```

| Field | Present | What it carries |
| - | - | - |
| `error` | Always | A sentence for a person. |
| `code` | Always | The kind of refusal. Append-only. |
| `retry` | Always | What to do next: one of the four values below. |
| `retry_after` | Where a wait applies | Whole seconds, equal to the `Retry-After` header. |
| `details` | Where the kind carries them | Typed values a program reads instead of the sentence. |
| `request_id` | Always | The Platform's identifier for the request, equal to the `Request-Id` header. |

### What to do next

| `retry` | Meaning |
| - | - |
| `as_is` | Nothing was written. Send the same request again — after `retry_after` where it is given. |
| `after_record` | A fact on record stands in the way. Act on the record first — retract, reverse, settle — and the sentence names how. |
| `after_change` | The request itself is wrong. Send a different one. |
| `never` | No request of yours will be served for this, by this key or for this lookup. |

### Details

The keys are a closed set, each present exactly where the kind of refusal carries it. Every value is a string.

| Key | On | What it carries |
| - | - | - |
| `attempt_id` | `source_unavailable` | The Platform's handle for a lookup whose response was lost. Your source ledger carries it. |
| `bound`, `observed` | `invalid_context`, `invalid_idempotency_key`, `invalid_page_size`, `rate_limited`, `concurrency_limited`, `rollover_limit_reached`, `return_exceeds_principal`, `client_open_loans`, `client_exposure` | The bound you exceeded, and what in your request exceeded it. On the two client guards, `observed` is what the Client already holds: the Loans open, or the principal outstanding. |
| `field` | `reference_reused`, `conflicting_registration`, `window_disagrees` | The statement your repeat differs on. |
| `recorded_id` | the same three | The record the reference, the identifier or the decision already names. |
| `recorded_value`, `stated_value` | `reference_reused` | What the record says of `field` and what your request says, as the sentence renders them — `none` for nothing stated. |

### The request identifier

Every response carries `Request-Id` — successes, refusals and failures alike — and every refusal carries the same value as `request_id`. It is the identifier on the Platform's own request log: quote it to name a request, above all a `500`, whose body says only that the request could not be served. You may send your own identifier in `Request-Id`; it comes back as `Caller-Request-Id` and is recorded beside the Platform's, never in its place.

<Note>
  The catalogue of codes is append-only. A published code is never renamed and never reused for a different kind of refusal. Treat a code you do not recognise as a new refusal, not as a fault in your client.
</Note>

## Status codes

| Status | Meaning | What to do |
| - | - | - |
| `400` | The request could not be read: bad JSON, an unknown field, a bad cursor or query parameter. | Fix the request. The message names the field. |
| `401` | The request carries no valid signature, or the key is revoked. | Check the key and the signed canonical string. See [API keys](/authentication/api-keys). |
| `403` | The key is valid but not allowed to do this. | Use a key with the right scope, or the right tenant. |
| `404` | The route or the record does not exist under your tenant. | Check the path and the ids. Another tenant's record answers the same `404`. |
| `405` | The route exists, the method does not. | Check the method in the reference. |
| `406` | The record stands in a state your `Contract-Version` does not know. | State the version `details.bound` names. See the [changelog](/changelog). |
| `409` | A fact already on record stands in the way. Nothing was written. | The message names the fact and how to clear it: retract it, reverse it, or send the recorded body again. |
| `413` | The body is over the size limit. | Send less. |
| `415` | The body is not `application/json`. | Set the `Content-Type` header. |
| `422` | The request was read, and a value in it is not acceptable. | The message names the field and the bound. Send a different request. |
| `424` | A credit source was reached and did not answer. Nothing was recorded and the `reference` stays free. | Read `retry`: `as_is` where the source's failure is retryable — resubmit the same application under the same reference — and `never` where a resubmission meets the same failure; settle the outcome with the source first. `details.attempt_id` names the lookup. |
| `429` | A rate limit or a source quota is spent. | Wait for the `Retry-After` header and send again. |
| `500` | The Platform failed. | Retry with backoff. The code is always `internal_error`; quote `request_id` to an operator. |
| `503` | The Platform is starting or draining. | Retry. The code is `unavailable`. |

<Tip>
  A `409` is never a defect of your request. It says the record already holds something the request contradicts: a receipt on a loan you want to cancel, a cancellation under a reference you reused, a write-off in force. Read the message, act on the record, then send the request again.
</Tip>

## Requests and access

| Code | Status | Meaning and what to do |
| - | - | - |
| `malformed_request` | `400` | The body is not valid JSON, repeats a key, or carries a field the route does not read. The message names it. |
| `invalid_query` | `400` | A query parameter the route does not read, or one it cannot parse. |
| `invalid_cursor` | `400` | A `cursor` that is not one the Platform issued. Start the listing again. |
| `invalid_page_size` | `400` | A `limit` outside `1` to `200`. The default is `50`. |
| `invalid_context` | `400` | A `context` object with more than 16 keys, a key over 64 bytes, or over 4,096 bytes in all. |
| `invalid_idempotency_key` | `400` | An `Idempotency-Key` that is not UTF-8, carries a NUL, or is over 255 bytes. |
| `invalid_contract_version` | `400` | A `Contract-Version` that is not a published version, not a day, or stated twice. See the [changelog](/changelog). |
| `unauthenticated` | `401` | No signature, a signature that does not verify, or a revoked key. |
| `not_permitted_scope` | `403` | The key does not carry the scope the route needs. |
| `not_permitted_tenant` | `403` | The key belongs to another tenant than the path names. |
| `not_permitted_actor`, `not_permitted_role` | `403` | At the Console door: the user is not a member, or not in the role the action needs. |
| `no_such_route` | `404` | No route at this path. |
| `not_found` | `404` | No such record under your tenant. |
| `method_not_allowed` | `405` | The route does not answer this method. |
| `contract_version_required` | `406` | The record stands in a state your `Contract-Version` does not know. State the version in `details.bound`. |
| `idempotency_mismatch` | `422` | The same `Idempotency-Key` with a different request. Use a new key. |
| `body_too_large` | `413` | The body is over the route's limit. |
| `unsupported_media_type` | `415` | The body is not `application/json`. |
| `contended` | `409` | Another write for the same client held its turn past two seconds. Nothing was written. `retry` is `as_is`: send it again. |
| `rate_limited`, `concurrency_limited` | `429` | Too many requests, or too many at once. Honour `Retry-After`. `retry` is `as_is`, and `details.bound` is the number in force for you: the rate per second, or the requests in flight. By default you are held to 16 in flight, 2 seconds of patience for a slot, 50 started per second and a burst of 100; [read yours](/api-reference/bounds). |
| `internal_error` | `500` | The Platform failed. Retry with backoff. |
| `unavailable` | `503` | The Platform is not serving yet. Retry. |

## Products

| Code | Status | Meaning and what to do |
| - | - | - |
| `unknown_archetype` | `422` | `archetype` is not `instalment`, `bnpl` or `payday`. |
| `missing_field` | `422` | A required field is absent: `id` on a create, or a field of `values` the archetype requires. The message names it. |
| `field_not_in_frame` | `422` | A field that belongs to another archetype. |
| `field_locked` | `422` | A field the archetype fixes, such as `terms` on a payday product. Remove it. |
| `out_of_bounds` | `422` | A value outside the bound the [endpoint page](/api-reference/product-configurations/create) states for it. |
| `inconsistent` | `422` | Two values that contradict each other: amounts in two currencies, a minimum above its maximum, or a repeated entry in the term menu. |
| `invalid_schedule` | `422` | The wrong parameter for the schedule kind, grid days outside the month, a day stated on the product, or one kind admitted twice. |
| `invalid_rate` | `422` | A rate that is not an exact decimal percentage per year. |
| `unknown_currency` | `422` | A currency code the Platform does not carry. |
| `invalid_tenant` | `422` | The `tenant_id` in the path is not a valid identifier. |

## Lookup keys

| Code | Status | Meaning and what to do |
| - | - | - |
| `lookup_key_taken` | `409` | The key names another version and the body did not state `transfer: true`. `details.recorded_id` is the product it names, `details.recorded_value` the version. Add `transfer: true` if you mean to move it. |
| `no_configuration` | `404` | No product with that `configuration_id` and version under your tenant. |
| `invalid_identifier` | `422` | The `lookup_key` is empty, over 64 characters, carries a character outside letters, digits, `-` and `_`, or is shaped like an identifier the Platform composes (`inst-000001`) or mints (a UUID). |
| `missing_field` | `422` | The body names no `configuration_id` or no `configuration_version`. |
| `not_found` | `404` | A read or removal of a key you do not hold, or removed. |
| `invalid_query` | `400` | The list named more than ten keys; `details.bound` is `10`. |

<Note>
  Two codes are retired on this surface and are never reassigned. `invalid_name` was answered until 2026-09-11, when a product configuration stopped carrying a name. `invalid_identifier` and `conflicting_configuration` were answered for one day, 2026-09-11 to 2026-09-12, while a tenant stated its configuration's identifier; the Platform composes it now, so neither can be raised on a configuration. `invalid_identifier` is unchanged on [Clients](#clients), where it always belonged, and is answered for a [lookup key](#lookup-keys) — your own word, not a configuration's identifier.
</Note>

## Clients

| Code | Status | Meaning and what to do |
| - | - | - |
| `invalid_identifier` | `422` | The `id` is empty, over 64 characters, or carries a character outside letters, digits, `-` and `_`. `details.bound` is the pattern, `^[A-Za-z0-9_-]{1,64}$`, and `details.observed` the identifier's length. |
| `invalid_label` | `422` | The `label` is empty, `null`, or over 200 characters. Leave it out instead. |
| `conflicting_registration` | `409` | The `id` is registered with a different label. Replay the recorded body or correct the label. |

## Loans: issuance

| Code | Status | Meaning and what to do |
| - | - | - |
| `unknown_client` | `422` | `client_id` is not registered under your tenant. Register it first. |
| `unversioned` | `422` | `configuration_version` is missing, not an integer, or `"latest"`. |
| `no_configuration` | `404` | No product with that `configuration_id` and version under your tenant. |
| `term_refused` | `422` | The term is outside the product's menu, the principal outside its range, a rate outside its corridor, or the schedule named is not one the version admits. |
| `missing_term` | `422` | A term the product requires is absent, such as `down_payment` on a BNPL loan or `schedule` under a version admitting several. |
| `below_minimum_instalment` | `422` | The payment falls under the product's `minimum_instalment`. |
| `penalty_rate_above_cap` | `422` | The penalty rate is over the market's cap. In Mexico, twice the ordinary rate. `details.bound` is the cap at the Loan's own rate, `details.observed` the penalty rate; [the market's rules](/api-reference/markets/read) state the cap's form. |
| `late_fee_beside_penalty` | `422` | The product has a late fee and the loan a penalty rate above zero. In Mexico a delay is charged once: drop the fee, or set the penalty corridor to zero. |
| `invalid_tax_rule` | `422` | The `tax` rule names a component the Platform does not charge, or has no rate. |
| `tax_rule_not_admitted` | `422` | The `tax` rule is not one of the rules the loan's jurisdiction admits, or leaves out a component the version charges. The message names the jurisdiction and its rules. |
| `no_tax_rules` | `422` | The Platform asserts no tax for the loan's jurisdiction. |
| `no_penalty_rule`, `incoherent_penalty_rule` | `422` | The market's penalty rule is absent or contradicts itself for this loan. |
| `no_disclosure_rule`, `no_disclosure_rate` | `422` | The market has no disclosure rule, or the loan's inputs give it no rate. |
| `no_fee_rule`, `no_equal_payment`, `empty_payment`, `ambiguous_flows` | `422` | The pricing engine cannot produce a plan from these terms. The message says which. |
| `rate_not_annual`, `invalid_basis`, `invalid_period` | `422` | A rate on the wrong basis, an unknown day count, or a period count of zero. |
| `invalid_date`, `invalid_cadence`, `invalid_series` | `422` | A date that does not exist, a cadence the calendar cannot step, or a schedule series it cannot build. |
| `invalid_jurisdiction`, `unknown_jurisdiction` | `422` | `jurisdiction` is not a two-letter code, or not one the Platform serves. |
| `beyond_horizon` | `422` | A date of the plan runs past the holiday calendar. Shorten the term. `details.bound` is the last covered day — the `calendar.horizon` [the market's rules](/api-reference/markets/read) answer — and `details.observed` the date asked about. |
| `reference_reused` | `409` | The `reference` names a loan with a different body. `details` names the field that differs, both values and the loan. |

### Under a facility

| Code | Status | Meaning and what to do |
| - | - | - |
| `facility_required` | `422` | The product requires a facility and the request names none. |
| `no_facility` | `422` | No facility with that id under your tenant. |
| `facility_client` | `422` | The facility was granted to another client. |
| `facility_product` | `422` | The facility was granted under another product. |
| `facility_currency` | `422` | The facility is in another currency. |
| `facility_rate` | `422` | The rate is outside what the facility allows. |
| `facility_window` | `422` | The anchor is outside the facility's window. |
| `facility_headroom` | `422` | The principal exceeds what the facility has left. |
| `facility_arrears` | `422` | A loan already under the facility is overdue. Settle it first. |
| `facility_superseded` | `422` | The line was renewed by another line, and the anchor falls on or after the day the new line opens. Draw from the new line. |
| `decision_disagrees` | `422` | The credit decision named on the issuance was made for other terms. |

### The client guards

Held at every issuance and preview where the configuration version states them, over the Client's whole book on the anchor, whatever facility the Loan draws from.

| Code | Status | Meaning and what to do |
| - | - | - |
| `client_open_loans` | `422` | The Client already holds as many open Loans as the version admits. `details.bound` is the count, `details.observed` the Loans open. |
| `client_exposure` | `422` | The Loan would take the Client's outstanding principal past the version's cap. `details.bound` is the cap, `details.observed` what is outstanding. |
| `client_overdue` | `422` | A Loan of the Client's stands overdue — under the version's guard, or on any draw from a line. Cure the arrears first. |

## Loans: acts

| Code | Status | Meaning and what to do |
| - | - | - |
| `invalid_event` | `422` | The date is missing, before the anchor, or before a fact still in force. The message names the day. |
| `not_retractable` | `422` | The event is the issuance. Record a cancellation instead. |
| `already_cancelled`, `already_accelerated`, `already_written_off`, `already_ceded`, `already_retracted`, `already_reversed` | `409` | The act already stands. The retry that answers `200` is the one under the recorded reference. |
| `acceleration_in_force`, `write_off_in_force`, `cession_in_force` | `409` | A fact stands that this act cannot follow. Retract it first. |
| `receipt_allocated` | `409` | Money has reached the loan, so it cannot be cancelled. Reverse the receipts first. |
| `acceleration_refused` | `422` | Not enough periods are overdue, or notice was not given as the market requires. |
| `nothing_to_write_off` | `409` | The loan owes nothing. Reverse the receipt that settled it if it was an error. |
| `rollover_not_provided` | `422` | The product states no `rollover_fee`. Issue under a version that does. |
| `nothing_to_roll_over` | `409` | The loan owes nothing on the date. |
| `overdue_on_rollover` | `409` | A period is overdue on the date. Settle it first. |
| `rollover_not_forward` | `409` | The date is at or before the last rollover in force. |
| `rollover_limit_reached` | `409` | One more rollover would exceed the product's `rollover_limit`. |
| `return_not_provided` | `422` | The product is a Pay Day Loan, which has no purchase behind it, so nothing can be returned. |
| `return_exceeds_principal` | `409` | The returned amount is more than the outstanding principal on the date. |
| `undeterminable` | `409` | The loan's record is in a state the question cannot be answered from. `anomalies` on the loan says why. |
| `no_act` | `404` | No act was recorded under that reference. Read before you resend: [`GET /acts/{reference}`](/api-reference/loans/act). |

## Receipts

| Code | Status | Meaning and what to do |
| - | - | - |
| `receipt_refused` | `422` | The receipt states too little to record: no amount, no date, or an election the body cannot carry. |
| `reversal_refused` | `422` | The reversal states too little to record. |
| `no_receipt` | `404` | No receipt with that id under your tenant. |
| `no_allocation_rule`, `incoherent_allocation_rule` | `422` | The market's allocation rule is absent or contradicts itself. |
| `outside_loan`, `outside_plan` | `422` | A date before the loan's anchor, or past the end of its plan. |
| `not_a_prepayment` | `422` | An election on money that does not reach past every matured period. |
| `unreachable_payment` | `422` | No term can produce the payment the election asks for. |
| `out_of_range` | `422` | An amount the Platform's integer money cannot hold. |

## Company profile

| Code | Status | Meaning and what to do |
| - | - | - |
| `no_profile` | `404` | No profile has been stated yet. State one first. |
| `invalid_statement` | `422` | The profile statement is incomplete or contradicts itself. |
| `unknown_country` | `422` | A country the Platform does not serve. |
| `invalid_time_zone`, `no_time_zone` | `422` | A time zone that does not exist, or a profile that states none where one is needed. |

## Credit policy and decisions

| Code | Status | Meaning and what to do |
| - | - | - |
| `unknown_policy`, `unknown_version` | `422` | No policy, or no such version of it, under your tenant. |
| `no_adoption` | `404` | The tenant has adopted no policy. |
| `no_decision` | `404` | No decision with that id. |
| `not_approved` | `422` | The decision named is not an approval. |
| `invalid_application` | `422` | The application is missing a field the policy reads. |
| `rate_outside_corridor` | `422` | The rate asked for is outside the policy's corridor. |
| `unknown_state` | `422` | A Mexican state code the Platform does not carry. |
| `source_access` | `422` | The policy reads a credit source the tenant has not enabled. |
| `source_quota_spent` | `429` | The source's quota for the period is spent. Honour `Retry-After`. |
| `source_unavailable` | `424` | The source was reached and did not answer; nothing was recorded. `retry` says whether to resubmit and `details.attempt_id` names the lookup. |
| `invalid_creation`, `outside_window_bound` | `422` | A facility with an empty window, one longer than the bound, or a line asked of a decision that approved no `limit`. On `outside_window_bound`, `details.bound` is the window in days — the `facility_window` [the market's rules](/api-reference/markets/read) answer — and `details.observed` the stated expiry's distance from the decision's day. |
| `cannot_supersede` | `422` | The line named in `supersedes` is not this client's line for this configuration, or another line already renewed it. |
| `window_disagrees` | `409` | The decision already granted a facility with a different window or a different `context`. `details.recorded_id` names it. |
| `reference_conflict`, `version_conflict` | `409` | The same reference names a different decision, or the policy moved under you. Read it again. |
| `client_barred` | `409` | The adoption enforces the credit bar and a recorded refusal bars the client on the day the application states. Nothing was recorded; the message names the day the bar lifts. |

<Accordion title="Codes answered at the Console door only">
  These are answered when a policy is authored or a credential is managed at the Console, and never by the tenant API.

  | Code | Status | Meaning |
  | - | - | - |
  | `not_a_person`, `not_a_second_person`, `unattributed` | `403` | The action needs a signed-in person, a second distinct person, or an actor it can attribute. |
  | `policy_name_taken` | `422` | A policy with this name exists. |
  | `version_sequence` | `422` | The version number is not the next one. |
  | `digest_claim` | `422` | The signed digest does not match the policy text. |
  | `rejected_content` | `422` | The policy text is not one the lexicon accepts. |
  | `lexicon` | `422` | The declaration names a term the lexicon refuses. |
  | `unconfirmed`, `already_confirmed` | `422`, `409` | The version is not yet confirmed by the second person, or already is. |
  | `invalid_declaration`, `declaration_exists`, `declaration_withdrawn`, `unknown_declaration`, `declaration_limit` | `422`, `409`, `422`, `404`, `422` | An attribute declaration that is malformed, already stands, was withdrawn, does not exist, or would exceed the limit. |
  | `no_credential`, `unadmitted_scope`, `already_revoked`, `invalid_lifetime` | `422` | A source credential that is absent, out of scope, already revoked, or minted with a lifetime outside the bound. |
</Accordion>

## Retries

Read `retry` rather than the status. The status still classes a code you do not know, roughly so:

| Answer | `retry` |
| - | - |
| `409 contended`, `429`, `503`, `5xx` | `as_is`, with backoff and `retry_after` where given. |
| `409` naming a fact on record | `after_record`. |
| `409 reference_reused`, `422`, `400`, `401` | `after_change`. |
| `403` | `never` for this key. |
| `424` | `as_is` or `never`, by the source's declared failure. |

Requests that create records are safe to repeat as sent. An issuance, an act or a receipt repeated under its own `reference` answers `200` with the record on file — and where your response was lost, read the record by its reference before you resend. A product creation repeated under its `Idempotency-Key` answers the recorded outcome. See [What is safe to retry](/api-reference/overview#what-is-safe-to-retry).


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