Emergency Key Revoke
Immediately invalidate a leaked or compromised API key.
If you have any reason to believe an API key has been exposed (a public commit, a security alert from your monitoring, a former employee with credentials), use emergency revoke instead of standard rotation. Emergency revoke is destructive: there is no 24-hour grace window. The old key dies the moment the request returns, and any integration still using it will start failing.
When to use this vs. rotation
- Rotation is for routine key hygiene (~every 90 days). The old key remains valid for 24 hours so you can deploy the new one without downtime. Rotation requires a fresh two-factor step-up and is rate-limited.
- Emergency revoke is for suspected compromise. The old key dies immediately. The cooldown is bypassed. Use this only when you have reason to believe the key is in the wrong hands.
If you are not sure which to use, prefer rotation; it is the safe default.
Request
POST /v1/api-keys/{id}/emergency-revoke
This endpoint can only be triggered from the merchant dashboard with a signed-in operator session. API keys cannot call this endpoint, even on themselves: a compromised key must not be able to rotate itself silently.
Body
{
"confirmation": "Acme Inc.",
"reason_note": "Discovered key in a public GitHub commit at 14:32 UTC"
}
| Field | Type | Required | Notes |
|---|---|---|---|
confirmation | string | yes | Must equal your merchant account name exactly (case-sensitive). Same friction pattern as AWS IAM destructive operations. |
reason_note | string | no | Free-text incident note, up to 280 characters. Recorded in your audit log only; never included in webhook payloads. |
Response
200: Success
{
"merchant_id": 1,
"key_id": "prd_<new>",
"new_plaintext_key": "rail_full_live_<random64>",
"revoked_key_id": "prd_<old>",
"revoked_at": "2026-05-11T15:00:00.000Z",
"reason": "suspected_compromise"
}
The new_plaintext_key is returned once in this response. There is no way to retrieve it again: copy it immediately, update your integration, and discard the dashboard tab. The dashboard's modal displays the same value via copy-to-clipboard for convenience.
4xx / 5xx: Errors
| Code | HTTP | Cause |
|---|---|---|
ERR_1011 | 401 | No merchant session. Sign in to the dashboard first. |
ERR_1012 | 403 | The request was authenticated with an API key. Emergency revoke is dashboard-session only. |
ERR_2002 | 400 | Malformed key id in the path. |
ERR_2003 | 400 | reason_note exceeds 280 characters. |
ERR_3010 | 422 | Your merchant account is not provisioned. Complete provisioning from the dashboard. |
ERR_3012 | 404 | Key not found for your merchant. Check the id path parameter. |
ERR_3022 | 409 | Another operator on your team already emergency-revoked this key. The response includes the current active key state. |
ERR_3023 | 422 | The confirmation field is missing, empty, or does not match your merchant name exactly. |
ERR_5005 | 503 | Partial failure: a new key was issued but the old key was not killed in time. Retry the endpoint to complete the revoke. |
ERR_5099 | 500 | Internal server error. Contact support if it persists. |
ERR_3008 rotation_cooldown_active is explicitly NOT emitted by this endpoint. The cooldown applies only to the standard POST /v1/api-keys/{id}/rotate flow; emergency mode unconditionally bypasses it.
Webhook event
A successful emergency revoke triggers a key_emergency_revoked event delivered to every webhook endpoint you have registered. The payload:
{
"key_id": "prd_<old>",
"new_key_id": "prd_<new>",
"key_prefix": "rail_full_live_",
"revoked_at": "2026-05-11T15:00:00.000Z",
"revoked_by": {
"actor_type": "merchant",
"actor_id": "<16-char opaque hash>"
},
"reason": "suspected_compromise"
}
The revoked_by.actor_id field is a stable opaque identifier, useful for correlating multiple actions from the same operator across events. The new key's plaintext is never in the webhook payload; only the new key id and prefix appear. Your reason_note (if you provided one) is also never in the webhook payload; it lives only in your audit log.
The webhook is signed with TensorRail-Signature (HMAC-SHA512) like every other event TensorRail emits. See Webhooks for the verification pattern.
Audit log
Every emergency revoke, successful or rejected, is recorded in your merchant audit log. Visit the Audit tab in the dashboard to see the full history. Each entry captures:
- The operator who triggered the request (the signed-in dashboard operator).
- IP address, user agent, request ID.
- The key revoked and the new key issued.
- Your optional
reason_note. - The outcome (success, confirmation mismatch, race lost to another operator).
Race conditions
If two operators on your team trigger emergency revoke on the same key within milliseconds of each other:
- One request returns
200with the new key. - The other returns
409 ERR_3022 key_already_revokedwith the current active key id in the response. - Only one webhook event is emitted.
- Both attempts are recorded in your audit log.
This is the correct semantic: there is one revoke event, two operators witnessed it.
How to trigger it
Emergency revoke is a dashboard action, not an API-key call. In your dashboard, go to Developers → API Keys, find the compromised key, and click the red Emergency revoke button. You will be asked to type your merchant account name to confirm, and you can attach an optional incident note. The dashboard handles the operator session and confirmation flow for you, then displays the new key once via copy-to-clipboard.
Because this requires a signed-in operator session and cannot be authenticated with an API key, there is no integration curl recipe: a compromised key must not be able to revoke itself. The endpoint, body, and responses documented above describe what the dashboard sends on your behalf.
See also
- Authentication: API key prefixes and scopes
- Webhooks: signature verification and delivery semantics
- Idempotency:
Idempotency-Keyheader (note: emergency revoke does NOT respect idempotency; each call is processed fresh)