API

All API endpoints are prefixed with /api/v1. The Nix binary cache endpoints live at the root (outside /api/v1).

Reference

The full OpenAPI 3.1 specification is in the repository at docs/gradient-api.yaml. View it interactively:

Open in Swagger UI

Authentication

Endpoints under /api/v1 (except /auth/*, /health, and /config) require a bearer token:

Authorization: Bearer <token>

Two token types are accepted:

Type How to obtain Prefix
JWT POST /api/v1/auth/basic/login none
API key POST /api/v1/user/keys GRAD

Response Envelope

Every JSON response is wrapped in:

{ "error": false, "message": <payload> }

On errors, error is true and message is a string describing the problem.

Quick Reference

Auth (no authentication required)

Method Path Description
POST /auth/basic/register Register a new user
POST /auth/basic/login Log in, returns JWT
POST /auth/check-username Check username availability
GET /auth/verify-email?token=… Verify email address
POST /auth/resend-verification Resend verification email
POST /auth/oauth/authorize Get OIDC authorization URL
GET /auth/oidc/login Redirect to OIDC provider
GET /auth/oidc/callback OIDC callback handler
POST /auth/logout Logout
GET /health Health check
GET /config Server feature flags

User

Method Path Description
GET /user Current user info
DELETE /user Delete account
GET /user/keys List API keys
POST /user/keys Create API key
DELETE /user/keys Delete API key
GET /user/keys/permissions List the permission catalogue
PATCH /user/keys/{api_id} Update an API key's name / permissions / org pin
GET /user/settings Get profile settings
PATCH /user/settings Update profile settings

Configuring API-key options

Each API key carries its own permission set and an optional organization pin:

curl -X POST $API/user/keys \
  -H "Authorization: Bearer $SESSION" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "ci-runner",
        "permissions": ["triggerEvaluation", "viewOrg"],
        "organization": "acme",
        "expires_in_days": 90
      }'

The key's effective authority on every request is user_role_mask & key_mask, intersected with the org's role assignment for the caller. A key pinned to an organization 404s for every other org. The full permission catalogue is at GET /user/keys/permissions.

To tighten an existing key without rotating the secret:

curl -X PATCH $API/user/keys/$KEY_ID \
  -H "Authorization: Bearer $SESSION" \
  -H "Content-Type: application/json" \
  -d '{ "permissions": ["viewOrg"] }'

API-key-authenticated requests cannot create, edit, revoke, or delete API keys - only session-authenticated calls can. This prevents a leaked key from minting more powerful siblings.

Cache pinning

A key may be pinned to a single cache as an alternative to organization pinning (the two are mutually exclusive). Cache-pinned keys carry a CachePermission bitmask and can be used only on routes targeting the pinned cache. Creating a cache-pinned key requires the manageCacheMembers permission on the target cache. Use the availableCache field on GET /user/keys/permissions to enumerate valid capability names.

Organizations

Method Path Description
GET /orgs List organizations
PUT /orgs Create organization
GET /orgs/{org} Get organization
PATCH /orgs/{org} Update organization
DELETE /orgs/{org} Delete organization
GET/POST/PATCH/DELETE /orgs/{org}/users Manage members
GET/POST /orgs/{org}/ssh Get / regenerate SSH key
GET /orgs/{org}/subscribe List subscribed caches
POST/DELETE /orgs/{org}/subscribe/{cache} Subscribe / unsubscribe

Workers

Workers are gradient-worker processes that connect to the server over WebSocket to execute fetch, eval, build, and sign jobs.

Method Path Description
POST /orgs/{org}/workers Register a worker - returns peer_id and optionally a one-time token
GET /orgs/{org}/workers List registered workers (merges live state)
DELETE /orgs/{org}/workers/{worker_id} Unregister a worker
GET /admin/workers List all currently connected workers (superuser or GRADIENT_GLOBAL_STATS_PUBLIC)

All endpoints under /admin/* require the calling user to have the superuser flag set on their account.

Register a worker:

worker_id must be a UUID v4. On a NixOS host running the gradient-worker service the worker auto-generates one on first start and persists it to /var/lib/gradient-worker/worker-id - read it with:

cat /var/lib/gradient-worker/worker-id
# Server generates the token (returned once, store it immediately)
curl -X POST https://gradient.example.com/api/v1/orgs/myorg/workers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"worker_id": "550e8400-e29b-41d4-a716-446655440001"}'

Response (server-generated token):

{
  "error": false,
  "message": {
    "peer_id": "550e8400-e29b-41d4-a716-446655440000",
    "token": "a1b2c3..."
  }
}

Alternatively, supply a pre-generated token (openssl rand -base64 48 - exactly 64 standard base64 characters). The server stores its hash and does not return it in the response:

curl -X POST https://gradient.example.com/api/v1/orgs/myorg/workers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"worker_id\": \"550e8400-e29b-41d4-a716-446655440001\", \"token\": \"$(openssl rand -base64 48)\"}"

Response (pre-supplied token - no token field):

{
  "error": false,
  "message": {
    "peer_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Write the peer_id and token to the peers file on the worker host:

echo "<peer_id>:<token>" > /run/secrets/gradient-worker-peers

Set GRADIENT_WORKER_PEERS_FILE (or the NixOS peersFile option) to this path.

List workers:

GET /orgs/{org}/workers returns registered workers merged with live connection info:

{
  "error": false,
  "message": [
    {
      "worker_id": "build-01",
      "registered_at": "2026-04-12T10:00:00Z",
      "live": {
        "architectures": ["x86_64-linux"],
        "system_features": ["kvm", "big-parallel"],
        "max_concurrent_builds": 8,
        "assigned_job_count": 2,
        "draining": false
      }
    }
  ]
}

live is null if the worker is not currently connected.

Unregister a worker:

DELETE /orgs/{org}/workers/{worker_id} removes the registration. The worker stays connected until it disconnects, then cannot reconnect.

Projects

Method Path Description
GET /projects/{org} List projects
PUT /projects/{org} Create project
GET/PATCH/DELETE /projects/{org}/{project} Get / update / delete
GET /projects/{org}/{project}/details Aggregated project data
GET /projects/{org}/{project}/entry-points Root builds
POST /projects/{org}/{project}/check-repository Test repo access
POST /projects/{org}/{project}/evaluate Trigger evaluation
POST/DELETE /projects/{org}/{project}/active Enable / disable

Evaluations

Method Path Description
GET /evals/{id} Get evaluation
POST /evals/{id} Abort ({"method":"abort"})
GET /evals/{id}/builds List builds
POST /evals/{id}/builds Stream all build logs (NDJSON)

Builds

Method Path Description
POST /builds Submit direct build (multipart)
GET /builds/direct/recent Recent direct builds
GET /builds/{id} Build with outputs
GET/POST /builds/{id}/log Get log / stream live log
GET /builds/{id}/graph Full dependency graph
GET /builds/{id}/dependencies Direct dependencies
GET /builds/{id}/downloads List artefacts
GET /builds/{id}/download/{filename} Download artefact

Caches

Method Path Description
GET /caches List caches
PUT /caches Create cache
GET/PATCH/DELETE /caches/{cache} Get / update / delete
POST/DELETE /caches/{cache}/active Enable / disable
GET /caches/{cache}/key Public signing key

Commits

Method Path Description
GET /commits/{id} Get commit

Nix Binary Cache (root, no /api/v1 prefix)

Private caches require HTTP Basic Auth (any username, JWT or API key as password - returns 401 without credentials).

Substituter surface (used by nix, nixos-rebuild, etc.):

Method Path Description
GET /cache/{cache}/nix-cache-info Cache metadata (add ?json for JSON)
GET /cache/{cache}/gradient-cache-info Gradient cache metadata (add ?json for JSON)
GET /cache/{cache}/{hash}.narinfo Path info (add ?json for JSON)
GET /cache/{cache}/nar/{hash}.nar.zst NAR archive

Inspection surface (NAR content inspection and build logs):

Method Path Description
GET /cache/{cache}/ls/{hash} JSON tree listing of the NAR (nix-serve .ls v1 schema)
GET /cache/{cache}/serve/{hash}/{path} Extract a single file (bytes) or directory (tar.zst) from a NAR
GET /cache/{cache}/log/{drv} Build log for <drv>.drv (substituter compat - nix log)

The inspection endpoints (/ls, /serve) are rate-limited at 60 req/min. The /log endpoint is rate-limited at ~300 req/min on its own tier. All endpoints return 404 when the hash or derivation is unknown.

Example: Trigger an Evaluation

TOKEN=$(curl -s -X POST https://gradient.example.com/api/v1/auth/basic/login \
  -H 'Content-Type: application/json' \
  -d '{"loginname":"alice","password":"secret"}' | jq -r .message)

curl -X POST "https://gradient.example.com/api/v1/projects/my-org/my-project/evaluate" \
  -H "Authorization: Bearer $TOKEN"

Response:

{ "error": false, "message": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }