Abonner un webhook au journal des modifications
POST https://api.keenauth.com/v1/accounts/{account_id}/changelog-subscriptions · propriétaire du compte ou administrateur
Enregistre un point de terminaison HTTPS qui vous appartient et qui reçoit un webhook changelog.entry_published chaque fois qu'une entrée du journal des modifications appartenant à l'une des catégories choisies est publiée. La requête est effectuée avec les identifiants de keenauth login : le compte doit être celui dans lequel vous vous êtes connecté, et vous devez en être le propriétaire ou un administrateur. Un compte peut avoir au plus 5 abonnements activés.
Chaque abonnement possède son propre secret de signature, renvoyé une seule fois dans la réponse. Les livraisons suivent Standard Webhooks : chaque requête contient les en-têtes webhook-id, webhook-timestamp et webhook-signature, et la signature (v1,<base64>) est un HMAC-SHA256 calculé sur <webhook-id>.<webhook-timestamp>.<body>. La clé HMAC correspond aux 32 octets encodés par les 43 caractères qui suivent le préfixe idp_whsec_ (base62, big-endian) ; les 6 derniers caractères forment une somme de contrôle et ne font pas partie de la clé. Vérifiez la signature et rejetez un horodatage trop éloigné de l'heure actuelle (les bibliothèques Standard Webhooks acceptent 5 minutes) avant de faire confiance au contenu.
Ne placez pas de données personnelles, d'identifiants ou de jetons dans l'URL : elle est stockée avec l'abonnement et affichée à tous les propriétaires et administrateurs du compte.
Requête
En-têtes :
| En-tête | Obligatoire | Description |
|---|---|---|
Authorization |
oui | DPoP <access token> : le jeton d'accès reçu par la CLI lors de votre connexion. |
DPoP |
oui | Une preuve DPoP pour cette requête (htm POST, htu l'URL de cette requête), signée avec la clé à laquelle le jeton d'accès est lié ; elle contient le hachage du jeton d'accès et le nonce du serveur. |
Content-Type |
oui | application/json. |
Idempotency-Key |
non | 16 à 64 caractères parmi A-Z, a-z, 0-9, _ et -. Répéter la requête avec la même clé et le même corps dans les 24 heures renvoie le même abonnement sans le secret, avec secret_redacted: true et l'en-tête Idempotent-Replayed: true. |
Corps :
| Champ | Obligatoire | Description |
|---|---|---|
url |
oui | Le point de terminaison : https, port 443, un nom d'hôte (pas une adresse IP ni un nom local uniquement tel que .localhost, .local ou .internal), sans informations d'utilisateur ni fragment, au plus 2048 octets. |
categories |
non | Parmi api, sdk, deprecation, security et incident, chacune au plus une fois. Omettez le champ pour recevoir toutes les catégories ; un tableau vide est refusé. |
Réponse
201 Created avec Cache-Control: no-store :
| Champ | Description |
|---|---|
id |
L'abonnement (chlg_…). |
account_id |
Le compte (acct_…). |
url |
Le point de terminaison. |
categories |
Les catégories reçues, dans l'ordre api, sdk, deprecation, security, incident. |
status |
enabled, ou disabled lorsque les livraisons ont continué d'échouer. |
created_at |
Date de création de l'abonnement (RFC 3339, UTC). |
signing_secret |
Le secret de signature (idp_whsec_…). Renvoyé uniquement dans cette réponse ; conservez-le dans votre gestionnaire de secrets. |
secret_redacted |
true lors d'une rejouabilité idempotente, qui ne contient pas signing_secret. |
Erreurs
| Statut | Code | Cas |
|---|---|---|
| 400 | malformed_request |
Le corps n'est pas du JSON, ou l'en-tête Idempotency-Key est répété, vide ou mal formé. |
| 400 | unknown_field |
Le corps contient un champ autre que url et categories. |
| 401 | unauthenticated |
Aucun jeton d'accès n'a été envoyé. |
| 401 | invalid_token |
Le jeton d'accès est invalide, expiré ou n'appartient pas à la CLI ; reconnectez-vous avec keenauth login. |
| 401 | invalid_dpop_proof |
La preuve DPoP est absente, répétée ou ne correspond pas à la requête ou à la clé du jeton. |
| 401 | use_dpop_nonce |
Réessayez avec une preuve contenant le nonce de l'en-tête de réponse DPoP-Nonce. |
| 401 | insufficient_user_authentication |
L'authentification multifacteur est requise (ou, pour les propriétaires et administrateurs, une clé d'accès ou une clé de sécurité) ; reconnectez-vous. |
| 403 | insufficient_permissions |
Vous n'êtes ni propriétaire ni administrateur du compte. |
| 403 | account_restricted |
Le statut du compte ne permet pas de modifications. |
| 403 | quota_exceeded |
Le compte possède déjà 5 abonnements activés (errors[].code changelog_subscription_limit_exceeded). |
| 404 | not_found |
Le compte n'existe pas, n'est pas celui dans lequel vous vous êtes connecté, ou vous n'en êtes pas membre. |
| 413 | payload_too_large |
Le corps dépasse 8 Kio. |
| 415 | unsupported_media_type |
Le corps n'est pas application/json. |
| 422 | validation_failed |
url est absent ou invalide, une catégorie est inconnue ou répétée, categories est vide, ou un champ a un type incorrect. |
| 422 | idempotency_key_reuse |
Le Idempotency-Key a été utilisé avec un autre corps dans les 24 heures. |
| 429 | rate_limited |
Plus de 10 abonnements ont été créés pour le compte au cours de la dernière heure ; attendez Retry-After. |
Livraison des webhooks
Lorsqu'une entrée est publiée, KeenAuth envoie une POST à l'URL de chaque abonnement activé dont les catégories incluent l'une des catégories de l'entrée. La même entrée n'est jamais envoyée deux fois au même abonnement.
Requête
| En-tête | Description |
|---|---|
Content-Type |
application/json. |
webhook-id |
L'identifiant de l'événement id (evt_…). Il est identique à chaque nouvelle tentative pour la même entrée ; utilisez-le pour ignorer les doublons. |
webhook-timestamp |
Heure Unix en secondes à laquelle cette tentative a été signée. Elle change à chaque tentative. |
webhook-signature |
v1,<base64> : le HMAC-SHA256 décrit ci-dessus, calculé sur <webhook-id>.<webhook-timestamp>.<body>. |
Le corps fait au plus 16 Kio. KeenAuth ne suit pas les redirections.
Événement
{
"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"
}
}
| Champ | Description |
|---|---|
id |
L'événement (evt_…), identique à l'en-tête webhook-id. |
type |
Toujours changelog.entry_published. |
schema_version |
Toujours 1. De nouveaux champs facultatifs peuvent être ajoutés à data dans la version 1 ; ignorez les champs que vous ne connaissez pas. |
occurred_at |
Date de publication de l'entrée (RFC 3339). |
recorded_at |
Date à laquelle KeenAuth a mis la livraison en file d'attente (RFC 3339). |
data.id, data.url, data.title, data.date_published |
L'entrée, comme dans les flux du journal des modifications. |
data.tags |
Une ou plusieurs valeurs parmi added, changed, fixed, removed, deprecation, security, breaking et incident. |
data.categories |
Les catégories d'abonnement auxquelles appartient l'entrée. |
data.surfaces |
Les surfaces concernées : kind (api, sdk, cli, provider, mcp ou docs), name et, lorsqu'elle est connue, version. |
data.breaking |
true lorsque la modification est incompatible. |
data.sunset_at |
La date de retrait d'une obsolescence (YYYY-MM-DD), sinon null. |
Le schéma lisible par machine est changelog.entry_published.v1.
Réponses et nouvelles tentatives
Répondez avec un statut 2xx dans un délai de 15 secondes. Le corps de la réponse est ignoré. Tout autre résultat est une tentative échouée et est réessayée : une erreur de connexion ou TLS, un délai dépassé, une redirection, 429, 5xx et tout autre statut sauf 410. La première tentative est effectuée immédiatement ; les suivantes environ 1 minute, 5 minutes, 30 minutes, 2 heures, 8 heures, 24 heures et 36 heures après l'échec précédent (chaque intervalle varie jusqu'à 10 pour cent), soit 8 tentatives sur environ 3 jours au total.
Si la huitième tentative échoue, la livraison est abandonnée et l'abonnement est désactivé (status disabled). Les livraisons déjà en file d'attente pour cet abonnement sont annulées.
410 Gone
Répondez 410 Gone pour indiquer à KeenAuth que vous ne souhaitez plus recevoir de livraisons. L'abonnement est immédiatement désactivé, sans nouvelle tentative, et ses livraisons en file d'attente sont annulées. Un abonnement désactivé ne peut pas être réactivé et ne compte pas dans la limite de 5 ; créez-en un nouveau pour recevoir à nouveau des entrées. Chaque désactivation est consignée dans le journal d'audit du compte sous changelog.subscription_disabled, avec la raison receiver_gone, permanent_failure ou schedule_exhausted.
Exemple
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"]}'