Aller au contenu
Support

Webhooks

Les webhooks vous permettent de recevoir des notifications en temps reel lorsque des evenements se produisent dans votre compte LabelGrid. Utilisez les webhooks pour automatiser des workflows et vous integrer a des systemes externes.

Pour les developpeurs : Vous pouvez egalement gerer les webhooks de maniere programmatique via l’API. Consultez la documentation de l’API LabelGrid pour les endpoints et exemples.

  1. Vous configurez un webhook - Specifiez une URL et les evenements a ecouter
  2. Un evenement se produit - Par exemple, une sortie est livree a un store
  3. LabelGrid envoie une requete POST - Votre serveur recoit les donnees de l’evenement
  4. Votre systeme les traite - Automatisez des workflows en fonction de l’evenement

  1. Cliquez sur votre icone de profil en haut a droite
  2. Selectionnez Webhooks dans le menu deroulant

  1. Cliquez sur Create Webhook
  2. Saisissez un Name pour identifier ce webhook
  3. Saisissez l’URL ou vous souhaitez recevoir les notifications
  4. Selectionnez les Events qui doivent declencher ce webhook
  5. Cliquez sur Create

Lorsque vous creez un webhook, vous recevez une cle secrete. Utilisez-la pour verifier que les requetes entrantes proviennent bien de LabelGrid :

  • Stockez le secret de maniere securisee
  • Verifiez la signature des requetes entrantes
  • En cas de compromission, regenerez le secret

Configurez votre webhook pour ecouter ces evenements. L’identifiant d’evenement est la valeur que vous verrez dans la propriete event du payload et dans le header X-Webhook-Event :

Identifiant d’evenementDescription
delivery.completedDeclenche lorsqu’une sortie est livree avec succes a un store
delivery.failedDeclenche lorsque la livraison a un store echoue
takedown.completedDeclenche lorsqu’une demande de retrait est terminee
release.review.status_changedDeclenche lorsque le statut de revision d’une sortie change
release.preflight.report_readyDeclenche lorsque le rapport Preflight QC d’une sortie en attente est pret a etre recupere
stream_radar.flag_createdDeclenche lorsqu’un signalement Stream Radar est cree, ou qu’un signalement resolu rouvre sur une nouvelle detection
stream_radar.flag_resolvedDeclenche lorsqu’un signalement Stream Radar se resout parce que les detections ont cesse
release.distributedDeclenche lorsqu’une sortie est distribuee
payment.statement_readyDeclenche lorsqu’un releve de paiement est pret a etre consulte
transcode.completedDeclenche lorsque le transcodage audio d’une piste se termine avec succes
transcode.failedDeclenche lorsque le transcodage d’une piste echoue ou se termine de facon incomplete
distribution.outlet.status_changedDeclenche a chaque transition de statut de distribution par outlet

Vous pouvez selectionner plusieurs evenements pour un seul webhook, ou creer des webhooks separes pour differents types d’evenements.

Vous pouvez aussi lire cette liste de maniere programmatique : GET /api/public/webhooks/event-types renvoie chaque evenement accompagne d’un schema data decrivant les cles et les types de son payload, afin que les consommateurs a schema strict puissent elargir leur validation entrante a l’avance.


La liste des webhooks affiche :

ColonneDescription
NameLe nom que vous avez attribue au webhook
URLOu les notifications sont envoyees
EventsNombre d’evenements configures
StatusActive ou Inactive
Success / FailNombre de livraisons reussies et echouees
Last TriggeredDerniere activation du webhook
  1. Cliquez sur l’action Edit sur la ligne du webhook
  2. Modifiez le nom, l’URL ou les evenements
  3. Cliquez sur Save

Basculez le statut actif d’un webhook sans le supprimer :

  • Active - Le webhook recevra les notifications
  • Inactive - Le webhook est en pause, aucune notification envoyee
  1. Cliquez sur l’action Delete sur la ligne du webhook
  2. Confirmez la suppression

Avant de dependre d’un webhook en production, testez-le :

  1. Cliquez sur l’action Test sur votre webhook
  2. LabelGrid envoie un payload de test a votre URL
  3. Verifiez que votre endpoint a bien recu et traite correctement

