Incident response
Use the Management API to contain a leaked key, a runaway workload, or a compromised account quickly.
The job: something is wrong — a leaked secret, a runaway workload, a compromised account — and the response must be shorter than the time it takes to find someone with a browser. Each containment move below is one call, so it can live in a script in your repository and run from the incident channel, attributed to the person running it.
Prepare before the incident
Mint an incident-response token for each on-call engineer who is an Admin (Settings → API Tokens), with only the scopes the playbook needs:
| Scope | Used for |
|---|---|
keys:read, keys:write | Find and kill keys |
projects:read, projects:write | Disable a project |
members:read, members:write | Disable a member |
audit:read | See what happened |
Store it in the team's secret manager, and rehearse the scripts against a
test project. Use a fixed, incident-scoped Idempotency-Key (for example
incident-4711-…) so a panicked double-run is harmless.
The containment moves
| Move | Call | Why it is safe as one call |
|---|---|---|
| Contain a leaked key | DELETE /keys/{id} with {"confirmed_leak": true} | Permanent by design: a compromised secret does not get a grace window, and requests already streaming on it are cut off. |
| Stop a runaway workload | POST /projects/{id}/disable | Reversible and project-scoped: every key in the project stops, nothing else is touched. POST /projects/{id}/enable undoes it. |
| Contain a person | POST /members/{id}/disable | Blocks their sign-in, signs them out of every browser session and revokes all their personal access tokens. Reversible with POST /members/{id}/enable. |
| Contain a leaked token | Revoke in Settings → API Tokens, or DELETE /me/tokens/{id} by its owner | Immediate: the next call fails 401 token_revoked. |
Contain a leaked key
-
Find the key. If you have its masked prefix from a scanner alert, match it against the project's keys:
curl -fsS https://portal.<domain>/api/v1/mgmt/projects/$PROJECT_ID/keys \ -H "Authorization: Bearer $TF_PAT" \ | jq -r '.data[] | "\(.id) \(.key_prefix) \(.status) \(.name) last used \(.last_used_at)"' -
Delete it.
confirmed_leak: truealso cuts off requests already streaming on the key:curl -fsS -X DELETE https://portal.<domain>/api/v1/mgmt/keys/$KEY_ID \ -H "Authorization: Bearer $TF_PAT" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: incident-$INCIDENT-delete-$KEY_ID" \ -d '{"confirmed_leak": true}' -
Issue a replacement for the legitimate workload with
POST /projects/{id}/keys.
Stop a runaway workload
curl -fsS -X POST https://portal.<domain>/api/v1/mgmt/projects/$PROJECT_ID/disable \
-H "Authorization: Bearer $TF_PAT" \
-H "Idempotency-Key: incident-$INCIDENT-disable-$PROJECT_ID"When the cause is fixed, POST /projects/{id}/enable restores it. To cap
rather than stop, lower guardrails.spend_limit_usd with
PATCH /projects/{id}.
Contain a person
curl -fsS -X POST https://portal.<domain>/api/v1/mgmt/members/$MEMBER_ID/disable \
-H "Authorization: Bearer $TF_PAT" \
-H "Idempotency-Key: incident-$INCIDENT-disable-$MEMBER_ID"API keys the person created keep working — they are project assets. If the person may have copied secrets, list the keys they created and rotate or delete them by id:
curl -fsS https://portal.<domain>/api/v1/mgmt/projects/$PROJECT_ID/keys \
-H "Authorization: Bearer $TF_PAT" \
| jq -r --arg uid "$MEMBER_ID" '.data[] | select(.created_by.id == $uid) | .id'The last active admin cannot be disabled (409 last_admin), and you cannot
disable yourself (409 cannot_act_on_self). To sign someone out of every
browser without disabling them, use
DELETE /members/{id}/sessions.
Afterwards: what happened
Pull the audit trail for the window, including every action taken through the API and which token took it:
curl -fsS -G https://portal.<domain>/api/v1/mgmt/audit/export \
-H "Authorization: Bearer $TF_PAT" \
-d format=jsonl \
-d from=2026-09-24T08:00:00Z \
-o incident-$INCIDENT-audit.jsonlEvery containment call above appears in it under the on-call engineer's name,
with the token in metadata.pat_id / metadata.pat_name. An export covers
at most 10,000 entries per call (422 export_too_large beyond that) — pass
to as well and export in windows for a long incident. Changes to keys and
projects take effect on the data plane within the 60-second propagation window.