Skip to content
Fuse
Esc
↑↓navigate↵open⌘Jpreview

Create API key

Mints a new revocable API key. key is the raw secret (prefix fuse_sk_) and appears only in this response; it cannot be recovered afterward — only its SHA-256 is stored. An empty body creates an unlabeled full-access key; only a present-but-malformed body is 400.

Without scopes the key is full-access, as every key was before scopes existed, and only the master token may mint it: a full-access API key gets 403 code forbidden, reason missing_scope. With scopes the key is limited to the hitl routes (/v1/interventions, /v1/hitl/* and minting here), must set ttl_seconds (at most 86400), and may be limited to one environment and its forks (env_id) and pinned to an actor (actor). Scoped keys are revoked when their environment is destroyed and move with it across a migrate. Minting one records an invite_minted audit row.

A scoped key may mint a narrower copy of itself. Anything wider is 403 code forbidden, reason mint_escalation: a full-access key, a scope it does not hold, an environment it does not cover, an expiry past its own, or an actor other than its pinned one (pinning an actor from an unpinned key needs hitl:act-as). A key minted by a pinned key inherits that actor.

env_id without scopes, an empty scopes list, an unknown scope or a bad ttl_seconds is 400; an env_id that names no environment is 404 code not_found. Auth is never disabled on this route: it exists only when the Postgres key store is configured, and that same store makes bearer auth mandatory, so every request reaching this handler carries a principal. Requires the Postgres-backed key store (DATABASE_URL); without it this route is not registered and returns 404 route_not_found.

Note that ORCH_AUTH_TOKEN unset with DATABASE_URL set is permitted (unless ORCH_REQUIRE_AUTH=true), and in that configuration there is no master credential at all: every caller authenticates as an API key and therefore cannot mint a full-access key here. Configure a master token to use this route.

POST/v1/api-keys
Authorization
AuthorizationBearer token · headerrequired
`Authorization: Bearer <token>` — either the static master token (`ORCH_AUTH_TOKEN`) or a revocable API key issued via /v1/api-keys (API keys require Postgres; without a database only the master token is accepted). Master-only surfaces (exec, attach, API key management) refuse API keys with 403. An expired key is refused with 401 like an unknown one. A key minted with `scopes` reaches only the hitl routes (`/v1/interventions`, `/v1/hitl/*`, and POST /v1/api-keys to mint a narrower key). Any other route is 403 code `forbidden`, reason `env_scope`, except an `/v1/environments/{vmId}` route for an environment the key does not cover, which is 404 so the key cannot probe which environments exist. **Authentication can be disabled entirely.** When neither `ORCH_AUTH_TOKEN` nor an API key store (`DATABASE_URL`) is configured — the default local-development posture — the bearer middleware becomes a pass-through: every operation this document marks as secured is reachable with no credentials, requests carry no principal, and master-only surfaces therefore admit everyone. Never run that configuration on a reachable network; set `ORCH_REQUIRE_AUTH=true` to make the orchestrator refuse to start without a master token.
or
fuse_sessionAPI key · cookierequired
HttpOnly session cookie set by POST /login for browser callers. Consulted only when no Authorization header is present.
Header parameters
Fuse-Actorstring
The person behind this request, as an opaque id (`user_42`, `alice@corp.com`, `slack:U0123`). Fuse records it on hitl interventions and the audit log and never checks it. Accepted on every authenticated route; it matters wherever an actor is recorded (raising, deciding, minting). At most 128 bytes, no control characters; a malformed value is 400. How the recorded actor is resolved: - key pinned to an actor: that actor (`source: pinned`). A different `Fuse-Actor` is 403 `forbidden`, reason `actor_mismatch`. - key with `hitl:act-as`: the header value (`source: asserted`, with the asserting key's id), else `key:<key_id>`. - any other key: `key:<key_id>` named after the key's label (`source: key`). Sending the header is 403 `forbidden`, reason `actor_not_allowed`, rather than silently dropped. - master token: the header value (`source: asserted`), else `master` (`source: key`). An asserted actor is only as true as the key holder's own auth.
max length 128
Fuse-Actor-Namestring
Optional display name for `Fuse-Actor`, at most 256 bytes. Sending it without `Fuse-Actor` is 400.
max length 256
Request body
application/json
labelstring
Optional operator memory aid ("ci", "partner-acme").
scopesstring[]
Omit for a full-access key. Present (and non-empty) limits the key to the hitl routes and what these scopes grant: `hitl:read` lists and reads interventions and the audit log; `hitl:decide` approves and rejects; `hitl:secret` answers secret requests; `hitl:takeover` takes over the environment; `hitl:act-as` lets the key name the actor it acts for with `Fuse-Actor`. `hitl:decide` and `hitl:secret` imply `hitl:read`. Duplicates are dropped.
env_idstring
Limits the key to this environment and every environment forked from it. Needs `scopes`.
ttl_secondsinteger<int64>
Seconds until the key expires. Required with `scopes`, at most 86400 (24h). Optional for a full-access key; omit for one that never expires.
min 0 · max 86400
actorobject
Pins the actor every request on the key is recorded as (`source: pinned`). A different `Fuse-Actor` header on the key is then refused.
Show properties
idstringrequired
Opaque actor id, e.g. `user_42` or `alice@corp.com`.
max length 128
namestring
Optional display name.
max length 256
Responses
201Key created; `key` is shown exactly once.
idstringrequired
Public handle, prefix `ak_`. Used to revoke; not the secret.
labelstring
Optional operator memory aid. Omitted when empty.
created_atstring<date-time>required
last_used_atstring<date-time>
Omitted (not null) until the key first authenticates. Best-effort bump.
revoked_atstring<date-time>
Omitted (not null) while the key is live. Set when revoked by hand, and on a scoped key when its environment is destroyed.
scopesstring[]
Omitted for a full-access key.
env_idstring
The environment a scoped key is limited to (it also covers that environment's forks). Omitted when the key is not limited.
expires_atstring<date-time>
When the key stops authenticating. Omitted for a key that never expires; always set on a scoped key.
actorobject
The actor pinned on the key. Omitted when none.
Show properties
idstringrequired
Opaque actor id, e.g. `user_42` or `alice@corp.com`.
max length 128
namestring
Optional display name.
max length 256
created_bystring
Who minted the key: `master`, or the id of the scoped key that minted it.
keystringrequired
Raw secret, prefix `fuse_sk_`. Shown exactly once; only its SHA-256 is stored.
400Malformed request.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
401Missing or invalid bearer token.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
403The caller authenticated, but may not make this request. Master-only surfaces (exec, attach, listing and revoking API keys) refuse API keys; those 403 bodies carry code `unauthorized`. CIDR allowlist rejections return 403 with code `forbidden` and no reason. Access checks on the hitl routes and scoped keys return 403 with code `forbidden` and `details.reason` set to one of: - `missing_scope`: the key lacks the scope the route needs (`details.scope` names it). Full-access keys never hold `hitl:decide`, `hitl:secret`, `hitl:takeover` or `hitl:act-as`. - `env_scope`: a scoped key called a route outside the hitl routes. - `self_approval`: the key or actor that raised an intervention tried to decide it. - `actor_required`: the environment sets `require_actor` and the decision names no pinned or asserted actor. - `actor_mismatch`: `Fuse-Actor` differs from the key's pinned actor. - `actor_not_allowed`: `Fuse-Actor` sent by a key without `hitl:act-as`. - `hitl_denied`: a hitl gate in `deny` mode, or a gate's decider, refused an exec, lifecycle or computer action, or an edit matched a `deny` gate. `details.gate` names the gate and `details.intervention_id` the terminal record; the message is the gate's `reason`. - `mint_escalation`: a scoped key tried to mint a key wider than itself.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
404No Postgres key store is configured, so this route is not registered; the fallback handler answers with code `route_not_found`. Also returned with code `not_found` when `env_id` names no environment.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
500Unexpected server error.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
Request
curl -X POST "http://localhost:8080/v1/api-keys" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "string",
  "scopes": [
    "hitl:read"
  ],
  "env_id": "string",
  "ttl_seconds": 0,
  "actor": {
    "id": "string",
    "name": "string"
  }
}'
Response
{
  "id": "string",
  "label": "string",
  "created_at": "2024-01-01T00:00:00Z",
  "last_used_at": "2024-01-01T00:00:00Z",
  "revoked_at": "2024-01-01T00:00:00Z",
  "scopes": [
    "hitl:read"
  ],
  "env_id": "string",
  "expires_at": "2024-01-01T00:00:00Z",
  "actor": {
    "id": "string",
    "name": "string"
  },
  "created_by": "string",
  "key": "string"
}