For AI agents: a documentation index is available at https://docs.coverbase.com/llms.txt — this page is also available in markdown by appending .md to the URL.
This page covers behavior shared by every public API endpoint. Read it once; the per-resource pages assume it.
Base URLs
| Environment | Base URL |
|---|---|
| Production | https://api.coverbase.app |
| Sandbox | https://sandbox.api.coverbase.app |
/v1. The sandbox is a full, isolated copy of the API for integration testing — build and run your integration there first, then repoint the base URL at production.
Authentication
Every request must send a bearer token:- Service-account API key (
ak_...) — the primary credential for integrations. A Coverbase admin mints it from the dashboard (Clerk-backed). It is scoped to exactly one organization; everything the key reads or writes is implicitly scoped to that org. It authenticates as a service account. By default a key carries no scopes and can only call ordinary/v1/*routes; an admin can additionally grant it elevated scopes. - Logged-in user JWT — the dashboard session token, useful for first-party scripts running as a specific user.
ak_* keys self-service via the API Keys API — no support ticket required.
API key scopes
Mostak_* keys carry no scopes: they authenticate ordinary /v1/* calls but cannot reach the admin surfaces. An admin can mint a long-lived key with one or both elevated scopes for headless or middleware use:
| Scope | Grants |
|---|---|
keys:manage | List, create, rotate, and revoke ak_* keys via the API Keys API. |
audit:read | Read the org system audit log (GET /v1/system_audit_log and GET /v1/system_audit_log/metadata). |
ak_* key carrying the required scope, so you can run them headlessly without a short-lived session token. The JWT path is unchanged: key management requires the org admin role; audit-log reads accept the same dashboard roles as before (member, siloed-member, admin, guest).
Scopes can only be granted from an admin dashboard session (JWT). A scoped key cannot grant scopes — not to itself and not to another key — so a leaked integration key can never escalate its own access. A request that passes scopes without an admin JWT is rejected with 403 scope_grant_forbidden; a call to a scoped endpoint with a key that lacks the required scope returns 403 insufficient_scope.
There is no unauthenticated surface. An invalid/expired/revoked key returns 401 "Invalid or expired API key.". A credential whose role is not allowed on the public API returns 401 "Unauthorized role.".
Public API audit log
Every authenticated call that lands on a/v1/* public-API route is recorded in the org’s activity log as type=external_api_call with target_type=external_api_request. Each record captures:
- the HTTP method, request path, and matched route template
- the response status code and duration in milliseconds
- the names of query parameters used (values are not recorded)
- the
ak_*key ID and human-readable name that authenticated the request - the client IP and a per-request correlation ID
GET /v1/system_audit_log (and GET /v1/system_audit_log/metadata). Both read endpoints accept either a dashboard session JWT (member, siloed-member, admin, or guest role — unchanged from before) or an ak_* key that carries the audit:read scope.
Requests that fail authentication before the ak_* key resolves (e.g. an unknown or revoked key returning 401) are not stored in the activity log — they appear in Coverbase’s structured request logs only and can be retrieved via support if needed for incident response.
Test your credentials
GET /v1/utils/authtest returns 200 and {"msg": "Auth successful"} for any valid token.
cURL
API IP allowlist
An organization can optionally restrict all public API (ak_... key) requests to a set of IPv4/IPv6 CIDRs. This is an org-level control configured by an admin.
-
Default is unrestricted. An empty allowlist (
[], the default) imposes no restriction. -
When configured, a request whose client IP is outside every configured CIDR is rejected with
403: - Fail-closed. If an allowlist is configured but the client IP cannot be determined, the request is denied.
-
Scope. The allowlist applies to every
/v1/*public API route. It does not affect dashboard sign-in. -
Client IP source. The connecting IP is the value the Coverbase load balancer appends to
X-Forwarded-For. Clients cannot spoof it by sending their ownX-Forwarded-Forheader.
Configure the allowlist
Set the allowlist from the dashboard (Configuration → Coverbase API → “API IP allowlist”) or via the org update API. Your organization ID is theid field on the org object (also visible in the dashboard).
A list of CIDR strings (IPv4 or IPv6). Each entry is validated server-side; an invalid CIDR rejects the whole request with
422. An empty list clears the allowlist (unrestricted).Set the allowlist
Read it back
Clear it (unrestricted again)
GET/POST /v1/org/{id}) now includes api_ip_allowlist (a string array; [] when unrestricted). An invalid CIDR returns 422 with a free-form error body (not the coded envelope):
Find your current IP
GET /v1/whoami/ipapi_ip_allowlist check, so use it to confirm which IP to allowlist (it backs the dashboard editor’s “Add my current IP” helper).
cURL
The caller’s source IP.
null if the IP cannot be determined — the same condition under which a configured allowlist fails closed.IDs
Resource IDs are opaque, prefixed strings of the form<prefix>_<32 hex characters> (e.g. cbvndr_e448ba62882143f3ba0c140bb2e30162). Treat them as opaque — never parse or construct them.
| Prefix | Resource |
|---|---|
cbvndr_ | Vendor |
cbsvc_ | Service (child of a vendor) |
cbuser_ | User |
cbqsrw_ | Assessment |
cbtask_ | Finding |
cboblg_ | Obligation |
cbwr_ | Workflow run |
cbwd_ | Workflow definition |
cbwh_ | Webhook subscription |
cbctst_ | Control set |
cbst_ | Status |
cbtag_ | Tag |
cbsclvl_ | Scale level (inherent/residual risk level) |
cbvdoc_ | Vendor document |
cbbom_ | Bill of materials |
cbbomc_ | Bill of materials component |
cbevt_ | Webhook delivery event |
cbwhd_ | Webhook delivery (attempt record) |
cborg_ | Organization |
Findings are internally called “tasks”, so finding IDs use the
cbtask_ prefix. This is expected — a cbtask_... value is a finding ID.Timestamps
Every timestamp is a Unix epoch value in seconds (integer) — not milliseconds, not ISO-8601. This applies tocreated_at, updated_at, initiated_at, started_at, completed_at, due_date, occurred_at, and attempted_at.
Idempotency
State-changingPOSTs that support safe retries accept an optional Idempotency-Key request header.
| Endpoint | Honors Idempotency-Key |
|---|---|
POST /v1/users | Yes |
POST /v1/vendors | Yes |
POST /v1/assessments | Yes |
POST /v1/workflows/{workflow_name}/run | Yes |
POST /v1/vendors/{vendor_id}/services | No (header ignored) |
POST /v1/findings, POST /v1/obligations | No (header ignored) |
POST /v1/webhooks and other webhook writes | No (header ignored) |
PATCH endpoints | No |
Pagination
List endpoints (GET /v1/findings, GET /v1/obligations) use limit/offset query parameters:
| Param | Default | Range |
|---|---|---|
limit | 50 | 1–200 |
offset | 0 | ≥ 0 |
{ "items": [...], "total": <int>, "limit": <int>, "offset": <int> }. total is the full filtered count (ignoring limit/offset); page until offset + len(items) >= total.
Errors
Application errors use FastAPI’sdetail envelope. Most endpoints return a coded object:
POST /v1/assessments is the one exception — creation failures use a free-form error key:
Validation errors
Bodies/params that fail schema validation return422 with FastAPI’s standard array:
Common status codes
| Status | Meaning |
|---|---|
400 | Rejected by a business rule (bad reference ID, weak secret, non-HTTPS URL, missing vendor/assessment on a finding). |
401 | Missing/invalid credential, or a role not allowed on the public API. |
403 | Authenticated but not authorized — the key lacks a required scope (insufficient_scope), a non-admin tried to grant scopes (scope_grant_forbidden), or the client IP is outside the org allowlist (ip_not_allowed). |
404 | Resource not found or not in the API key’s org. |
409 | Conflict — e.g. user_exists, or running a disabled workflow (workflow_disabled). |
422 | Request body/query failed schema validation. |
502 | A downstream dispatch failed (e.g. workflow_dispatch_failed); safe to retry. |