Webhooks
Erhalten Sie signierte HTTP-Benachrichtigungen, wenn sich Links, Kampagnen und Beiträge in Ihrem Arbeitsbereich ändern.
Styrar-Webhooks senden signierte HTTP-Benachrichtigungen, wenn sich Ressourcen in Ihrem Arbeitsbereich ändern. Nutzen Sie sie, um Links und Klicks mit einem CRM zu synchronisieren, Automatisierungen in Ihrem Stack auszulösen oder Funnel-Ereignisse in ein Data Warehouse zu streamen.
Dies ersetzt die abgeschaltete, nur für Links gedachte Webhooks-API (/v1/link-webhooks). Neue Integrationen sollten ausschließlich /v1/webhooks/* verwenden.
Webhook einrichten
Folgen Sie diesen Schritten, um von null zu einem verifizierten, produktionsreifen Endpunkt zu kommen.
1. Empfänger-URL vorbereiten
Ihr Server benötigt eine öffentliche HTTPS-URL, die POST-Anfragen mit einem JSON-Body akzeptiert. In der Produktion müssen Callback-URLs HTTPS verwenden.
Ihr Handler sollte:
- Den rohen Request-Body als String oder Buffer lesen (JSON erst nach Verifizierung der Signatur parsen).
- Die Header
Styrar-Signature,Styrar-Event-IdundStyrar-Event-Typelesen. - Die Signatur verifizieren (siehe Abschnitt Signaturen verifizieren unten).
- Schnell mit 2xx antworten, sobald das Ereignis akzeptiert wurde. Aufwändige Arbeit asynchron in eine Warteschlange stellen.
Beispielpfad: https://your-app.com/webhooks/styrar
Styrar kann nicht an private oder Loopback-Adressen wie localhost liefern.
2. API-Zugriff erhalten (für die programmatische Einrichtung)
Wenn Sie Endpunkte über die API statt über das Dashboard erstellen, benötigen Sie ein Bearer-Token mit:
| Scope | Zugriff |
|---|---|
webhooks:read | Endpunkte, Zustellungen und Ereignistypen auflisten |
webhooks:write | Erstellen, aktualisieren, löschen, testen, Secret rotieren, Zustellungen wiederholen |
Erstellen Sie einen API-Schlüssel unter Einstellungen → API-Schlüssel mit webhooks:read und webhooks:write, oder verwenden Sie ein OAuth-Access-Token einer verbundenen App mit denselben Scopes.
Für Unternehmens-Arbeitsbereiche übergeben Sie companyId bei List- und Create-Anfragen.
3. Endpunkt im Dashboard erstellen
- Anmelden und Einstellungen → Webhooks öffnen (
/settings/webhooks). - Unter Endpoints Ihre Endpunkt-URL eingeben (der öffentliche HTTPS-Callback).
- Optional eine Beschreibung hinzufügen (zum Beispiel „Produktions-CRM-Sync").
- Die gewünschten Ereignisse auswählen (mindestens eines ist erforderlich). Übliche Wahl:
link.clickedfür Klick-Trackinglink.created/link.updated/link.deletedfür den Link-Lebenszykluspost.publishedundcampaign.createdfür Funnel-Ereignisse
- Auf Add endpoint klicken.
Nach dem Erstellen zeigt Styrar Ihr Signing Secret (whsec_live_… oder whsec_test_…). Kopieren Sie es sofort und speichern Sie es in Ihrem Secrets-Manager. Es wird nur einmal angezeigt. Falls Sie es verlieren, nutzen Sie Rotate secret am Endpunkt und aktualisieren Sie Ihren Server.
4. Endpunkt über die API erstellen
Authorization: Bearer sty_live_your_api_key
Content-Type: application/json
{
"companyId": "cmxyz_company",
"url": "https://your-app.com/webhooks/styrar",
"description": "Production CRM sync",
"eventTypes": [
"link.clicked",
"link.created"
]
}{
"id": "wh_ep_abc123",
"url": "https://your-app.com/webhooks/styrar",
"description": "Production CRM sync",
"enabled": true,
"eventTypes": [
"link.clicked",
"link.created"
],
"secretPrefix": "whsec_l",
"companyId": "cmxyz_company",
"userId": null,
"disabledAt": null,
"createdAt": "2026-06-22T12:00:00.000Z",
"updatedAt": "2026-06-22T12:00:00.000Z",
"secret": "whsec_live_abc123onlyShownOnce"
}Speichern Sie secret sofort. Er wird bei späteren GET-Anfragen nicht erneut zurückgegeben. Ein leeres eventTypes-Array abonniert alle Ereignistypen; das Dashboard verlangt mindestens ein explizites Ereignis.
5. Testereignis senden
Bestätigen Sie, dass Ihr Server erreichbar ist, bevor Sie sich auf Live-Traffic verlassen.
Dashboard: Klicken Sie in der Endpunkt-Zeile auf Send test. Styrar liefert ein webhook.test-Ereignis. Sie sollten eine Erfolgsmeldung mit dem HTTP-Statuscode sehen.
API:
Authorization: Bearer sty_live_your_api_key{
"ok": true,
"statusCode": 200
}Wenn der Test erfolgreich ist, sollte Ihr Server ein webhook.test-Ereignis erhalten haben (siehe Ein Ereignis auf Ihrem Server empfangen unten).
Prüfen Sie Recent deliveries im Dashboard (oder GET /v1/webhooks/deliveries), wenn der Test fehlschlägt. Häufige Probleme:
| Problem | Was zu prüfen ist |
|---|---|
| Connection refused / Timeout | URL ist öffentlich per HTTPS, Firewall erlaubt Styrar, Tunnel läuft |
| HTTP 4xx/5xx von Ihrer App | Handler antwortet nach Verifizierung mit 2xx; rohen Body und Header loggen |
| Signaturverifizierung schlägt fehl | Rohen Body, korrektes Secret, zeitkonstanten Vergleich verwenden |
| Endpunkt deaktiviert | Im Dashboard wieder aktivieren oder die Ursache für HTTP 410 beheben |
6. Zustellungen verifizieren und verarbeiten
Bei jedem POST an Ihre URL:
Styrar-Signaturein Zeitstempeltund Digestv1zerlegen.- Ablehnen, wenn
tälter als Ihre Toleranz ist (5 Minuten empfohlen). - HMAC-SHA256 von
"{t}.{raw_body}"mit Ihrem Endpunkt-Secret berechnen. - Mit
v1mittels zeitkonstantem Vergleich abgleichen. - JSON erst nach erfolgreicher Verifizierung parsen.
- Auf
Styrar-Event-Iddeduplizieren (At-least-once-Zustellung).
Siehe Abschnitt Signaturen verifizieren unten für ein Node.js-Beispiel.
7. Live gehen
- Den Endpunkt im Dashboard aktiviert lassen.
- Nur die Ereignisse abonnieren, die Sie wirklich brauchen (reduziert Rauschen und Kosten).
- Recent deliveries auf Fehler überwachen und bei Bedarf Retry nutzen.
- Das Secret rotieren, falls es geleakt sein könnte (Rotate secret im Dashboard oder
POST …/rotate-secret).
Schnellstart
- API-Zugriff erhalten mit den Scopes
webhooks:readundwebhooks:write(API-Schlüssel oder OAuth). - Öffentlichen HTTPS-Empfänger exponieren und einen Endpunkt unter Einstellungen → Webhooks oder via
POST /v1/webhooks/endpointserstellen. - Signing Secret kopieren, sobald es angezeigt wird, und sicher speichern.
- Testereignis senden und bestätigen, dass Ihr Server
Styrar-Signatureverifiziert und mit 2xx antwortet. - Live-Ereignisse verarbeiten mit Deduplizierung anhand von
Styrar-Event-Id.
Authentifizierung
Die Webhook-Verwaltung erfordert ein Bearer-Token mit den oben in Schritt 2 genannten Scopes. API-Schlüssel mit passenden Scopes funktionieren genauso wie OAuth-Access-Tokens.
Ereignistypen
Rufen Sie GET /v1/webhooks/event-types für den aktuellen Katalog in Ihrer Umgebung auf. Abonnieren Sie pro Endpunkt bestimmte Typen, oder übergeben Sie ein leeres eventTypes-Array, um alle Ereignisse zu erhalten.
webhook.test wird nur gesendet, wenn Sie auf Send test klicken; es wird nicht durch normale Arbeitsbereich-Aktivität ausgelöst.
Ereignis-Umschlag (Envelope)
Jede Zustellung verwendet dieselbe übergeordnete Form. Ereignisspezifische Felder liegen unter data.object:
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Eindeutige Ereignis-ID. Gleicher Wert wie der Header Styrar-Event-Id. |
type | string | Ereignisname (zum Beispiel link.clicked). |
api_version | string | Payload-Schemaversion (2026-06-22). |
created_at | string | ISO-8601-Zeitstempel zum Erstellungszeitpunkt des Ereignisses. |
workspace.type | string | company oder personal. |
workspace.company_id | string | null | Unternehmens-ID, wenn workspace.type gleich company ist. |
data.object | object | Ereignis-Payload (unten pro Ereignis dokumentiert). |
Vollständiges Zustellungsbeispiel (Envelope + link.created-Payload):
{
"id": "evt_outbox_abc123",
"type": "link.created",
"api_version": "2026-06-22",
"created_at": "2026-06-22T12:00:00.000Z",
"workspace": {
"type": "company",
"company_id": "cmxyz_company"
},
"data": {
"object": {
"id": "clink_abc123",
"short_code": "summer",
"title": "Summer sale",
"original_url": "https://example.com/sale",
"is_active": true,
"company_id": "cmxyz_company",
"campaign_id": "ccamp_abc",
"link_group_id": null,
"created_at": "2026-06-22T12:00:00.000Z"
}
}
}
Für persönliche Arbeitsbereiche ist workspace.type gleich personal und workspace.company_id ist null. Die Payload kann company_id: null bei Link-Objekten enthalten.
Ereignisübersicht
| Ereignis | Wann es ausgelöst wird | Typisches Volumen |
|---|---|---|
link.created | Ein Kurzlink wird erstellt (POST /v1/links, Bulk-Erstellung oder Bulk-Upsert) | Niedrig |
link.updated | Ein Kurzlink wird aktualisiert (PATCH /v1/links/:id, Bulk-Update oder Bulk-Upsert) | Niedrig |
link.deleted | Ein Kurzlink wird soft-gelöscht (DELETE /v1/links/:id oder Bulk-Löschung) | Niedrig |
link.clicked | Ein nachverfolgter Klick wird erfasst (asynchron, nach dem Redirect über den Click-Worker) | Hoch |
link.experiment.completed | Ein A/B-Link-Experiment ist abgeschlossen und ein Gewinner wurde ausgewählt | Niedrig |
campaign.created | Eine Kampagne wird erstellt (POST /v1/campaigns) | Niedrig |
post.published | Ein Social-Beitrag wird erfolgreich auf allen Plattform-Zielen veröffentlicht | Niedrig |
webhook.test | Sie führen Send test an einem Endpunkt aus | Bei Bedarf |
link.created
Wird ausgelöst, wenn ein neuer Kurzlink in Ihrem Arbeitsbereich gespeichert wird.
Übliche Anwendungen: Neue Links mit einem CRM synchronisieren, Willkommens-Automatisierungen auslösen oder Links in einem externen Suchindex erfassen.
data.object-Felder:
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Link-ID |
short_code | string | Öffentlicher Kurzcode |
title | string | null | Link-Titel |
original_url | string | null | Ziel-URL |
is_active | boolean | Ob der Link aktiv ist |
company_id | string | null | Besitzendes Unternehmen (Unternehmens-Arbeitsbereich) |
campaign_id | string | null | Verknüpfte Kampagne, falls vorhanden |
link_group_id | string | null | Ordner-/Gruppen-ID, falls vorhanden |
created_at | string | ISO-8601-Erstellungszeit |
{
"id": "clink_abc123",
"short_code": "summer",
"title": "Summer sale",
"original_url": "https://example.com/sale",
"is_active": true,
"company_id": "cmxyz_company",
"campaign_id": "ccamp_abc",
"link_group_id": null,
"created_at": "2026-06-22T12:00:00.000Z"
}
link.updated
Wird ausgelöst, wenn ein vorhandener Kurzlink geändert wird (Ziel, Titel, Tags, Ordner, UTM-Einstellungen usw.).
Übliche Anwendungen: Externe Systeme synchron halten, wenn sich ein Link ändert, ohne ihn neu zu erstellen.
data.object-Felder: Wie bei link.created (siehe oben).
{
"id": "clink_abc123",
"short_code": "summer",
"title": "Summer sale (updated)",
"original_url": "https://example.com/sale-v2",
"is_active": true,
"company_id": "cmxyz_company",
"campaign_id": "ccamp_abc",
"link_group_id": "lgrp_promos",
"created_at": "2026-06-22T12:00:00.000Z"
}
link.deleted
Wird ausgelöst, wenn ein Kurzlink soft-gelöscht wird (isActive auf false gesetzt). Der Link löst sich öffentlich nicht mehr auf, bleibt aber im Verlauf Ihres Arbeitsbereichs erhalten.
Übliche Anwendungen: Links aus externen Katalogen entfernen oder Automatisierungen pausieren, die an diesen Kurzcode gebunden sind.
data.object-Felder: Gleiche Form wie link.created, wobei is_active immer false ist.
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Link-ID |
short_code | string | Kurzcode zum Zeitpunkt der Löschung |
title | string | null | Link-Titel |
original_url | string | null | Ziel-URL |
is_active | boolean | false nach Soft-Delete |
company_id | string | null | Besitzendes Unternehmen |
campaign_id | string | null | Verknüpfte Kampagne, falls vorhanden |
link_group_id | string | null | Ordner-/Gruppen-ID, falls vorhanden |
created_at | string | Ursprüngliche Erstellungszeit |
{
"id": "clink_abc123",
"short_code": "summer",
"title": "Summer sale",
"original_url": "https://example.com/sale",
"is_active": false,
"company_id": "cmxyz_company",
"campaign_id": "ccamp_abc",
"link_group_id": null,
"created_at": "2026-06-22T12:00:00.000Z"
}
link.clicked
Wird ausgelöst, nachdem ein Besucherklick gespeichert wurde. Wird asynchron zugestellt (nicht auf dem Redirect-Pfad), daher ist bei hoher Last eine leichte Verzögerung zu erwarten.
Übliche Anwendungen: Echtzeit-Klickanalysen, geobasiertes Routing in Ihrem Stack, CRM-Aktivitätsfeeds oder Warehouse-Ingestion.
Datenschutz: Payloads enthalten Geo- und Geräte-Dimensionen, keine rohen IP-Adressen.
data.object-Felder:
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Link-ID |
link_id | string | Gleicher Wert wie id |
short_code | string | Kurzcode, der angeklickt wurde |
title | string | null | Link-Titel |
country | string | null | ISO-Ländercode (Geo-Lookup) |
city | string | null | Stadt aus Geo-Lookup |
device | string | null | Geräteklasse (zum Beispiel mobile, desktop) |
browser | string | null | Browsername |
os | string | null | Betriebssystem |
platform | string | null | Plattform-Hinweis aus dem Parsen des User-Agents |
referrer | string | null | Referrer-URL, sofern verfügbar |
routing_rule_id | string | null | Übereinstimmende Geo-/Geräte-Routing-Regel, falls vorhanden |
experiment_variant_id | string | null | A/B-Varianten-ID, wenn der Klick durch ein Experiment lief |
clicked_at | string | ISO-8601-Klickzeit |
{
"id": "clink_abc123",
"link_id": "clink_abc123",
"short_code": "summer",
"title": "Summer sale",
"country": "NL",
"city": "Amsterdam",
"device": "mobile",
"browser": "Chrome",
"os": "iOS",
"platform": null,
"referrer": "https://instagram.com/",
"routing_rule_id": null,
"experiment_variant_id": null,
"clicked_at": "2026-06-22T12:00:00.000Z"
}
link.experiment.completed
Wird ausgelöst, wenn ein A/B-Test an einem Kurzlink als abgeschlossen markiert wird und eine Gewinner-Variante ausgewählt wurde.
Übliche Anwendungen: Experiment-Ergebnisse protokollieren, Dashboards aktualisieren oder Folgekampagnen basierend auf dem Gewinner auslösen.
data.object-Felder:
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Experiment-ID |
link_id | string | Übergeordnete Link-ID |
short_code | string | null | Kurzcode des übergeordneten Links |
status | string | completed |
assignment_mode | string | null | Wie der Traffic aufgeteilt wurde (zum Beispiel gewichtet) |
winning_variant_id | string | null | ID der Gewinner-Variante |
ended_at | string | null | ISO-8601-Abschlusszeit |
variants | array | Ergebnisse pro Variante (siehe unten) |
Jeder Eintrag in variants:
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Varianten-ID |
label | string | Menschenlesbares Label |
weight | number | Im Test verwendetes Traffic-Gewicht |
destination_url | string | URL, an die diese Variante Traffic geschickt hat |
clicks | number | Insgesamt für diese Variante erfasste Klicks |
{
"id": "lexp_abc123",
"link_id": "clink_abc123",
"short_code": "summer",
"status": "completed",
"assignment_mode": "weighted",
"winning_variant_id": "lvar_b",
"ended_at": "2026-06-22T14:00:00.000Z",
"variants": [
{
"id": "lvar_a",
"label": "Control",
"weight": 50,
"destination_url": "https://example.com/a",
"clicks": 120
},
{
"id": "lvar_b",
"label": "Variant B",
"weight": 50,
"destination_url": "https://example.com/b",
"clicks": 184
}
]
}
campaign.created
Wird ausgelöst, wenn eine neue Kampagne in Ihrem Arbeitsbereich erstellt wird.
Übliche Anwendungen: Kampagnenordner in externen Tools anlegen, Attributions-Workflows starten oder Account-Manager benachrichtigen.
data.object-Felder:
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Kampagnen-ID |
name | string | Kampagnenname |
description | string | null | Kampagnenbeschreibung |
company_id | string | null | Besitzendes Unternehmen |
user_id | string | null | Erstellender Nutzer (persönliche Kampagnen) |
brand_id | string | null | Marken-Scope, falls gesetzt |
created_at | string | ISO-8601-Erstellungszeit |
{
"id": "ccamp_abc123",
"name": "Summer launch",
"description": "Q2 product push",
"company_id": "cmxyz_company",
"user_id": null,
"brand_id": "cbrand_abc",
"created_at": "2026-06-22T12:00:00.000Z"
}
post.published
Wird ausgelöst, wenn ein geplanter oder sofort veröffentlichter Beitrag erfolgreich auf allen konfigurierten Plattform-Zielen erscheint.
Übliche Anwendungen: Nachgelagerte Systeme benachrichtigen, dass Inhalte live sind, Kampagnen-Automatisierungen abschließen oder den Veröffentlichungsstatus in ein Data Warehouse synchronisieren.
Hinweis: Wird nur ausgelöst, wenn jedes Ziel erfolgreich ist. Teilweise Fehlschläge lösen dieses Ereignis derzeit nicht aus.
data.object-Felder:
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Beitrags-ID |
post_id | string | Gleicher Wert wie id |
status | string | PUBLISHED |
company_id | string | null | Besitzendes Unternehmen |
user_id | string | null | Besitzender Nutzer (persönlicher Arbeitsbereich) |
brand_id | string | null | Marken-Scope, falls gesetzt |
campaign_id | string | null | Verknüpfte Kampagne, falls gesetzt |
published_at | string | ISO-8601-Veröffentlichungszeit |
platform_count | number | Anzahl der Plattform-Ziele des Beitrags |
{
"id": "cpost_abc123",
"post_id": "cpost_abc123",
"status": "PUBLISHED",
"company_id": "cmxyz_company",
"user_id": null,
"brand_id": "cbrand_abc",
"campaign_id": "ccamp_abc123",
"published_at": "2026-06-22T12:00:00.000Z",
"platform_count": 3
}
webhook.test
Synthetischer Ping, der nur gesendet wird, wenn Sie im Dashboard auf Send test klicken oder POST /v1/webhooks/endpoints/:id/test aufrufen.
Übliche Anwendungen: Ihre Empfänger-URL, TLS, Signaturverifizierung und Antwortverarbeitung validieren, bevor Sie live gehen.
data.object-Felder:
| Feld | Typ | Beschreibung |
|---|---|---|
endpoint_id | string | Endpunkt, der den Test erhalten hat |
message | string | Feste Nachricht: Styrar webhook test ping |
{
"endpoint_id": "wh_ep_abc123",
"message": "Styrar webhook test ping"
}
API-Beispiele
Endpunkte auflisten
Authorization: Bearer sty_live_your_api_key[
{
"id": "wh_ep_abc123",
"url": "https://your-app.com/webhooks/styrar",
"description": "Production CRM sync",
"enabled": true,
"eventTypes": ["link.clicked", "link.created"],
"secretPrefix": "whsec_l",
"companyId": "cmxyz_company",
"userId": null,
"disabledAt": null,
"createdAt": "2026-06-22T12:00:00.000Z",
"updatedAt": "2026-06-22T12:00:00.000Z"
}
]Ein Ereignis auf Ihrem Server empfangen
Wenn ein abonniertes Ereignis ausgelöst wird, sendet Styrar einen POST an Ihre Endpunkt-URL. Dies ist die Payload, die Ihr Handler empfängt:
Content-Type: application/json
Styrar-Event-Id: evt_outbox_abc123
Styrar-Event-Type: link.clicked
Styrar-Signature: t=1719052800,v1=8f4b2c1a9e0d3f7b6a5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3
User-Agent: Styrar-Webhooks/1.0
{
"id": "evt_outbox_abc123",
"type": "link.clicked",
"api_version": "2026-06-22",
"created_at": "2026-06-22T12:00:00.000Z",
"workspace": {
"type": "company",
"company_id": "cmxyz_company"
},
"data": {
"object": {
"id": "clink_abc123",
"link_id": "clink_abc123",
"short_code": "summer",
"title": "Summer sale",
"country": "NL",
"city": "Amsterdam",
"device": "mobile",
"browser": "Chrome",
"os": "iOS",
"referrer": "https://instagram.com/",
"routing_rule_id": null,
"experiment_variant_id": null,
"clicked_at": "2026-06-22T12:00:00.000Z"
}
}
}{
"received": true
}Ihr Antwort-Body kann leer sein. Geben Sie einen beliebigen 2xx-Status zurück, nachdem Sie die Signatur verifiziert und das Ereignis akzeptiert haben. Styrar wiederholt die Zustellung bei Nicht-2xx-Antworten.
Test-Ping-Payload
Eine Send test-Aktion liefert webhook.test mit einer kleineren Payload:
Content-Type: application/json
Styrar-Event-Id: evt_test_abc123
Styrar-Event-Type: webhook.test
Styrar-Signature: t=1719052800,v1=...
{
"id": "evt_test_abc123",
"type": "webhook.test",
"api_version": "2026-06-22",
"created_at": "2026-06-22T12:00:00.000Z",
"workspace": {
"type": "company",
"company_id": "cmxyz_company"
},
"data": {
"object": {
"endpoint_id": "wh_ep_abc123",
"message": "Styrar webhook test ping"
}
}
}Letzte Zustellungen auflisten
Authorization: Bearer sty_live_your_api_key[
{
"id": "wh_del_abc123",
"endpointId": "wh_ep_abc123",
"outboxEventId": "wh_out_abc123",
"eventType": "link.clicked",
"status": "delivered",
"attemptCount": 1,
"lastStatusCode": 200,
"lastError": null,
"durationMs": 142,
"nextAttemptAt": null,
"deliveredAt": "2026-06-22T12:00:01.000Z",
"createdAt": "2026-06-22T12:00:00.500Z"
}
]Endpunkte verwalten
GET /v1/webhooks/endpoints?companyId={id}
POST /v1/webhooks/endpoints
GET /v1/webhooks/endpoints/:id
PATCH /v1/webhooks/endpoints/:id
DELETE /v1/webhooks/endpoints/:id
POST /v1/webhooks/endpoints/:id/test
POST /v1/webhooks/endpoints/:id/rotate-secret
| Feld | Beschreibung |
|---|---|
url | HTTPS-Callback-URL |
description | Optionales Label |
eventTypes | Array der abonnierten Ereignistypen; leer bedeutet alle Ereignisse |
enabled | Standard true |
Das Signing Secret (whsec_live_… / whsec_test_…) wird genau einmal bei der Erstellung und bei rotate-secret zurückgegeben. Sie können bis zu 25 Endpunkte pro Arbeitsbereich-Scope registrieren.
Zustellungen
GET /v1/webhooks/deliveries?endpointId={id}
GET /v1/webhooks/deliveries/:id
POST /v1/webhooks/deliveries/:id/retry
Jede Zustellungszeile verfolgt status, attemptCount, nextAttemptAt, lastStatusCode, lastError und deliveredAt. Fehlschläge im Dashboard unter Einstellungen → Webhooks prüfen oder über die API wiederholen.
Zustellformat
Jedes Live-Ereignis ist ein HTTP-POST an Ihre Callback-URL. Header und Body sind oben unter Ein Ereignis auf Ihrem Server empfangen dargestellt.
| Header | Beschreibung |
|---|---|
Styrar-Event-Id | Eindeutige Ereignis-ID (zur Deduplizierung verwenden) |
Styrar-Event-Type | z. B. link.clicked |
Styrar-Signature | t={unix_ts},v1={hmac_sha256_hex} |
User-Agent | Styrar-Webhooks/1.0 |
Envelope-Felder:
| Feld | Beschreibung |
|---|---|
id | Ereignis-ID (entspricht Styrar-Event-Id) |
type | Ereignistyp-String |
api_version | Payload-Schemaversion |
created_at | ISO-8601-Zeitstempel |
workspace | Scope company oder personal |
data.object | Ereignisspezifische Payload |
Klick-Payloads enthalten Geo- und Geräte-Dimensionen, keine rohen IP-Adressen.
Signaturen verifizieren
Die Signatur folgt einem Stripe-ähnlichen Schema. Sie signiert "{timestamp}.{raw_body}" mit HMAC-SHA256 unter Verwendung Ihres Endpunkt-Secrets.
Styrar-Signatureint(Unix-Zeitstempel) undv1(Hex-Digest) zerlegen.- Ablehnen, wenn
taußerhalb Ihrer Toleranz liegt (wir empfehlen 5 Minuten). - Den erwarteten Digest berechnen und mit
v1mittels zeitkonstantem Vergleich abgleichen.
import crypto from 'node:crypto'
function verifyStyrarWebhook(secret, signatureHeader, rawBody, toleranceSeconds = 300) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((part) => part.trim().split('=')),
)
const timestamp = Number(parts.t)
const receivedDigest = parts.v1 || ''
if (!Number.isFinite(timestamp)) return false
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false
const expectedDigest = crypto
.createHmac('sha256', secret)
.update(\`${timestamp}.${rawBody}\`)
.digest('hex')
try {
return crypto.timingSafeEqual(
Buffer.from(expectedDigest, 'utf8'),
Buffer.from(receivedDigest, 'utf8'),
)
} catch {
return false
}
}
Immer gegen den rohen Request-Body verifizieren, bevor JSON geparst wird.
Zuverlässigkeit
- Zustellungen erfolgen at-least-once. Mit
Styrar-Event-Idoder der Envelope-iddeduplizieren. - Fehlgeschlagene Zustellungen werden mit exponentiellem Backoff wiederholt, bis sie erfolgreich sind oder die maximale Anzahl an Versuchen erreicht ist.
- Wenn Ihr Server HTTP 410 zurückgibt, deaktiviert Styrar den Endpunkt automatisch.
- Mit 2xx antworten, sobald Sie das Ereignis akzeptiert haben; aufwändige Arbeit asynchron verarbeiten.
Limits
- HTTPS erforderlich in der Produktion für Callback-URLs.
- 25 Endpunkte pro persönlichem oder Unternehmens-Arbeitsbereich-Scope.
- Signing Secrets werden einmal bei Erstellung und Rotation angezeigt; in Ihrem Secrets-Manager speichern.
Verwandte Themen
- Conversion-Pixel — Conversions auf Zielseiten verfolgen, die Sie nicht kontrollieren
- Kurzlinks und QR-Codes im Hilfe-Center
- API-Schlüssel und verbundene Apps