Back to docs

Webhooks

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-Id und Styrar-Event-Type lesen.
  • 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:

ScopeZugriff
webhooks:readEndpunkte, Zustellungen und Ereignistypen auflisten
webhooks:writeErstellen, 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

  1. Anmelden und Einstellungen → Webhooks öffnen (/settings/webhooks).
  2. Unter Endpoints Ihre Endpunkt-URL eingeben (der öffentliche HTTPS-Callback).
  3. Optional eine Beschreibung hinzufügen (zum Beispiel „Produktions-CRM-Sync").
  4. Die gewünschten Ereignisse auswählen (mindestens eines ist erforderlich). Übliche Wahl:
    • link.clicked für Klick-Tracking
    • link.created / link.updated / link.deleted für den Link-Lebenszyklus
    • post.published und campaign.created für Funnel-Ereignisse
  5. 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

RequestPOST
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"
  ]
}
Response201 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:

RequestPOST
Authorization: Bearer sty_live_your_api_key
Response200 OK
{
  "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:

ProblemWas zu prüfen ist
Connection refused / TimeoutURL ist öffentlich per HTTPS, Firewall erlaubt Styrar, Tunnel läuft
HTTP 4xx/5xx von Ihrer AppHandler antwortet nach Verifizierung mit 2xx; rohen Body und Header loggen
Signaturverifizierung schlägt fehlRohen Body, korrektes Secret, zeitkonstanten Vergleich verwenden
Endpunkt deaktiviertIm Dashboard wieder aktivieren oder die Ursache für HTTP 410 beheben

6. Zustellungen verifizieren und verarbeiten

Bei jedem POST an Ihre URL:

  1. Styrar-Signature in Zeitstempel t und Digest v1 zerlegen.
  2. Ablehnen, wenn t älter als Ihre Toleranz ist (5 Minuten empfohlen).
  3. HMAC-SHA256 von "{t}.{raw_body}" mit Ihrem Endpunkt-Secret berechnen.
  4. Mit v1 mittels zeitkonstantem Vergleich abgleichen.
  5. JSON erst nach erfolgreicher Verifizierung parsen.
  6. Auf Styrar-Event-Id deduplizieren (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

  1. API-Zugriff erhalten mit den Scopes webhooks:read und webhooks:write (API-Schlüssel oder OAuth).
  2. Öffentlichen HTTPS-Empfänger exponieren und einen Endpunkt unter Einstellungen → Webhooks oder via POST /v1/webhooks/endpoints erstellen.
  3. Signing Secret kopieren, sobald es angezeigt wird, und sicher speichern.
  4. Testereignis senden und bestätigen, dass Ihr Server Styrar-Signature verifiziert und mit 2xx antwortet.
  5. 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:

FeldTypBeschreibung
idstringEindeutige Ereignis-ID. Gleicher Wert wie der Header Styrar-Event-Id.
typestringEreignisname (zum Beispiel link.clicked).
api_versionstringPayload-Schemaversion (2026-06-22).
created_atstringISO-8601-Zeitstempel zum Erstellungszeitpunkt des Ereignisses.
workspace.typestringcompany oder personal.
workspace.company_idstring | nullUnternehmens-ID, wenn workspace.type gleich company ist.
data.objectobjectEreignis-Payload (unten pro Ereignis dokumentiert).

Vollständiges Zustellungsbeispiel (Envelope + link.created-Payload):

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

EreignisWann es ausgelöst wirdTypisches Volumen
link.createdEin Kurzlink wird erstellt (POST /v1/links, Bulk-Erstellung oder Bulk-Upsert)Niedrig
link.updatedEin Kurzlink wird aktualisiert (PATCH /v1/links/:id, Bulk-Update oder Bulk-Upsert)Niedrig
link.deletedEin Kurzlink wird soft-gelöscht (DELETE /v1/links/:id oder Bulk-Löschung)Niedrig
link.clickedEin nachverfolgter Klick wird erfasst (asynchron, nach dem Redirect über den Click-Worker)Hoch
link.experiment.completedEin A/B-Link-Experiment ist abgeschlossen und ein Gewinner wurde ausgewähltNiedrig
campaign.createdEine Kampagne wird erstellt (POST /v1/campaigns)Niedrig
post.publishedEin Social-Beitrag wird erfolgreich auf allen Plattform-Zielen veröffentlichtNiedrig
webhook.testSie führen Send test an einem Endpunkt ausBei 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:

FeldTypBeschreibung
idstringLink-ID
short_codestringÖffentlicher Kurzcode
titlestring | nullLink-Titel
original_urlstring | nullZiel-URL
is_activebooleanOb der Link aktiv ist
company_idstring | nullBesitzendes Unternehmen (Unternehmens-Arbeitsbereich)
campaign_idstring | nullVerknüpfte Kampagne, falls vorhanden
link_group_idstring | nullOrdner-/Gruppen-ID, falls vorhanden
created_atstringISO-8601-Erstellungszeit
JSON
{
  "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).

JSON
{
  "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.

FeldTypBeschreibung
idstringLink-ID
short_codestringKurzcode zum Zeitpunkt der Löschung
titlestring | nullLink-Titel
original_urlstring | nullZiel-URL
is_activebooleanfalse nach Soft-Delete
company_idstring | nullBesitzendes Unternehmen
campaign_idstring | nullVerknüpfte Kampagne, falls vorhanden
link_group_idstring | nullOrdner-/Gruppen-ID, falls vorhanden
created_atstringUrsprüngliche Erstellungszeit
JSON
{
  "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:

FeldTypBeschreibung
idstringLink-ID
link_idstringGleicher Wert wie id
short_codestringKurzcode, der angeklickt wurde
titlestring | nullLink-Titel
countrystring | nullISO-Ländercode (Geo-Lookup)
citystring | nullStadt aus Geo-Lookup
devicestring | nullGeräteklasse (zum Beispiel mobile, desktop)
browserstring | nullBrowsername
osstring | nullBetriebssystem
platformstring | nullPlattform-Hinweis aus dem Parsen des User-Agents
referrerstring | nullReferrer-URL, sofern verfügbar
routing_rule_idstring | nullÜbereinstimmende Geo-/Geräte-Routing-Regel, falls vorhanden
experiment_variant_idstring | nullA/B-Varianten-ID, wenn der Klick durch ein Experiment lief
clicked_atstringISO-8601-Klickzeit
JSON
{
  "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:

FeldTypBeschreibung
idstringExperiment-ID
link_idstringÜbergeordnete Link-ID
short_codestring | nullKurzcode des übergeordneten Links
statusstringcompleted
assignment_modestring | nullWie der Traffic aufgeteilt wurde (zum Beispiel gewichtet)
winning_variant_idstring | nullID der Gewinner-Variante
ended_atstring | nullISO-8601-Abschlusszeit
variantsarrayErgebnisse pro Variante (siehe unten)

Jeder Eintrag in variants:

FeldTypBeschreibung
idstringVarianten-ID
labelstringMenschenlesbares Label
weightnumberIm Test verwendetes Traffic-Gewicht
destination_urlstringURL, an die diese Variante Traffic geschickt hat
clicksnumberInsgesamt für diese Variante erfasste Klicks
JSON
{
  "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:

FeldTypBeschreibung
idstringKampagnen-ID
namestringKampagnenname
descriptionstring | nullKampagnenbeschreibung
company_idstring | nullBesitzendes Unternehmen
user_idstring | nullErstellender Nutzer (persönliche Kampagnen)
brand_idstring | nullMarken-Scope, falls gesetzt
created_atstringISO-8601-Erstellungszeit
JSON
{
  "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:

FeldTypBeschreibung
idstringBeitrags-ID
post_idstringGleicher Wert wie id
statusstringPUBLISHED
company_idstring | nullBesitzendes Unternehmen
user_idstring | nullBesitzender Nutzer (persönlicher Arbeitsbereich)
brand_idstring | nullMarken-Scope, falls gesetzt
campaign_idstring | nullVerknüpfte Kampagne, falls gesetzt
published_atstringISO-8601-Veröffentlichungszeit
platform_countnumberAnzahl der Plattform-Ziele des Beitrags
JSON
{
  "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:

FeldTypBeschreibung
endpoint_idstringEndpunkt, der den Test erhalten hat
messagestringFeste Nachricht: Styrar webhook test ping
JSON
{
  "endpoint_id": "wh_ep_abc123",
  "message": "Styrar webhook test ping"
}

API-Beispiele

Endpunkte auflisten

RequestGET
Authorization: Bearer sty_live_your_api_key
Response200 OK
[
  {
    "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:

RequestPOST
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"
    }
  }
}
Response200 OK
{
  "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:

RequestPOST
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"
    }
  }
}
Response200 OK

Letzte Zustellungen auflisten

RequestGET
Authorization: Bearer sty_live_your_api_key
Response200 OK
[
  {
    "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

Plain text
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
FeldBeschreibung
urlHTTPS-Callback-URL
descriptionOptionales Label
eventTypesArray der abonnierten Ereignistypen; leer bedeutet alle Ereignisse
enabledStandard 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

Plain text
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.

HeaderBeschreibung
Styrar-Event-IdEindeutige Ereignis-ID (zur Deduplizierung verwenden)
Styrar-Event-Typez. B. link.clicked
Styrar-Signaturet={unix_ts},v1={hmac_sha256_hex}
User-AgentStyrar-Webhooks/1.0

Envelope-Felder:

FeldBeschreibung
idEreignis-ID (entspricht Styrar-Event-Id)
typeEreignistyp-String
api_versionPayload-Schemaversion
created_atISO-8601-Zeitstempel
workspaceScope company oder personal
data.objectEreignisspezifische 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.

  1. Styrar-Signature in t (Unix-Zeitstempel) und v1 (Hex-Digest) zerlegen.
  2. Ablehnen, wenn t außerhalb Ihrer Toleranz liegt (wir empfehlen 5 Minuten).
  3. Den erwarteten Digest berechnen und mit v1 mittels zeitkonstantem Vergleich abgleichen.
JavaScript
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-Id oder der Envelope-id deduplizieren.
  • 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

Join the beta waitlist

By signing up you agree to receive marketing email. See our Privacy Policy.