
# Actualizar la política de claves de firma

`PATCH /v1/signing-keys/policy` · permiso `signing-keys:write`

Actualiza la política de claves de firma con un JSON merge patch (`application/merge-patch+json`, RFC 7396). `If-Match` es obligatorio: si falta, `428`; si la versión está desactualizada, `412`. Si la política nunca se escribió, la versión es `"0"`. Un patch que no cambia nada no escribe nada y conserva la versión.

Cambiar `algorithm` genera de inmediato una nueva clave `next` con el nuevo algoritmo; puede activarse como pronto 7 días después, para que los verificadores que almacenan el JWKS en caché la vean primero (una rotación `immediate` anterior falla con `409 next_key_too_young`). Hasta entonces la clave activa sigue firmando. Cambiar `rotation_interval_days` desplaza `next_rotation_at`. Un cambio correcto escribe el registro de auditoría de gestión `signing_key.policy_updated`.

## Solicitud

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `If-Match` | header | sí | El `ETag` de la política leído por última vez (`"0"` si nunca se escribió). Si falta: `428`; desactualizado: `412`. |
| `algorithm` | string | no | `RS256`, `ES256`, `PS256` o `EdDSA`. No puede ser `null`. |
| `rotation_interval_days` | integer | no | De 30 a 365. No puede ser `null`. |
| `storage` | string | no | `wrapped` (la clave privada se guarda cifrada con la clave del entorno y solo se usa dentro del servicio de firma). `transit` (clave custodiada en el servicio de gestión de claves) aún no está disponible y se rechaza con `422`. No puede ser `null`. |

## Respuesta

`200 OK` con la política actualizada y el nuevo `ETag`.

```json
{
  "algorithm": "ES256",
  "rotation_interval_days": 90,
  "storage": "wrapped",
  "next_rotation_at": "2026-12-04T09:30:00.123456Z",
  "updated_at": "2026-10-05T09:30:00.123456Z"
}
```

## Errores

| HTTP | código | Cuándo |
|---|---|---|
| 400 | `unknown_field` | El patch contiene un campo desconocido o de solo lectura, o no es un objeto JSON (`malformed_request`). |
| 412 | `precondition_failed` | `If-Match` no coincide con la versión actual. |
| 415 | `unsupported_media_type` | El tipo de contenido no es `application/merge-patch+json`. |
| 422 | `validation_failed` | Un valor no es válido (`invalid_value`), está fuera de rango (`out_of_range`), se puso a `null` (`invalid`), o `storage` es `transit`. |
| 428 | `precondition_required` | Falta el encabezado `If-Match`. |
| 401 | `unauthenticated` | La credencial falta, es inválida o ha sido revocada. |
| 403 | `insufficient_permissions` | La credencial carece del permiso requerido. |
| 429 | `rate_limited` | Se superó el límite de velocidad; reintente después del período `Retry-After`. |
| 501 | `not_implemented` | La operación aún no está disponible en este despliegue; reintentar no cambia el resultado. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintente después del período `Retry-After`. |

## Ejemplo

```bash
curl -X PATCH https://api.eu.keenauth.com/v1/signing-keys/policy \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -H 'If-Match: "0"' \
  -d '{"algorithm":"ES256","rotation_interval_days":90}'
```
