EventSub : abonnements aux événements
Recevez en temps réel les nouveaux suiveurs, les abonnements, les messages de clavardage, les drops et plus encore, par webhook ou par WebSocket.
Vue d’ensemble
EventSub avise votre application dès qu’un événement se produit sur Woupi : une chaîne passe en direct, un spectateur suit une chaîne, un message est publié dans le clavardage, un drop est réclamé, etc. Vous n’avez pas à interroger l’API en boucle : vous créez un abonnement, puis Woupi vous envoie chaque événement correspondant.
Un abonnement se compose de quatre éléments :
type: le type d’événement, par exemplechannel.follow(voir Types d’événements).version: la version du format de l’événement; seule la version"1"existe pour le moment.condition: le filtre, par exemple la chaîne à surveiller (broadcaster_user_id). Les champs dépendent du type.transport: la façon dont les événements vous sont livrés, parwebhookou parwebsocket.
Remarque
Les événements ne sont livrés que si votre compte développeur est approuvé. Si votre compte est suspendu, vos abonnements restent en place mais ne reçoivent plus rien.
Transports
Deux transports sont offerts. Choisissez selon l’endroit où votre code s’exécute.
| Transport | Jeton requis | Pour qui | Livraison |
|---|---|---|---|
webhook | Jeton d’application (client_credentials) | Serveurs joignables publiquement en HTTPS | Requête HTTP POST signée, avec nouvelles tentatives en cas d’échec |
websocket | Jeton d’utilisateur | Robots de clavardage, applications de bureau, scripts sans serveur public | Message sur la connexion ouverte; aucune nouvelle tentative, et les abonnements sont supprimés à la fermeture |
Les détails de chaque transport sont décrits dans Webhooks et Transport WebSocket. Pour obtenir un jeton, consultez Authentification.
Obtenir un jeton d’application pour les abonnements webhook :
curl -X POST https://api.woupi.buzz/oauth2/token \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET" \
-d grant_type=client_credentialsAutorisations requises
Les événements publics (mise en ondes, changement de titre, clips, etc.) ne demandent aucune portée. Les événements privés exigent qu’un utilisateur précis ait autorisé votre application avec la bonne portée : c’est l’utilisateur nommé dans la condition, et non la personne qui crée l’abonnement.
| Types | Condition | Portée requise | Accordée par |
|---|---|---|---|
stream.online, stream.offline, channel.update, channel.chat_settings.update, channel.clip.create, channel.vod.ready | broadcaster_user_id | Aucune | — |
channel.follow | broadcaster_user_id | channel:read:followers | Le diffuseur (broadcaster_user_id) |
channel.subscribe, channel.subscription.gift | broadcaster_user_id | channel:read:subscriptions | Le diffuseur (broadcaster_user_id) |
channel.chat.message, channel.chat.message_delete | broadcaster_user_id, user_id | chat:read | Le compte lecteur (user_id), par exemple votre robot |
drop.entitlement.grant | campaign_id (facultatif) | Aucune (niveau application) | Seules les campagnes de votre application sont livrées |
user.authorization.grant, user.authorization.revoke | client_id (celui de votre application) | Aucune (niveau application) | — |
- Webhook : le diffuseur (ou le compte lecteur) doit avoir autorisé votre application avec la portée requise, par exemple au moyen du flux par code d’autorisation. Vous créez ensuite l’abonnement avec votre jeton d’application.
- WebSocket : en plus de l’autorisation, l’utilisateur nommé dans la condition doit être celui du jeton d’utilisateur. Un robot qui lit le clavardage met donc son propre identifiant dans
user_id. - Sans autorisation valide, la création échoue avec le code 403 et le message
subscription missing proper authorization.
{
"error": "Forbidden",
"status": 403,
"message": "subscription missing proper authorization"
}Attention
Lorsqu’un utilisateur déconnecte votre application (Paramètres, Sécurité, Applications connectées), ses jetons sont révoqués, un événement user.authorization.revoke est émis et les abonnements qui dépendaient de son autorisation sont supprimés. Les abonnements webhook concernés reçoivent d’abord un message de révocation avec le statut authorization_revoked.
Créer un abonnement
POST https://api.woupi.buzz/v1/eventsub/subscriptions avec un corps JSON :
| Champ | Type | Description |
|---|---|---|
type | string | Type d’événement. |
version | string | Toujours "1". |
condition | object | Champs de condition du type; les identifiants sont des chaînes. |
transport.method | string | webhook | websocket |
transport.callback | string | Webhook seulement : URL HTTPS publique qui recevra les messages. |
transport.secret | string | Webhook seulement : de 10 à 100 caractères, sert à signer chaque message. |
transport.session_id | string | WebSocket seulement : identifiant reçu dans le message session_welcome. |
Abonnement webhook, avec un jeton d’application :
curl -X POST https://api.woupi.buzz/v1/eventsub/subscriptions \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Client-Id: $CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"type": "channel.follow",
"version": "1",
"condition": { "broadcaster_user_id": "1042" },
"transport": {
"method": "webhook",
"callback": "https://example.com/woupi/webhook",
"secret": "a-long-random-secret-string"
}
}'La réponse 202 contient l’abonnement créé. Un abonnement webhook commence au statut webhook_callback_verification_pending : Woupi envoie aussitôt une requête de vérification à votre URL de rappel (voir Webhooks). Le champ total indique le nombre d’abonnements de votre application.
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"data": [
{
"id": "f1c2ae15-3b4d-4c9e-9a7b-0d8e6f5a4c31",
"status": "webhook_callback_verification_pending",
"type": "channel.follow",
"version": "1",
"condition": { "broadcaster_user_id": "1042" },
"transport": {
"method": "webhook",
"callback": "https://example.com/woupi/webhook"
},
"created_at": "2026-09-24T14:05:12.000Z"
}
],
"total": 1,
"max_total_cost": 10000
}Abonnement WebSocket, avec le jeton de l’utilisateur et l’identifiant de session reçu sur la connexion. Il est actif (enabled) dès sa création.
curl -X POST https://api.woupi.buzz/v1/eventsub/subscriptions \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Client-Id: $CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"type": "channel.chat.message",
"version": "1",
"condition": { "broadcaster_user_id": "1042", "user_id": "2177" },
"transport": {
"method": "websocket",
"session_id": "9b4e7c2a-51f3-4d8e-a0c6-3e7f2b1d9a54"
}
}'Erreurs possibles :
| Statut | Cause |
|---|---|
| 400 | Type, version, condition ou transport invalide; secret hors limites; URL de rappel refusée (HTTP, port autre que 443, adresse privée ou locale); session WebSocket inconnue; session liée à un autre client (session belongs to another client). |
| 401 | Jeton absent, expiré ou révoqué; Client-Id absent ou différent; mauvais type de jeton (webhook exige un jeton d’application, WebSocket un jeton d’utilisateur). |
| 403 | subscription missing proper authorization : l’autorisation requise est absente. |
| 409 | Un abonnement identique (même type, même condition, même destination) existe déjà. |
| 429 | too many websocket sessions : trop de sessions WebSocket pour ce couple application et utilisateur. |
Remarque
Une application peut détenir au plus 10 000 abonnements (max_total_cost).
Lister les abonnements
GET https://api.woupi.buzz/v1/eventsub/subscriptions retourne les abonnements de votre application. Filtres facultatifs : status, type, et after pour la page suivante (valeur de pagination.cursor). Avec un jeton d’utilisateur, seuls les abonnements WebSocket de cet utilisateur sont retournés.
curl "https://api.woupi.buzz/v1/eventsub/subscriptions?status=enabled&type=channel.follow" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Client-Id: $CLIENT_ID"{
"data": [
{
"id": "f1c2ae15-3b4d-4c9e-9a7b-0d8e6f5a4c31",
"status": "enabled",
"type": "channel.follow",
"version": "1",
"condition": { "broadcaster_user_id": "1042" },
"transport": {
"method": "webhook",
"callback": "https://example.com/woupi/webhook"
},
"created_at": "2026-09-24T14:05:12.000Z"
}
],
"total": 1,
"pagination": {}
}Supprimer un abonnement
DELETE https://api.woupi.buzz/v1/eventsub/subscriptions?id=<id> supprime l’abonnement et répond 204 sans contenu. Les abonnements WebSocket sont aussi supprimés automatiquement à la fermeture de leur connexion.
curl -X DELETE "https://api.woupi.buzz/v1/eventsub/subscriptions?id=f1c2ae15-3b4d-4c9e-9a7b-0d8e6f5a4c31" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Client-Id: $CLIENT_ID"Statuts d’un abonnement
| Statut | Signification |
|---|---|
enabled | Actif : les événements sont livrés. |
webhook_callback_verification_pending | Webhook créé; Woupi attend la bonne réponse à la requête de vérification. |
webhook_callback_verification_failed | Votre URL de rappel n’a pas renvoyé le défi. Aucune nouvelle tentative : supprimez l’abonnement et recréez-le après correction. |
notification_failures_exceeded | 50 tentatives de livraison consécutives ont échoué. Un message de révocation a été envoyé et plus rien n’est livré; recréez l’abonnement. |
authorization_revoked | L’utilisateur dont dépendait l’abonnement a retiré son autorisation. L’abonnement est supprimé; ce statut n’apparaît que dans le message de révocation. |
Format des notifications
Chaque notification contient l’abonnement concerné et l’objet event, dont le contenu dépend du type. Par webhook, cet objet est le corps de la requête POST; par WebSocket, il se trouve dans payload d’un message notification.
{
"subscription": {
"id": "f1c2ae15-3b4d-4c9e-9a7b-0d8e6f5a4c31",
"type": "channel.follow",
"version": "1",
"status": "enabled",
"condition": { "broadcaster_user_id": "1042" },
"transport": { "method": "webhook" },
"created_at": "2026-09-24T14:05:12.000Z"
},
"event": {
"broadcaster_user_id": "1042",
"broadcaster_user_login": "lunaplays",
"broadcaster_user_name": "LunaPlays",
"user_id": "3318",
"user_login": "max_quebec",
"user_name": "Max",
"followed_at": "2026-09-24T14:07:31.000Z"
}
}- Tous les identifiants sont des chaînes de caractères.
- Toutes les dates sont en ISO 8601, en UTC.
- Les événements liés à une chaîne contiennent le trio
broadcaster_user_id,broadcaster_user_loginetbroadcaster_user_name. - Les événements de test envoyés depuis le portail contiennent
"is_test": truedansevent.
Types d’événements
Le tableau ci-dessous donne, pour chaque type, sa condition, la portée requise et un exemple de l’objet event.
| Type | Version | Condition | Portée requise | Description |
|---|---|---|---|---|
stream.online | 1 | broadcaster_user_id | Aucune | La chaîne commence une diffusion en direct. |
stream.offline | 1 | broadcaster_user_id | Aucune | La diffusion en direct de la chaîne se termine. |
channel.update | 1 | broadcaster_user_id | Aucune | Le titre, la catégorie ou la classification de contenu de la chaîne change. `content_rating` est la classification effective (0, 16 ou 18). |
channel.follow | 1 | broadcaster_user_id | channel:read:followersAccordée par l'utilisateur de broadcaster_user_id | Une personne suit la chaîne. |
channel.subscribe | 1 | broadcaster_user_id | channel:read:subscriptionsAccordée par l'utilisateur de broadcaster_user_id | Une personne s'abonne à la chaîne. |
channel.subscription.gift | 1 | broadcaster_user_id | channel:read:subscriptionsAccordée par l'utilisateur de broadcaster_user_id | Une personne offre des abonnements à la chaîne. |
channel.chat.message | 1 | broadcaster_user_iduser_id | chat:readAccordée par l'utilisateur de user_id | Un message est publié dans le clavardage de la chaîne. |
channel.chat.message_delete | 1 | broadcaster_user_iduser_id | chat:readAccordée par l'utilisateur de user_id | Un message du clavardage est supprimé par la modération. |
channel.chat_settings.update | 1 | broadcaster_user_id | Aucune | Les modes du clavardage de la chaîne changent. |
channel.clip.create | 1 | broadcaster_user_id | Aucune | Un clip est créé à partir d'une vidéo de la chaîne. |
channel.vod.ready | 1 | broadcaster_user_id | Aucune | L'enregistrement d'une diffusion est prêt à être visionné. |
drop.entitlement.grant | 1 | campaign_id (facultatif) | Aucune (événements de votre propre application) | Une personne réclame une récompense Drops de l'une de vos campagnes. |
user.authorization.grant | 1 | client_id | Aucune (événements de votre propre application) | Une personne autorise votre application. |
user.authorization.revoke | 1 | client_id | Aucune (événements de votre propre application) | Une personne déconnecte votre application. |
stream.online
La chaîne commence une diffusion en direct.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"id": "1790000000000",
"type": "live",
"started_at": "2026-09-24T18:00:00.000Z"
}stream.offline
La diffusion en direct de la chaîne se termine.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer"
}channel.update
Le titre, la catégorie ou la classification de contenu de la chaîne change. `content_rating` est la classification effective (0, 16 ou 18).
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"title": "Speedrun any%",
"category_id": "42",
"category_name": "Minecraft",
"content_rating": 16,
"content_labels": [
"language"
]
}channel.follow
Une personne suit la chaîne.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"user_id": "2048",
"user_login": "viewer",
"user_name": "Viewer",
"followed_at": "2026-09-24T18:05:00.000Z"
}channel.subscribe
Une personne s'abonne à la chaîne.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"user_id": "2048",
"user_login": "viewer",
"user_name": "Viewer",
"tier": "1000",
"is_gift": false,
"expires_at": "2026-10-24T18:05:00.000Z"
}channel.subscription.gift
Une personne offre des abonnements à la chaîne.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"user_id": "2048",
"user_login": "viewer",
"user_name": "Viewer",
"total": 5,
"tier": "1000",
"is_anonymous": false
}channel.chat.message
Un message est publié dans le clavardage de la chaîne.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"chatter_user_id": "2048",
"chatter_user_login": "viewer",
"chatter_user_name": "Viewer",
"message_id": "1790000000000-a1b2c3",
"message": {
"text": "Salut tout le monde !"
},
"chatter_level": 7
}channel.chat.message_delete
Un message du clavardage est supprimé par la modération.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"message_id": "1790000000000-a1b2c3"
}channel.chat_settings.update
Les modes du clavardage de la chaîne changent.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"emote_mode": false,
"follower_mode": true,
"subscriber_mode": false,
"slow_mode": true,
"slow_mode_s": 10
}channel.clip.create
Un clip est créé à partir d'une vidéo de la chaîne.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"clip_id": "812",
"creator_user_id": "2048",
"creator_user_login": "viewer",
"creator_user_name": "Viewer",
"video_id": "799"
}channel.vod.ready
L'enregistrement d'une diffusion est prêt à être visionné.
Exemple d'objet « event » :
{
"broadcaster_user_id": "1337",
"broadcaster_user_login": "streamer",
"broadcaster_user_name": "Streamer",
"video_id": "799",
"title": "Speedrun any%",
"duration_s": 7260
}drop.entitlement.grant
Une personne réclame une récompense Drops de l'une de vos campagnes.
Exemple d'objet « event » :
{
"entitlement_id": "3f1c2b9e-7d4a-4c1e-9b2a-5e6f7a8b9c0d",
"campaign_id": "12",
"reward_id": "34",
"reward_name": "Épée légendaire",
"category_id": "42",
"category_name": "Minecraft",
"user_id": "2048",
"user_login": "viewer",
"user_name": "Viewer",
"claimed_at": "2026-09-24T19:00:00.000Z"
}user.authorization.grant
Une personne autorise votre application.
Exemple d'objet « event » :
{
"client_id": "abcdefghijklmnopqrstuvwxyz0123",
"user_id": "2048",
"user_login": "viewer",
"user_name": "Viewer",
"scopes": [
"chat:read",
"chat:write"
]
}user.authorization.revoke
Une personne déconnecte votre application.
Exemple d'objet « event » :
{
"client_id": "abcdefghijklmnopqrstuvwxyz0123",
"user_id": "2048",
"user_login": "viewer",
"user_name": "Viewer"
}