Surveillez l’activite des webhooks et depannez les problemes :

  1. Cliquez sur l’action View Logs sur un webhook
  2. Consultez l’historique de toutes les livraisons du webhook

Chaque entree de journal affiche :

ChampDescription
Event TypeL’evenement qui a declenche cette livraison
Response StatusCode de statut HTTP de votre serveur
DurationDuree de la requete
AttemptNumero de la tentative de renvoi
TimestampDate et heure de la livraison

Lorsqu’un evenement se produit, LabelGrid envoie une requete POST a votre URL avec un payload JSON :

{
"event": "delivery.completed",
"timestamp": "2026-05-05T10:00:00+00:00",
"webhook_id": "123",
"data": {
// Donnees specifiques a l'evenement
}
}

Le champ timestamp utilise le format ISO 8601. webhook_id est l’ID de votre webhook configure (il correspond au header X-Webhook-Id).


La structure de l’objet data depend du type d’event. Tous les types de champs ci-dessous sont des types JSON tels que serialises dans le payload.

Declenche une fois par outlet lorsqu’une livraison de sortie atteint un etat de succes terminal.

{
"event": "delivery.completed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "complete"
}
}
ChampTypeDescription
distro_queue_idintegerID interne de la file pour cette tentative de livraison
release_idintegerLa sortie qui a ete livree
label_idintegerLe label proprietaire de la sortie, pour router l’evenement sans requete supplementaire
release_catstring | nullVotre reference de catalogue de sortie
outlet_idinteger | nullL’ID de l’outlet de destination
outlet_namestring | nullNom lisible de l’outlet (par exemple, "Spotify")
statusstringToujours "complete" pour cet evenement

Declenche une fois par outlet lorsqu’une livraison de sortie atteint un etat d’echec terminal. Meme payload que delivery.completed plus un champ message.

{
"event": "delivery.failed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "error",
"message": "Outlet rejected the delivery: missing ISRC."
}
}
ChampTypeDescription
statusstringL’un de error, fault, rejected, batch_exception
messagestring | nullRaison de l’echec depuis l’outlet ou le pipeline de distribution

Declenche une fois par outlet lorsqu’une demande de retrait reussit. Meme forme que delivery.completed plus un indicateur takedown: true.

{
"event": "takedown.completed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "complete",
"takedown": true
}
}

Declenche une fois par sortie lorsque la sortie passe a l’etat de livraison distributed. Ne se declenche que sur la transition vers distributed, pas sur les enregistrements suivants pendant que la sortie est deja distribuee.

{
"event": "release.distributed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"release_title": "Summer EP",
"delivery_status": "distributed"
}
}

Declenche chaque fois qu’une sortie passe d’un etat de revision a un autre.

{
"event": "release.review.status_changed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"release_title": "Summer EP",
"previous_status": "to_review",
"new_status": "approved"
}
}
ChampTypeDescription
previous_statusstringStatut precedent. L’un de draft, to_review, approved, rejected, require_changes, audit
new_statusstringNouveau statut. Meme ensemble de valeurs
review_issuesarray (optionnel)Present uniquement sur les transitions vers require_changes et rejected : les problemes qui demandent votre attention. La cle est omise sur toute autre transition, ne considerez donc pas qu’elle est toujours presente

Declenche lorsque le rapport de qualite Preflight QC d’une sortie en mise en attente avant revision est pret a etre recupere. Necessite l’option Preflight QC sur votre compte.

{
"event": "release.preflight.report_ready",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"release_title": "Summer EP",
"generated_at": "2026-07-07T09:58:12+00:00",
"profile": { "name": "quality_report", "version": 2 },
"counts": { "blocking": 1, "informational": 2, "requires_feedback": 1 }
}
}
ChampTypeDescription
release_idintegerLa sortie a laquelle appartient le rapport
label_idintegerLe label proprietaire de la sortie
release_catstring | nullVotre reference de catalogue de sortie
release_titlestring | nullLe titre de la sortie
generated_atstringQuand les verifications se sont terminees (ISO 8601). Correspond au report.generated_at de l’endpoint du rapport de qualite
profileobjectLe profil de qualite selon lequel les decomptes ont ete calcules : {name, version}
countsobjectUniquement des decomptes agreges : {blocking, informational, requires_feedback}. Le decompte requires_feedback recoupe les deux autres

Declenche lorsqu’un signalement Stream Radar est cree, soit un signalement entierement nouveau, soit un signalement precedemment resolu qui rouvre sur une nouvelle detection. Necessite l’option Stream Radar sur votre compte. Le champ transition distingue les deux cas : published pour un nouveau signalement, reopened pour un signalement redevenu actif.

{
"event": "stream_radar.flag_created",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"flag_id": 4501,
"dsp": "spotify",
"isrc": "USRC12345678",
"release_id": 789,
"track_id": 654,
"severity": "high",
"status": "active",
"transition": "published",
"first_detected_at": "2026-07-06T00:00:00+00:00",
"last_detected_at": "2026-07-07T00:00:00+00:00",
"estimated_affected_streams": 12500,
"published_at": "2026-07-07T09:58:12+00:00",
"resolved_at": null
}
}
ChampTypeDescription
flag_idintegerL’identifiant stable du signalement ; correspond a id sur les endpoints Stream Radar
dspstringLa plateforme sur laquelle le motif a ete observe (p. ex. spotify)
isrcstringL’ISRC de l’enregistrement concerne
release_idintegerLa sortie a laquelle appartient l’enregistrement
track_idinteger | nullLa piste precise, lorsque l’ISRC correspond sans ambiguite a l’une de vos pistes
severitystringlow, medium ou high
statusstringactive pour cet evenement
transitionstringpublished pour un nouveau signalement, reopened lorsqu’un signalement resolu est redevenu actif
first_detected_atstring | nullQuand le motif a ete observe pour la premiere fois pour cette piste et cette plateforme (ISO 8601)
last_detected_atstring | nullLa detection la plus recente (ISO 8601)
estimated_affected_streamsinteger | nullUne estimation du nombre d’ecoutes impliquees
published_atstringQuand le signalement vous a ete remonte pour la premiere fois (ISO 8601)
resolved_atstring | nullnull tant que le signalement est actif

Declenche lorsqu’un signalement Stream Radar se resout parce que les detections ont cesse. Necessite l’option Stream Radar. Memes champs que stream_radar.flag_created (sans transition), avec status regle sur resolved et resolved_at renseigne.

{
"event": "stream_radar.flag_resolved",
"timestamp": "2026-07-14T10:00:00+00:00",
"webhook_id": "123",
"data": {
"flag_id": 4501,
"dsp": "spotify",
"isrc": "USRC12345678",
"release_id": 789,
"track_id": 654,
"severity": "high",
"status": "resolved",
"first_detected_at": "2026-07-06T00:00:00+00:00",
"last_detected_at": "2026-07-12T00:00:00+00:00",
"estimated_affected_streams": 18700,
"published_at": "2026-07-07T09:58:12+00:00",
"resolved_at": "2026-07-14T09:55:03+00:00"
}
}

Declenche lorsqu’un releve de paiement est genere et pret a etre consulte.

{
"event": "payment.statement_ready",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"payment_request_id": 1024,
"invoice_number": "INV-2026-001",
"period": "2026-04-30",
"amount": 1234.56,
"total_due_usd": 1234.56,
"currency": "USD"
}
}
ChampTypeDescription
payment_request_idintegerID interne de la demande de paiement
invoice_numberstringReference de facture pour le releve
periodstring | nullDate de fin de periode (date ISO 8601, YYYY-MM-DD)
amountnumberMontant du releve dans la devise currency
total_due_usdnumberTotal du releve converti en USD
currencystringCode de devise ISO 4217 (par defaut USD)

Declenche lorsque le transcodage audio d’une piste se termine. transcode.completed se declenche en cas de succes ; transcode.failed se declenche lorsque le transcodage echoue ou se termine de facon incomplete. Les deux partagent la meme forme de payload.

