Suscribir un webhook al registro de cambios

POST https://api.keenauth.com/v1/accounts/{account_id}/changelog-subscriptions · propietario de la cuenta o administrador

Registra un endpoint HTTPS propio que recibe un webhook changelog.entry_published cada vez que se publica una entrada del registro de cambios en una de las categorías elegidas. La solicitud se realiza con las credenciales de keenauth login: la cuenta debe ser aquella en la que ha iniciado sesión, y usted debe ser su propietario o administrador. Una cuenta puede tener como máximo 5 suscripciones habilitadas.

Cada suscripción tiene su propio secreto de firma, devuelto una sola vez en la respuesta. Las entregas siguen Standard Webhooks: cada solicitud incluye los encabezados webhook-id, webhook-timestamp y webhook-signature, y la firma (v1,<base64>) es un HMAC-SHA256 sobre <webhook-id>.<webhook-timestamp>.<body>. La clave HMAC son los 32 bytes que codifican los 43 caracteres posteriores al prefijo idp_whsec_ (base62, big-endian); los últimos 6 caracteres son una suma de control y no forman parte de la clave. Verifique la firma y rechace una marca de tiempo que esté demasiado alejada de la hora actual (las bibliotecas de Standard Webhooks permiten 5 minutos) antes de confiar en el cuerpo.

No incluya datos personales, credenciales ni tokens en la URL: se almacena con la suscripción y se muestra a todos los propietarios y administradores de la cuenta.

Solicitud

Encabezados:

Encabezado Obligatorio Descripción
Authorization sí DPoP <access token>: el token de acceso que la CLI recibió al iniciar sesión.
DPoP sí Una prueba DPoP para esta solicitud (htm POST, htu la URL de esta solicitud), firmada con la clave a la que está vinculado el token de acceso; contiene el hash del token de acceso y el nonce del servidor.
Content-Type sí application/json.
Idempotency-Key no De 16 a 64 caracteres de A-Z, a-z, 0-9, _ y -. Repetir la solicitud con la misma clave y el mismo cuerpo en un plazo de 24 horas devuelve la misma suscripción sin el secreto, con secret_redacted: true y el encabezado Idempotent-Replayed: true.

Cuerpo:

Campo Obligatorio Descripción
url sí El endpoint: https, puerto 443, un nombre de host (no una dirección IP ni un nombre solo local como .localhost, .local o .internal), sin información de usuario ni fragmento, de como máximo 2048 bytes.
categories no Cualquiera de api, sdk, deprecation, security y incident, cada uno como máximo una vez. Omita el campo para recibir todas las categorías; un array vacío se rechaza.

Respuesta

201 Created con Cache-Control: no-store:

Campo Descripción
id La suscripción (chlg_…).
account_id La cuenta (acct_…).
url El endpoint.
categories Las categorías recibidas, en el orden api, sdk, deprecation, security, incident.
status enabled, o disabled cuando las entregas siguen fallando.
created_at Cuándo se creó la suscripción (RFC 3339, UTC).
signing_secret El secreto de firma (idp_whsec_…). Se devuelve solo en esta respuesta; guárdelo en su gestor de secretos.
secret_redacted true en una repetición idempotente, que no incluye signing_secret.

Errores

Estado Código Cuándo
400 malformed_request El cuerpo no es JSON, o el encabezado Idempotency-Key está repetido, vacío o mal formado.
400 unknown_field El cuerpo tiene un campo distinto de url y categories.
401 unauthenticated No se envió ningún token de acceso.
401 invalid_token El token de acceso no es válido, ha caducado o no es el de la CLI; inicie sesión de nuevo con keenauth login.
401 invalid_dpop_proof La prueba DPoP falta, está repetida o no coincide con la solicitud ni con la clave del token.
401 use_dpop_nonce Reintente con una prueba que incluya el nonce del encabezado DPoP-Nonce de la respuesta.
401 insufficient_user_authentication Se requiere autenticación multifactor (o, para propietarios y administradores, una clave de acceso o una clave de seguridad); inicie sesión de nuevo.
403 insufficient_permissions No es propietario ni administrador de la cuenta.
403 account_restricted El estado de la cuenta no permite cambios.
403 quota_exceeded La cuenta ya tiene 5 suscripciones habilitadas (errors[].code changelog_subscription_limit_exceeded).
404 not_found La cuenta no existe, no es la cuenta en la que ha iniciado sesión, o no es miembro de ella.
413 payload_too_large El cuerpo supera 8 KiB.
415 unsupported_media_type El cuerpo no es application/json.
422 validation_failed url falta o no es válido, una categoría es desconocida o está repetida, categories está vacío, o un campo tiene un tipo incorrecto.
422 idempotency_key_reuse Se usó Idempotency-Key con otro cuerpo en un plazo de 24 horas.
429 rate_limited Se crearon más de 10 suscripciones para la cuenta en la última hora; espere Retry-After.

