Zum Inhalt springen
Support

Webhooks

Webhooks ermoeglichen es Ihnen, Echtzeit-Benachrichtigungen zu erhalten, wenn Ereignisse in Ihrem LabelGrid-Konto auftreten. Verwenden Sie Webhooks, um Workflows zu automatisieren und mit externen Systemen zu integrieren.

Fuer Entwickler: Sie koennen Webhooks auch programmatisch ueber die API verwalten. Siehe die LabelGrid API-Dokumentation fuer Endpunkte und Beispiele.

  1. Sie konfigurieren einen Webhook - Geben Sie eine URL und die zu ueberwachenden Ereignisse an
  2. Ein Ereignis tritt ein - Zum Beispiel wird ein Release an einen Store geliefert
  3. LabelGrid sendet einen POST-Request - Ihr Server empfaengt die Ereignisdaten
  4. Ihr System verarbeitet es - Automatisieren Sie Workflows basierend auf dem Ereignis

  1. Klicken Sie auf Ihr Profilsymbol oben rechts
  2. Waehlen Sie Webhooks aus dem Dropdown-Menue

  1. Klicken Sie auf Create Webhook
  2. Geben Sie einen Name ein, um diesen Webhook zu identifizieren
  3. Geben Sie die URL ein, an die Sie Benachrichtigungen erhalten moechten
  4. Waehlen Sie, welche Events diesen Webhook ausloesen sollen
  5. Klicken Sie auf Create

Wenn Sie einen Webhook erstellen, erhalten Sie einen Secret Key. Verwenden Sie diesen, um zu ueberpruefen, ob eingehende Anfragen tatsaechlich von LabelGrid stammen:

  • Speichern Sie das Secret sicher
  • Ueberpruefen Sie die Signatur eingehender Anfragen
  • Falls kompromittiert, generieren Sie das Secret neu

Konfigurieren Sie Ihren Webhook fuer diese Ereignisse. Der Ereignis-Identifier ist der Wert, den Sie in der event-Eigenschaft des Payloads und im X-Webhook-Event-Header sehen werden:

Ereignis-IdentifierBeschreibung
delivery.completedAusgeloest, wenn ein Release erfolgreich an einen Store geliefert wurde
delivery.failedAusgeloest, wenn die Lieferung an einen Store fehlschlaegt
takedown.completedAusgeloest, wenn ein Takedown-Request abgeschlossen ist
release.review.status_changedAusgeloest, wenn sich der Review-Status eines Releases aendert
release.preflight.report_readyAusgeloest, wenn der Preflight-QC-Bericht fuer ein zurueckgehaltenes Release zum Abruf bereit ist
stream_radar.flag_createdAusgeloest, wenn eine Stream-Radar-Meldung erstellt wird oder eine aufgeloeste Meldung bei einer neuen Erkennung erneut geoeffnet wird
stream_radar.flag_resolvedAusgeloest, wenn eine Stream-Radar-Meldung aufgeloest wird, weil die Erkennungen aufgehoert haben
release.distributedAusgeloest, wenn ein Release verteilt wird
payment.statement_readyAusgeloest, wenn eine Zahlungsabrechnung zur Ansicht bereit ist
transcode.completedAusgeloest, wenn das Audio-Transcoding eines Tracks erfolgreich abgeschlossen wird
transcode.failedAusgeloest, wenn das Transcoding eines Tracks fehlschlaegt oder unvollstaendig endet
distribution.outlet.status_changedAusgeloest bei jedem Statuswechsel des Vertriebs pro Outlet

Sie koennen mehrere Ereignisse fuer einen einzelnen Webhook auswaehlen oder separate Webhooks fuer verschiedene Ereignistypen erstellen.

Sie koennen diese Liste auch programmatisch abrufen: GET /api/public/webhooks/event-types gibt jedes Ereignis zusammen mit einem data-Schema zurueck, das dessen Payload-Schluessel und -Typen beschreibt, sodass Consumer mit striktem Schema ihre eingehende Validierung vorab erweitern koennen.


