> ## Documentation Index
> Fetch the complete documentation index at: https://docs.knoxcall.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rotate Master Key

> Rotate the tenant master key and enqueue a background rewrap job to re-encrypt existing tenant data.

## POST /admin/tenant-kms/rotate

Rotate the tenant master key. Creates a new 32-byte master key wrapped under your customer KMS key, sets it as the active version, and retires the current active version. A background rewrap job immediately begins re-encrypting existing tenant data under the new version.

**Auth**: `Authorization: Bearer <session jwt>` + **`X-Tenant-ID`**. Requires an owner/admin session.

This endpoint requires a **recent step-up verification** (re-authentication) performed within the last **5 minutes** by the signed-in user. No step-up header or token is sent on this request — the server checks for a recent, unused verification tied to your session. To create one, re-authenticate via `POST /auth/2fa/verify-step-up` (TOTP), `POST /auth/passkey/verify-step-up` (passkey), or the email fallback (`POST /auth/step-up/email-challenge` then `POST /auth/step-up/verify-email`); check `GET /auth/step-up/status` to confirm a valid step-up exists. If none is present, the request returns `403` with `{ "error": "Recent step-up verification required for this action", "requires_step_up": true, "max_age_minutes": 5 }`.

<Warning>
  Rotation is **non-reversible**. The old version becomes `retired` and cannot be made active again. Data encrypted under the old version remains decryptable so that existing tenant data can still be read while the background rewrap job completes.
</Warning>

Cannot be called while the tenant is sealed — unseal first.

### Response

```json theme={"dark"}
{
  "rotated": true,
  "new_version": 2,
  "rewrap_lease_id": 42
}
```

`rewrap_lease_id` identifies the background rewrap job. This job processes 100 rows per batch at 5-second intervals, re-encrypting all existing tenant secrets under `new_version`. The old version remains available for decryption until the rewrap completes. `rewrap_lease_id` is `null` if the rewrap lease failed to issue (the rotation itself succeeded — contact support to trigger rewrap manually).

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST https://admin.knoxcall.com/admin/tenant-kms/rotate \
    -H "Authorization: Bearer $KC_ADMIN_JWT" \
    -H "X-Tenant-ID: $KC_TENANT_ID"
  ```

  ```python Python theme={"dark"}
  resp = requests.post(
      "https://admin.knoxcall.com/admin/tenant-kms/rotate",
      headers={
          "Authorization": f"Bearer {KC_ADMIN_JWT}",
          "X-Tenant-ID": KC_TENANT_ID,
      },
  )
  resp.raise_for_status()
  print(resp.json()["new_version"])  # 2
  ```

  ```javascript Node.js theme={"dark"}
  const resp = await fetch("https://admin.knoxcall.com/admin/tenant-kms/rotate", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${KC_ADMIN_JWT}`,
      "X-Tenant-ID": KC_TENANT_ID,
    },
  });
  const { rotated, new_version, rewrap_lease_id } = await resp.json();
  ```
</CodeGroup>
