AIOS Govern docs

Governance OS for AI agents. Agents submit envelopes; humans decide; you get the result back via signed webhook, long-poll, or realtime. Start with the Quickstart, or jump to a reference below.

Quickstart →

Get a key, submit your first envelope over REST or MCP, and act on decisions.

Architecture →

Roles, accounts & teams, the envelope lifecycle, routing, audit, licensing, and the data model.

API reference →

The full REST surface and the live OpenAPI spec.

MCP server →

Connect Claude Desktop, Cursor, or any MCP agent via /api/mcp.


REST reference

Quick inline reference. See Quickstart for a full walkthrough and API reference for the OpenAPI spec.

Quickstart

  1. Sign in and visit /agents. Click New invite URL.
  2. Have your agent POST /api/register-channel with the invite token.
  3. Save the returned api_key and webhook_secret — shown only once.
  4. Submit envelopes with POST /api/channels/:id/requests.
  5. Listen for the webhook callback, or call GET /api/requests/:id/wait.

Envelope schema

{
  "type": "approval_request" | "informational" | "permission_elevation"
        | "escalation" | "review_request" | "poll_request"
        | "choice" | "input_request" | "spend_approval",
  "title": "Approve domain transfer",
  "summary": "Agent wants to move example.com to Cloudflare.",
  "risk_level": "low" | "medium" | "high" | "critical",
  "payload": { /* arbitrary JSON */ },
  "approval_rule": { "type": "single" } |
                   { "type": "sequential", "steps": [...] } |
                   { "type": "n_of_x", "required": 2, "approvers": [...] },

  // Routing / authority — WHO acts on this envelope. Default "approval".
  "route": "log_only" | "approval" | "approval_self" | "approve_manager"
         | "approve_team" | "approve_any_admin" | "notify_manager" | "notify_team_owner",
  "team_id": "uuid (required for approve_team / notify_team_owner)",

  // Presentation hints.
  "bg_color": "#1b1b1b",          // card background
  "badge": "URGENT",              // diagonal corner ribbon (red for URGENT)
  "accent_bar": "green",          // left edge bar (notify/log read as green FYI)
  "pulse": "none" | "light" | "strong",
  "top_sort": true,               // pop to top of inbox/lists

  // TTL / expiry modifier.
  "expires_in_seconds": 3600,     // window; re-arms on escalate-on-timeout
  "expires_at": "2026-06-02T18:00:00Z",
  "on_timeout": "escalate" | "auto_approve" | "auto_reject" | "expire",
  "poll_interval_seconds": 120,   // re-poll cadence (60..432000); omit to auto-pick

  // Request-changes round-trip.
  "tweak_allowed": true,          // let the human bounce it back with edits
  "parent_envelope_id": "uuid",   // set when responding to a tweak with a NEW card

  // Links the reviewer should consult before deciding (doc, KB, deck, SharePoint…).
  "reference_links": [{ "label": "Q3 roadmap deck", "url": "https://…" }],
  "require_ack": true,            // disable decision buttons until a link is opened

  "return_mode": "webhook" | "long_poll" | "both",
  "return_webhook": "https://yourapp.example.com/aigos-callback",
  "long_poll_timeout_seconds": 300
}

Request changes (tweak)

If you submit with tweak_allowed: true, a human can bounce the envelope back with edits instead of approving or rejecting. You receive a tweak_requested event — over your return_webhook and on the long-poll response:

{
  "event": "tweak_requested",
  "request_id": "...",
  "tweak_id": "...",
  "request_text": "Reduce the spend cap to $500 and resubmit.",
  "requested_by": "user_uuid",
  "envelope_snapshot": { ... }
}

Process it and usually submit a new envelope with parent_envelope_id set to the original — that resolves the tweak and links the cards into one history. The human can also pull the tweak back, which emits tweak_withdrawn.

Card types

Routing & authority

route decides who acts, and enforces separation of duties:

Submit an envelope

POST /api/channels/{channel_id}/requests
Authorization: Bearer {agent_api_key}
Content-Type: application/json

{ ...envelope... }

Long-poll for the decision

GET /api/requests/{request_id}/wait?timeout=60
Authorization: Bearer {agent_api_key}

200 OK
{
  "request_id": "...",
  "status": "approved" | "rejected" | "acknowledged",
  "approved_by": "user_uuid",
  "decision_time": "2026-05-09T14:12:00Z",
  "comments": "...",
  "envelope_snapshot": { ... }
}

408 Request Timeout    (Retry-After: <repoll_after_seconds>)
{ "error": { "code": "timeout", "message": "Decision pending" },
  "request_id": "...", "state": "pending", "repoll_after_seconds": 120 }

Webhook callback (signed)

On a completed decision AIOS Govern POSTs the same payload as the long-poll response to return_webhook. Verify X-AIGOS-Signature with your webhook_secret:

X-AIGOS-Signature: sha256=<hmac>
X-AIGOS-Timestamp: <unix_seconds>
X-AIGOS-Delivery: <delivery_id>

const expected = "sha256=" + crypto
  .createHmac("sha256", webhook_secret)
  .update(rawBody)
  .digest("hex");
// timing-safe compare expected vs X-AIGOS-Signature header

Poll-request envelopes

AIOS Govern does not host polls. The envelope carries a render hint that points at an external poll provider; the dashboard embeds it.

{
  "type": "poll_request",
  "title": "Approve domain name choice",
  "summary": "Agent ran a poll on whatdohumansthink.com — review and approve.",
  "payload": {
    "poll_provider": "whatdohumansthink",
    "poll_url": "https://whatdohumansthink.com/take/POLLTOKEN",
    "poll_results_url": "https://whatdohumansthink.com/v1/polls/POLLID/results",
    "poll_id": "POLLID",
    "poll_instructions": "Click through to vote, or review the live tally."
  }
}

Retry policy

Webhook deliveries retry on non-2xx with exponential backoff at 1m, 5m, 15m, 1h, 6h, then mark failed. Admins can replay from /admin/webhooks.

Agent run ingestion (cost metering)

Report what an agent run cost — including external agents not built on AIOS Govern — so it shows up in the gallery (cost/run, estimate vs actual) and the org-chart budgets. Set the agent's ingest token in Agents → Gallery → Edit, then POST runs:

POST /api/agents/{agent_id}/runs
x-ingest-token: <the agent's ingest token>
Content-Type: application/json

// one run, or an array of runs
{
  "external_id": "run_8821",   // optional, dedupes re-sends
  "started_at": "2026-06-03T18:00:00Z",
  "tokens_in": 12000,
  "tokens_out": 2400,
  "model": "claude-sonnet-4-6",
  "vendor": "anthropic",
  "cost_usd": 0.21,
  "status": "success"
}

Or let AIOS Govern pull: set ingestion to scheduled with a URL that returns { "runs": [ … ] }; the hourly cron fetches and dedupes it. Agents that route LLM calls through the BYOK gateway are metered automatically — see gateway/README.md and GET /v1/usage.