Skip to main content

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 modelHeaderRoute families
API key (Bearer)Authorization: Bearer cz_live_... or cz_test_.../v1/*, /v1/sdk/*, /api/scout/*
Dashboard sessionFirebase ID token (SaaS) or session JWT (self-managed) via Authorization: Bearer .../api/*
Machine signatureCryptographically signed request headers bound to an enrolled machine_id/api/enroll, /api/heartbeat, /api/policy, /api/audit
SCIM bearerAuthorization: Bearer <scim_token> (org-specific)/scim/v2/*
Signature-verifiedProvider 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.

Gateway vs backend header names

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-ID response 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: 401 invalid key, 402 usage 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: 404 no policy published, 409 tampered 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: 400 invalid token, 409 already enrolled, 503 feature 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).

MethodPathPurpose
POST/api/scout/registerRegister an agent instance.
POST/api/scout/heartbeatAgent heartbeat.
POST/api/scout/discoveriesSubmit a batch of discovered AI traffic.
GET/api/scout/configPull current Scout config.
POST/api/scout/license/validateOn-prem license validation.

Public API (/v1/*)

GET /v1/policies

List policies visible to the API key.

  • Auth: API key, read scope.

POST /v1/policies

Create a new policy.

  • Auth: API key, write scope.
  • Request body: policy document (YAML-equivalent JSON).

PATCH /v1/policies/{policyID}

Update a policy by ID.

  • Auth: API key, write scope.

DELETE /v1/policies/{policyID}

Delete a policy by ID.

  • Auth: API key, write scope.

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

MethodPathPurpose
GET/api/orgsList organizations the user belongs to.
POST/api/orgsCreate a new organization.
GET/api/orgs/{orgID}Get an organization.
PUT/api/orgs/{orgID}/settingsUpdate organization settings.
GET/api/orgs/{orgID}/policiesList policies across all projects in the org.

Members and invites

MethodPathPurpose
GET/api/orgs/{orgID}/membersList members.
POST/api/orgs/{orgID}/members/inviteInvite a new member by email.
PATCH/api/orgs/{orgID}/members/{memberID}/roleChange a member's role.
DELETE/api/orgs/{orgID}/members/{memberID}Remove a member.
GET/api/orgs/{orgID}/invitesList outstanding invites.
DELETE/api/orgs/{orgID}/invites/{inviteID}Revoke an invite.
PATCH/api/orgs/{orgID}/product-typeChange the product type selection.
POST/invites/acceptAccept an invite as the authenticated user.

SSO (/api/orgs/{orgID}/sso) — Available in Teams

Gated by the sso_saml feature flag.

MethodPathPurpose
GET/api/orgs/{orgID}/ssoGet SAML / SCIM configuration.
POST/api/orgs/{orgID}/ssoCreate or update SAML / SCIM configuration.
DELETE/api/orgs/{orgID}/ssoDelete SSO configuration.
POST/api/orgs/{orgID}/sso/test-connectionTest the IdP connection.

Enrollment tokens (/api/orgs/{orgID}/enrollment-tokens)

Feature-gated behind ENROLLMENT_API_ENABLED. Admin+ required.

MethodPathPurpose
POST/api/orgs/{orgID}/enrollment-tokensMint a single-use enrollment token.
GET/api/orgs/{orgID}/enrollment-tokensList 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.

MethodPathPurpose
GET/api/orgs/{orgID}/dlp/rulesList rules.
POST/api/orgs/{orgID}/dlp/rulesCreate rule.
POST/api/orgs/{orgID}/dlp/rules/testDry-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}/publishPublish a draft rule.
POST/api/orgs/{orgID}/dlp/rules/{ruleID}/request-approvalSubmit for approval.
POST/api/orgs/{orgID}/dlp/rules/{ruleID}/approveApprove.
POST/api/orgs/{orgID}/dlp/rules/{ruleID}/rejectReject.
POST/api/orgs/{orgID}/dlp/rules/{ruleID}/rollbackRoll back to previous version.
GET/api/orgs/{orgID}/dlp/rules/{ruleID}/versionsList historical versions.
DELETE/api/orgs/{orgID}/dlp/rules/{ruleID}Soft-delete a rule.

Browser extension compliance and deployment

MethodPathPurpose
GET/api/orgs/{orgID}/compliance/browser-ext-ackRead the compliance acknowledgment required before browser-scoped DLP rules.
POST/api/orgs/{orgID}/compliance/browser-ext-ackRecord the acknowledgment.
GET/api/orgs/{orgID}/browser-extension/bundleDownload the signed browser-extension bundle for MDM deployment (available in Teams).
POST/api/orgs/{orgID}/browser-extension/keysMint deployment keys for the extension.
GET/api/orgs/{orgID}/browser-extension/keysList deployment keys.
DELETE/api/orgs/{orgID}/browser-extension/keys/{keyID}Revoke a deployment key.

Fleet management

MethodPathPurpose
GET/api/orgs/{orgID}/fleetList 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}/configUpdate per-machine config.

Home, coverage, audit retention, settings, notifications

MethodPathPurpose
GET/api/orgs/{orgID}/home/summaryAggregated home-page summary (fleet, coverage, DLP, 24h decisions).
GET/api/orgs/{orgID}/coverageList coverage gaps.
POST/api/orgs/{orgID}/coverage/users/{userID}/notifySend coverage-gap reminder.
POST/api/orgs/{orgID}/coverage/users/{userID}/exemptMark a user exempt.
POST/api/orgs/{orgID}/coverage/users/{userID}/escalateEscalate the gap.
GET/api/orgs/{orgID}/settings/audit-retentionRead org audit retention days.
POST/api/orgs/{orgID}/settings/audit-retentionSet org audit retention (owner only).
GET/api/orgs/{orgID}/settingsRead org settings (tamper behavior, etc.).
PATCH/api/orgs/{orgID}/settingsUpdate org settings.
GET/api/orgs/{orgID}/notifications/settingsRead notification channels (Solo+).
PUT/api/orgs/{orgID}/notifications/settingsUpdate notification channels.
POST/api/orgs/{orgID}/notifications/test/{type}Send a test notification.
GET/api/orgs/{orgID}/notifications/logsList notification delivery logs.

Projects (under orgs)

MethodPathPurpose
GET/api/orgs/{orgID}/projectsList projects.
POST/api/orgs/{orgID}/projectsCreate 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

MethodPathPurpose
GET/api/projects/{projectID}Get project.
PATCH/api/projects/{projectID}Update project.
GET/api/projects/{projectID}/api-keysList API keys.
POST/api/projects/{projectID}/api-keysCreate API key.
DELETE/api/projects/{projectID}/api-keys/{keyID}Revoke API key.
GET/api/projects/{projectID}/toolsList tools.
POST/api/projects/{projectID}/toolsCreate tool.
PATCH/api/projects/{projectID}/tools/{toolID}Update tool.
DELETE/api/projects/{projectID}/tools/{toolID}Delete tool.
GET/api/projects/{projectID}/secretsList secrets (metadata only).
POST/api/projects/{projectID}/secretsCreate / set a secret.
DELETE/api/projects/{projectID}/secrets/{secretID}Delete a secret.
GET/api/projects/{projectID}/policiesList project policies.
POST/api/projects/{projectID}/policiesCreate a project policy.
POST/api/projects/{projectID}/policies/assignAssign 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/bundleGenerate a signed bundle.
GET/api/projects/{projectID}/policies/downloadDownload the current bundle.

Key management

MethodPathPurpose
POST/api/projects/{projectID}/keys/provisionProvision org/project signing keys.
POST/api/projects/{projectID}/keys/rotateRotate signing keys.
GET/api/projects/{projectID}/keys/publicGet the project's public signing key.

Tamper alerts, logs, analytics, anomalies, compliance, export

MethodPathPurpose
GET/api/projects/{projectID}/tamper-alertsList tamper alerts.
GET/api/projects/{projectID}/logsQuery audit logs.
GET/api/projects/{projectID}/logs/statsAudit log stats.
GET/api/projects/{projectID}/logs/toolsPer-tool stats.
GET/api/projects/{projectID}/analyticsAnalytics summary.
GET/api/projects/{projectID}/analytics/costsCost summary.
GET/api/projects/{projectID}/analytics/costs/by-modelCost broken out per model.
GET/api/projects/{projectID}/analytics/costs/by-dayCost broken out per day.
GET/api/projects/{projectID}/anomaliesAnomaly detections (Teams).
GET/api/projects/{projectID}/complianceCompliance report.
GET/api/projects/{projectID}/compliance/exportExport compliance report.
GET/api/projects/{projectID}/exportExport audit logs (Solo+, gated by audit_export).

Hosted MCP (Teams)

Gated by mcp_server_hosted.

MethodPathPurpose
POST/api/projects/{projectID}/mcp/provisionProvision a hosted MCP server for the project.
GET/api/projects/{projectID}/mcp/statusGet provisioning status.
DELETE/api/projects/{projectID}/mcp/deprovisionDeprovision.

Shadow AI Scout (Teams)

Gated by scout_agent.

MethodPathPurpose
GET/api/projects/{projectID}/scout/agentsList deployed Scout agents.
GET/api/projects/{projectID}/scout/discoveriesList discovered AI endpoints.
GET/api/projects/{projectID}/scout/summaryScout summary.

Dashboard overview and misc

MethodPathPurpose
GET/api/dashboard/overviewCross-project overview.
GET/api/users/ensureForce lazy-provisioning; used by the onboarding recovery page. Returns { "ready": true, "user_id": uuid }.

Billing (/api/billing/*) and account

MethodPathPurpose
POST/api/billing/checkoutCreate a Stripe checkout session.
POST/api/billing/portalCreate a Stripe customer-portal session.
GET/api/billing/statusTier-based billing status (non-Stripe).
GET/api/billing/invoicesList invoices.
DELETE/api/accountDelete 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.

MethodPathAuthPurpose
POST/auth/registernoneRegister a new user.
POST/auth/loginnonePassword login; returns a session JWT.
POST/auth/request-resetnoneRequest a password-reset email.
POST/auth/reset-passwordnoneComplete reset with token.
POST/auth/logoutsessionInvalidate the session.
POST/auth/change-passwordsessionChange the current password.
GET/auth/mesessionCurrent user.

SAML 2.0 (/saml/{orgID}/*)

Mounted without the dashboard auth chain. Each handler does its own signature verification.

MethodPathPurpose
GET/saml/{orgID}/metadataSP metadata XML document.
GET/saml/{orgID}/loginRedirects unauthenticated browsers to the IdP.
POST/saml/{orgID}/acsIdP 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.

MethodPathPurpose
GET/scim/v2/UsersList users.
POST/scim/v2/UsersCreate 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.

MethodPathPurpose
GET/api/admin/stats/overviewPlatform-wide statistics.
GET/api/admin/orgsList all organizations.
GET/api/admin/orgs/{orgID}Get an organization.
PUT/api/admin/orgs/{orgID}Update an organization.
GET/api/admin/orgs/{orgID}/usageGet usage metrics.
POST/api/admin/orgs/{orgID}/reset-usageReset monthly usage counters.
PATCH/api/admin/orgs/{orgID}/tierChange billing tier.
GET/api/admin/usersList all users.
PUT/api/admin/users/{userID}Update user status.
DELETE/api/admin/users/{userID}Delete a user account (cascades).
GET/api/admin/notifications/settingsAdmin notification settings.
PUT/api/admin/notifications/settingsUpdate admin notification settings.
POST/api/admin/notifications/test/{type}Send admin test notification.
GET/api/admin/notifications/logsAdmin notification log.
GET/POST/DELETE/api/admin/blacklist/domains and /usersDomain and email blacklist.
GET/api/admin/governance/alertsPlatform governance alerts.
POST/api/admin/governance/alerts/{alertID}/resolveResolve alert.
GET/POST/PUT/api/admin/featuresFeature flags.
GET/POST/DELETE/api/admin/adminsPlatform admin roster.
GET/api/admin/audit-logsPlatform-wide audit log.
GET/api/admin/system/healthSystem health.
GET/api/admin/system/metricsSystem metrics.
GET/api/admin/system/backupsBackup status.
POST/api/admin/system/backups/triggerTrigger a backup.
GET/api/admin/system/rate-limitsRate-limit activity.
GET/api/admin/analytics/overviewPlatform analytics.
GET/api/admin/analytics/timeseriesAnalytics timeseries.
GET/api/admin/analytics/top-orgsTop orgs by usage.
GET/api/admin/analytics/decisionsDecision metrics.
GET/api/admin/analytics/tamper-alertsPlatform tamper alerts.
GET/api/admin/billing/summaryBilling summary across tenants.
GET/api/admin/billing/orgsPer-org billing.
GET/api/admin/anomaliesCross-org anomalies.
POST/api/admin/licensesGenerate an on-prem license.
GET/api/admin/licensesList issued licenses.
POST/api/admin/demo/seedSeed demo data (internal use).
POST/api/admin/demo/resetReset demo data.

Error codes

All endpoints return one of the following when something goes wrong:

StatusMeaning
400Validation error (bad body, bad query parameter).
401Missing or invalid credentials.
402Usage limit exceeded (SDK endpoints only).
403Authenticated but not authorized (wrong scope, wrong role, wrong org).
404Resource not found.
409Conflict (duplicate, tampered state).
413Request body > 1 MB.
429Rate limit exceeded.
500Server error.
503Feature-flagged route disabled.