FixControl/Documentation

Admin Guide

Public API (admin)

Operational view of the FixControl public API — keys, scopes, rate limits, audit.

The Public API is bearer-token, tenant-scoped REST. Base path: /api/public/v1/. User-facing setup steps and the endpoint catalog are in the user API guide. This page is the operational view.

Key model

API keys list with key prefix, last-used timestamp, and revoke action visible per row
API keys list with key prefix, last-used timestamp, and revoke action visible per row

Every API key:

  • Is tenant-scoped. A key authenticates as the tenant, not as an individual user.
  • Has a prefix (the first 12 characters, e.g. fck_live_a1b2) shown in the UI so you can identify it without ever exposing the full token.
  • Is stored as a one-way hash. The full token is shown once at creation and never again — even an admin with full database access cannot recover it.
  • Carries a set of scopes (issues:read, issues:write, patches:read, patches:write, integrations:read, webhooks:read, webhooks:write).
  • Has a name (your label), a created by user, and created, last used, and revoked timestamps.

Revocation is non-destructive: the key stops working immediately but the record stays for audit. Authenticating with a revoked key returns 401 with code: invalid_token.

Authentication errors

SituationHTTPcode
No Authorization header401missing_token
Unknown or revoked key401invalid_token
Valid key, wrong scope for the endpoint403insufficient_scope
Over the per-key rate limit429rate_limited

Rate limits

Default: 60 requests per minute per key, with bursts of up to 60 and a sustained rate of 1 request / second after the burst is exhausted. Hitting the limit returns 429 with a Retry-After header and code: rate_limited.

Limits are tracked per key, not per tenant. Two keys in the same tenant don't share a budget, so one runaway integration can't starve another. Contact support if you need a higher tier for a specific key.

Scopes

There are 7 scopes:

ScopeEndpoints
issues:readGET /issues, GET /issues/:key
issues:writePOST /issues
patches:readGET /patches, GET /patches/:id
patches:writereserved — no endpoints today
integrations:readGET /integrations/status
webhooks:readGET /webhooks
webhooks:writePOST /webhooks

A key without any scope can authenticate but every authorized endpoint returns 403. Useful as a canary: "tell me when this key is being used somewhere I didn't expect".

Audit

Every API request — success and every kind of failure — lands in the audit log:

  • eventpublic_api.used on success, public_api.auth_failed on every failure mode.
  • tenant — the tenant the key belongs to (or empty if the token didn't resolve).
  • targetKind: "api_key", targetId — which key was used (or empty for an unresolved bearer).
  • fields.reason — on failures: missing_token, invalid_token, missing_scope, rate_limited.
  • fields.scope — the scope the endpoint required.
  • fields.path — the request path.

Why audit failures too: an authenticated API is your external attack surface. A scanner trying random tokens or hammering a scope it shouldn't have is a leading indicator of compromise, and the audit log is the durable record you can review and alert on.

OpenAPI

The OpenAPI 3.1 spec is served at /api/public/v1/openapi.json. Point any OpenAPI-aware client (Postman, Insomnia, code generators) at that URL to get an up-to-date description of every endpoint, request shape, and response shape.

Operational guidance

  • One key per integration. Easier rotation, easier audit, easier scope.
  • Don't generate a key with all scopes "just in case". Limit blast radius.
  • Rotate on a fixed cadence. 90 days is reasonable for high-traffic keys; longer is fine for low-traffic ones if you have audit alerts.
  • Wire the `request_id` from API responses into your client logs. When an API call fails, support can correlate by request id; sharing yours saves a round-trip.
  • Prefer webhooks to polling. A poll loop on GET /issues is allowed but wasteful — subscribe an outbound webhook for issue.* events and react instead.

FAQ

Is the Public API the same as the dashboard's internal API? No. The dashboard uses internal routes that require a logged-in session and are not stable. The Public API at /api/public/v1/... is what we commit to — versioned, OpenAPI-described, and breaking changes go through deprecation notices.

Can a key access more than one tenant? Never. By design.

Why is there an `integrations:read` scope but no `integrations:write`? Today the public API exposes the read-only health snapshot. Mutating integrations is a UI flow because it requires multi-step OAuth handshakes that don't fit cleanly under bearer-token write semantics.

What's the SLA? Availability targets and incident comms are part of your subscription agreement, not the docs. Reach out via /contact for the operational details.

Anything unclear or wrong?Let us know →

FixControl is a trade name of FixControl B.V. i.o.