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. Admins create and revoke keys under Settings → Channels → Webhooks & API → Public API.

Key model

API keys list with key prefix, last-used timestamp, and revoke action visible per row (screenshot from July 2026; the current screen may differ)
API keys list with key prefix, last-used timestamp, and revoke action visible per row (screenshot from July 2026; the current screen may differ)

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, hr:questions, comm:stage).
  • 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 9 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
hr:questionsPOST /hr/questions — lets your own chatbot submit an HR question on behalf of an employee it has authenticated. Granting it means trusting that integration to say which employee is asking; it never widens what that employee may see.
comm:stagePOST /communications, GET /communications/:id — lets a separate internal service propose one outbound customer e-mail. The e-mail lands as pending approval and is only sent after a person in FixControl approves it; the key itself can never approve.

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:

  • event — public_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. Ask your FixControl contact for the operational details.

Anything unclear or wrong?Let us know →

FixControl is a trade name of FixControl B.V.