Install & use AIOS Govern
AIOS Govern is a human-in-the-loop governance layer for AI agents. An agent submits an envelope (an approval request, spend approval, choice, info notice, …) to a human inbox, then receives the human's decision back over a signed webhook, a long-poll, or realtime. This page walks an agent or integrator from zero to a working round-trip over both REST and MCP.
1 · Get access
Every call is authenticated with a Bearer key bound to your account. There are two ways to get one:
Self-serve MCP key
Visit /connect and mint an MCP key (kind mcp). This is the fastest path for connecting Claude Desktop, Cursor, or any MCP-capable agent. The key is shown once — store it securely.
REST agent key
REST agent keys (kind agent) are issued through the invite / register-channel flow on /agents:
- An admin opens
/agentsand creates a New invite URL. - The agent
POSTs the invite token to/api/register-channel. - The response returns
api_key(e.g.aigos_sk_…) and awebhook_secret— both shown only once.
Each key carries scopes (submit, read, close, approve) and is rate-limited. New non-admin sign-ups land in a pending state until an admin approves the account — see the Architecture page for the approval gate.
2 · REST quickstart
Submit an envelope to a channel, then wait for the decision. Submission returns request_id, the resting state, a long_poll_url, and repoll_after_seconds.
# Submit an envelope
curl -X POST https://<your-domain>/api/channels/{channel_id}/requests \
-H "Authorization: Bearer aigos_sk_..." \
-H "Content-Type: application/json" \
-d '{
"type": "approval_request",
"title": "Approve domain transfer",
"summary": "Move example.com to Cloudflare.",
"risk_level": "high",
"route": "approval",
"return_mode": "long_poll",
"payload": { "domain": "example.com", "to": "cloudflare" }
}'
# → 201 Created
# {
# "request_id": "…",
# "state": "pending",
# "held": false,
# "long_poll_url": "/api/requests/…/wait",
# "repoll_after_seconds": 120
# }Long-poll for the decision
GET …/wait blocks up to timeout seconds (max 300). On a decision it returns 200 with the decision payload. If still pending it returns 408 with repoll_after_seconds in the body and a matching Retry-After header — honor it and call again.
curl https://<your-domain>/api/requests/{request_id}/wait?timeout=60 \
-H "Authorization: Bearer aigos_sk_..."
# → 200 OK
# {
# "request_id": "…",
# "status": "approved", // approved | rejected | acknowledged | completed
# "approved_by": "user_uuid",
# "decision_time": "2026-06-02T14:12:00Z",
# "comments": "…",
# "envelope_snapshot": { ... }
# }
# → 408 Request Timeout (Retry-After: 120)
# {
# "error": { "code": "timeout", "message": "Decision pending" },
# "request_id": "…",
# "state": "pending",
# "repoll_after_seconds": 120
# }Or: receive the signed webhook
Submit with return_mode: "webhook" (or "both") and set return_webhook. On a decision, AIOS Govern POSTs the same decision payload to your URL. Verify the X-AIGOS-Signature header (HMAC-SHA256 of the raw body using your webhook_secret) before trusting it.
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-Signature3 · MCP quickstart
AIOS Govern exposes a streamable-HTTP MCP server at /api/mcp. Point any MCP client (Claude Desktop, Cursor, …) at it with your key. See the MCP page for the full reference.
{
"mcpServers": {
"aigos": {
"url": "https://<your-domain>/api/mcp",
"headers": {
"Authorization": "Bearer <YOUR_MCP_KEY>"
}
}
}
}Agents discover the full toolset at connect time via the standard tools/list JSON-RPC method — no hard-coding required. The tools:
submit_governance_request— submit an envelope to a human inbox.get_request_status— read current state + latest decision.wait_for_decision— block up to 60s, else return re-poll guidance.list_pending_requests/list_folders— list pending envelopes / folders.acknowledge·complete— acknowledge info / approve-or-mark-info per route.approve·reject·request_info·escalate— act on a request.
Each key is scoped (submit / read / close / approve) and tools are gated accordingly. Separation of duties is enforced: a key cannot approve a request its own agent submitted under route: "approval". Use approval_self only where the submitter is allowed to self-approve.
4 · The envelope shape
The canonical request body. type, title, summary, risk_level and return_mode are required; the rest have sensible defaults.
{
// ── required ──────────────────────────────────────────────
"type": "approval_request", // | informational | permission_elevation
// | escalation | review_request | poll_request
// | choice | input_request | spend_approval
"title": "Approve domain transfer", // 1–300 chars
"summary": "Move example.com.", // 1–2000 chars
"risk_level": "high", // low | medium | high | critical
"return_mode": "long_poll", // webhook | long_poll | both
// ── payload + decision rule ───────────────────────────────
"payload": { }, // arbitrary JSON (options, amounts, …)
"approval_rule": { "type": "single" }, // | sequential | n_of_x | chain
// ── routing / authority — WHO acts (default "approval") ───
"route": "approval", // 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
"accent_bar": "green", // left edge bar
"pulse": "none", // none | light | strong
"top_sort": false, // pop to top of inbox/lists
// ── expiry / TTL ──────────────────────────────────────────
"expires_in_seconds": 3600, // window (30..2592000); re-arms on escalate
"expires_at": "2026-06-02T18:00:00Z", // or an absolute timestamp
"on_timeout": "escalate", // escalate | auto_approve | auto_reject | expire
"poll_interval_seconds": 120, // re-poll cadence (60..432000); omit to auto-pick
// ── delivery + tweak round-trip ───────────────────────────
"return_webhook": "https://yourapp.example.com/aigos-callback",
"long_poll_timeout_seconds": 300, // 1..300
"tweak_allowed": false, // let a human bounce it back with edits
"parent_envelope_id": "uuid" // set when answering a tweak with a NEW card
}5 · Acting on requests
Decisions (human side)
Humans act through the inbox UI, which hits the request endpoints: approve, reject, request-info, escalate, delegate, and mark-informational (acknowledge). Each transition is recorded as an immutable decision in the audit trail.
Answering a needs_info question (agent side)
When a human picks request info, the envelope moves to needs_info. Your agent answers by POSTing to the respond endpoint, which records the answer in the decision thread and moves the envelope back to pending for re-review:
curl -X POST https://<your-domain>/api/requests/{request_id}/respond \
-H "Authorization: Bearer aigos_sk_..." \
-H "Content-Type: application/json" \
-d '{ "answer": "The transfer is reversible within 5 days." }'
# → 200 OK { "request_id": "…", "state": "pending" }The tweak round-trip
Submit with tweak_allowed: true and a human can bounce the envelope back with edits instead of a hard approve/reject. You receive a tweak_requested event — over your webhook and on the long-poll response — carrying the requested change and an 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. A human can also withdraw the tweak (tweak_withdrawn).
{
"event": "tweak_requested",
"request_id": "…",
"tweak_id": "…",
"request_text": "Reduce the spend cap to $500 and resubmit.",
"requested_by": "user_uuid",
"envelope_snapshot": { ... }
}Expiry & escalate-rearm
An envelope with expires_in_seconds + on_timeout: "escalate" will, on lapse, escalate to a higher authority and re-arm the same countdown window so it keeps pressing for a decision. Other timeout modes — auto_approve, auto_reject, expire — resolve terminally instead.
Next: how the system fits together, the full REST reference, or the MCP server docs.