Skip to main content
api.aethis.ai serves engine 0.49.1, which is the contract these pages describe; every response carries engine_version so you can confirm which build answered you. What the deployed engine serves.
The public API returns a detail object on every 4xx / 5xx, typically shaped:
A handful of endpoints return detail as a plain string (notably 404s from lookup endpoints). In those cases there’s no reason_code; match on the status code and message. For a working debugging flow using these codes, see Debug a failing decide.
A 401 or 403 is usually a tier boundary, not a bug. Evaluating a published public ruleset needs no key at all — if you are getting 401, check you are sending ruleset_id with a leaf slug rather than rulebook_id, which is always key-gated. Authoring and publishing are invite-only: request access. Which operations sit in which tier: Capabilities and access.
A 200 is not automatically a success on /decide. A non-empty field_errors object means an input could not be applied and forces decision: "undetermined" — a positive verdict never appears beside one. Read field_errors before you read decision. See the response truth table.

By reason_code

missing_api_key — 401

The endpoint requires an API key and you didn’t send one. Anonymous /decide accepts ruleset_id on public rulesets; rulebook_id and all authoring endpoints do not. Add an x-api-key header.

invalid_api_key — 401

The key doesn’t match any active record — typo, wrong environment, or revoked. Re-run aethis login to mint a fresh one, or retrieve the correct key from your credential store.

api_key_expired — 401

Create a new key. Expiring keys are uncommon on the public platform today but will become standard when scheduled rotation lands.

denied_missing_permission — 403

Your key authenticated but lacks the scope the endpoint requires. missing_permissions is the authoritative list. For most authoring flows you’ll want decide, rulesets:read, rulesets:write, projects:write; composed rulebooks additionally need rulebooks:read / rulebooks:write. Run aethis whoami to see the scopes your key actually carries. See Authentication & API keys for the full scope model, or REST API scopes.
Keys minted by aethis login (and aethis account generate with no --scope) do not include rulebooks:*, so your first aethis rulebooks create/publish will 403. Mint a rulebook-capable key by passing the full scope set explicitly — --scope/-s replaces the default list, so include every scope you want:

reserved_namespace — 403

You tried to publish a ruleset or rulebook under a reserved slug prefix (aethis/*). Pick a different namespace (e.g. my-team/*, my-company/*).

invalid_slug_format — 422

Slugs are lowercase ASCII letters + digits + hyphens, organised into /-separated segments, starting with a letter. Common causes: uppercase letters, spaces, trailing /, leading -. See Nomenclature.

slug_conflict — 409

Slugs are globally unique across tenants. If you own the slug on a prior ruleset (same tenant), the publish flow will transfer it to the new ruleset automatically. If a different tenant holds it, you need a different slug.

daily_quota_exceeded — 429

category is the operation class the request was metered against. Limits apply per rolling 24-hour window, so waiting frees budget as the oldest counted hour ages out (Retry-After: 3600). Upgrade your tier via support, or check remaining budget ahead of time with the X-RateLimit-* headers or GET /usage. The stable reason_code remains daily_quota_exceeded for backward compatibility.
generate currently runs in report-only mode: over-limit generation requests are recorded but not rejected while the ceiling is tuned. The other classes (decide, author, read, keys, admin) are enforced.

rate_limit_backend_unavailable — 503

Transient. Retry with exponential backoff. If it persists, contact eng@aethis.ai with the decision_id from the most recent successful call for correlation.

Bare-string 404s (no reason_code)

Lookup endpoints return detail as a string rather than an object when the target doesn’t exist:
Common causes:
  • Slug typo. Whitespace, capitalisation, or a / mismatch.
  • Using ruleset_id to look up a rulebook slug. aethis/uk-fsm is a rulebook slug; it won’t resolve from the ruleset_id field. Move the value to rulebook_id and add x-api-key. See Nomenclature.
  • Ruleset is private. Anonymous /decide only returns public rulesets; private rulesets 404 from an unauthenticated caller to avoid leaking their existence.
  • Slug typo on /explain-failure. /explain-failure resolves a slug the same way /decide, /schema, and /explain do — pass either the friendly slug (aethis/uk-fsm/child-eligibility) or the concrete ruleset_id from your /decide envelope. A 404 here is a spelling/visibility issue, not a slug-vs-id issue.

Validation errors from FastAPI (422)

If you send a malformed request body — missing required field, wrong type, unknown enum value — FastAPI returns its own 422 shape:
loc pinpoints the offending field. Common causes in authoring flows:
  • Omitting expected_outcome on POST /rulesets/{id}/explain-failure
  • Sending outcome_logic as { "expr": "A AND B" } on POST /rulebooks/ — the server requires a full Expr AST since aethis-core 0.7.2. See Author a rule.

extra_forbidden — an undefined top-level request key

Request bodies are strict. A top-level key the API does not define is rejected, not ignored:
If you have client code sending a field that is not in the API reference, it was never doing what you thought — the server was not implementing it silently.

ruleset_record_corrupt — 422 on a direct read

A stored ruleset that fails validation when read is reported as a structured 422 on a direct lookup, rather than surfacing as a page-wide 500. On a catalogue listing the same record is skipped instead, and the count is reported in the X-Aethis-Records-Omitted response header — read that header before concluding a short page means the end of the catalogue. See Honest catalogue counts.

Date field values

Date fields are stored internally as integer ordinals (days since year 1). Since aethis-core 0.31.0, /decide and /explain-failure also accept strict ISO date strings on date fields and convert server-side — "2025-04-13" and 739354 are equivalent inputs. Two caveats:
  • Bare YYYY-MM-DD only. ISO datetimes ("2025-04-13T00:00:00Z") are not accepted.
  • Rulebook evaluations still require ordinals. ISO acceptance applies when evaluating a ruleset (ruleset_id); the rulebook_id path takes integer ordinals only.
A date value that parses as neither form ("2026-02-30", "next tuesday") does not fail the request: /decide returns 200 with the field named in field_errors and that answer ignored, so the decision is computed from the remaining fields (typically undetermined). To convert an ISO date to an ordinal yourself:

Source of truth

This page is hand-maintained. The authoritative list of reason_code values lives in the server source under aethis-core/aethis_core/public/ — grep for reason_code to enumerate. When a new reason_code is introduced, update this page in the same PR.