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.
Error shape
{
"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 carriesRequest-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.
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.
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. |
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. |
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. |
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.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. |
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. |
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 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. |
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, where it always belonged, and is answered for a lookup key — your own word, not a configuration’s identifier.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 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 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}. |
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 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. |
Codes answered at the Console door only
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. |
Retries
Readretry 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. |
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.