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