Die Webhook-Liste zeigt:

SpalteBeschreibung
NameDer von Ihnen zugewiesene Webhook-Name
URLWohin Benachrichtigungen gesendet werden
EventsAnzahl der konfigurierten Ereignisse
StatusActive oder Inactive
Success / FailAnzahl erfolgreicher und fehlgeschlagener Zustellungen
Last TriggeredWann der Webhook zuletzt aufgerufen wurde
  1. Klicken Sie auf die Aktion Edit in der Webhook-Zeile
  2. Aendern Sie Name, URL oder Ereignisse
  3. Klicken Sie auf Save

Schalten Sie den aktiven Status eines Webhooks um, ohne ihn zu loeschen:

  • Active - Webhook empfaengt Benachrichtigungen
  • Inactive - Webhook ist pausiert, keine Benachrichtigungen werden gesendet
  1. Klicken Sie auf die Aktion Delete in der Webhook-Zeile
  2. Bestaetigen Sie die Loeschung

Bevor Sie sich in der Produktion auf einen Webhook verlassen, testen Sie ihn:

  1. Klicken Sie auf die Aktion Test bei Ihrem Webhook
  2. LabelGrid sendet einen Test-Payload an Ihre URL
  3. Ueberpruefen Sie, ob Ihr Endpunkt ihn korrekt empfangen und verarbeitet hat

Ueberwachen Sie Webhook-Aktivitaeten und beheben Sie Probleme:

  1. Klicken Sie auf die Aktion View Logs bei einem Webhook
  2. Sehen Sie den Verlauf aller Webhook-Zustellungen

Jeder Log-Eintrag zeigt:

FeldBeschreibung
Event TypeWelches Ereignis diese Zustellung ausgeloest hat
Response StatusHTTP-Statuscode von Ihrem Server
DurationWie lange die Anfrage gedauert hat
AttemptWiederholungsversuch-Nummer
TimestampWann die Zustellung erfolgte

Wenn ein Ereignis eintritt, sendet LabelGrid einen POST-Request an Ihre URL mit einem JSON-Payload:

{
"event": "delivery.completed",
"timestamp": "2026-05-05T10:00:00+00:00",
"webhook_id": "123",
"data": {
// Ereignisspezifische Daten
}
}

Das Feld timestamp verwendet das ISO-8601-Format. webhook_id ist die ID Ihres konfigurierten Webhooks (sie entspricht dem X-Webhook-Id-Header).


Die Struktur des data-Objekts haengt vom event-Typ ab. Alle Feldtypen unten sind JSON-Typen, wie sie im Payload serialisiert werden.

Wird einmal pro Outlet ausgeloest, wenn eine Release-Zustellung einen finalen Erfolgszustand erreicht.

{
"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"
}
}
FeldTypBeschreibung
distro_queue_idintegerInterne Queue-ID fuer diesen Zustellversuch
release_idintegerDie Release, die zugestellt wurde
label_idintegerDas besitzende Label der Release, sodass Sie das Ereignis ohne zusaetzlichen Lookup weiterleiten koennen
release_catstring | nullIhre Release-Katalog-Referenz
outlet_idinteger | nullDie Outlet-Ziel-ID
outlet_namestring | nullLesbarer Outlet-Name (z. B. "Spotify")
statusstringImmer "complete" fuer dieses Ereignis

Wird einmal pro Outlet ausgeloest, wenn eine Release-Zustellung einen finalen Fehlerzustand erreicht. Gleicher Payload wie delivery.completed plus ein message-Feld.

{
"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."
}
}
FeldTypBeschreibung
statusstringEiner von error, fault, rejected, batch_exception
messagestring | nullFehlergrund vom Outlet oder von der Distributions-Pipeline

Wird einmal pro Outlet ausgeloest, wenn eine Takedown-Anfrage erfolgreich ist. Gleiche Form wie delivery.completed plus ein takedown: true-Flag.

