Skip to content

API overview and authentication

Faber exposes a small JSON API over HTTP. The contract is an OpenAPI 3.1 document, openapi/openapi.yaml in the repository, which the server also serves. The reference on this site is generated from it.

http://<host>:3000/api/v1

Faber speaks plain HTTP. Put a TLS-terminating reverse proxy in front of it before it leaves the host; see Docker deployment.

Every route except /health, /openapi.yaml and /openapi.json needs the API key in the Authorization header, either as a bearer token or as the raw key:

Authorization: Bearer 3f9c…
Authorization: 3f9c…

A missing or wrong key gets 401 with WWW-Authenticate: Bearer and:

{ "error": "Missing or invalid API key", "code": "unauthorized" }

The server is configured with API_KEY. It accepts a comma-separated list, so keys can be rotated without downtime: deploy old,new, move clients to new, then deploy new alone. Keys are compared in constant time.

Method and pathAuthPurpose
POST /executeyesRun steps in a sandbox. Execution model
POST /fileyesUpload a file (multipart)
GET /fileyesList stored files
GET /file/{id}yesDownload a stored file
DELETE /file/{id}yesDelete a stored file
DELETE /cacheyesClear the step cache namespace
GET /infoyesVersion, architecture, limits, profiles, cache state, toolchains
GET /metricsyesPrometheus metrics (text)
GET /healthnoLiveness and readiness: 200 normally, 503 while shutting down
GET /openapi.yaml, GET /openapi.jsonnoThe OpenAPI document

Each has a page in the reference, with request and response schemas.

Every response has an x-request-id header. The same ID is in the service’s log line for the request. Include it when you report a problem, and log it on the client side for failed calls; the SDKs attach it to their errors.

Error responses have one shape, with a stable machine-readable code:

{ "error": "Execution capacity is currently exhausted", "code": "capacity_exhausted" }

Errors lists every code, its status and what to do about it. Remember that a task failing is not an API error: /execute returns 200 and reports the task’s outcome in the body.

  • Retry 503 after the Retry-After header (seconds) when present, with exponential backoff otherwise. It means no task slots were free, the instance is shutting down, or the request was cancelled.
  • Retry connection errors the same way.
  • Do not retry 4xx. The request is wrong and will fail again.
  • Client timeout. A request can take up to OVERALL_TIMEOUT_MS (30 s by default, see GET /info) plus a few seconds. Set your HTTP timeout above that.

Re-sending an /execute request is safe: it has no side effects apart from the step cache. Uploads are idempotent (the ID is the content hash).

  • Clients should ignore unknown fields in responses and treat unknown enum values (for example a new outcome, or a new error code) as a generic case. New fields and values may be added within v1.
  • The 0.2 request format (a bare JSON array of steps) is still accepted on /execute and answered with a bare array.
Terminal window
curl -s http://localhost:3000/api/v1/openapi.json | jq .info

Use it to generate a client in a language without an SDK, or to validate requests in tests. The repository’s openapi/examples/ holds example requests and responses that are checked against the schema.