{
"event": "transcode.completed",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"label_id": 321,
"track_id": 654,
"transcoder_queue_id": 987,
"status": "complete",
"status_message": "transcode_complete",
"files": [
{ "asset_type_id": 2, "status": "complete" }
]
}
}
ChampTypeDescription
release_idintegerLa sortie a laquelle appartient la piste
label_idintegerLe label proprietaire de la sortie
track_idintegerLa piste qui a ete transcodee
transcoder_queue_idintegerID interne de la file de transcodage
statusstringStatut brut de la file : complete, error ou incomplete
status_messagestringCode de motif sur et enumere : transcode_complete, transcode_error ou transcode_incomplete
filesarrayDetail par fichier pour la piste : {asset_type_id, status} par fichier transcode

Declenche a chaque transition de statut de distribution par outlet (par exemple scheduled → transcoding → batched → complete), pas seulement les transitions terminales couvertes par delivery.completed, delivery.failed et takedown.completed. Cet evenement est bavard par nature : abonnez-vous a lui uniquement si vous voulez la progression complete par outlet.

{
"event": "distribution.outlet.status_changed",
"timestamp": "2026-07-07T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"label_id": 321,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"previous_status": "transcoding",
"status": "batched"
}
}
ChampTypeDescription
distro_queue_idintegerID interne de la file pour cette livraison
release_idintegerLa sortie en cours de distribution
label_idintegerLe label proprietaire de la sortie
release_catstring | nullVotre reference de catalogue de sortie
outlet_idinteger | nullL’ID de l’outlet de destination
outlet_namestring | nullNom lisible de l’outlet
previous_statusstring | nullLe statut precedent ; null lorsque la ligne n’avait aucun statut precedent reconnu
statusstringLe nouveau statut

Chaque livraison de webhook est signee afin que vous puissiez verifier qu’elle provient bien de LabelGrid. Verifiez toujours la signature avant de traiter l’evenement.

Chaque requete POST de webhook inclut ces headers :

