API overview

Everything in the UI is available via the REST API. JSON requests + responses, cursor-based pagination, deterministic error shapes, per-route RBAC enforcement. 190+ endpoints under /api/v1.

01Base URL

The same host as the UI, under /api/v1. Example: https://cap.example.com/api/v1. Everything that follows uses relative paths rooted there.

02Versioning

  • Major version in the path (/api/v1). A v2 would live at /api/v2 alongside v1 during a transition window.
  • No breaking changes within a major. Additive changes only: new endpoints, new optional request fields, new response fields.
  • When a breaking change is necessary, v2 opens for a deprecation window of ≥ 12 months, with Deprecation + Sunset headers on v1 responses.

03Tenancy in the URL

Every project-scoped resource is under /projects/:projectId/...:

/api/v1/projects/{projectId}/certificates
/api/v1/projects/{projectId}/acme-accounts
/api/v1/projects/{projectId}/distributions

Org-scoped resources don't carry the project prefix:

/api/v1/users
/api/v1/audit-logs
/api/v1/ca-providers

There is no cross-project read endpoint. If you need cross-project data, iterate GET /projects and fan out.

04Content type

  • Request: Content-Type: application/json for all mutations. The server rejects any other content type on POST/PUT/PATCH.
  • Response: application/json, UTF-8.
  • One exception: file downloads (cert bundles, exports) return the appropriate application/x-pem-file, application/zip, or application/x-pkcs12.

05Pagination

All list endpoints use cursor-based pagination:

GET /api/v1/projects/{projectId}/certificates?limit=50
# →
{
  "items": [ ... ],
  "next_cursor": "eyJpZCI6IC4uLn0="
}

GET /api/v1/projects/{projectId}/certificates?limit=50&cursor=eyJpZCI6IC4uLn0=
# → next page; returns empty next_cursor when exhausted
  • limit: default 50, max 500.
  • cursor: opaque. Don't parse or manipulate.
  • Stable sort by ID — even if records are added or deleted mid-iteration, you don't skip or double-yield.

06Filtering & sorting

Per-endpoint query params. The pattern:

GET /api/v1/projects/{projectId}/certificates
  ?status=active,renewal_failed
  &expiring_within_days=30
  &issuer_type=acme
  &sort=expires_at
  &order=asc

Comma-separated lists for OR filters. Per-field knobs documented on each endpoint in the full reference.

07Error shape

HTTP 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "code": "policy_violation",
    "message": "Key type ECDSA-P256 is not allowed by this project's policy.",
    "details": [
      {
        "rule": "allowed_key_types",
        "offending_value": "ECDSA-P256",
        "allowed": ["RSA-2048", "RSA-3072"]
      }
    ],
    "request_id": "req_01H..."
  }
}
  • code: machine-readable; stable per release.
  • message: human-readable; may change wording.
  • details: structured error context — present for validation / policy-violation / rate-limit errors.
  • request_id: trace it in the audit log + OpenTelemetry trace.

08Common status codes

CodeMeaning in this API
200OK — GET / PATCH success.
201Created — POST that created a resource.
202Accepted — async action queued (e.g. cert issuance).
204No Content — DELETE success.
400Client error — malformed JSON, bad enum.
401No auth / expired auth.
403Authenticated but role insufficient.
404Resource not found (or not visible to your tenant).
409Conflict — duplicate, state precondition failed, license limit.
422Unprocessable — policy violation, validation failure.
429Rate-limited (auth failures or per-tenant quotas).
503Service temporarily unavailable (scheduler rebooting, DB primary failover).

09Idempotency

Every mutation accepts an Idempotency-Key header:

POST /api/v1/projects/{projectId}/certificates
Idempotency-Key: create-api-cert-2026-04-21
Content-Type: application/json
{ ... }

Repeating the same request within 24 h returns the original response — both on success and on error. Different bodies with the same key return 409. Skip the header if you don't need the safety; the server doesn't synthesise one.

10Rate limits

  • API-key auth failures: 10 per IP per minute → 429.
  • Per-tenant quotas: absent by default. Add at your reverse proxy / API gateway for multi-tenant deployments.
  • CA-imposed limits: cert issuance respects the CA's weekly quotas (zone page surfaces them). Not the same as CertAutoPilot's own rate limit.

11Request correlation

  • Every response carries X-Request-ID. Log it; it's the key into the audit log and the OpenTelemetry trace.
  • Clients can set their own request ID by sending X-Request-ID — the server echoes it.

12Client libraries

Official client: none (yet). Everything is plain JSON, so any curl / ky / requests / go http.Client works. OpenAPI spec lives at /api/v1/openapi.json — generate your own SDK with openapi-generator or similar.