Rotate signing keys
POST /v1/signing-keys:rotate · permission signing-keys:write
Runs a signing key rotation now, outside the automatic schedule.
mode: immediate: the regular rotation step runs at once. The pre-publishednextkey starts signing, the oldactivekey staysretiredfor 7 days and a newnextkey is published. Ifnextuses a different algorithm than the active key (an algorithm change) and was published less than 7 days ago, the call fails with409 next_key_too_young.mode: emergency(suspected key compromise): the oldactivekey is revoked and leaves the JWKS immediately;nextbecomes active and a newnextis generated.revoke_next: truerevokesnextas well and activates a freshly generated, never-published key.revoke_sessions: truealso revokes every session and refresh token family of the environment, so users sign in again.reasonis required.
Federation keys
use can also name one of the environment's federation keys: saml_sp_signing and saml_sp_encryption (the SAML service provider signing and encryption keys published in your SP metadata) or oidc_rp_signing (the private_key_jwt key of enterprise OIDC connections, published at /sso/jwks.json). These keys are always RSA-3072 and rotate once a year on their own: a new next key is published 30 days before it takes over, and the old key stays published for 30 days after.
mode: immediatemakes that pre-publishednextkey active now; the old key stays published for 30 more days (it appears inretired_kids). Without a publishednextkey the call fails with409 conflict; if thenextkey was published less than 7 days ago, with409 next_key_too_young.mode: emergencyremoves the active key from the metadata or JWKS immediately and destroys its private key (revoked_kids). The publishednextkey becomes active; if there is none, or withrevoke_next: true, a new, never-published key does. Identity providers that load your metadata or JWKS from its URL pick the change up on their next refresh. Providers where you uploaded the metadata or certificate by hand reject sign-ins until you upload the new certificate; for that case aconnection.sp_certificate_rotation_startedwebhook event is sent for each affected connection.
For a federation key, next_kid in the response is null, removed_kids is empty and revoke_sessions is not allowed. If the environment never used SAML or private_key_jwt, there is no key to rotate and the call fails with 409 conflict.
Connection keys
A SAML connection can have its own dedicated SP keys instead of the environment's. To rotate those, add connection_id (conn_…) with use: saml_sp_signing or saml_sp_encryption. The same modes, permissions and rules apply, but only that connection's key set changes: the environment's keys and the other connections' keys stay as they are, and the connection.sp_certificate_rotation_started event is sent for that connection only. The audit record names the environment as its target and the connection in metadata.connection_id. An unknown connection, or a connection of another environment, fails with 404 not_found; a connection that uses the environment's keys fails with 409 connection_keys_not_dedicated (rotate the environment's keys without connection_id). Without connection_id the environment's key set is rotated.
Emergency mode needs, besides signing-keys:write, the signing-keys:emergency permission granted explicitly on the credential: only a restricted key or an M2M client carrying that permission can call it. A full-access secret key (idp_sk_…) gets 403 insufficient_permissions in emergency mode.
New token signing keys use the algorithm of the environment policy (get the policy). A successful token signing key rotation emits the signing_key.rotated or signing_key.revoked webhook event; every rotation writes a management audit record. This endpoint belongs to the "key and secret issuance" rate limit family. Send an Idempotency-Key: a retry after a network error then returns the first result instead of rotating twice.
Request
JSON body (unknown fields are rejected with 400 unknown_field):
| Field | Type | Required | Description |
|---|---|---|---|
Idempotency-Key |
header | no | Recommended. 16-64 characters of A-Z a-z 0-9 _ -. |
mode |
string | yes | immediate or emergency. |
use |
string | no | The key to rotate. Default token_signing (access and ID token signing key). The federation keys are saml_sp_signing, saml_sp_encryption and oidc_rp_signing (see Federation keys). |
revoke_sessions |
boolean | no | Emergency and token_signing only. Also revoke all sessions and refresh tokens. Default false. |
revoke_next |
boolean | no | Emergency only. Also revoke the published next key. Default false. |
reason |
string | emergency: yes | Emergency only. 1-512 printable ASCII characters, for example an incident number. Do not put secrets or personal data here. |
connection_id |
string | no | saml_sp_signing and saml_sp_encryption only. A SAML connection (conn_…) with dedicated SP keys; its key set is rotated instead of the environment's (see Connection keys). |
With mode: immediate, sending revoke_sessions: true, revoke_next: true or a reason fails with 422 (not_allowed). A connection_id with token_signing or oidc_rp_signing fails the same way.
Response
200 OK with the result of the rotation. activated_kid is the key that signs from now on and next_kid the newly published next key (null for a federation key). retired_kids are keys that stopped signing and stay published (7 days for a token signing key, 30 days for a federation key); revoked_kids are keys removed from the JWKS by an emergency rotation; removed_kids are retired keys whose 7-day window ended and that left the JWKS in the same step.
{
"mode": "immediate",
"use": "token_signing",
"activated_kid": "ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs",
"next_kid": "GKw-c0PwFokMUQ6T-TUmEWnZ4_VlQ2Qpgw-vCTT0-OQ",
"retired_kids": ["PiPoFgA5WUoziU9lZOGxNIu9egCI1CxKy3PurtWcAJ0"],
"revoked_kids": [],
"removed_kids": ["Ln0sA6lQeuJl7PW1NWiFpTOTogKdJBOUmXJloaJa78Y"],
"revoke_sessions": false
}
Errors
| HTTP | code | When |
|---|---|---|
| 400 | unknown_field |
The body contains an unknown field. |
| 400 | malformed_request |
The body is not valid JSON, or the Idempotency-Key header is malformed or repeated. |
| 403 | insufficient_permissions |
The credential lacks signing-keys:write, or emergency mode was requested without an explicitly granted signing-keys:emergency. |
| 404 | not_found |
connection_id names no SAML connection of this environment. |
| 409 | connection_keys_not_dedicated |
connection_id names a connection that uses the environment's SP keys. |
| 409 | next_key_too_young |
An algorithm change is pending and the next key was published less than 7 days ago, or (federation key, immediate) the next key was published less than 7 days ago. |
| 409 | conflict |
The environment has no published next key, has no active key of the federation use, or the previous federation key is still in its 30-day overlap. |
| 415 | unsupported_media_type |
The content type is not application/json. |
| 422 | validation_failed |
A field is missing or invalid; the pointer names the field, for example /reason. |
| 422 | idempotency_key_reuse |
The Idempotency-Key was already used with a different body. |
| 501 | not_implemented |
The operation is not available in this deployment yet. |
| 401 | unauthenticated |
The credential is missing, invalid or revoked. |
| 429 | rate_limited |
The rate limit was exceeded; retry after the Retry-After period. |
| 503 | service_unavailable |
A dependency is temporarily unavailable; retry after the Retry-After period. |
Example
Regular rotation:
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"mode":"immediate"}'
Emergency rotation with a restricted key that grants signing-keys:write and signing-keys:emergency:
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
-H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"mode":"emergency","revoke_sessions":true,"reason":"INC-2041 key exposure"}'
Emergency rotation of the SAML SP signing key, also revoking the published next key:
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
-H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"mode":"emergency","use":"saml_sp_signing","revoke_next":true,"reason":"INC-2042 SP key exposure"}'
Emergency rotation of the dedicated SAML SP signing key of one connection:
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
-H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"mode":"emergency","use":"saml_sp_signing","connection_id":"conn_01h2xcejqtf2nbrexx3vqjhp41","reason":"INC-2043 connection key exposure"}'