HeaderDescription
X-Webhook-SignatureHMAC-SHA256 du corps brut de la requete, en hexadecimal minuscule, sans prefixe d’algorithme
X-Webhook-TimestampHorodatage ISO 8601 de la livraison (meme valeur que la propriete timestamp dans le corps)
X-Webhook-EventIdentifiant d’evenement (par exemple, delivery.completed)
X-Webhook-IdL’ID du webhook recevant la livraison
User-AgentLabelGrid-Webhooks/1.0
Content-Typeapplication/json
  • Algorithme : HMAC-SHA256
  • Encodage : Hexadecimal minuscule
  • Prefixe : Aucun — la valeur est juste le digest hex, pas sha256=...
  • Contenu signe : Le corps JSON brut complet de la requete (le corps inclut l’horodatage en tant que propriete, donc l’horodatage est signe implicitement)
  1. Lisez le corps brut de la requete avant tout parsing ou transformation JSON. Re-serialiser le JSON parse peut produire des octets differents et casser la signature.
  2. Calculez HMAC-SHA256(corps_brut, votre_secret_webhook) et prenez le digest hexadecimal minuscule.
  3. Comparez avec X-Webhook-Signature en utilisant une comparaison a temps constant.
  4. (Recommande) Rejetez la requete si X-Webhook-Timestamp est plus ancien que votre fenetre de tolerance de rejeu — nous suggerons 5 minutes.
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit('Signature invalide');
}
// Rejeter les livraisons de plus de 5 minutes
if (abs(time() - strtotime($timestamp)) > 300) {
http_response_code(401);
exit('Livraison obsolete');
}
$payload = json_decode($rawBody, true);
// ... traiter l'evenement
http_response_code(200);
const crypto = require('crypto');
// Express : capturer le corps brut AVANT tout middleware JSON
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body; // Buffer
const signature = req.header('X-Webhook-Signature') || '';
const timestamp = req.header('X-Webhook-Timestamp') || '';
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(rawBody)
.digest('hex');
const sigBuf = Buffer.from(signature, 'hex');
const expBuf = Buffer.from(expected, 'hex');
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
return res.status(401).send('Signature invalide');
}
if (Math.abs(Date.now() - new Date(timestamp).getTime()) > 5 * 60 * 1000) {
return res.status(401).send('Livraison obsolete');
}
const payload = JSON.parse(rawBody.toString('utf8'));
// ... traiter l'evenement
res.sendStatus(200);
});
import hmac, hashlib
from datetime import datetime, timezone
raw_body = request.get_data() # Flask : bytes, avant tout parsing JSON
signature = request.headers.get('X-Webhook-Signature', '')
timestamp = request.headers.get('X-Webhook-Timestamp', '')
expected = hmac.new(
webhook_secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
return ('Signature invalide', 401)
delivery_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
if abs((datetime.now(timezone.utc) - delivery_time).total_seconds()) > 300:
return ('Livraison obsolete', 401)
return ('', 200) # traiter l'evenement puis acquitter
  • Re-serialiser le corps avant de le hacher. Les frameworks qui parsent automatiquement le JSON (Express express.json(), le corps de requete par defaut de Laravel) perdent les octets originaux. Capturez d’abord le corps brut.
  • Utiliser une comparaison qui n’est pas a temps constant (==, ===). Susceptible aux attaques temporelles — utilisez toujours hash_equals (PHP), crypto.timingSafeEqual (Node), hmac.compare_digest (Python), ou l’equivalent dans votre langage.
  • S’attendre a un prefixe sha256=. La valeur du header est juste le digest hex sans prefixe.
  • Omettre la verification de l’horodatage. Sans cela, une signature divulguee pourrait etre rejouee indefiniment.

LimiteValeur
Timeout de requete10 secondes
Taille maximale du payload64 Ko
Maximum de webhooks par utilisateur10

Si votre endpoint ne repond pas dans les 10 secondes, la livraison est consideree comme un echec et renvoyee.

Si votre endpoint retourne un statut non-2xx ou expire, LabelGrid renvoie avec un backoff exponentiel :

TentativeAttente avant le renvoi
1 → 230 secondes
2 → 31 minute
3 → 42 minutes
4 → 54 minutes
5 → 68 minutes
6 → 716 minutes
7 → 832 minutes
8 → 964 minutes
9 → 10128 minutes

Chaque intervalle inclut 0 a 30 secondes de jitter. Apres 10 tentatives (~4,5 heures de temps total ecoule), la livraison est journalisee comme definitivement echouee et n’est plus renvoyee.

Si un webhook accumule 100 livraisons echouees cumulees, il est automatiquement desactive. Reactivez-le manuellement dans la liste des webhooks apres avoir corrige votre endpoint. La reactivation d’un webhook desactive remet son compteur d’echecs a zero.

  • Retournez une reponse 2xx rapidement (en moins de 10 secondes)
  • Traitez les donnees de maniere asynchrone apres avoir acquitte
  • Verifiez la signature de chaque requete (voir Verification des signatures webhook)
  • Surveillez votre compteur d’echecs dans la liste des webhooks
  • Consultez les journaux de livraison lorsque vous enquetez sur des evenements manques

  • Envoyer des messages Slack lorsque les sorties sont en ligne
  • Emailer votre equipe en cas d’echec de livraison
  • Mettre a jour les tableaux de bord internes
  • Declencher des campagnes marketing lorsque les sorties sont distribuees
  • Mettre a jour votre site web lorsque du nouveau contenu est disponible
  • Synchroniser le statut avec des outils de gestion de projet externes
  • Obtenir des alertes instantanees en cas d’echec de livraison
  • Suivre la progression de la distribution en temps reel
  • Surveiller les changements de statut de revision

  1. Verifiez le statut - Le webhook est-il Active ?
  2. Verifiez l’URL - L’endpoint est-il accessible depuis internet ?
  3. Verifiez les evenements - Les bons evenements sont-ils selectionnes ?
  4. Consultez les journaux - Des erreurs sont-elles enregistrees ?
  1. Verifiez votre endpoint - Retourne-t-il 200 OK ?
  2. Verifiez le temps de reponse - Repond-il dans le delai imparti ?
  3. Examinez les messages d’erreur - Qu’est-ce qui echoue ?
  4. Testez manuellement - Envoyez un webhook de test

Si votre secret webhook est compromis :

  1. Cliquez sur Regenerate Secret dans les parametres du webhook
  2. Mettez a jour votre application avec le nouveau secret
  3. L’ancien secret cesse immediatement de fonctionner

Si vous avez des questions sur les webhooks, contactez notre equipe support.

Vous n’utilisez pas encore LabelGrid ?

Tout ce que vous venez de lire est disponible sur notre plateforme.

Découvrez ce que LabelGrid peut faire →