Save a writing rule (append-only by supersession)
PUT /v1/rules
Write or edit a rule (D11; ANY agent may write shared/house-style rules; the audit/undo log is the safety net). scope=‘general’ iff category_id is empty (house-style, D2). With supersedes_id the write is an EDIT: a new rev (same lineage) is inserted and the prior is flipped to superseded; else a new lineage is created. Writes a create/supersede audit row, bumps the rule high-water, and emits rule_changed nudges to affected in-flight drafts (born-stale, never silently invalidated).
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”Write or edit a rule (append-only by supersession; D11). scope=‘general’ iff category_id is empty (house-style, D2). With supersedes_id the write is an EDIT (rev+1, same lineage). kind defaults soft. propagate_to_pending is the D8 retro- propagation HUMAN OPT-IN (default OFF): when true, a NEW category rule that could apply to pending siblings enqueues ONE propagate_general_rule nudge (siblings + suggested_batch) so the agent redrafts a FEW at a time; never the whole queue. Agent-plane saves are ALWAYS project-layer (the saved rule’s rule_layer is project, bound to the key’s project); creating org-layer/house-style rules is console/admin-only in v1.
object
Stable retry identifier; also accepted through Idempotency-Key. Replays the original response for 24 hours.
Defaults from category_id (general iff empty).
Category id (cat_…); empty = house-style/general.
Defaults soft.
Provenance (rr_…).
Provenance (turn_…).
Set to EDIT the prior version (rule_…).
Set for a per-agent override; empty = all org agents.
D8 retro-propagation HUMAN OPT-IN (default false): enqueue a propagate_general_rule nudge to pending siblings of a NEW category rule.
Override the propagate batch (0 = base 3, bounded by rework_batch_max); never fans one nudge to the whole queue.
Responses
Section titled “ Responses ”Ok
A learned writing rule (rule_…) in the Review Loop (D2/D11). House-style/ general (scope=‘general’, applies across all categories) or category-scoped. Append-only by supersession: an edit is a new rev (same lineage_id) with the prior flipped to superseded. Read by the agent at compose/redraft time via the ordered get_rules ladder; we never apply it. Opaque ids only (D10).
object
Opaque rule id (rule_…).
Ownership layer (org/project model). org = house-style inherited by every project in the org; project = layered on top (the agent-plane default). Project/per-agent rules outrank broader org rules in the ordered get_rules ladder.
The org this rule belongs to.
The project this rule belongs to; empty for an org-layer rule.
Stable lineage handle across versions (rln_…).
General = house-style (D2).
Set iff scope=category (cat_…).
Set for a per-agent override; empty = all org agents.
Hard = non-overridable.
The prior rev this row superseded (rule_…).
Invalid request.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeMissing or invalid credential.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeAuthenticated but lacking the required scope, or out of quota.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeResource not found.
The canonical error envelope. error is a stable machine code.
object
Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).
Example
forbiddenHuman-readable detail (never leaks internals).
Example
missing required scopeA Review Loop conflict, as problem+json. Branch on code, NOT on the 409 status; the four codes demand opposite behavior. stale: the revision/version you named is no longer current (a human moved the draft) and NOTHING was mutated; errors[] carries the current state, revision and version, so re-apply your change on top and resubmit with the new parent_revision (retry, bounded to ~3). born_stale: the redraft was built against an older rule high-water; re-read the rules and resubmit, or restamp_review if nothing genuinely changed (at most one retry per high-water). wrong_state: this VERB is illegal from the current state but the draft is still live; NEVER retry the same verb; errors[] repeats an allowed_action entry per verb that IS legal right now. terminal: the review is already sent/auto_sent/cancelled; nothing will ever succeed, stop retrying, and a front_run_next review event carries the outcome. send_needs_reconciliation: a prior attempt is unconfirmed and parked for recover-by-Message-ID; do NOT resend, poll the review.
RFC-9457 problem+json error body. code is a closed machine enum clients switch on; type is a dereferenceable URI under https://extrovert.dev/problems/. Served as application/problem+json.
object
The CLOSED machine code. The Review Loop members split what used to be a single opaque conflict, because an agent must take a DIFFERENT action on each: stale (the revision/version you named is no longer current; nothing was mutated; re-read, re-apply, resubmit; retryable, bounded) and born_stale (built against an older rule high-water; re-read the rules or restamp_review; at most one retry per high-water) are the ONLY retryable 409s. wrong_state means this VERB is illegal from the current state while the draft is still live; never retry the same verb, read the state and the repeated allowed_action hints in errors[] and pick a legal one. terminal means the review is already sent/auto_sent/cancelled and nothing will EVER succeed; stop, and drain your review events for the outcome. send_needs_reconciliation means a prior send is unconfirmed and parked. Do not resend. Poll instead. unavailable (503) is the retryable fail-closed answer when a dependency could not be read; it carries Retry-After and is distinct from not_configured, which is permanent for this deployment.