REST API
The control-plane API: 110 routes for policy, decisions, runs, approvals, secrets, audit, SSO, and provisioning.
The SDKs cover the tool-executor boundary from inside your process. The REST API is for everything else — CI checks, internal tooling, and anything that needs to reach the control plane without running an agent.
Base URL and authentication
$ export CIRVIX_API=https://cirvix.internal.example.com $ curl -s $CIRVIX_API/health{"status":"ok","version":"0.3.0","uptime":12.4} $ curl -s $CIRVIX_API/v1/agents \ -H "authorization: Bearer cvx_..."| Credential | Prefix | Lifetime | Used by |
|---|---|---|---|
| API key | cvx_ | Until revoked | The endpoint daemon, CI, the SDKs |
| Session access token | JWT | 15 minutes | The console |
| Refresh token | cvr_ | 30 days, rotating | The console |
| SCIM token | scim_ | Until revoked | Your identity provider |
A SCIM token can only reach /scim/v2/*, and a console credential can only reach /v1/*. That separation is structural, not a check that could be forgotten: a SCIM principal carries no role, so the permission test could never be satisfied by one.
Two invariants
orgIdis never read from a body, query, or path. It comes only from the authenticated principal, so a client cannot address another tenant's data even by guessing ids — the parameter that would allow it does not exist in the routing layer.- Every route declares the permission it needs, or
nullfor an explicitly public one. The router throws at startup otherwise, so an endpoint cannot be added unauthenticated by omission.
Selected endpoints
| Method | Path | Permission |
|---|---|---|
| GET | /health | public |
| GET | /metrics | public, or CIRVIX_METRICS_TOKEN |
| POST | /v1/leads | public |
| GET | /v1/leads | CIRVIX_LEAD_TOKEN |
| POST | /v1/auth/login | public |
| GET | /v1/policy | policy:read |
| POST | /v1/policy | policy:write |
| POST | /v1/policy/validate | policy:read |
| POST | /v1/policy/simulate | policy:read |
| GET | /v1/decisions/:decisionId | decision:read |
| POST | /v1/runs/:runId/replay | policy:read |
| GET | /v1/approvals | decision:read |
| POST | /v1/approvals/:approvalId/decide | approval:decide |
| POST | /v1/secrets/handles | secret:use |
| GET | /v1/audit/verify | audit:read |
| GET | /v1/compliance/report | audit:read |
| GET | /v1/export | audit:read |
| GET | /v1/stream | decision:read |
Publishing a rule set
$ curl -X POST $CIRVIX_API/v1/policy \ -H "authorization: Bearer cvx_..." \ -H "content-type: application/json" \ -d @cirvix.policy.jsonRules are structurally validated before anything is written — publishing a malformed set fans it out to the entire fleet. POST /v1/policy/validate returns every problem at once rather than throwing on the first, because an operator fixing one error per round trip is an operator who gives up.
Fetching a decision
$ curl -s $CIRVIX_API/v1/decisions/dec_01JQ8F2K7M \ -H "authorization: Bearer cvx_..."{ "id": "dec_01JQ8F2K7M", "runId": "run_01JQ8F2K7M", "agent": "pr-triage", "action": "fs.read", "resource": "/repo/.env.production", "verdict": "deny", "rule": "deny-dotenv-read", "reason": "Reading .env files is denied outside an approved secrets flow.", "ts": "2026-08-06T09:14:02.418Z"}Rate limits
| Bucket | Default | Keyed by |
|---|---|---|
| General | 600/min (CIRVIX_RATE_LIMIT) | API key id or session id |
| Login, SSO, invitations | 10/min | IP |
| SCIM | 600/min | SCIM token id |
A sliding window, not a fixed one — a fixed window lets a caller spend the full budget at the end of one window and again at the start of the next. A refused request still counts against the budget, so a hot retry loop does not recover. /v1/stream is exempt.