{
"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
}
}

Wird einmal pro Release ausgeloest, wenn die Release in den Zustellzustand distributed wechselt. Wird nur beim Uebergang zu distributed ausgeloest, nicht bei nachfolgenden Speicherungen, waehrend die Release bereits distributed ist.

{
"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"
}
}

Wird ausgeloest, wenn eine Release zwischen Pruefungszustaenden wechselt.

{
"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"
}
}
FeldTypBeschreibung
previous_statusstringVorheriger Status. Einer von draft, to_review, approved, rejected, require_changes, audit
new_statusstringNeuer Status. Gleicher Wertesatz
review_issuesarray (optional)Nur bei Uebergaengen zu require_changes und rejected vorhanden: die Probleme, die Ihre Aufmerksamkeit erfordern. Bei jedem anderen Uebergang fehlt der Schluessel, gehen Sie also nicht davon aus, dass er immer vorhanden ist

Wird ausgeloest, wenn der Qualitaetsbericht von Preflight QC fuer ein Release in der Wartephase vor der Pruefung zum Abruf bereit ist. Erfordert die Preflight-QC-Zusatzoption in Ihrem Konto.

{
"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 }
}
}
FeldTypBeschreibung
release_idintegerDas Release, zu dem der Bericht gehoert
label_idintegerDas besitzende Label der Release
release_catstring | nullIhre Release-Katalog-Referenz
release_titlestring | nullDer Release-Titel
generated_atstringWann die Pruefungen abgeschlossen wurden (ISO 8601). Entspricht report.generated_at des Qualitaetsbericht-Endpunkts
profileobjectDas Qualitaetsprofil, ueber das die Zaehlungen berechnet wurden: {name, version}
countsobjectNur aggregierte Zaehlungen: {blocking, informational, requires_feedback}. Die Zaehlung requires_feedback ueberschneidet sich mit den beiden anderen

Ausgeloest, wenn eine Stream-Radar-Meldung erstellt wird – entweder eine ganz neue Meldung oder eine zuvor aufgeloeste Meldung, die bei einer neuen Erkennung erneut geoeffnet wird. Erfordert die Stream-Radar-Zusatzoption in Ihrem Konto. Das Feld transition unterscheidet die beiden Faelle: published fuer eine neue Meldung, reopened fuer eine wieder aktiv gewordene Meldung.

{
"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
}
}
FeldTypBeschreibung
flag_idintegerDie stabile Kennung der Meldung; entspricht id auf den Stream-Radar-Endpunkten
dspstringDie Plattform, auf der das Muster beobachtet wurde (z. B. spotify)
isrcstringDer ISRC der betroffenen Aufnahme
release_idintegerDas Release, zu dem die Aufnahme gehoert
track_idinteger | nullDer konkrete Track, wenn der ISRC eindeutig einem Ihrer Tracks zugeordnet ist
severitystringlow, medium oder high
statusstringactive fuer dieses Ereignis
transitionstringpublished fuer eine neue Meldung, reopened, wenn eine aufgeloeste Meldung wieder aktiv geworden ist
first_detected_atstring | nullWann das Muster fuer diesen Track und diese Plattform erstmals beobachtet wurde (ISO 8601)
last_detected_atstring | nullDie juengste Erkennung (ISO 8601)
estimated_affected_streamsinteger | nullEine Schaetzung, wie viele Streams beteiligt sind
published_atstringWann Ihnen die Meldung erstmals gemeldet wurde (ISO 8601)
resolved_atstring | nullnull, solange die Meldung aktiv ist

Ausgeloest, wenn eine Stream-Radar-Meldung aufgeloest wird, weil die Erkennungen aufgehoert haben. Erfordert die Stream-Radar-Zusatzoption. Dieselben Felder wie stream_radar.flag_created (ohne transition), mit status auf resolved und gesetztem resolved_at.

{
"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"
}
}

Wird ausgeloest, wenn eine Zahlungsabrechnung generiert und zur Ansicht bereit ist.

