Skip to main content
Examples use $AUDIT_KEY, $APPROVER_KEY, $OPS_KEY and $ADMIN_KEY for credentials of that role.

Approvals

GET /v1/approvals?status=&run_id= (auditor+)

status: pending, approved, denied, consumed, expired. Only argument names are stored. required_roles (roles or groups that may decide; empty means any approver), quorum, decisions, on_timeout and escalated come from the manifest and policy (approvals). A decided request also has break_glass when overridden that way.

POST /v1/approvals/{id}/approve and /deny (approver+)

Optional body {"note":"…"}. Returns the updated record (with quorum, status stays pending until enough distinct approvers agree). Errors: 404 unknown; 403 you are not among the required roles or groups, the request was made by your own run, or you already voted; 409 no longer pending (approval … is approved, not pending). The caller’s roles and groups are taken from the credential.

POST /v1/approvals/{id}/break-glass (admin)

Emergency override of a pending request. reason needs at least 10 characters (400 otherwise) and evidence a ticket or incident reference. The record becomes approved with break_glass: {by, reason, evidence, ms} and the console shows a break-glass badge on it.

Delegations

GET /v1/delegations (auditor+), POST /v1/delegations (approver+), DELETE /v1/delegations/{id} (the delegator or an admin). Body of the POST: {"to":"bob","hours":24,"reason":"on leave","groups":["finance"]}; from defaults to you and only an admin may set another. → 201 with the delegation. reversi-server serves these routes. The same delegations are visible in the console (Approvals page) and reversi delegate.

Evidence

GET /v1/runs/{id}/bundle and GET /v1/ledger/bundle (auditor+)

A signed evidence bundle. 404 unless the server has REVERSI_SIGNING_KEY.

GET /v1/evidence/key (auditor+)

GET /v1/ledger/verify (auditor+)

{"ok":true,"records":3,"head":"e3c2c625…"} or {"ok":false,"seq":7,"error":"…"}.

Status and kill switch

GET /v1/status (auditor+)

POST /v1/halt (admin)

reason is required. Optional tool, actor, tenant and environment (globs) narrow the scope; with none, every new call is blocked. halted in the answer is the global halt message (null for a scoped halt). Rollbacks still work. The caller’s name is appended to the reason.

GET /v1/halts (auditor+) and POST /v1/resume (admin)

GET → {"halts":[…]}. POST /v1/resume with {"id":"halt_…"} lifts one; with no body it lifts all → {"halted":null,"lifted":1}. See retention and evidence.

Manifest and policy registry

These are the routes reversi apply --server uses. They need a server started on a registry folder (and a policy versions folder for the policy routes), else 404 no manifest registry folder is configured on this server. See GitOps.

GET /v1/registry (operator+)

policy is null without a policy versions folder.

POST /v1/registry/manifests

status "draft" needs operator; anything else publishes as approved and needs admin, then reloads the running configuration. 400 for an invalid manifest (the message lists the problems). Optional note. New versions are signed if the server has a signing key.

POST /v1/registry/manifests/{tool}/approve and /deprecate (admin)

approve body {"version"?: n, "four_eyes"?: true} promotes a draft (the latest by default) to a new approved version; with four_eyes the author cannot be the approver. deprecate body {"replaced_by"?: "tool"}. Both → {"tool","version"} and reload.

POST /v1/registry/policy and POST /v1/registry/policy/{version}/activate (admin)

{"text":"<policy YAML>","note":"…","activate":true} validates and stores a new version (activating it unless activate is false) → 201 with the version record. activate makes an older version current, which is how you roll back → {"active":1}. If the saved configuration fails to load the answer is 409 saved, but the new configuration did not load: … and the previous one stays active.

POST /v1/reload (admin)

Re-read manifests and policy now.
error is present, with changed: false, if the new configuration was rejected.

API keys

Need REVERSI_KEYS to point at a writable file (404 otherwise). Admin only.
  • GET /v1/keys lists keys by index with name, roles, groups, tenant, expiry, disabled, allow_observe, max_permissions and a 12-character fingerprint (never the key).
  • POST /v1/keys body: name, role or roles, optional groups, tenant, max_permissions, expires_in_days. Only the SHA-256 is stored.
  • PATCH /v1/keys/{index} with {"disabled": true} and/or {"expires_at": "<ISO date>" | null}.
  • DELETE /v1/keys/{index}.
Changes take effect immediately.

Slack callback

POST /integrations/slack/actions approves or denies from the buttons on a Slack notification, verifying Slack’s signature and a 5-minute timestamp window. reversi-server enables it when REVERSI_SLACK_SIGNING_SECRET and REVERSI_SLACK_USERS (a JSON file mapping Slack user ids to names, roles and groups) are set, and otherwise answers 404 Slack approvals are not enabled. A mapped user needs the approver role or higher to decide (authentication).

Operational

GET /healthz → {"ok":true}. GET /readyz → {"ok":true,"ledger_head":"…","halted":null}, 503 if the store cannot be read. GET /metrics (auditor, or open with REVERSI_METRICS_PUBLIC=1): see monitoring.