Sottoscrivi un webhook al changelog

POST https://api.keenauth.com/v1/accounts/{account_id}/changelog-subscriptions · proprietario dell'account o amministratore

Registra un endpoint HTTPS di tua proprietà che riceve un webhook changelog.entry_published ogni volta che una voce del changelog in una delle categorie scelte viene pubblicata. La richiesta viene effettuata con le credenziali di keenauth login: l'account deve essere quello con cui hai effettuato l'accesso e tu devi esserne il proprietario o un amministratore. Un account può avere al massimo 5 sottoscrizioni abilitate.

Ogni sottoscrizione ha un proprio segreto di firma, restituito una sola volta nella risposta. Le consegne seguono Standard Webhooks: ogni richiesta contiene le intestazioni webhook-id, webhook-timestamp e webhook-signature, e la firma (v1,<base64>) è un HMAC-SHA256 calcolato su <webhook-id>.<webhook-timestamp>.<body>. La chiave HMAC sono i 32 byte codificati dai 43 caratteri che seguono il prefisso idp_whsec_ (base62, big-endian); gli ultimi 6 caratteri sono un checksum e non fanno parte della chiave. Verifica la firma e rifiuta un timestamp troppo distante dall'ora corrente (le librerie Standard Webhooks ammettono 5 minuti) prima di considerare attendibile il corpo.

Non inserire dati personali, credenziali o token nell'URL: viene memorizzato insieme alla sottoscrizione e mostrato a ogni proprietario e amministratore dell'account.

Richiesta

Intestazioni:

