
# API-Schlüssel rotieren

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

Rotiert einen Schlüssel: Es wird ein NEUER Schlüssel mit demselben Namen, Typ, denselben Berechtigungen und demselben `allowed_cidrs` erstellt und zurückgegeben. Der alte Schlüssel bleibt für `overlap_hours` gültig und funktioniert danach nicht mehr.

- **Neuer Schlüssel.** Die Antwort beschreibt den neuen Schlüssel; `rotated_from` ist die ID des alten Schlüssels, und `key` enthält den Rohschlüssel genau einmal (`Cache-Control: no-store`). Stellen Sie ihn bereit und lassen Sie den alten Schlüssel anschließend auslaufen.
- **Überlappung.** Der alte Schlüssel erhält `revoked_at` in der Zukunft und `revoke_reason: rotated`; bei `overlap_hours: 0` wird er sofort widerrufen.
- **Keine Rechteausweitung.** Ein Schlüssel kann keinen Schlüssel rotieren, dessen Berechtigungen seine eigenen übersteigen, und ein `restricted`-Schlüssel kann keinen `secret`-Schlüssel rotieren. Der neue Schlüssel darf nicht nach dem aufrufenden Schlüssel ablaufen, auch dann nicht, wenn ein Schlüssel sich selbst rotiert (eine Laufzeit verlängern Sie im Dashboard). Ein aufrufender Schlüssel mit `allowed_cidrs` kann nur Schlüssel rotieren, deren Netzwerke innerhalb der eigenen liegen. Ein im Dashboard erstellter Schlüssel sowie jeder daraus rotierte Schlüssel kann nur durch sich selbst oder im Dashboard rotiert werden (`permission_escalation`). Der neue Schlüssel behält das `created_by` des alten Schlüssels; der rotierende Schlüssel wird im Audit-Log erfasst.
- **Idempotente Wiederholung.** Eine Wiederholung mit demselben `Idempotency-Key` lässt `key` weg und liefert `secret_redacted: true`.

## Anfrage

| Parameter | In | Beschreibung |
|---|---|---|
| `key_id` | path | ID des API-Schlüssels (`key_…`). |

Body:

Der Body ist optional; ohne ihn gelten alle Standardwerte.

| Feld | Typ | Beschreibung |
|---|---|---|
| `overlap_hours` | integer | Optional, 0–168, Standard 24. Wie viele Stunden der alte Schlüssel gültig bleibt. |
| `expires_at` | string (date-time) | Optional. Ablauf des NEUEN Schlüssels; Standard ist jetzt + 365 Tage, höchstens das Ablaufdatum des aufrufenden Schlüssels und höchstens 730 Tage. |

## Antwort

`200 OK` mit `Cache-Control: no-store`. Das Objekt des NEUEN Schlüssels zuzüglich `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>"
}
```

## Fehler

| HTTP | `code` | Wann |
|---|---|---|
| 401 | `unauthenticated` | Der Schlüssel fehlt, ist ungültig oder widerrufen. |
| 403 | `insufficient_permissions` | Der Schlüssel besitzt die erforderliche Berechtigung nicht. |
| 403 | `permission_escalation` | Die Anfrage würde einem Schlüssel mehr Berechtigungen, einen breiteren Netzwerkbereich oder ein späteres Ablaufdatum geben, als der aufrufende Schlüssel besitzt. Ein `restricted`-Schlüssel versucht, über seine eigenen Berechtigungen hinaus zu erstellen, zu rotieren oder zu widerrufen, oder ein anderer Schlüssel versucht, einen im Dashboard erstellten Schlüssel zu rotieren oder zu widerrufen. |
| 404 | `not_found` | Der Schlüssel existiert in dieser Umgebung nicht. |
| 409 | `conflict` | Der Schlüssel ist bereits widerrufen, abgelaufen oder bereits rotiert (nicht aktiv). |
| 409 | `api_key_limit_reached` | In der Umgebung sind bereits die maximal 50 aktiven Schlüssel vorhanden. |
| 422 | `validation_failed` | Der Body ist ungültig: `overlap_hours` liegt außerhalb von 0–168, oder `expires_at` liegt in der Vergangenheit oder mehr als 730 Tage in der Zukunft. |

## Beispiel

```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}'
```
