Webhooks
Met webhooks ontvang je realtime meldingen wanneer er iets gebeurt in je LabelGrid-account. Gebruik webhooks om workflows te automatiseren en aan externe systemen te koppelen.
Voor ontwikkelaars: Je kunt webhooks ook programmatisch beheren via de API. Zie de LabelGrid API-documentatie voor endpoints en voorbeelden.
Hoe webhooks werken
Section titled “Hoe webhooks werken”- Je stelt een webhook in - Geef een URL op en de gebeurtenissen waarop je wilt reageren
- Er gebeurt iets - Bijvoorbeeld: een release wordt geleverd aan een winkel
- LabelGrid stuurt een POST-verzoek - Je server ontvangt de gegevens van de gebeurtenis
- Je systeem verwerkt het - Automatiseer workflows op basis van de gebeurtenis
Webhooks openen
Section titled “Webhooks openen”- Klik rechtsboven op je profielpictogram
- Kies Webhooks in het uitklapmenu
Een webhook aanmaken
Section titled “Een webhook aanmaken”- Klik op Create Webhook
- Voer een Naam in om deze webhook te herkennen
- Voer de URL in waarop je de meldingen wilt ontvangen
- Kies welke Gebeurtenissen deze webhook moeten activeren
- Klik op Create
Webhook-secret
Section titled “Webhook-secret”Bij het aanmaken van een webhook krijg je een secret key. Daarmee controleer je of binnenkomende verzoeken echt van LabelGrid komen:
- Bewaar de secret veilig
- Controleer de handtekening van binnenkomende verzoeken
- Genereer de secret opnieuw als die is gelekt
Beschikbare gebeurtenissen
Section titled “Beschikbare gebeurtenissen”Stel je webhook in om op deze gebeurtenissen te reageren. De Gebeurtenis-identifier is de waarde die je terugziet in de event-property van de payload en in de X-Webhook-Event-header:
| Gebeurtenis-identifier | Beschrijving |
|---|---|
delivery.completed | Treedt op wanneer een release succesvol is geleverd aan een outlet |
delivery.failed | Treedt op wanneer de levering aan een outlet mislukt |
takedown.completed | Treedt op wanneer een takedown-verzoek is voltooid |
release.review.status_changed | Treedt op wanneer de reviewstatus van een release verandert |
release.distributed | Treedt op wanneer een release wordt gedistribueerd |
payment.statement_ready | Treedt op wanneer een afrekening klaarstaat om te bekijken |
Je kunt meerdere gebeurtenissen selecteren voor één webhook, of aparte webhooks aanmaken voor verschillende gebeurtenistypen.
Webhooks beheren
Section titled “Webhooks beheren”Je webhooks bekijken
Section titled “Je webhooks bekijken”De webhooklijst toont:
| Kolom | Beschrijving |
|---|---|
| Naam | De naam die je aan de webhook hebt gegeven |
| URL | Waar de meldingen naartoe gaan |
| Gebeurtenissen | Aantal ingestelde gebeurtenissen |
| Status | Actief of inactief |
| Geslaagd / Mislukt | Aantal geslaagde en mislukte leveringen |
| Laatst geactiveerd | Wanneer de webhook voor het laatst is aangeroepen |
Een webhook bewerken
Section titled “Een webhook bewerken”- Klik op de actie Edit op de webhookrij
- Pas de naam, URL of gebeurtenissen aan
- Klik op Save
Activeren / deactiveren
Section titled “Activeren / deactiveren”Wissel de actieve status van een webhook zonder hem te verwijderen:
- Actief - De webhook ontvangt meldingen
- Inactief - De webhook staat op pauze, er worden geen meldingen verstuurd
Een webhook verwijderen
Section titled “Een webhook verwijderen”- Klik op de actie Delete op de webhookrij
- Bevestig het verwijderen
Webhooks testen
Section titled “Webhooks testen”Test een webhook voordat je er in productie op vertrouwt:
- Klik op de actie Test bij je webhook
- LabelGrid stuurt een testpayload naar je URL
- Controleer of je endpoint die correct heeft ontvangen en verwerkt
Webhook-logs bekijken
Section titled “Webhook-logs bekijken”Houd webhookactiviteit in de gaten en los problemen op:
- Klik op de actie View Logs bij een webhook
- Bekijk de volledige geschiedenis van webhookleveringen
Logdetails
Section titled “Logdetails”Elke logregel toont:
| Veld | Beschrijving |
|---|---|
| Gebeurtenistype | Welke gebeurtenis deze levering heeft geactiveerd |
| Responsstatus | HTTP-statuscode van je server |
| Duur | Hoe lang het verzoek duurde |
| Poging | Nummer van de herhaalpoging |
| Tijdstempel | Wanneer de levering plaatsvond |
Opbouw van de webhookpayload
Section titled “Opbouw van de webhookpayload”Wanneer er iets gebeurt, stuurt LabelGrid een POST-verzoek naar je URL met een JSON-payload:
{ "event": "delivery.completed", "timestamp": "2026-05-05T10:00:00+00:00", "webhook_id": "123", "data": { // Event-specific data }}Het timestamp-veld gebruikt de ISO 8601-notatie. webhook_id is de ID van je ingestelde webhook (die overeenkomt met de X-Webhook-Id-header).
Gebeurtenispayloads
Section titled “Gebeurtenispayloads”De structuur van het data-object hangt af van het event-type. Alle veldtypen hieronder zijn de JSON-typen zoals ze in de payload zijn geserialiseerd.
delivery.completed
Section titled “delivery.completed”Wordt één keer per outlet afgevuurd wanneer de levering van een release een definitieve succesvolle status bereikt.
{ "event": "delivery.completed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "complete" }}| Veld | Type | Beschrijving |
|---|---|---|
distro_queue_id | integer | Interne queue-ID voor deze leveringspoging |
release_id | integer | De geleverde release |
release_cat | string | null | Je catalogusreferentie voor de release |
outlet_id | integer | De ID van de doel-outlet |
outlet_name | string | null | Leesbare naam van de outlet (bijv. "Spotify") |
status | string | Altijd "complete" bij deze gebeurtenis |
delivery.failed
Section titled “delivery.failed”Wordt één keer per outlet afgevuurd wanneer de levering van een release definitief mislukt. Dezelfde payload als delivery.completed, plus een message-veld.
{ "event": "delivery.failed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "error", "message": "Outlet rejected the delivery: missing ISRC." }}| Veld | Type | Beschrijving |
|---|---|---|
status | string | Een van error, fault, rejected, batch_exception |
message | string | null | Reden van het mislukken vanuit de outlet of de distributiepijplijn |
takedown.completed
Section titled “takedown.completed”Wordt één keer per outlet afgevuurd wanneer een takedown-verzoek slaagt. Dezelfde vorm als delivery.completed, plus een takedown: true-vlag.
{ "event": "takedown.completed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "complete", "takedown": true }}release.distributed
Section titled “release.distributed”Wordt één keer per release afgevuurd wanneer de release overgaat naar de leveringsstatus distributed. Vuurt alleen af bij de overgang naar distributed, niet bij latere bewaaracties terwijl de release al gedistribueerd is.
{ "event": "release.distributed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "release_cat": "ABC123", "release_title": "Summer EP", "delivery_status": "distributed" }}release.review.status_changed
Section titled “release.review.status_changed”Wordt afgevuurd telkens als een release tussen reviewstatussen wisselt.
{ "event": "release.review.status_changed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "release_cat": "ABC123", "release_title": "Summer EP", "previous_status": "to_review", "new_status": "approved" }}| Veld | Type | Beschrijving |
|---|---|---|
previous_status | string | Vorige status. Een van draft, to_review, approved, rejected, require_changes, audit |
new_status | string | Nieuwe status. Dezelfde set waarden |
payment.statement_ready
Section titled “payment.statement_ready”Wordt afgevuurd wanneer een afrekening is gegenereerd en klaarstaat om te bekijken.
{ "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" }}| Veld | Type | Beschrijving |
|---|---|---|
payment_request_id | integer | Interne ID van het betaalverzoek |
invoice_number | string | Factuurreferentie van de afrekening |
period | string | null | Einddatum van de periode (ISO 8601-datum, YYYY-MM-DD) |
amount | number | Bedrag van de afrekening in currency |
total_due_usd | number | Totaal van de afrekening omgerekend naar USD |
currency | string | ISO 4217-valutacode (standaard USD) |
Webhook-handtekeningen verifiëren
Section titled “Webhook-handtekeningen verifiëren”Elke webhooklevering is ondertekend, zodat je kunt controleren of die echt van LabelGrid komt. Verifieer de handtekening altijd voordat je de gebeurtenis verwerkt.
Request-headers
Section titled “Request-headers”Elk webhook-POST-verzoek bevat deze headers:
| Header | Beschrijving |
|---|---|
X-Webhook-Signature | HMAC-SHA256 van de ruwe request-body, kleine letters hex, zonder algoritmeprefix |
X-Webhook-Timestamp | ISO 8601-tijdstempel van de levering (dezelfde waarde als de timestamp-property in de body) |
X-Webhook-Event | Gebeurtenis-identifier (bijv. delivery.completed) |
X-Webhook-Id | De ID van de webhook die de levering ontvangt |
User-Agent | LabelGrid-Webhooks/1.0 |
Content-Type | application/json |
Algoritme
Section titled “Algoritme”- Algoritme: HMAC-SHA256
- Codering: Hexadecimaal in kleine letters
- Prefix: Geen. De waarde is enkel de hex-digest, niet
sha256=... - Ondertekende inhoud: De volledige ruwe JSON request-body (de body bevat de timestamp zelf als property, dus de timestamp wordt impliciet mee-ondertekend)
Verificatierecept
Section titled “Verificatierecept”- Lees de ruwe request-body voordat je JSON parseert of transformeert. De geparseerde JSON opnieuw serialiseren kan andere bytes opleveren en de handtekening breken.
- Bereken
HMAC-SHA256(raw_body, your_webhook_secret)en neem de hex-digest in kleine letters. - Vergelijk die met
X-Webhook-Signaturemet een vergelijking in constante tijd. - (Aanbevolen) Weiger het verzoek als
X-Webhook-Timestampouder is dan je replay-tolerantievenster. We raden 5 minuten aan.
PHP-voorbeeld
Section titled “PHP-voorbeeld”$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('Invalid signature');}
// Reject deliveries older than 5 minutesif (abs(time() - strtotime($timestamp)) > 300) { http_response_code(401); exit('Stale delivery');}
$payload = json_decode($rawBody, true);// ... process the eventhttp_response_code(200);Node.js-voorbeeld
Section titled “Node.js-voorbeeld”const crypto = require('crypto');
// Express: capture raw body BEFORE any JSON middlewareapp.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('Invalid signature'); }
if (Math.abs(Date.now() - new Date(timestamp).getTime()) > 5 * 60 * 1000) { return res.status(401).send('Stale delivery'); }
const payload = JSON.parse(rawBody.toString('utf8')); // ... process the event res.sendStatus(200);});Python-voorbeeld
Section titled “Python-voorbeeld”import hmac, hashlibfrom datetime import datetime, timezone
raw_body = request.get_data() # Flask: bytes, before any JSON parsingsignature = 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 ('Invalid signature', 401)
delivery_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))if abs((datetime.now(timezone.utc) - delivery_time).total_seconds()) > 300: return ('Stale delivery', 401)
return ('', 200) # process the event, then ackVeelgemaakte fouten
Section titled “Veelgemaakte fouten”- De body opnieuw serialiseren voordat je hasht. Frameworks die JSON automatisch parsen (Express
express.json(), de standaard request-body van Laravel) verliezen de oorspronkelijke bytes. Leg eerst de ruwe body vast. - Een vergelijking gebruiken die niet in constante tijd werkt (
==,===). Die is vatbaar voor timingaanvallen. Gebruik altijdhash_equals(PHP),crypto.timingSafeEqual(Node),hmac.compare_digest(Python) of het equivalent in jouw taal. - Een
sha256=-prefix verwachten. De headerwaarde is enkel de hex-digest, zonder prefix. - De timestampcontrole overslaan. Zonder die controle kan een gelekte handtekening eindeloos worden hergebruikt.
Limieten en betrouwbaarheid
Section titled “Limieten en betrouwbaarheid”Verzoeklimieten
Section titled “Verzoeklimieten”| Limiet | Waarde |
|---|---|
| Time-out van het verzoek | 10 seconden |
| Maximale payloadgrootte | 64 KB |
| Maximaal aantal webhooks per gebruiker | 10 |
Reageert je endpoint niet binnen 10 seconden, dan geldt de levering als mislukt en wordt die opnieuw geprobeerd.
Herhaalschema
Section titled “Herhaalschema”Geeft je endpoint een status buiten de 2xx-reeks of een time-out, dan probeert LabelGrid het opnieuw met exponentiële backoff:
| Poging | Wachttijd voor herhaling |
|---|---|
| 1 → 2 | 30 seconden |
| 2 → 3 | 1 minuut |
| 3 → 4 | 2 minuten |
| 4 → 5 | 4 minuten |
| 5 → 6 | 8 minuten |
| 6 → 7 | 16 minuten |
| 7 → 8 | 32 minuten |
| 8 → 9 | 64 minuten |
| 9 → 10 | 128 minuten |
Elk interval bevat 0 tot 30 seconden jitter. Na 10 pogingen (in totaal ongeveer 4,5 uur) wordt de levering gelogd als definitief mislukt en niet meer opnieuw geprobeerd.
Automatische uitschakeling
Section titled “Automatische uitschakeling”Loopt een webhook op tot 100 mislukte leveringen in totaal, dan wordt die automatisch uitgeschakeld. Schakel hem na het herstellen van je endpoint handmatig weer in vanuit de webhooklijst. Een uitgeschakelde webhook opnieuw activeren zet de teller met mislukte leveringen op nul.
Tips voor betrouwbaarheid
Section titled “Tips voor betrouwbaarheid”- Geef snel (binnen 10 seconden) een 2xx-respons
- Verwerk de gegevens asynchroon nadat je hebt bevestigd
- Verifieer de handtekening van elk verzoek (zie Webhook-handtekeningen verifiëren)
- Houd je aantal mislukte leveringen in de webhooklijst in de gaten
- Bekijk de leveringslogs als je gemiste gebeurtenissen onderzoekt
Toepassingen
Section titled “Toepassingen”Geautomatiseerde meldingen
Section titled “Geautomatiseerde meldingen”- Stuur Slack-berichten wanneer releases live gaan
- Mail je team wanneer leveringen mislukken
- Werk interne dashboards bij
Workflowautomatisering
Section titled “Workflowautomatisering”- Start marketingcampagnes wanneer releases worden gedistribueerd
- Werk je website bij wanneer er nieuwe content beschikbaar is
- Synchroniseer de status naar externe projectmanagementtools
Monitoring en alerts
Section titled “Monitoring en alerts”- Krijg meteen meldingen bij mislukte leveringen
- Volg de distributievoortgang in realtime
- Houd wijzigingen in de reviewstatus in de gaten
Problemen oplossen
Section titled “Problemen oplossen”Webhook ontvangt geen gebeurtenissen
Section titled “Webhook ontvangt geen gebeurtenissen”- Controleer de status - Staat de webhook op actief?
- Controleer de URL - Is het endpoint bereikbaar vanaf het internet?
- Controleer de gebeurtenissen - Zijn de juiste gebeurtenissen geselecteerd?
- Bekijk de logs - Staan er fouten geregistreerd?
Veel mislukte leveringen
Section titled “Veel mislukte leveringen”- Controleer je endpoint - Geeft het 200 OK terug?
- Controleer de responstijd - Reageert het binnen de time-out?
- Bekijk de foutmeldingen - Wat gaat er mis?
- Test handmatig - Stuur een testwebhook
De secret opnieuw genereren
Section titled “De secret opnieuw genereren”Als je webhook-secret is gelekt:
- Klik op Regenerate Secret in de webhookinstellingen
- Werk je applicatie bij met de nieuwe secret
- De oude secret werkt meteen niet meer
Hulp nodig?
Section titled “Hulp nodig?”Heb je vragen over webhooks, neem dan contact op met ons supportteam.
Gebruik je LabelGrid nog niet?
Alles wat je net hebt gelezen, kun je gebruiken op ons platform.
Ontdek wat LabelGrid kan →