rotate_api_key

Rotate an API key

act entity tenant-private since 2026-10-01

Issues a successor to one of the org's API keys, with the same name, mode, API version and limits, and keeps the old key working for an overlap window (24 hours unless overlap_hours says otherwise) so the integration can switch without a gap. The answer carries the successor's whole key in `secret` ONCE. A BK_ key's successor is an sk_live_ key. The successor carries its predecessor's grants (TRA-619). Send an Idempotency-Key header: a retry with the same key and inputs answers the same successor, without its secret.

Contract

shape
entity
authority
act
evidence
none
subscription
false
permission
any one of: org.admin (carried by the caller's role template in the org `org_id`); a deactivated caller is refused first
idempotency
keyed
emits
api_key.rotated
MCP tool
none
decision_set
approve, reject
REST
POST https://api.containertradehq.com/v1/cap/rotate_api_key

Idempotency: keyed by the caller's Idempotency-Key, which is required: the same key and inputs replay the first result, the same key with other inputs is 409 idempotency_key_reused

Inputs

nametyperequiredvalidation
org_idorg_idrequiredtradehq-org-id; 1 to 20 characters
The org the key belongs to.
key_idtextrequired1 to 12 characters
The key to succeed.
overlap_hoursintegeroptional0 to 168; default 24
How long the old key keeps working, in hours: 24 by default, at most a week, 0 to end it now.

Response

  • key_id · string · The key's id, its public part.
  • org_id · string · The live org it was issued on.
  • name · string · What it is for.
  • service_account_id · stringnull · The service account that owns the key and that it acts as (TRA-493); null only for a BK_ key the core issued.
  • kind · string · sk for a key this plane issued, legacy for a BK_ key.
  • mode · string · live, or test for a key that reaches the test twin.
  • status · string · active; expiring when it has an expiry still ahead, as a rotated key does in its overlap; expired; or revoked.
  • display · string · The key's prefix and id, safe to show anywhere. Never the secret.
  • created_at · string · When it was issued.
  • created_by · stringnull · Who issued it.
  • expires_at · stringnull · When it stops working, if it does.
  • revoked_at · stringnull · When it was revoked.
  • api_version · stringnull · The API version it is pinned to; null for a BK_ key, which follows the current version.
  • requests_per_minute · integernull · Its per-minute limit, or null for none.
  • requests_per_day · integernull · Its per-day limit, or null for none.
  • rotated_from · stringnull · The key it succeeded.
  • rotated_to · stringnull · The key that succeeded it.
  • grants · string[] · What the key was granted (TRA-619), in name order: the permissions it may use, each only while its service account's role template carries it. Empty for a key granted nothing and for a BK_ key.
  • secret · stringnull · The whole key, on the call that issued it and never again: it is stored nowhere, so a replay answers null.
Full JSON Schema
{
  "type": "object",
  "properties": {
    "key_id": {
      "type": "string",
      "description": "The key's id, its public part.",
      "x-data-class": "operational"
    },
    "org_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-org-id",
      "description": "The live org it was issued on.",
      "x-data-class": "operational"
    },
    "name": {
      "type": "string",
      "description": "What it is for.",
      "x-data-class": "operational"
    },
    "service_account_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "The service account that owns the key and that it acts as (TRA-493); null only for a BK_ key the core issued.",
      "x-data-class": "operational"
    },
    "kind": {
      "type": "string",
      "enum": [
        "sk",
        "legacy"
      ],
      "description": "sk for a key this plane issued, legacy for a BK_ key.",
      "x-data-class": "operational"
    },
    "mode": {
      "type": "string",
      "enum": [
        "live",
        "test"
      ],
      "description": "live, or test for a key that reaches the test twin.",
      "x-data-class": "operational"
    },
    "status": {
      "type": "string",
      "enum": [
        "active",
        "expiring",
        "expired",
        "revoked"
      ],
      "description": "active; expiring when it has an expiry still ahead, as a rotated key does in its overlap; expired; or revoked.",
      "x-data-class": "operational"
    },
    "display": {
      "type": "string",
      "description": "The key's prefix and id, safe to show anywhere. Never the secret.",
      "x-data-class": "operational"
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "description": "When it was issued.",
      "x-data-class": "operational"
    },
    "created_by": {
      "type": [
        "string",
        "null"
      ],
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-user-id",
      "description": "Who issued it.",
      "x-data-class": "operational"
    },
    "expires_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When it stops working, if it does.",
      "x-data-class": "operational"
    },
    "revoked_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When it was revoked.",
      "x-data-class": "operational"
    },
    "api_version": {
      "type": [
        "string",
        "null"
      ],
      "description": "The API version it is pinned to; null for a BK_ key, which follows the current version.",
      "x-data-class": "operational"
    },
    "requests_per_minute": {
      "type": [
        "integer",
        "null"
      ],
      "description": "Its per-minute limit, or null for none.",
      "x-data-class": "operational"
    },
    "requests_per_day": {
      "type": [
        "integer",
        "null"
      ],
      "description": "Its per-day limit, or null for none.",
      "x-data-class": "operational"
    },
    "rotated_from": {
      "type": [
        "string",
        "null"
      ],
      "description": "The key it succeeded.",
      "x-data-class": "operational"
    },
    "rotated_to": {
      "type": [
        "string",
        "null"
      ],
      "description": "The key that succeeded it.",
      "x-data-class": "operational"
    },
    "grants": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "What the key was granted (TRA-619), in name order: the permissions it may use, each only while its service account's role template carries it. Empty for a key granted nothing and for a BK_ key.",
      "x-data-class": "operational"
    },
    "secret": {
      "type": [
        "string",
        "null"
      ],
      "description": "The whole key, on the call that issued it and never again: it is stored nowhere, so a replay answers null.",
      "x-data-class": "operational"
    }
  },
  "required": [
    "key_id",
    "org_id",
    "name",
    "service_account_id",
    "kind",
    "mode",
    "status",
    "display",
    "created_at",
    "created_by",
    "expires_at",
    "revoked_at",
    "api_version",
    "requests_per_minute",
    "requests_per_day",
    "rotated_from",
    "rotated_to",
    "grants",
    "secret"
  ],
  "additionalProperties": false
}

Refusals

not yet declared

The platform refusals apply to every call: version, authentication, authority, validation and rate limits, each in the same envelope.

Surfaces

DesktopF
MobileD5
APIF
MCPN
VoiceN
PushF
OneBoxN
OutboundN
  • Mobile: degraded: composition
  • MCP: the answer carries a secret, which is never handed to an agent
  • Voice: the answer carries a secret, which is never read aloud
  • OneBox: no document originates an API key
  • Outbound: crosses to no counterparty

Events

Related

get_api_usage, revoke_api_key, set_api_key_limits, set_api_key_version