{
"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"
}
}
FeldTypBeschreibung
payment_request_idintegerInterne Zahlungsanforderungs-ID
invoice_numberstringRechnungsreferenz fuer die Abrechnung
periodstring | nullEnde-des-Zeitraums-Datum (ISO-8601-Datum, YYYY-MM-DD)
amountnumberAbrechnungsbetrag in der Waehrung currency
total_due_usdnumberAbrechnungssumme umgerechnet in USD
currencystringISO-4217-Waehrungscode (Standard USD)

Wird ausgeloest, wenn das Audio-Transcoding eines Tracks abgeschlossen wird. transcode.completed wird bei Erfolg ausgeloest; transcode.failed wird ausgeloest, wenn das Transcoding fehlschlaegt oder unvollstaendig endet. Beide haben dieselbe Payload-Form.

{
"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" }
]
}
}
FeldTypBeschreibung
release_idintegerDas Release, zu dem der Track gehoert
label_idintegerDas besitzende Label der Release
track_idintegerDer Track, der transcodiert wurde
transcoder_queue_idintegerInterne Transcoder-Queue-ID
statusstringRoher Queue-Status: complete, error oder incomplete
status_messagestringSicherer, aufgezaehlter Grundcode: transcode_complete, transcode_error oder transcode_incomplete
filesarrayDetails pro Datei fuer den Track: {asset_type_id, status} je transcodierter Datei

Wird bei jedem Statuswechsel des Vertriebs pro Outlet ausgeloest (zum Beispiel scheduled → transcoding → batched → complete), nicht nur bei den finalen, die von delivery.completed, delivery.failed und takedown.completed abgedeckt werden. Dieses Ereignis ist absichtlich gespraechig: Abonnieren Sie es nur, wenn Sie den vollstaendigen Verlauf pro Outlet wuenschen.

{
"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"
}
}
FeldTypBeschreibung
distro_queue_idintegerInterne Queue-ID fuer diese Zustellung
release_idintegerDas Release, das vertrieben wird
label_idintegerDas besitzende Label der Release
release_catstring | nullIhre Release-Katalog-Referenz
outlet_idinteger | nullDie Outlet-Ziel-ID
outlet_namestring | nullLesbarer Outlet-Name
previous_statusstring | nullDer vorherige Status; null, wenn die Zeile keinen erkannten vorherigen Status hatte
statusstringDer neue Status

Jede Webhook-Zustellung wird signiert, damit Sie verifizieren koennen, dass sie tatsaechlich von LabelGrid stammt. Verifizieren Sie die Signatur immer, bevor Sie das Ereignis verarbeiten.

Jeder Webhook-POST-Request enthaelt diese Header:

HeaderBeschreibung
X-Webhook-SignatureHMAC-SHA256 des Roh-Request-Bodys, in Kleinbuchstaben-Hexadezimal, ohne Algorithmus-Praefix
X-Webhook-TimestampPraktische Kopie der timestamp-Eigenschaft im Body. Nicht von der Signatur abgedeckt — entscheiden Sie damit niemals, ob eine Zustellung aktuell ist.
X-Webhook-EventEreignis-Identifier (z. B. delivery.completed)
X-Webhook-IdDie ID der Webhook-Konfiguration, die die Zustellung empfängt (keine ID der einzelnen Zustellung)
User-AgentLabelGrid-Webhooks/1.0
Content-Typeapplication/json
  • Algorithmus: HMAC-SHA256
  • Encoding: Hexadezimal in Kleinbuchstaben
  • Praefix: Keiner — der Wert ist nur der Hex-Digest, nicht sha256=...
  • Signierter Inhalt: Der vollstaendige rohe JSON-Request-Body — und sonst nichts. Kein Header wird signiert.

