---
title: "Rotate every active key of a kind (cutover)"
method: POST
path: "/credits/tenants/me/keys/rotate"
tags: ["Tenants"]
---

# Rotate every active key of a kind (cutover)

`POST /credits/tenants/me/keys/rotate`

## Request

- Base URL: `https://api.paywithlocus.com/api`
- URL: `https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate`
- Auth: HTTP bearer

Creates ONE replacement and supersedes ALL other active keys of that kind. Requires a workspace owner or admin with credentials:manage and MFA or passkey step-up completed in the last ten minutes. A human caller cannot delegate a secret-key scope absent from their own effective membership scopes. For rotating a single key while its siblings keep working, use POST /credits/tenants/me/keys/{keyId}/rotate.

## Headers

- `X-Locus-Step-Up` string, password

## Request body

- object
  - `kind` 'secret' | 'publishable' | 'sandbox', required
  - `expiresIn` integer, nullable: Lifetime of the new key in seconds. Omit for the default.
  - `scopes` CreditsTenantScope[]: Scopes for the new key. Omit to keep the kind's defaults. Publishable keys accept only widget:read.
  - `name` string: Name for the replacement key.
  - `tools` object: Endpoint allowlist for the replacement; makes it a burn-only execution credential.
    - `enable` string[], required
  - `pricing` CredentialPricingPolicy
    - `markupBps` integer
    - `flatMarkupUsdc` string: Non-negative exact decimal string with at most six fractional digits. Endpoint-specific positivity and amount limits still apply.
    - `endpoints` object
  - `gracePeriodSeconds` integer: Keep the old key valid this long for zero-downtime cutover. Omitted means the old key dies with this response.

## Response `200`

{ success, kind, key, keyPrefix } — the raw key is returned exactly once. Unlike create and per-key rotation, this cutover endpoint returns keyPrefix rather than a keyMetadata object. Without gracePeriodSeconds the superseded keys stop working with this response; with it they keep authenticating until the overlap ends.

## Other responses

- `400`: Invalid field or unknown body field — including a requested secret scope the acting member does not hold, or a bare rotation that would silently widen authority (some active key of the kind carries an endpoint allowlist, pricing, execution audience, or narrowed scopes; pass the replacement's shape explicitly).
- `403`: STEP_UP_REQUIRED or INSUFFICIENT_SCOPE.
- `409`: TOOL_SET_BOUND_KEYS: an active key of this kind is bound to a tool set, and one replacement on Main would move its traffic off that set. Refused whatever scopes, tools, or pricing are passed; rotate those keys with POST /credits/tenants/me/keys/{keyId}/rotate, which keeps the set.

---

[API](https://skmtc.dev/paywithlocus/apis/locus-pro-api.md) - [All operations](https://skmtc.dev/paywithlocus/apis/locus-pro-api/llms.txt) - [OpenAPI document](https://skmtc.dev/paywithlocus/apis/locus-pro-api/revisions/aab8fa5d6cef?raw)
