Skip to main content
Small page, high-value. If you’re writing a /decide call and unsure whether to pass ruleset_id or rulebook_id, you’re in the right place.

The two objects

The platform has two user-facing nouns: a Rulebook and a Ruleset. Everything else (sections of legislation, authoring projects, the state machine that moves a ruleset towards live) describes how these two objects are managed, not extra primitives you create. We talk about “sections of legislation” in prose (“this ruleset encodes the household-qualifying-criteria section of the Free School Meals (FSM) regulations”) but a Section is not a separate object. The named Ruleset within a Rulebook is the encoded section. A concrete example — UK Free School Meals:

The ruleset state machine

A ruleset version flows through four states: The only way to move a ruleset to live is aethis rulesets promote-to-live. The operation is atomic:
  1. Demote prior live ruleset of the same name → archived.
  2. Promote the candidate → live.
  3. Update the Rulebook’s live_ruleset_pins.
  4. Cut a new Rulebook version (v(n+1)) recording the change.
So iteration is cheap (do as much as you want in draft / testing); promotion is explicit and visible (every live change cuts a new Rulebook version).

ruleset_id vs rulebook_id on /decide

The public POST /decide endpoint has two separate fields on the request body, and they go through different resolution paths:
  • ruleset_id — evaluates one ruleset version in isolation. Useful for testing a candidate before promoting. Anon OK on public rulesets.
  • rulebook_id — evaluates the composed rulebook: every live ruleset combined via the rulebook’s outcome_logic. This is the “real decision” path. Always requires x-api-key — rulebook evaluation is unconditionally scope-gated, regardless of rulebook visibility. Anonymous callers get HTTP 401. (For an unauthenticated taste, hit a public section ruleset by its slug — ruleset_id: "aethis/uk-fsm/child-eligibility" — and the path is anonymous.)
Sending ruleset_id: "aethis/uk-fsm" — a rulebook slug — returns Ruleset 'aethis/uk-fsm' not found. The resolver only looks in the ruleset collection for that field; it doesn’t fall through to rulebooks. If you want the composed decision, move the value into the rulebook_id field.
See interfaces/rest-api.mdx for the full endpoint contract.

Visibility — public vs private

Every new ruleset defaults to private. The exception: publishing with a slug under the reserved aethis/* namespace auto-flips visibility to public. For everything else, call PATCH /rulesets/<id>/visibility after publish.

The aethis/* namespace

Reserved for first-party rulesets maintained by Aethis. External tenants attempting to publish slugs starting with aethis/ get HTTP 403 with reason_code: "reserved_namespace". This is intentional: aethis/* is where the canonical public demo rulebooks and rulesets live (see /getting-started/try-it and /getting-started/examples). The namespace doubles as an allow-list for “safe to reference from docs without fear of the tenant renaming it”.

Authoring lifecycle (one screen)

The four CLI commands that move a Ruleset between states:
aethis rulebooks decide aethis/uk-fsm -i '{...}' runs the composed evaluation against whatever set of rulesets is currently live in the rulebook.

See also

  • Decision envelope — what /decide returns and how to replay it
  • REST API — endpoint contracts + rate limits + auth
  • MCP tools — the agent-callable wrappers around these endpoints