API overview
Base URL
Section titled “Base URL”https://cavsnode.com/api/v1Health checks sit outside the version prefix:
| Endpoint | Auth | Purpose |
|---|---|---|
GET /readyz |
none | Readiness, including a database ping. Use this for external checks. |
GET /healthz |
none | Liveness. Internal only — externally the load balancer answers this path itself. |
GET /api/v1/plans |
none | The public plan catalogue |
curl -sS https://cavsnode.com/readyz # → readycurl -sS https://cavsnode.com/api/v1/plans # → { "plans": [...] }Versioning
Section titled “Versioning”Everything is under /api/v1. Additive changes (new fields, new endpoints) ship
without a version bump, so clients must tolerate unknown response fields.
The server may reply with X-CAVS-Deprecation: <message>. Surface it as a warning,
never as an error.
Authentication
Section titled “Authentication”Authorization: Bearer <credential>Two credential families on the same header:
| Credential | Shape | Who |
|---|---|---|
| Firebase ID token | a JWT | the dashboard |
| CAVS API token | cavs_pat_…, cavs_repo_…, cavs_ci_… |
everything else |
See Authentication and API tokens.
Path parameters
Section titled “Path parameters”| Parameter | Accepts |
|---|---|
{orgID} |
organization UUID or slug |
{repoID} |
repository UUID only |
{oid} |
64-character lowercase hex SHA-256 |
{sha} |
a Git commit SHA |
{tag} |
a Git tag name |
Requests
Section titled “Requests”| Rule | Detail |
|---|---|
| Content type | application/json on any request with a body |
| Body limit | 1 MiB for control-plane requests |
| Unknown fields | rejected with 400, not ignored |
| Timeout | 60 seconds per request, server-side |
That unknown-field strictness is deliberate: a typo’d field name fails loudly instead of being silently dropped.
Responses
Section titled “Responses”Success bodies wrap their payload in a named key:
{ "repositories": [ … ] }{ "session": { … }, "generation": 42 }{ "webhook": { … } }Errors always use the same envelope:
{ "error": { "code": "quota_exceeded", "message": "storage quota exceeded for plan developer", "request_id": "8c1f2e4a-…", "details": { "reason": "storage", "limit": 107374182400, "used": 106300000000 } }}See Errors for the full taxonomy.
Security headers
Section titled “Security headers”Every response carries a locked-down header set — Content-Security-Policy: default-src 'none', X-Frame-Options: DENY, Referrer-Policy: no-referrer,
Cache-Control: no-store, and HSTS over TLS. Details in
Security model.
Idempotency
Section titled “Idempotency”Idempotency-Key: <stable string>Honoured on: creating an upload session, finalizing an upload, creating a snapshot. Resending the same key returns the same resource instead of duplicating work.
Pick a key derived from what makes the operation unique — a build id, a content hash, a period — not a random value:
-H "Idempotency-Key: upload-$OID"-H "Idempotency-Key: build-$CI_PIPELINE_ID"-H "Idempotency-Key: monthly-$(date -u +%Y-%m)"Rate limits
Section titled “Rate limits”| Sustained | 30 requests/second |
| Burst | 60 |
| Keyed by | token id → user id → client IP |
Exceeding it returns 429 with code: "rate_limited".
Presigned URLs are not rate-limited by CAVS: bulk transfers run against object storage, so throughput doesn’t consume your API budget.
Pagination
Section titled “Pagination”Two styles, depending on the endpoint.
Offset — most list endpoints:
?limit=50&skip=0| Default | Max | |
|---|---|---|
limit |
50 | 200 (100 or 50 on some endpoints) |
skip |
0 | 10000 |
Cursor — time-ordered lists. Pass the last item’s timestamp back as before
(RFC 3339):
?limit=50&before=2026-07-01T00:00:00ZThe organization artifact registry returns next_before for you to feed back.
Correlation ids
Section titled “Correlation ids”Every response carries:
X-Correlation-Id: 8c1f2e4a-…X-Request-Id: 8c1f2e4a-…Both are the same value, and error bodies embed it as request_id. Send your own
X-Correlation-Id to thread a client-side trace through the platform. Quote it in
support requests — it’s how a specific failure gets found in the logs.
Telemetry headers
Section titled “Telemetry headers”User-Agent: cavs-<lang>/<version>X-CAVS-Client: python-sdk | javascript-sdk | cli | generic-httpX-CAVS-Integration: cli | python-sdk | github-actions | gitlab-ci | generic-http | …X-CAVS-Integration-Version: <version>X-CAVS-Run-ID: <optional>Set them in your own clients.
Only the configured dashboard origins are allowed, with an explicit allowlist:
methods GET POST PUT PATCH DELETE OPTIONS, headers Authorization,
Content-Type, Idempotency-Key, Accept. Credentials are allowed; preflight is
cached for 300 seconds.
Endpoint groups
Section titled “Endpoint groups”| Group | Reference |
|---|---|
| Identity, search, notifications, invitations | Identity & discovery |
| Organizations, members, tokens, webhooks, storage, billing | Organizations · Tokens, webhooks & billing |
| Repositories, files, commits, releases, graph, analytics | Repositories |
| Upload sessions | Uploads |
| Download authorization | Downloads |
| Git LFS batch and the CAVS chunk tree | Git LFS endpoints |
| Git index ingest | Git index ingest |
| Everything, in one table | Endpoint index |
Not available
Section titled “Not available”Documented in the SDK contract but not deployed — these return 404:
GET /resolve?uri=cavs://…GET /artifacts/{id}and/artifacts/{id}/versions(the repository-scopedGET /repositories/{repoID}/artifacts/{oid}does exist)POST /repositories/{repoID}/lfs/verify- The Runs API (
/runs) - Service accounts (
/organizations/{org}/service-accounts) andcavs_sk_keys
There is no published OpenAPI document yet.
- Errors — codes, retries and debugging.
- Endpoint index — every route in one table.