Der Body enthält seine eigene timestamp-Eigenschaft, dieser Wert ist also durch die Signatur geschützt. Der Header X-Webhook-Timestamp ist lediglich eine Kopie davon, die der Bequemlichkeit halber mitgeschickt wird; wer eine Zustellung abfängt, kann den Header beliebig ändern, ohne die Signatur ungültig zu machen. Aktualitätsprüfungen müssen timestamp deshalb aus dem geparsten Body lesen, niemals aus dem Header.

  1. Lesen Sie den Roh-Request-Body, bevor JSON geparst oder transformiert wird. Eine Re-Serialisierung des geparsten JSON kann andere Bytes erzeugen und die Signatur ungueltig machen.
  2. Berechnen Sie HMAC-SHA256(roh_body, ihr_webhook_secret) und nehmen Sie den Hex-Digest in Kleinbuchstaben.
  3. Vergleichen Sie mit X-Webhook-Signature mithilfe eines konstanten Zeitvergleichs. Stoppen Sie hier, wenn er nicht übereinstimmt.
  4. Erst jetzt parsen Sie den Body und lehnen den Request ab, wenn dessen timestamp-Eigenschaft älter als Ihr Replay-Toleranzfenster ist — wir empfehlen 5 Minuten. Weil dieser Wert signiert ist, kann ein Angreifer ihn nicht auffrischen, um eine abgefangene Zustellung aktuell erscheinen zu lassen.

Jeder Wiederholungsversuch wird erneut mit einem neuen timestamp signiert, sodass ein 5-Minuten-Fenster niemals eine legitime Wiederholung ablehnt, wie spät im Backoff-Plan sie auch eintrifft.