Intestazione Obbligatoria Descrizione
Authorization sì DPoP <access token>: il token di accesso ricevuto dalla CLI quando hai effettuato l'accesso.
DPoP sì Una prova DPoP per questa richiesta (htm POST, htu l'URL di questa richiesta), firmata con la chiave a cui è vincolato il token di accesso; contiene l'hash del token di accesso e il nonce del server.
Content-Type sì application/json.
Idempotency-Key no Da 16 a 64 caratteri di A-Z, a-z, 0-9, _ e -. Ripetere la richiesta con la stessa chiave e lo stesso corpo entro 24 ore restituisce la stessa sottoscrizione senza il segreto, con secret_redacted: true e l'intestazione Idempotent-Replayed: true.

Corpo:

Campo Obbligatorio Descrizione
url sì L'endpoint: https, porta 443, un nome host (non un indirizzo IP né un nome valido solo in locale come .localhost, .local o .internal), senza informazioni utente o frammento, al massimo 2048 byte.
categories no Uno o più tra api, sdk, deprecation, security e incident, ciascuno al massimo una volta. Ometti il campo per ricevere tutte le categorie; un array vuoto viene rifiutato.

Risposta

201 Created con Cache-Control: no-store:

Campo Descrizione
id La sottoscrizione (chlg_…).
account_id L'account (acct_…).
url L'endpoint.
categories Le categorie ricevute, nell'ordine api, sdk, deprecation, security, incident.
status enabled, oppure disabled quando le consegne continuano a fallire.
created_at Quando la sottoscrizione è stata creata (RFC 3339, UTC).
signing_secret Il segreto di firma (idp_whsec_…). Restituito solo in questa risposta; conservalo nel tuo gestore di segreti.
secret_redacted true in caso di ripetizione idempotente, che non contiene signing_secret.

Errori

Stato Codice Quando
400 malformed_request Il corpo non è JSON, oppure l'intestazione Idempotency-Key è ripetuta, vuota o malformata.
400 unknown_field Il corpo contiene un campo diverso da url e categories.
401 unauthenticated Non è stato inviato alcun token di accesso.
401 invalid_token Il token di accesso non è valido, è scaduto o non appartiene alla CLI; accedi di nuovo con keenauth login.
401 invalid_dpop_proof La prova DPoP manca, è ripetuta oppure non corrisponde alla richiesta o alla chiave del token.
401 use_dpop_nonce Riprova con una prova che contenga il nonce dell'intestazione di risposta DPoP-Nonce.
401 insufficient_user_authentication È richiesta l'autenticazione a più fattori (oppure, per proprietari e amministratori, una passkey o una chiave di sicurezza); accedi di nuovo.
403 insufficient_permissions Non sei proprietario né amministratore dell'account.
403 account_restricted Lo stato dell'account non consente modifiche.
403 quota_exceeded L'account ha già 5 sottoscrizioni abilitate (errors[].code changelog_subscription_limit_exceeded).
404 not_found L'account non esiste, non è quello con cui hai effettuato l'accesso, oppure non ne sei membro.
413 payload_too_large Il corpo supera 8 KiB.
415 unsupported_media_type Il corpo non è application/json.
422 validation_failed url manca o non è valido, una categoria è sconosciuta o ripetuta, categories è vuoto, oppure un campo ha il tipo sbagliato.
422 idempotency_key_reuse Idempotency-Key è stata usata con un altro corpo entro 24 ore.
429 rate_limited Nell'ultima ora sono state create più di 10 sottoscrizioni per l'account; attendi Retry-After.

Consegna del webhook

Quando una voce viene pubblicata, KeenAuth invia una POST all'URL di ogni sottoscrizione abilitata le cui categorie includono una delle categorie della voce. La stessa voce non viene mai inviata due volte alla stessa sottoscrizione.

Richiesta

Intestazione Descrizione
Content-Type application/json.
webhook-id L'evento id (evt_…). È lo stesso in ogni nuovo tentativo della stessa voce, quindi usalo per ignorare i duplicati.
webhook-timestamp Tempo Unix in secondi in cui questo tentativo è stato firmato. È nuovo a ogni tentativo.
webhook-signature v1,<base64>: l'HMAC-SHA256 descritto sopra, calcolato su <webhook-id>.<webhook-timestamp>.<body>.

Il corpo è al massimo di 16 KiB. KeenAuth non segue i reindirizzamenti.

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 Descrizione
id L'evento (evt_…), uguale all'intestazione webhook-id.
type Sempre changelog.entry_published.
schema_version Sempre 1. Nella versione 1 possono essere aggiunti nuovi campi facoltativi a data; ignora i campi che non conosci.
occurred_at Quando la voce è stata pubblicata (RFC 3339).
recorded_at Quando KeenAuth ha messo in coda la consegna (RFC 3339).
data.id, data.url, data.title, data.date_published La voce, come nei feed del changelog.
data.tags Uno o più tra added, changed, fixed, removed, deprecation, security, breaking e incident.
data.categories Le categorie della sottoscrizione a cui appartiene la voce.
data.surfaces Le superfici interessate: kind (api, sdk, cli, provider, mcp oppure docs), name e, quando noti, version.
data.breaking true quando la modifica è breaking.
data.sunset_at La data di rimozione di una deprecazione (YYYY-MM-DD), altrimenti null.

Lo schema leggibile dalle macchine è changelog.entry_published.v1.

Risposte e nuovi tentativi

Rispondi con uno stato 2xx entro 15 secondi. Il corpo della risposta viene ignorato. Qualsiasi altro esito è un tentativo fallito e viene ritentato: un errore di connessione o TLS, un timeout, un reindirizzamento, 429, 5xx e ogni altro stato tranne 410. Il primo tentativo viene effettuato subito; i successivi circa 1 minuto, 5 minuti, 30 minuti, 2 ore, 8 ore, 24 ore e 36 ore dopo il fallimento precedente (ogni intervallo varia fino al 10 per cento), 8 tentativi in circa 3 giorni in tutto.

Se anche l'ottavo tentativo fallisce, la consegna viene abbandonata e la sottoscrizione viene disabilitata (status disabled). Le consegne già in coda per essa vengono annullate.

410 Gone

Rispondi 410 Gone per comunicare a KeenAuth che non vuoi più ricevere consegne. La sottoscrizione viene disabilitata subito, senza ulteriori tentativi, e le consegne in coda vengono annullate. Una sottoscrizione disabilitata non viene riabilitata e non conta nel limite di 5; per ricevere di nuovo le voci, creane una nuova. Ogni disabilitazione viene registrata nel registro di audit dell'account come changelog.subscription_disabled, con motivo receiver_gone, permanent_failure o schedule_exhausted.

Esempio

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"]}'