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.
/v1/api-keysAuthorizationBearer token · headerrequiredfuse_sessionAPI key · cookierequiredFuse-ActorstringFuse-Actor-Namestringapplication/jsonlabelstringscopesstring[]env_idstringttl_secondsinteger<int64>actorobjectShow propertiesHide properties
idstringrequirednamestringidstringrequiredlabelstringcreated_atstring<date-time>requiredlast_used_atstring<date-time>revoked_atstring<date-time>scopesstring[]env_idstringexpires_atstring<date-time>actorobjectShow propertiesHide properties
idstringrequirednamestringcreated_bystringkeystringrequirederrorobjectrequiredShow propertiesHide properties
codestringrequirednot_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlockedmessagestringrequireddetailsobjecterrorobjectrequiredShow propertiesHide properties
codestringrequirednot_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlockedmessagestringrequireddetailsobjecterrorobjectrequiredShow propertiesHide properties
codestringrequirednot_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlockedmessagestringrequireddetailsobjecterrorobjectrequiredShow propertiesHide properties
codestringrequirednot_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlockedmessagestringrequireddetailsobjecterrorobjectrequiredShow propertiesHide properties
codestringrequirednot_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlockedmessagestringrequireddetailsobjectcurl -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"
}
}'const response = await fetch("http://localhost:8080/v1/api-keys", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
"label": "string",
"scopes": [
"hitl:read"
],
"env_id": "string",
"ttl_seconds": 0,
"actor": {
"id": "string",
"name": "string"
}
})
});import requests
response = requests.post(
"http://localhost:8080/v1/api-keys",
headers={
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
json={
"label": "string",
"scopes": [
"hitl:read"
],
"env_id": "string",
"ttl_seconds": 0,
"actor": {
"id": "string",
"name": "string"
}
},
){
"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"
}{
"error": {
"code": "not_found",
"message": "string",
"details": {}
}
}{
"error": {
"code": "not_found",
"message": "string",
"details": {}
}
}{
"error": {
"code": "not_found",
"message": "string",
"details": {}
}
}{
"error": {
"code": "not_found",
"message": "string",
"details": {}
}
}{
"error": {
"code": "not_found",
"message": "string",
"details": {}
}
}