$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit('Ungueltige Signatur');
}
// Erst parsen, nachdem die Bytes als authentisch nachgewiesen sind.
$payload = json_decode($rawBody, true);
// Die Aktualität stammt aus dem SIGNIERTEN Zeitstempel im Body,
// niemals aus dem Header X-Webhook-Timestamp.
if (! isset($payload['timestamp'])
|| abs(time() - strtotime($payload['timestamp'])) > 300) {
http_response_code(401);
exit('Veraltete Zustellung');
}
// ... Ereignis verarbeiten (siehe „Wiederholte Zustellungen verarbeiten“ unten)
http_response_code(200);
const crypto = require('crypto');
// Express: Roh-Body VOR jeglicher JSON-Middleware erfassen
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body; // Buffer
const signature = req.header('X-Webhook-Signature') || '';
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('Ungueltige Signatur');
}
// Erst parsen, nachdem die Bytes als authentisch nachgewiesen sind.
const payload = JSON.parse(rawBody.toString('utf8'));
// Die Aktualität stammt aus dem SIGNIERTEN Zeitstempel im Body,
// niemals aus dem Header X-Webhook-Timestamp.
const sentAt = new Date(payload.timestamp).getTime();
if (Number.isNaN(sentAt) || Math.abs(Date.now() - sentAt) > 5 * 60 * 1000) {
return res.status(401).send('Veraltete Zustellung');
}
// ... Ereignis verarbeiten (siehe „Wiederholte Zustellungen verarbeiten“ unten)
res.sendStatus(200);
});
import hmac, hashlib, json
from datetime import datetime, timezone
raw_body = request.get_data() # Flask: bytes, vor jeglichem JSON-Parsing
signature = request.headers.get('X-Webhook-Signature', '')
expected = hmac.new(
webhook_secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
return ('Ungueltige Signatur', 401)
payload = json.loads(raw_body) # erst parsen, wenn die Bytes echt sind
delivery_time = datetime.fromisoformat(payload['timestamp']) # der SIGNIERTE Wert, nie der Header
if abs((datetime.now(timezone.utc) - delivery_time).total_seconds()) > 300:
return ('Veraltete Zustellung', 401)
return ('', 200) # Ereignis verarbeiten, dann bestaetigen
  • Aktualitätsprüfung anhand des Headers X-Webhook-Timestamp. Der Header wird nicht signiert. Wer eine Zustellung abfängt, kann denselben Body mit derselben Signatur und einem frischen Header-Wert erneut senden und eine header-basierte Prüfung dauerhaft bestehen. Lesen Sie timestamp stattdessen aus dem geparsten Body — dieser Wert ist signiert.
  • Re-Serialisieren des Bodys vor dem Hashing. Frameworks, die JSON automatisch parsen (Express express.json(), Laravels Standard-Request-Body), verlieren die Original-Bytes. Erfassen Sie zuerst den Roh-Body.
  • Verwenden eines nicht zeitkonstanten Vergleichs (==, ===). Anfaellig fuer Timing-Angriffe — verwenden Sie immer hash_equals (PHP), crypto.timingSafeEqual (Node), hmac.compare_digest (Python) oder das Aequivalent Ihrer Sprache.
  • Erwarten eines sha256=-Praefix. Der Header-Wert ist nur der Hex-Digest ohne Praefix.
  • Überspringen der Aktualitätsprüfung. Ohne sie kann eine abgefangene Zustellung unbegrenzt gegen Ihren Endpunkt wiederholt werden.
  • X-Webhook-Id als Zustellungs-ID betrachten. Sie identifiziert die Webhook-Konfiguration, nicht die einzelne Zustellung, und ist ebenfalls nicht signiert.

Die Webhook-Zustellung erfolgt mindestens einmal: Eine Zustellung, die Ihr Endpunkt tatsächlich verarbeitet hat, kann erneut eintreffen, wenn Ihre 2xx-Antwort verloren ging oder erst nach dem 10-Sekunden-Timeout ankam und LabelGrid sie daraufhin wiederholt. Die Signaturprüfung belegt, dass eine Anfrage echt ist — sie belegt nicht, dass Sie sie noch nicht verarbeitet haben.

Zustellungen tragen keine eindeutige ID pro Zustellung; bilden Sie deshalb einen eigenen Idempotenzschlüssel aus dem signierten Payload. Der Ereignistyp plus die Bezeichner in data genügen in der Regel — zum Beispiel delivery.completed plus distro_queue_id oder transcode.completed plus track_id. Halten Sie den Schlüssel fest, wenn Sie ein Ereignis verarbeiten, und ignorieren Sie alles, was Sie bereits festgehalten haben.

Verwenden Sie dafür weder die Signatur noch den timestamp. Jeder Versuch wird im Moment des Versands neu signiert, sodass die Wiederholung eines bereits verarbeiteten Ereignisses mit einem anderen timestamp und einer anderen Signatur eintrifft — der Schlüssel muss aus den Bezeichnern des Ereignisses selbst stammen.

Kombinieren Sie das mit der Aktualitätsprüfung oben: Die Aktualität begrenzt, wie lange eine abgefangene Zustellung wiederholbar bleibt, und Idempotenz macht eine Wiederholung harmlos — ob sie nun von einem Wiederholungsversuch oder von einem Angreifer innerhalb des Fensters stammt.


LimitWert
Request-Timeout10 Sekunden
Maximale Payload-Groesse64 KB
Maximale Anzahl Webhooks pro Benutzer10

Wenn Ihr Endpunkt nicht innerhalb von 10 Sekunden antwortet, wird die Zustellung als Fehler behandelt und wiederholt.

Wenn Ihr Endpunkt einen Non-2xx-Status zurueckgibt oder einen Timeout hat, wiederholt LabelGrid mit exponentiellem Backoff:

VersuchWartezeit vor Wiederholung
1 → 230 Sekunden
2 → 31 Minute
3 → 42 Minuten
4 → 54 Minuten
5 → 68 Minuten
6 → 716 Minuten
7 → 832 Minuten
8 → 964 Minuten
9 → 10128 Minuten

Jedes Intervall enthaelt 0–30 Sekunden Jitter. Nach 10 Versuchen (~4,5 Stunden Gesamtdauer) wird die Zustellung als dauerhaft fehlgeschlagen protokolliert und nicht weiter wiederholt.

Wenn der Endpunkt eines Webhooks wiederholt fehlschlaegt — mehrere fehlgeschlagene Zustellungen ohne eine einzige erfolgreiche dazwischen —, deaktiviert LabelGrid den Webhook automatisch, um einen Endpunkt nicht weiter anzusteuern, der offensichtlich keine Ereignisse empfangen kann. Der Fehlerzaehler wird bei jeder erfolgreichen Zustellung zurueckgesetzt, sodass ein gelegentlicher Aussetzer einen Webhook nie deaktiviert; nur anhaltendes, ununterbrochenes Fehlschlagen tut dies.

Wenn ein Webhook auf diese Weise deaktiviert wird, erhaelt sein Eigentuemer eine E-Mail. Die E-Mail nennt den Namen des Webhooks und die URL seines Endpunkts sowie die Art des Fehlers, der die Deaktivierung ausgeloest hat — zum Beispiel eine Zeitueberschreitung der Verbindung oder wiederholte HTTP-Fehler.

Die Reaktivierung erfolgt im Selfservice: Beheben Sie Ihren Endpunkt und schalten Sie den Webhook anschliessend unter Profil → Webhooks wieder ein. Die Reaktivierung eines deaktivierten Webhooks setzt seinen Fehlerzaehler zurueck. Die Webhook-Liste zeigt den aktiven Status und den aktuellen Fehlerzaehler jedes Webhooks, sodass Sie einen fehlerhaften Endpunkt auf einen Blick erkennen.

  • Geben Sie schnell eine 2xx-Antwort zurueck (innerhalb von 10 Sekunden)
  • Verarbeiten Sie die Daten asynchron nach der Bestaetigung
  • Verifizieren Sie die Signatur bei jeder Anfrage (siehe Webhook-Signaturen verifizieren)
  • Machen Sie Ihren Handler idempotent — die Zustellung erfolgt mindestens einmal (siehe Wiederholte Zustellungen verarbeiten)
  • Ueberwachen Sie Ihren Fehlerzaehler in der Webhook-Liste
  • Pruefen Sie die Zustell-Logs, wenn Sie verpasste Ereignisse untersuchen

  • Slack-Nachrichten senden, wenn Releases live gehen
  • Ihr Team per E-Mail benachrichtigen, wenn Lieferungen fehlschlagen
  • Interne Dashboards aktualisieren
  • Marketing-Kampagnen ausloesen, wenn Releases verteilt werden
  • Ihre Website aktualisieren, wenn neue Inhalte verfuegbar sind
  • Status mit externen Projektmanagement-Tools synchronisieren
  • Sofortige Benachrichtigungen bei Lieferfehlern erhalten
  • Vertriebsfortschritt in Echtzeit verfolgen
  • Review-Status-Aenderungen ueberwachen

  1. Status pruefen - Ist der Webhook Active?
  2. URL verifizieren - Ist der Endpunkt aus dem Internet erreichbar?
  3. Ereignisse pruefen - Sind die richtigen Ereignisse ausgewaehlt?
  4. Logs ueberpruefen - Sind Fehler aufgezeichnet?
  1. Ihren Endpunkt pruefen - Gibt er 200 OK zurueck?
  2. Antwortzeit pruefen - Antwortet er innerhalb des Timeouts?
  3. Fehlermeldungen ueberpruefen - Was schlaegt fehl?
  4. Manuell testen - Einen Test-Webhook senden

Wenn Ihr Webhook-Secret kompromittiert ist:

  1. Klicken Sie auf Regenerate Secret in den Webhook-Einstellungen
  2. Aktualisieren Sie Ihre Anwendung mit dem neuen Secret
  3. Das alte Secret funktioniert sofort nicht mehr

Wenn Sie Fragen zu Webhooks haben, kontaktieren Sie unser Support-Team.

Sie nutzen LabelGrid noch nicht?

Alles, was Sie gerade gelesen haben, steht Ihnen auf unserer Plattform zur Verfügung.

Entdecken Sie, was LabelGrid kann →