Entrega de webhooks

Cuando se publica una entrada, KeenAuth envía un POST a la URL de cada suscripción habilitada cuyas categorías incluyan alguna de las categorías de la entrada. La misma entrada nunca se envía dos veces a una misma suscripción.

Solicitud

Encabezado Descripción
Content-Type application/json.
webhook-id El identificador del evento id (evt_…). Es el mismo en cada reintento de la misma entrada, así que úselo para ignorar duplicados.
webhook-timestamp Tiempo Unix en segundos en el que se firmó este intento. Es nuevo en cada intento.
webhook-signature v1,<base64>: el HMAC-SHA256 descrito arriba, sobre <webhook-id>.<webhook-timestamp>.<body>.

El cuerpo tiene como máximo 16 KiB. KeenAuth no sigue redirecciones.

Evento

{
  "id": "evt_01jb0000000000000000000000",
  "type": "changelog.entry_published",
  "schema_version": 1,
  "occurred_at": "2026-11-04T00:00:00Z",
  "recorded_at": "2026-11-04T08:30:12Z",
  "data": {
    "id": "2026-11-04-example",
    "url": "https://docs.keenauth.com/en/changelog/#2026-11-04-example",
    "title": "Example entry",
    "date_published": "2026-11-04T00:00:00Z",
    "tags": ["deprecation"],
    "categories": ["api", "deprecation"],
    "surfaces": [{"kind": "api", "name": "management-v1", "version": "v1"}],
    "breaking": false,
    "sunset_at": "2027-11-04"
  }
}
Campo Descripción
id El evento (evt_…), igual al encabezado webhook-id.
type Siempre changelog.entry_published.
schema_version Siempre 1. Pueden añadirse nuevos campos opcionales a data dentro de la versión 1; ignore los campos que no conozca.
occurred_at Cuándo se publicó la entrada (RFC 3339).
recorded_at Cuándo puso KeenAuth en cola la entrega (RFC 3339).
data.id, data.url, data.title, data.date_published La entrada, tal como aparece en los feeds del registro de cambios.
data.tags Una o más de added, changed, fixed, removed, deprecation, security, breaking y incident.
data.categories Las categorías de suscripción a las que pertenece la entrada.
data.surfaces Las superficies afectadas: kind (api, sdk, cli, provider, mcp o docs), name y, cuando se conoce, version.
data.breaking true cuando el cambio es disruptivo.
data.sunset_at La fecha de retirada de una deprecación (YYYY-MM-DD); en caso contrario, null.

El esquema legible por máquina es changelog.entry_published.v1.

Respuestas y reintentos

Responda con cualquier estado 2xx en un plazo de 15 segundos. El cuerpo de la respuesta se ignora. Cualquier otro resultado es un intento fallido y se reintenta: un error de conexión o de TLS, un tiempo de espera, una redirección, 429, 5xx y cualquier otro estado distinto de 410. El primer intento se realiza de inmediato; los siguientes, unos 1 minuto, 5 minutos, 30 minutos, 2 horas, 8 horas, 24 horas y 36 horas después del fallo anterior (cada intervalo varía hasta un 10 por ciento), 8 intentos en unos 3 días en total.

Si el octavo intento falla, la entrega se abandona y la suscripción se deshabilita (status disabled). Las entregas que ya estaban en cola para ella se cancelan.

410 Gone

Responda 410 Gone para indicar a KeenAuth que ya no desea recibir entregas. La suscripción se deshabilita de inmediato, sin más intentos, y sus entregas en cola se cancelan. Una suscripción deshabilitada no vuelve a habilitarse y no cuenta para el límite de 5; cree una nueva para volver a recibir entradas. Cada deshabilitación se registra en el registro de auditoría de la cuenta como changelog.subscription_disabled, con el motivo receiver_gone, permanent_failure o schedule_exhausted.

Ejemplo

curl -X POST https://api.keenauth.com/v1/accounts/acct_01jb0000000000000000000000/changelog-subscriptions \
  -H "Authorization: DPoP $ACCESS_TOKEN" \
  -H "DPoP: $DPOP_PROOF" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: changelog-hooks-2026-10" \
  -d '{"url": "https://hooks.example.com/keenauth/changelog", "categories": ["deprecation", "security"]}'