Skip to content

Graduate a category one rung up (console plane, human authority)

POST
/v1/admin/orgs/{org_id}/projects/{project_id}/categories/{id}/graduate

Flip the graduation bit ONE rung up: supervised→auto_notify→auto_silent (D16). never_graduate is locked at supervised (409 graduation_locked); a non-adjacent target is 400; graduating to auto_silent requires the MATURITY GATE (clean_approval_count ≥ N AND category age ≥ min_age_hours) or it is 409 maturity_gate_unmet. On success it writes a ctr_ transition, emits category.graduated, and enqueues recheck_category nudges to pending drafts. Console-only (never an MCP tool, D17). A foreign id is 404.

org_id
required
string
project_id
required
string
id
required
string

Graduate a category ONE rung up to to_state (console plane, D16).

object
to_state
required
string
Allowed values: auto_notify auto_silent

Graduated.

A category (cat_…) in the registry (Review Loop, D9/D10). name + description are the skill-style metadata an agent fuzzy-matches against (NO LLM on our side; the agent does the matching). Categories are CUSTOMER-scoped and agent-attributed; nothing keys on the name (renames never break a reference).

object
id
required

Opaque category id (cat_…).

string
name
required
string
description
required
string
scope
required
string
Allowed values: org_shared agent_private
state
required
string
Allowed values: supervised auto_notify auto_silent
merged_into

Survivor category id when this one was merged/soft-deleted (cat_…).

string
created_by_agent_id
string
author_kind
required
string
Allowed values: agent human
rule_high_water
required
integer
rules_version
required
integer
never_graduate

Human lock pinning the category at supervised (no live auto-send; D16).

boolean
clean_approval_count

Clean human-approve-as-is count toward the maturity gate (D16).

integer
drift_count

Human edit/reject count in a graduated category toward drift auto-demote (K; §7).

integer
graduated_at

When the category was last graduated (omitted if never).

string format: date-time
graduated_by

Who last graduated it (‘human:’); empty if never/demoted.

string
created_at
string format: date-time
updated_at
string format: date-time

Invalid request.

The canonical error envelope. error is a stable machine code.

object
error
required

Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).

string
Example
forbidden
message

Human-readable detail (never leaks internals).

string
Example
missing required scope

Missing or invalid credential.

The canonical error envelope. error is a stable machine code.

object
error
required

Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).

string
Example
forbidden
message

Human-readable detail (never leaks internals).

string
Example
missing required scope

Authenticated but lacking the required scope, or out of quota.

The canonical error envelope. error is a stable machine code.

object
error
required

Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).

string
Example
forbidden
message

Human-readable detail (never leaks internals).

string
Example
missing required scope

Resource not found.

The canonical error envelope. error is a stable machine code.

object
error
required

Stable error code (e.g. unauthorized, forbidden, not_found, invalid, quota_exceeded, rate_limited).

string
Example
forbidden
message

Human-readable detail (never leaks internals).

string
Example
missing required scope

A 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
type
required
string format: uri
title
required
string
status
required
integer
detail
string
code
required

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.

string
Allowed values: bad_request unauthorized forbidden_scope not_found conflict idempotency_conflict breadth_required quota_exceeded rate_limited domain_not_allowed recipient_blocked recipient_suppressed not_configured domain_unavailable internal intent_required wrong_state terminal stale born_stale send_needs_reconciliation graduation_locked maturity_gate_unmet scope_taken unavailable
request_id
string
errors
Array<object>
object
field
string
code
string
detail
string