Webhooks
Ontvang ondertekende HTTP-meldingen wanneer links, campagnes en posts in je werkruimte veranderen.
Styrar-webhooks sturen ondertekende HTTP-meldingen wanneer resources in je werkruimte veranderen. Gebruik ze om links en clicks te synchroniseren met een CRM, automatiseringen in je stack te triggeren, of funnelgebeurtenissen naar een datawarehouse te streamen.
Dit vervangt de uitgefaseerde, alleen-link-webhooks-API (/v1/link-webhooks). Nieuwe integraties moeten uitsluitend /v1/webhooks/* gebruiken.
Een webhook instellen
Volg deze stappen om van nul naar een geverifieerd, productieklaar endpoint te gaan.
1. Bereid je ontvanger-URL voor
Je server heeft een publieke HTTPS-URL nodig die POST-verzoeken met een JSON-body accepteert. In productie moeten callback-URL's HTTPS gebruiken.
Je handler moet:
- De ruwe request body lezen als string of buffer (parse de JSON niet voordat je de handtekening hebt geverifieerd).
- De headers
Styrar-Signature,Styrar-Event-IdenStyrar-Event-Typelezen. - De handtekening verifiëren (zie de sectie Handtekeningen verifiëren hieronder).
- Snel 2xx teruggeven nadat de gebeurtenis is geaccepteerd. Plan zwaar werk asynchroon in.
Voorbeeldpad: https://your-app.com/webhooks/styrar
Styrar kan niet leveren aan private of loopback-adressen zoals localhost.
2. Verkrijg API-toegang (voor programmatische opzet)
Als je endpoints via de API aanmaakt in plaats van via het dashboard, heb je een Bearer-token nodig met:
| Scope | Toegang |
|---|---|
webhooks:read | Endpoints, leveringen en gebeurtenistypen opvragen |
webhooks:write | Aanmaken, bijwerken, verwijderen, testen, secret roteren, leveringen opnieuw proberen |
Maak een API-sleutel aan in Instellingen → API-sleutels met webhooks:read en webhooks:write, of gebruik een OAuth-accesstoken van een gekoppelde app met dezelfde scopes.
Voor bedrijfswerkruimten geef je companyId mee bij list- en create-verzoeken.
3. Maak een endpoint aan in het dashboard
- Log in en open Instellingen → Webhooks (
/settings/webhooks). - Voer onder Endpoints je Endpoint-URL in (de publieke HTTPS-callback).
- Voeg optioneel een Omschrijving toe (bijvoorbeeld "Productie CRM-sync").
- Selecteer de Gebeurtenissen die je wilt (minimaal één is verplicht). Veelgebruikte keuzes:
link.clickedvoor clicktrackinglink.created/link.updated/link.deletedvoor de levenscyclus van linkspost.publishedencampaign.createdvoor funnelgebeurtenissen
- Klik op Endpoint toevoegen.
Na het aanmaken toont Styrar je signing secret (whsec_live_… of whsec_test_…). Kopieer deze direct en bewaar hem in je secrets manager. Hij wordt maar één keer getoond. Ben je hem kwijt? Gebruik Secret roteren op het endpoint en werk je server bij.
4. Maak een endpoint aan via de API
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"
}Bewaar secret direct. Hij wordt niet teruggegeven bij latere GET-verzoeken. Een leeg eventTypes-array abonneert op alle gebeurtenistypen; het dashboard vereist minimaal één expliciete gebeurtenis.
5. Stuur een testgebeurtenis
Bevestig dat je server bereikbaar is voordat je op live verkeer vertrouwt.
Dashboard: Klik op de endpointregel op Test versturen. Styrar levert een webhook.test-gebeurtenis af. Je zou een succesmelding moeten zien met de HTTP-statuscode.
API:
Authorization: Bearer sty_live_your_api_key{
"ok": true,
"statusCode": 200
}Als de test slaagt, zou je server een webhook.test-gebeurtenis hebben ontvangen (zie Een gebeurtenis op je server ontvangen hieronder).
Bekijk Recente leveringen in het dashboard (of GET /v1/webhooks/deliveries) als de test mislukt. Veelvoorkomende problemen:
| Probleem | Wat te controleren |
|---|---|
| Connectie geweigerd / timeout | URL is publiek HTTPS, firewall laat Styrar toe, tunnel draait |
| HTTP 4xx/5xx van je app | Handler geeft 2xx terug na verificatie; log ruwe body en headers |
| Handtekeningverificatie mislukt | Gebruik ruwe body, juiste secret, constant-time vergelijking |
| Endpoint uitgeschakeld | Schakel opnieuw in via dashboard of verhelp de oorzaak van HTTP 410 |
6. Verifieer en verwerk leveringen
Bij elke POST naar je URL:
- Parse
Styrar-Signaturenaar timestampten digestv1. - Weiger als
touder is dan je tolerantie (5 minuten aanbevolen). - Berekenen de HMAC-SHA256 van
"{t}.{raw_body}"met je endpoint-secret. - Vergelijk met
v1met een constant-time check. - Parse de JSON pas na succesvolle verificatie.
- Dedupliceer op
Styrar-Event-Id(at-least-once levering).
Zie de sectie Handtekeningen verifiëren hieronder voor een Node.js-voorbeeld.
7. Ga live
- Houd het endpoint Ingeschakeld in het dashboard.
- Abonneer alleen op gebeurtenissen die je nodig hebt (vermindert ruis en kosten).
- Monitor Recente leveringen op fouten en gebruik Opnieuw proberen waar nodig.
- Roteer het secret als het mogelijk is uitgelekt (Secret roteren in dashboard of
POST …/rotate-secret).
Snelstart
- Verkrijg API-toegang met de scopes
webhooks:readenwebhooks:write(API-sleutel of OAuth). - Stel een publieke HTTPS-ontvanger bloot en maak een endpoint aan in Instellingen → Webhooks of via
POST /v1/webhooks/endpoints. - Kopieer de signing secret zodra deze getoond wordt en bewaar hem veilig.
- Stuur een testgebeurtenis en bevestig dat je server
Styrar-Signatureverifieert en 2xx teruggeeft. - Verwerk live gebeurtenissen met deduplicatie op
Styrar-Event-Id.
Authenticatie
Webhookbeheer vereist een Bearer-token met de scopes uit stap 2 hierboven. API-sleutels met de juiste scopes werken op dezelfde manier als OAuth-accesstokens.
Gebeurtenistypen
Roep GET /v1/webhooks/event-types aan voor de actuele catalogus in jouw omgeving. Abonneer per endpoint op specifieke typen, of geef een leeg eventTypes-array op om alle gebeurtenissen te ontvangen.
webhook.test wordt alleen verstuurd wanneer je op Test versturen klikt; het wordt niet uitgezonden door normale werkruimteactiviteit.
Gebeurtenis-envelope
Elke levering gebruikt dezelfde envelopvorm. Gebeurtenisspecifieke velden staan onder data.object:
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Unieke gebeurtenis-id. Zelfde waarde als de header Styrar-Event-Id. |
type | string | Naam van de gebeurtenis (bijvoorbeeld link.clicked). |
api_version | string | Schemaversie van de payload (2026-06-22). |
created_at | string | ISO 8601-tijdstempel van het ontstaan van de gebeurtenis. |
workspace.type | string | company of personal. |
workspace.company_id | string | null | Bedrijfs-id wanneer workspace.type company is. |
data.object | object | Gebeurtenis-payload (hieronder per gebeurtenis gedocumenteerd). |
Volledig leveringsvoorbeeld (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"
}
}
}
Voor persoonlijke werkruimten is workspace.type gelijk aan personal en workspace.company_id is null. De payload kan company_id: null bevatten op linkobjecten.
Overzicht gebeurtenissen
| Gebeurtenis | Wanneer het vuurt | Typisch volume |
|---|---|---|
link.created | Een korte link wordt aangemaakt (POST /v1/links, bulk create, of bulk upsert) | Laag |
link.updated | Een korte link wordt bijgewerkt (PATCH /v1/links/:id, bulk update, of bulk upsert) | Laag |
link.deleted | Een korte link wordt soft-deleted (DELETE /v1/links/:id of bulk delete) | Laag |
link.clicked | Een getrackte click wordt geregistreerd (asynchroon, na redirect via de click worker) | Hoog |
link.experiment.completed | Een A/B-linkexperiment is voltooid en er is een winnaar geselecteerd | Laag |
campaign.created | Een campagne wordt aangemaakt (POST /v1/campaigns) | Laag |
post.published | Een social post wordt succesvol gepubliceerd naar alle platformdoelen | Laag |
webhook.test | Je voert Test versturen uit op een endpoint | Op aanvraag |
link.created
Vuurt wanneer een nieuwe korte link in je werkruimte wordt opgeslagen.
Veelvoorkomend gebruik: Nieuwe links synchroniseren naar een CRM, welkomstautomatiseringen triggeren, of links indexeren in een externe zoekopslag.
Velden van data.object:
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Link-id |
short_code | string | Publieke korte code |
title | string | null | Linktitel |
original_url | string | null | Bestemmings-URL |
is_active | boolean | Of de link actief is |
company_id | string | null | Eigenaarbedrijf (bedrijfswerkruimte) |
campaign_id | string | null | Gekoppelde campagne, indien aanwezig |
link_group_id | string | null | Map/groep-id, indien aanwezig |
created_at | string | ISO 8601-aanmaaktijd |
{
"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
Vuurt wanneer een bestaande korte link wordt aangepast (bestemming, titel, tags, map, UTM-instellingen, enzovoort).
Veelvoorkomend gebruik: Externe systemen gesynchroniseerd houden wanneer een link verandert zonder hem opnieuw aan te maken.
Velden van data.object: Zelfde als link.created (zie hierboven).
{
"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
Vuurt wanneer een korte link wordt soft-deleted (isActive ingesteld op false). De link wordt niet langer publiek opgelost, maar blijft in je werkruimtegeschiedenis.
Veelvoorkomend gebruik: Links verwijderen uit externe catalogi of automatiseringen die aan die korte code gekoppeld zijn pauzeren.
Velden van data.object: Zelfde vorm als link.created, met is_active altijd false.
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Link-id |
short_code | string | Korte code op moment van verwijdering |
title | string | null | Linktitel |
original_url | string | null | Bestemmings-URL |
is_active | boolean | false na soft delete |
company_id | string | null | Eigenaarbedrijf |
campaign_id | string | null | Gekoppelde campagne, indien aanwezig |
link_group_id | string | null | Map/groep-id, indien aanwezig |
created_at | string | Oorspronkelijke aanmaaktijd |
{
"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
Vuurt nadat een bezoekersclick is opgeslagen. Wordt asynchroon afgeleverd (niet op het redirectpad), dus verwacht een lichte vertraging bij hoge belasting.
Veelvoorkomend gebruik: Realtime click-analytics, geo-gebaseerde routering in je stack, CRM-activiteitsfeeds, of warehouse-ingestie.
Privacy: Payloads bevatten geo- en apparaatdimensies, geen ruwe IP-adressen.
Velden van data.object:
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Link-id |
link_id | string | Zelfde als id |
short_code | string | Korte code waarop geklikt werd |
title | string | null | Linktitel |
country | string | null | ISO-landcode (geo-lookup) |
city | string | null | Stad uit geo-lookup |
device | string | null | Apparaatklasse (bijvoorbeeld mobile, desktop) |
browser | string | null | Browsernaam |
os | string | null | Besturingssysteem |
platform | string | null | Platformhint uit user-agent parsing |
referrer | string | null | Referrer-URL indien beschikbaar |
routing_rule_id | string | null | Gematchte geo/apparaat-routeringsregel, indien aanwezig |
experiment_variant_id | string | null | A/B-variant-id wanneer de click via een experiment liep |
clicked_at | string | ISO 8601-clicktijd |
{
"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
Vuurt wanneer een A/B-test op een korte link wordt afgerond en er een winnende variant wordt geselecteerd.
Veelvoorkomend gebruik: Experimentresultaten loggen, dashboards bijwerken, of vervolgcampagnes triggeren op basis van de winnaar.
Velden van data.object:
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Experiment-id |
link_id | string | Bovenliggende link-id |
short_code | string | null | Korte code van de bovenliggende link |
status | string | completed |
assignment_mode | string | null | Hoe verkeer werd verdeeld (bijvoorbeeld gewogen) |
winning_variant_id | string | null | Id van de winnende variant |
ended_at | string | null | ISO 8601-afrondingstijd |
variants | array | Resultaten per variant (zie onder) |
Elk item in variants:
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Variant-id |
label | string | Leesbaar label |
weight | number | Verkeersgewicht gebruikt in de test |
destination_url | string | URL waar deze variant verkeer naartoe stuurde |
clicks | number | Totaal aantal clicks geregistreerd voor deze variant |
{
"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
Vuurt wanneer een nieuwe campagne wordt aangemaakt in je werkruimte.
Veelvoorkomend gebruik: Campagnemappen aanmaken in externe tools, attributieworkflows starten, of accountmanagers informeren.
Velden van data.object:
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Campagne-id |
name | string | Campagnenaam |
description | string | null | Campagnebeschrijving |
company_id | string | null | Eigenaarbedrijf |
user_id | string | null | Aanmakende gebruiker (persoonlijke campagnes) |
brand_id | string | null | Merkscope indien ingesteld |
created_at | string | ISO 8601-aanmaaktijd |
{
"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
Vuurt wanneer een geplande of directe post succesvol wordt gepubliceerd naar alle geconfigureerde platformdoelen.
Veelvoorkomend gebruik: Downstream-systemen informeren dat content live is, campagne-automatiseringen afronden, of publicatiestatus synchroniseren naar een datawarehouse.
Let op: Wordt alleen uitgezonden wanneer elk doel slaagt. Gedeeltelijke fouten zenden deze gebeurtenis vandaag niet uit.
Velden van data.object:
| Veld | Type | Beschrijving |
|---|---|---|
id | string | Post-id |
post_id | string | Zelfde als id |
status | string | PUBLISHED |
company_id | string | null | Eigenaarbedrijf |
user_id | string | null | Eigenaargebruiker (persoonlijke werkruimte) |
brand_id | string | null | Merkscope indien ingesteld |
campaign_id | string | null | Gekoppelde campagne indien ingesteld |
published_at | string | ISO 8601-publicatietijd |
platform_count | number | Aantal platformdoelen op de post |
{
"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
Synthetische ping die alleen wordt verstuurd wanneer je op Test versturen klikt in het dashboard of POST /v1/webhooks/endpoints/:id/test aanroept.
Veelvoorkomend gebruik: Je ontvanger-URL, TLS, handtekeningverificatie en responsafhandeling valideren voordat je live gaat.
Velden van data.object:
| Veld | Type | Beschrijving |
|---|---|---|
endpoint_id | string | Endpoint dat de test ontving |
message | string | Vast bericht: Styrar webhook test ping |
{
"endpoint_id": "wh_ep_abc123",
"message": "Styrar webhook test ping"
}
API-voorbeelden
Endpoints opvragen
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"
}
]Een gebeurtenis op je server ontvangen
Wanneer een geabonneerde gebeurtenis plaatsvindt, doet Styrar een POST naar je endpoint-URL. Dit is de payload die je handler ontvangt:
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
}Je responsbody mag leeg zijn. Geef een willekeurige 2xx-status terug nadat je de handtekening hebt geverifieerd en de gebeurtenis hebt geaccepteerd. Styrar probeert het opnieuw bij niet-2xx-responses.
Test-ping-payload
Een actie Test versturen levert webhook.test af met een kleinere 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"
}
}
}Recente leveringen opvragen
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"
}
]Endpoints beheren
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
| Veld | Beschrijving |
|---|---|
url | HTTPS-callback-URL |
description | Optioneel label |
eventTypes | Array van geabonneerde gebeurtenistypen; leeg betekent alle gebeurtenissen |
enabled | Standaard true |
De signing secret (whsec_live_… / whsec_test_…) wordt precies één keer teruggegeven bij het aanmaken en bij rotate-secret. Je kunt tot 25 endpoints per werkruimtescope registreren.
Leveringen
GET /v1/webhooks/deliveries?endpointId={id}
GET /v1/webhooks/deliveries/:id
POST /v1/webhooks/deliveries/:id/retry
Elke leveringsregel houdt status, attemptCount, nextAttemptAt, lastStatusCode, lastError en deliveredAt bij. Bekijk mislukkingen in het dashboard onder Instellingen → Webhooks of probeer het opnieuw via de API.
Leveringsformaat
Elke live gebeurtenis is een HTTP-POST naar je callback-URL. Headers en body staan hierboven onder Een gebeurtenis op je server ontvangen.
| Header | Beschrijving |
|---|---|
Styrar-Event-Id | Unieke gebeurtenis-id (gebruik voor deduplicatie) |
Styrar-Event-Type | bijv. link.clicked |
Styrar-Signature | t={unix_ts},v1={hmac_sha256_hex} |
User-Agent | Styrar-Webhooks/1.0 |
Envelopvelden:
| Veld | Beschrijving |
|---|---|
id | Gebeurtenis-id (komt overeen met Styrar-Event-Id) |
type | Gebeurtenistype-string |
api_version | Schemaversie van de payload |
created_at | ISO 8601-tijdstempel |
workspace | Scope company of personal |
data.object | Gebeurtenisspecifieke payload |
Clickpayloads bevatten geo- en apparaatdimensies, geen ruwe IP-adressen.
Handtekeningen verifiëren
De handtekening volgt een Stripe-achtig schema. Het ondertekent "{timestamp}.{raw_body}" met HMAC-SHA256 met je endpoint-secret.
- Parse
Styrar-Signaturenaart(unix-tijdstempel) env1(hex-digest). - Weiger als
tbuiten je tolerantie valt (we adviseren 5 minuten). - Berekenen de verwachte digest en vergelijk met
v1met een constant-time check.
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
}
}
Verifieer altijd tegen de ruwe request body voordat je JSON parset.
Betrouwbaarheid
- Leveringen zijn at-least-once. Dedupliceer met
Styrar-Event-Idof het envelopveldid. - Mislukte leveringen worden opnieuw geprobeerd met exponentiële backoff tot succes of het maximale aantal pogingen.
- Als je server HTTP 410 teruggeeft, schakelt Styrar het endpoint automatisch uit.
- Reageer met 2xx zodra je de gebeurtenis hebt geaccepteerd; verwerk zwaar werk asynchroon.
Limieten
- HTTPS verplicht in productie voor callback-URL's.
- 25 endpoints per persoonlijke of bedrijfswerkruimtescope.
- Signing secrets worden eenmalig getoond bij aanmaken en roteren; bewaar ze in je secrets manager.
Verwant
- Conversiepixel — track conversies op bestemmingspagina's die je niet beheert
- Korte links en QR in het helpcentrum
- API-sleutels en gekoppelde apps