Skip to main content

API key lifecycle and rotation

An Orbit API key moves through three states — active, grace-rotated, and revoked or expired — and two rotation modes move it across the boundaries. This page defines the states, the difference between grace and immediate rotation, how the cutoff on the old key is computed, and when to rotate versus revoke. For the credential classes and resolution rule, start with the authentication and session model.

Section 1 — The lifecycle at a glance

Every API key is born in one of two states and ends in one of two states:
  • Active. The key authenticates requests. active is true and neither a grace window nor a scheduled cutoff constrains it.
  • Grace-rotated. A rotation ran with the default grace mode: a replacement key was minted and the old key keeps authenticating until its computed cutoff (old_key_expires_at). Both keys are valid simultaneously during this window.
  • Revoked / expired. The key no longer authenticates — either because an immediate rotation or an explicit revoke killed it, or because its expiry instant (expires_at or old_key_expires_at) passed.
A key does not need rotation to reach the end state: POST /api/v1/settings/api-keys/:id/revoke, a scheduled revocation cutoff (revoke_at), or a customer-set expires_at all terminate it directly.

Section 2 — The two rotation modes

POST /api/v1/settings/api-keys/:id/rotate mints a replacement key and retires the old one. The mode field picks which retirement path the old key takes: Two properties of the mode resolution are worth knowing:
  • Grace is the floor for routine rotations. Omitting mode, sending mode: "grace", or sending an unrecognized value all resolve to the grace path. The grace window is never zero — it is clamped to a minimum of 1 hour — so a routine rotation cannot become an accidental panic-revoke through a typo.
  • Immediate is an explicit, separate action. It is not “grace with zero hours”; the grace path enforces the 1-hour minimum precisely so the dual-validity guarantee holds for routine rotations. Sending mode: "immediate" is the only way the old key dies in the same transaction, and on that path grace_period_hours is ignored.
The two modes also leave different audit trails: grace rotation writes an api_key.rotated entry, immediate rotation writes api_key.revoked_immediate, so a compliance review can separate routine rotations from panic-revokes without reading request payloads.

Section 3 — The dual-key grace window

In grace mode the old and new keys validate at the same time until old_key_expires_at. That overlap is the entire point: you rotate, update every integration to the new key on its own schedule, and the old key keeps answering requests until the window closes. This is deliberately not a “grace = 0” behavior. The 1-hour floor preserves the overlap for every routine rotation — a zero-length window is only reachable through the explicit immediate mode, which exists for keys that must die now. Plan the cutover against the cutoff returned in the rotation response. If the window ends before you finish migrating, every request still carrying the old key starts failing 401 at that instant.

Section 4 — How old_key_expires_at is computed

Rotation never silently extends the life of the old key. The cutoff is the earlier of two instants:
  1. The old key’s pre-existing expires_at, if one was set at creation.
  2. The new grace window (rotation time plus grace_period_hours).
If you gave the key a 2-hour expiry and then rotate it with the default 24-hour grace window, the old key dies after 2 hours, not 24. For immediate mode the cutoff is the rotation instant itself. The resolved cutoff comes back as old_key_expires_at in the response so the dashboard (and your runbook) can show “old key valid until …” without a second call.

Section 5 — Provenance and audit capture

Every rotation records the old-to-new pair without ever writing the secret to the audit log:
  • The old key’s lastFourChars suffix is captured before the rotation runs — after the rotation, the key list shows the new suffix, so without this look-back the “rotated from …1234 to …5678” provenance would be lost.
  • The audit entry carries the key id, the replacement key id, the key label, both suffixes, the resolved mode, and (for grace) the requested window. The plaintext of the new key never appears there.
The new key’s key field — its plaintext — is returned exactly once, in the rotation response, with Cache-Control: no-store. Store it in your secret manager immediately; later reads only ever show the masked prefix and suffix.

Section 6 — Rotate versus revoke

One billing note for the leak case: revoking a leaked key stops the meter at the revoke timestamp — usage billed into your account ends there regardless of what the leaked key does afterward, so revoking is always the right first move even before you investigate.

Section 7 — A guided cutover

  1. List keys first so you target the right id: GET /api/v1/settings/api-keys returns labels, prefixes, suffixes, and expiry metadata — secrets are never listed.
  2. Rotate with grace.
  1. Copy key into your secret manager now — this response is the only place the plaintext ever appears.
  2. Deploy the new key to every integration. The old key keeps working against the same scopes until oldKeyExpiresAt, so you can roll service by service.
  3. Confirm the cutover. After oldKeyExpiresAt passes, any request still carrying the old key fails 401 — grep your access logs for the old suffix (…5b7d) ahead of the cutoff if you want zero surprises.
Owner, admin, or developer roles can rotate. For a leaked key, the same call with "mode": "immediate" mints the replacement and revokes the old key in one transaction — do the copy step before you send it.