
# Rotar una clave de API

`POST /v1/api-keys/{key_id}:rotate` · permiso `api-keys:write` · `Idempotency-Key` opcional

Rota una clave: crea una clave NUEVA con el mismo nombre, tipo, permisos y `allowed_cidrs`, y la devuelve. La clave anterior sigue siendo válida durante `overlap_hours` y, después, deja de funcionar.

- **Clave nueva.** La respuesta describe la clave nueva; `rotated_from` es el identificador de la clave anterior y `key` contiene la clave sin procesar una sola vez (`Cache-Control: no-store`). Despliéguela y, después, deje que la clave anterior caduque.
- **Solapamiento.** La clave anterior recibe un `revoked_at` en el futuro y `revoke_reason: rotated`; con `overlap_hours: 0` se revoca de inmediato.
- **Sin escalada.** Una clave no puede rotar una clave cuyos permisos superen los suyos, y una clave `restricted` no puede rotar una clave `secret`. La clave nueva no puede caducar después de la clave que realiza la llamada, incluso cuando una clave rota a sí misma (para extender una duración, hágalo en el panel de control), y una clave que realiza la llamada con `allowed_cidrs` solo puede rotar claves limitadas a redes incluidas en las suyas. Una clave creada en el panel de control, y cada clave rotada desde ella, solo puede rotarse a sí misma o desde el panel de control (`permission_escalation`). La clave nueva conserva el `created_by` de la clave anterior; la clave que rota queda registrada en el registro de auditoría.
- **Reproducción idempotente.** Una reproducción con el mismo `Idempotency-Key` omite `key` y devuelve `secret_redacted: true`.

## Solicitud

| Parámetro | En | Descripción |
|---|---|---|
| `key_id` | ruta | Identificador de la clave de API (`key_…`). |

Cuerpo:

El cuerpo es opcional; sin él se aplican todos los valores predeterminados.

| Campo | Tipo | Descripción |
|---|---|---|
| `overlap_hours` | entero | Opcional, de 0 a 168, predeterminado 24. Cuántas horas sigue siendo válida la clave anterior. |
| `expires_at` | cadena (date-time) | Opcional. Caducidad de la clave NUEVA; predeterminado ahora + 365 días, como máximo la caducidad de la clave que realiza la llamada y como máximo 730 días. |

## Respuesta

`200 OK` con `Cache-Control: no-store`. El objeto de la clave NUEVA más `key`.

```json
{
  "id": "key_0000000000000000000000001z",
  "name": "ci",
  "type": "restricted",
  "permissions": ["users:read"],
  "allowed_cidrs": [],
  "last4": "xxxx",
  "created_by": "key_…",
  "created_at": "2026-10-01T10:00:00Z",
  "expires_at": "2027-10-01T10:00:00Z",
  "last_used_at": null,
  "last_used_ip": null,
  "rotated_from": "key_…",
  "revoked_at": null,
  "revoke_reason": null,
  "key": "<shown once>"
}
```

## Errores

| HTTP | `code` | Cuándo |
|---|---|---|
| 401 | `unauthenticated` | La clave falta, no es válida o está revocada. |
| 403 | `insufficient_permissions` | La clave no tiene el permiso requerido. |
| 403 | `permission_escalation` | La solicitud daría a una clave más permisos, un rango de red más amplio o una caducidad posterior a los de la clave que realiza la llamada; una clave `restricted` intenta crear, rotar o revocar más allá de sus propios permisos, o otra clave intenta rotar o revocar una clave creada en el panel de control. |
| 404 | `not_found` | La clave no existe en este entorno. |
| 409 | `conflict` | La clave ya está revocada, caducada o ya fue rotada (no está activa). |
| 409 | `api_key_limit_reached` | El entorno ya tiene el máximo de 50 claves activas. |
| 422 | `validation_failed` | El cuerpo no es válido: `overlap_hours` está fuera del rango de 0 a 168 o `expires_at` está en el pasado o más de 730 días en el futuro. |

## Ejemplo

```bash
curl -X POST "https://api.eu.keenauth.com/v1/api-keys/key_0000000000000000000000001z:rotate" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"overlap_hours":24}'
```
