---
title: "Rotate Api Key"
method: POST
path: "/v1/keys/{key_id}/rotate"
tags: ["keys"]
---

# Rotate Api Key

`POST /v1/keys/{key_id}/rotate`

## Request

- Base URL: `https://api.ctindex.io`
- URL: `https://api.ctindex.io/v1/keys/{key_id}/rotate`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Issue a new key with the same tier/email, then retire the old one.

Used by the portal's "rotate" action. The new key inherits the old key's
``label`` with a ``_rotated`` suffix, the same tier, and the same
``user_email`` so it appears in the same scoped list. The plaintext key
is returned exactly once — same contract as POST /v1/keys.

Old-key retirement (task #618): with no body (or ``grace_period_hours``
= 0) the old key is revoked immediately — the historical contract. A
positive ``grace_period_hours`` instead sets ``expires_at`` on the old
key so both keys overlap for that window, letting integrations swap
credentials without a hard cut.

## Path parameters

- `key_id` string, uuid, required

## Cookies

- `ctix_session` string, nullable

## Request body

- ApiKeyRotateRequest: Optional body for the rotate endpoints. ``grace_period_hours = 0`` (the default, and the behaviour when no body is sent at all) revokes the old key immediately — today's contract. A positive value keeps the old key working for that many hours so integrations can swap credentials without a hard cut; after the window the old key starts returning 403 ``expired_api_key``.
  - `grace_period_hours` integer: Hours the OLD key keeps working after rotation. 0 (default) revokes it immediately; max 168 (7 days).

## Response `201`

Successful Response

- ApiKeyRotateResponse: Rotate response — the new key plus the old key's fate.
  - `id` string, uuid, required
  - `key` string, required
  - `label` string, nullable
  - `tier` string, required
  - `commercial_use_allowed` boolean
  - `created_at` string, date-time, nullable
  - `example_call` string, nullable: Copy-paste curl against a known-indexed domain (GH #706). Host is taken from EXAMPLE_CALL_DOMAIN (allow-listed); substitute $CERTINDEX_API_KEY.
  - `old_key_id` string, uuid, nullable
  - `old_key_revoked` boolean
  - `old_key_expires_at` string, date-time, nullable

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/ctindex/apis/certindex-api.md) · [All operations](https://skmtc.dev/ctindex/apis/certindex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ctindex/apis/certindex-api/revisions/d5bc0ec85bc9?raw)
