Complete REST API Reference
This page documents every HTTP route exposed by the Control Zero backend. It is an exhaustive reference intended for developers writing direct API clients. For a narrative introduction and higher-level concepts, see the API Overview.
Base URL
- SaaS production:
https://api.controlzero.ai - Staging:
https://staging-api.controlzero.ai - Self-managed: the hostname you configured during
czctl init
All endpoints listed below are relative to the base URL.
Authentication
Control Zero exposes three distinct authentication mechanisms, each used by a different class of route.
| Auth model | Header | Route families |
|---|---|---|
| API key (Bearer) | Authorization: Bearer cz_live_... or cz_test_... | /v1/*, /v1/sdk/*, /api/scout/* |
| Dashboard session | Firebase ID token (SaaS) or session JWT (self-managed) via Authorization: Bearer ... | /api/* |
| Machine signature | Cryptographically signed request headers bound to an enrolled machine_id | /api/enroll, /api/heartbeat, /api/policy, /api/audit |
| SCIM bearer | Authorization: Bearer <scim_token> (org-specific) | /scim/v2/* |
| Signature-verified | Provider signature (Stripe, Firebase) | /webhooks/* |
API keys have a required scope. read is the minimum for all /v1/sdk/*
operations; write is required for creating or mutating policies via
/v1/policies. The scout scope is required for /api/scout/*.
The backend also accepts the X-API-Key header as an equivalent to
Authorization: Bearer.
The hosted gateway at gateway.controlzero.ai uses a different header
(X-ControlZero-API-Key or Authorization: Bearer) to avoid colliding with
upstream provider headers like Anthropic's X-Api-Key. The backend at
api.controlzero.ai uses X-API-Key or Authorization: Bearer. See the
API Overview for details.
Rate limits
All authenticated routes pass through a combined rate limiter:
- Per-IP: 1,000 requests/min (protects against single-source DDoS).
- Per-API-key: 500 requests/min (protects against single-tenant abuse).
A request that exceeds either limit receives 429 Too Many Requests.
Requests larger than 1 MB receive 413 Request Entity Too Large.
Conventions
- All request and response bodies are JSON unless otherwise stated.
- All timestamps are RFC 3339 UTC.
- Errors use the shape
{ "error": "message" }with an appropriate HTTP status code. - Every request emits an
X-Request-IDresponse header for correlation.
Health and meta
GET /
Unauthenticated. Returns basic service metadata (version, environment).
GET /health
Unauthenticated. Liveness probe. Returns 200 OK if the process is up.
GET /ready
Unauthenticated. Readiness probe. Returns 200 OK only when the rate-limit cache, the
secret store, and the configured auth backend all respond. Used by
Kubernetes / load-balancer health checks.
GET /metrics
Prometheus scrape endpoint. Requires the request to originate from an
internal network (enforced by InternalOnly middleware).
SDK endpoints (/v1/sdk/*)
All /v1/sdk/* endpoints require a Bearer API key with at least the
read scope and are subject to the per-tenant usage limiter.
POST /v1/sdk/init
Performs SDK initialization. Returns the project configuration needed to bootstrap an SDK instance.
- Auth: API key (read scope).
- Request body:
{ "sdk_version": string, "hostname": string }. - Response: project metadata and session descriptor.
- Errors:
401invalid key,402usage limit exceeded.
POST /v1/sdk/logs
Ingests a batch of audit log entries from the SDK into the audit pipeline.
- Auth: API key (read scope).
- Request body:
{ "entries": [ { ... } ] }. - Response:
{ "accepted": int, "rejected": int }.
POST /v1/sdk/refresh
Refreshes the SDK session descriptor (fresh cached policy pointer, rotating hint for bundle ETag).
- Auth: API key.
GET /v1/sdk/policies/pull
Returns the signed + encrypted .czpolicy policy bundle. Supports
ETag-based conditional requests (If-None-Match); returns 304 Not Modified when the client already has the current bundle.
- Auth: API key.
- Response: binary bundle body with headers:
X-Policy-Signature: request signature, base64-encoded.ETag: bundle fingerprint.
- Errors:
404no policy published,409tampered state.
POST /v1/sdk/tamper-alert
Receives tamper-detection alerts from SDKs when local bundle integrity
fails. Alerts are written to the observability store and may trigger
notifications per the org tamper_behavior setting.
- Auth: API key.
- Request body:
{ "machine_id": string, "reason": string, "bundle_hash": string }.
GET /v1/sdk/keys/public
Returns the org-level verification public key used to verify policy bundle signatures.
- Auth: API key.
- Response:
{ "kid": string, "public_key": base64 }.
GET /v1/sdk/bootstrap
Returns the project encryption key and signing public key needed for
the SDK to verify and decrypt .czpolicy bundles.
- Auth: API key.
- Response:
{ "encryption_key": base64, "signing_public_key": base64, "kid": string }.
POST /v1/sdk/audit
Hosted-mode audit ingest. Accepts audit entries via the Bearer API key and does not require machine enrollment. Use this path when the SDK is running without prior enrollment.
- Auth: API key.
- Request body:
{ "entries": [...] }.
Machine-signed SDK endpoints
These endpoints authenticate via a cryptographic signature over the request,
bound to the machine_id that was issued during enrollment. They are
feature-gated behind ENROLLMENT_API_ENABLED and return 503 when
disabled.
POST /api/enroll
Performs initial enrollment. Unauthenticated: the request carries a
single-use enrollment token and a freshly generated verification public key
bound to the caller's machine_id.
- Request body:
{ "enrollment_token": string, "public_key": base64, "machine_id": string, "hostname": string }. - Response:
{ "machine_id": string, "org_id": uuid, "project_id": uuid }. - Errors:
400invalid token,409already enrolled,503feature off.
POST /api/heartbeat
Machine check-in. Called periodically by enrolled SDKs / agents to report liveness and receive fleet-level configuration updates.
- Auth: machine signature.
GET /api/policy
Same semantics as /v1/sdk/policies/pull but using machine-signed
auth. Returns the signed bundle.
- Auth: machine signature.
POST /api/audit
Machine-signed audit ingest. Equivalent to /v1/sdk/logs but
authenticated by machine signature.
- Auth: machine signature.
Shadow AI Scout (/api/scout/*)
Used by deployed Scout agents to report discovered AI traffic. Requires
an API key with the scout scope, and is gated by the scout_agent
feature flag (Teams tier).
| Method | Path | Purpose |
|---|---|---|
| POST | /api/scout/register | Register an agent instance. |
| POST | /api/scout/heartbeat | Agent heartbeat. |
| POST | /api/scout/discoveries | Submit a batch of discovered AI traffic. |
| GET | /api/scout/config | Pull current Scout config. |
| POST | /api/scout/license/validate | On-prem license validation. |
Public API (/v1/*)
GET /v1/policies
List policies visible to the API key.
- Auth: API key,
readscope.
POST /v1/policies
Create a new policy.
- Auth: API key,
writescope. - Request body: policy document (YAML-equivalent JSON).
PATCH /v1/policies/{policyID}
Update a policy by ID.
- Auth: API key,
writescope.
DELETE /v1/policies/{policyID}
Delete a policy by ID.
- Auth: API key,
writescope.
Event stream
GET /api/events/stream
Server-sent events stream of governance events. Accepts the API key via
the standard Authorization header, or via the ?access_token= query
parameter (required for browser EventSource, which cannot set custom
headers).
- Auth: API key.
- Response:
text/event-stream.
Dashboard: organization routes (/api/orgs/*)
All /api/* routes require a valid dashboard session (Firebase ID
token in SaaS, session JWT in self-managed mode) and, for mutating
verbs, a valid CSRF token obtained from GET /api/csrf-token. Local
dev mode bypasses both.
Organization CRUD
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs | List organizations the user belongs to. |
| POST | /api/orgs | Create a new organization. |
| GET | /api/orgs/{orgID} | Get an organization. |
| PUT | /api/orgs/{orgID}/settings | Update organization settings. |
| GET | /api/orgs/{orgID}/policies | List policies across all projects in the org. |
Members and invites
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs/{orgID}/members | List members. |
| POST | /api/orgs/{orgID}/members/invite | Invite a new member by email. |
| PATCH | /api/orgs/{orgID}/members/{memberID}/role | Change a member's role. |
| DELETE | /api/orgs/{orgID}/members/{memberID} | Remove a member. |
| GET | /api/orgs/{orgID}/invites | List outstanding invites. |
| DELETE | /api/orgs/{orgID}/invites/{inviteID} | Revoke an invite. |
| PATCH | /api/orgs/{orgID}/product-type | Change the product type selection. |
| POST | /invites/accept | Accept an invite as the authenticated user. |
SSO (/api/orgs/{orgID}/sso) — Available in Teams
Gated by the sso_saml feature flag.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs/{orgID}/sso | Get SAML / SCIM configuration. |
| POST | /api/orgs/{orgID}/sso | Create or update SAML / SCIM configuration. |
| DELETE | /api/orgs/{orgID}/sso | Delete SSO configuration. |
| POST | /api/orgs/{orgID}/sso/test-connection | Test the IdP connection. |
Enrollment tokens (/api/orgs/{orgID}/enrollment-tokens)
Feature-gated behind ENROLLMENT_API_ENABLED. Admin+ required.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/orgs/{orgID}/enrollment-tokens | Mint a single-use enrollment token. |
| GET | /api/orgs/{orgID}/enrollment-tokens | List active enrollment tokens. |
| DELETE | /api/orgs/{orgID}/enrollment-tokens/{tokenID} | Revoke a token. |
DLP rules (/api/orgs/{orgID}/dlp) — Solo+ tier
Gated by dlp_scanning feature flag.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs/{orgID}/dlp/rules | List rules. |
| POST | /api/orgs/{orgID}/dlp/rules | Create rule. |
| POST | /api/orgs/{orgID}/dlp/rules/test | Dry-run a rule against sample text. |
| GET | /api/orgs/{orgID}/dlp/rules/{ruleID} | Get a rule. |
| PATCH | /api/orgs/{orgID}/dlp/rules/{ruleID} | Patch a rule. |
| POST | /api/orgs/{orgID}/dlp/rules/{ruleID}/publish | Publish a draft rule. |
| POST | /api/orgs/{orgID}/dlp/rules/{ruleID}/request-approval | Submit for approval. |
| POST | /api/orgs/{orgID}/dlp/rules/{ruleID}/approve | Approve. |
| POST | /api/orgs/{orgID}/dlp/rules/{ruleID}/reject | Reject. |
| POST | /api/orgs/{orgID}/dlp/rules/{ruleID}/rollback | Roll back to previous version. |
| GET | /api/orgs/{orgID}/dlp/rules/{ruleID}/versions | List historical versions. |
| DELETE | /api/orgs/{orgID}/dlp/rules/{ruleID} | Soft-delete a rule. |
Browser extension compliance and deployment
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs/{orgID}/compliance/browser-ext-ack | Read the compliance acknowledgment required before browser-scoped DLP rules. |
| POST | /api/orgs/{orgID}/compliance/browser-ext-ack | Record the acknowledgment. |
| GET | /api/orgs/{orgID}/browser-extension/bundle | Download the signed browser-extension bundle for MDM deployment (available in Teams). |
| POST | /api/orgs/{orgID}/browser-extension/keys | Mint deployment keys for the extension. |
| GET | /api/orgs/{orgID}/browser-extension/keys | List deployment keys. |
| DELETE | /api/orgs/{orgID}/browser-extension/keys/{keyID} | Revoke a deployment key. |
Fleet management
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs/{orgID}/fleet | List enrolled machines. |
| GET | /api/orgs/{orgID}/fleet/{machineID} | Get machine detail. |
| DELETE | /api/orgs/{orgID}/fleet/{machineID} | Remove a machine. |
| POST | /api/orgs/{orgID}/fleet/{machineID}/config | Update per-machine config. |
Home, coverage, audit retention, settings, notifications
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs/{orgID}/home/summary | Aggregated home-page summary (fleet, coverage, DLP, 24h decisions). |
| GET | /api/orgs/{orgID}/coverage | List coverage gaps. |
| POST | /api/orgs/{orgID}/coverage/users/{userID}/notify | Send coverage-gap reminder. |
| POST | /api/orgs/{orgID}/coverage/users/{userID}/exempt | Mark a user exempt. |
| POST | /api/orgs/{orgID}/coverage/users/{userID}/escalate | Escalate the gap. |
| GET | /api/orgs/{orgID}/settings/audit-retention | Read org audit retention days. |
| POST | /api/orgs/{orgID}/settings/audit-retention | Set org audit retention (owner only). |
| GET | /api/orgs/{orgID}/settings | Read org settings (tamper behavior, etc.). |
| PATCH | /api/orgs/{orgID}/settings | Update org settings. |
| GET | /api/orgs/{orgID}/notifications/settings | Read notification channels (Solo+). |
| PUT | /api/orgs/{orgID}/notifications/settings | Update notification channels. |
| POST | /api/orgs/{orgID}/notifications/test/{type} | Send a test notification. |
| GET | /api/orgs/{orgID}/notifications/logs | List notification delivery logs. |
Projects (under orgs)
| Method | Path | Purpose |
|---|---|---|
| GET | /api/orgs/{orgID}/projects | List projects. |
| POST | /api/orgs/{orgID}/projects | Create project. |
Dashboard: project-scoped routes (/api/projects/{projectID}/*)
All routes below go through the project authorization middleware, which resolves the caller's org membership for the project.
Project, API keys, tools, secrets, policies
| Method | Path | Purpose |
|---|---|---|
| GET | /api/projects/{projectID} | Get project. |
| PATCH | /api/projects/{projectID} | Update project. |
| GET | /api/projects/{projectID}/api-keys | List API keys. |
| POST | /api/projects/{projectID}/api-keys | Create API key. |
| DELETE | /api/projects/{projectID}/api-keys/{keyID} | Revoke API key. |
| GET | /api/projects/{projectID}/tools | List tools. |
| POST | /api/projects/{projectID}/tools | Create tool. |
| PATCH | /api/projects/{projectID}/tools/{toolID} | Update tool. |
| DELETE | /api/projects/{projectID}/tools/{toolID} | Delete tool. |
| GET | /api/projects/{projectID}/secrets | List secrets (metadata only). |
| POST | /api/projects/{projectID}/secrets | Create / set a secret. |
| DELETE | /api/projects/{projectID}/secrets/{secretID} | Delete a secret. |
| GET | /api/projects/{projectID}/policies | List project policies. |
| POST | /api/projects/{projectID}/policies | Create a project policy. |
| POST | /api/projects/{projectID}/policies/assign | Assign an org-level policy to the project. |
| PATCH | /api/projects/{projectID}/policies/{policyID} | Update policy. |
| DELETE | /api/projects/{projectID}/policies/{policyID} | Delete policy. |
| POST | /api/projects/{projectID}/policies/bundle | Generate a signed bundle. |
| GET | /api/projects/{projectID}/policies/download | Download the current bundle. |
Key management
| Method | Path | Purpose |
|---|---|---|
| POST | /api/projects/{projectID}/keys/provision | Provision org/project signing keys. |
| POST | /api/projects/{projectID}/keys/rotate | Rotate signing keys. |
| GET | /api/projects/{projectID}/keys/public | Get the project's public signing key. |
Tamper alerts, logs, analytics, anomalies, compliance, export
| Method | Path | Purpose |
|---|---|---|
| GET | /api/projects/{projectID}/tamper-alerts | List tamper alerts. |
| GET | /api/projects/{projectID}/logs | Query audit logs. |
| GET | /api/projects/{projectID}/logs/stats | Audit log stats. |
| GET | /api/projects/{projectID}/logs/tools | Per-tool stats. |
| GET | /api/projects/{projectID}/analytics | Analytics summary. |
| GET | /api/projects/{projectID}/analytics/costs | Cost summary. |
| GET | /api/projects/{projectID}/analytics/costs/by-model | Cost broken out per model. |
| GET | /api/projects/{projectID}/analytics/costs/by-day | Cost broken out per day. |
| GET | /api/projects/{projectID}/anomalies | Anomaly detections (Teams). |
| GET | /api/projects/{projectID}/compliance | Compliance report. |
| GET | /api/projects/{projectID}/compliance/export | Export compliance report. |
| GET | /api/projects/{projectID}/export | Export audit logs (Solo+, gated by audit_export). |
Hosted MCP (Teams)
Gated by mcp_server_hosted.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/projects/{projectID}/mcp/provision | Provision a hosted MCP server for the project. |
| GET | /api/projects/{projectID}/mcp/status | Get provisioning status. |
| DELETE | /api/projects/{projectID}/mcp/deprovision | Deprovision. |
Shadow AI Scout (Teams)
Gated by scout_agent.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/projects/{projectID}/scout/agents | List deployed Scout agents. |
| GET | /api/projects/{projectID}/scout/discoveries | List discovered AI endpoints. |
| GET | /api/projects/{projectID}/scout/summary | Scout summary. |
Dashboard overview and misc
| Method | Path | Purpose |
|---|---|---|
| GET | /api/dashboard/overview | Cross-project overview. |
| GET | /api/users/ensure | Force lazy-provisioning; used by the onboarding recovery page. Returns { "ready": true, "user_id": uuid }. |
Billing (/api/billing/*) and account
| Method | Path | Purpose |
|---|---|---|
| POST | /api/billing/checkout | Create a Stripe checkout session. |
| POST | /api/billing/portal | Create a Stripe customer-portal session. |
| GET | /api/billing/status | Tier-based billing status (non-Stripe). |
| GET | /api/billing/invoices | List invoices. |
| DELETE | /api/account | Delete the authenticated user's account (and cascading cleanup). |
Billing POST endpoints bypass CSRF (Bearer token only) because they are called by the Next.js server-side proxy, not the browser directly.
Webhooks (/webhooks/*)
Unauthenticated at the framework level. Each handler verifies a provider-issued signature before taking action.
POST /webhooks/firebase/register
Invoked by the frontend after a successful Firebase Auth sign-up. Verifies the Firebase ID token and provisions the user's first org, project, and API key. Skipped in local-dev mode.
POST /webhooks/stripe
Receives Stripe billing events. The handler verifies the Stripe- Signature header against the configured Stripe webhook secret; bad or
missing signatures are rejected with 400.
Self-managed auth (/auth/*)
Registered only when the backend is configured with
AUTH_MODE=self-managed. SaaS (Firebase) deployments do not mount
these routes.
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | /auth/register | none | Register a new user. |
| POST | /auth/login | none | Password login; returns a session JWT. |
| POST | /auth/request-reset | none | Request a password-reset email. |
| POST | /auth/reset-password | none | Complete reset with token. |
| POST | /auth/logout | session | Invalidate the session. |
| POST | /auth/change-password | session | Change the current password. |
| GET | /auth/me | session | Current user. |
SAML 2.0 (/saml/{orgID}/*)
Mounted without the dashboard auth chain. Each handler does its own signature verification.
| Method | Path | Purpose |
|---|---|---|
| GET | /saml/{orgID}/metadata | SP metadata XML document. |
| GET | /saml/{orgID}/login | Redirects unauthenticated browsers to the IdP. |
| POST | /saml/{orgID}/acs | IdP callback; verifies the SAMLResponse signature against the configured IdP certificate and completes login. |
The post-login destination is the URL in the FRONTEND_URL environment
variable (defaults to /).
SCIM 2.0 (/scim/v2/*) — Teams tier
Mounted without the dashboard auth chain. Authentication is performed
by the SCIMHandler.Auth middleware, which hashes the bearer token
and looks up the matching SSO row. Rotated or missing tokens always
return 401.
| Method | Path | Purpose |
|---|---|---|
| GET | /scim/v2/Users | List users. |
| POST | /scim/v2/Users | Create a user. |
| GET | /scim/v2/Users/{id} | Get a user. |
| PATCH | /scim/v2/Users/{id} | Patch a user. |
| DELETE | /scim/v2/Users/{id} | Deprovision a user. |
Admin API (/api/admin/*)
Used by Control Zero platform operators. Access requires membership in
the platform_admins table (SaaS) or the configured admin email list
(self-managed). Not intended for tenant use.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/admin/stats/overview | Platform-wide statistics. |
| GET | /api/admin/orgs | List all organizations. |
| GET | /api/admin/orgs/{orgID} | Get an organization. |
| PUT | /api/admin/orgs/{orgID} | Update an organization. |
| GET | /api/admin/orgs/{orgID}/usage | Get usage metrics. |
| POST | /api/admin/orgs/{orgID}/reset-usage | Reset monthly usage counters. |
| PATCH | /api/admin/orgs/{orgID}/tier | Change billing tier. |
| GET | /api/admin/users | List all users. |
| PUT | /api/admin/users/{userID} | Update user status. |
| DELETE | /api/admin/users/{userID} | Delete a user account (cascades). |
| GET | /api/admin/notifications/settings | Admin notification settings. |
| PUT | /api/admin/notifications/settings | Update admin notification settings. |
| POST | /api/admin/notifications/test/{type} | Send admin test notification. |
| GET | /api/admin/notifications/logs | Admin notification log. |
| GET/POST/DELETE | /api/admin/blacklist/domains and /users | Domain and email blacklist. |
| GET | /api/admin/governance/alerts | Platform governance alerts. |
| POST | /api/admin/governance/alerts/{alertID}/resolve | Resolve alert. |
| GET/POST/PUT | /api/admin/features | Feature flags. |
| GET/POST/DELETE | /api/admin/admins | Platform admin roster. |
| GET | /api/admin/audit-logs | Platform-wide audit log. |
| GET | /api/admin/system/health | System health. |
| GET | /api/admin/system/metrics | System metrics. |
| GET | /api/admin/system/backups | Backup status. |
| POST | /api/admin/system/backups/trigger | Trigger a backup. |
| GET | /api/admin/system/rate-limits | Rate-limit activity. |
| GET | /api/admin/analytics/overview | Platform analytics. |
| GET | /api/admin/analytics/timeseries | Analytics timeseries. |
| GET | /api/admin/analytics/top-orgs | Top orgs by usage. |
| GET | /api/admin/analytics/decisions | Decision metrics. |
| GET | /api/admin/analytics/tamper-alerts | Platform tamper alerts. |
| GET | /api/admin/billing/summary | Billing summary across tenants. |
| GET | /api/admin/billing/orgs | Per-org billing. |
| GET | /api/admin/anomalies | Cross-org anomalies. |
| POST | /api/admin/licenses | Generate an on-prem license. |
| GET | /api/admin/licenses | List issued licenses. |
| POST | /api/admin/demo/seed | Seed demo data (internal use). |
| POST | /api/admin/demo/reset | Reset demo data. |
Error codes
All endpoints return one of the following when something goes wrong:
| Status | Meaning |
|---|---|
400 | Validation error (bad body, bad query parameter). |
401 | Missing or invalid credentials. |
402 | Usage limit exceeded (SDK endpoints only). |
403 | Authenticated but not authorized (wrong scope, wrong role, wrong org). |
404 | Resource not found. |
409 | Conflict (duplicate, tampered state). |
413 | Request body > 1 MB. |
429 | Rate limit exceeded. |
500 | Server error. |
503 | Feature-flagged route disabled. |