Back to docs

Webhooks

Webhook-uri

Primește notificări HTTP semnate atunci când linkurile, campaniile și postările din spațiul tău de lucru se schimbă.

Webhook-urile Styrar trimit notificări HTTP semnate atunci când resursele din spațiul tău de lucru se schimbă. Folosește-le pentru a sincroniza linkuri și click-uri cu un CRM, pentru a declanșa automatizări în propriul stack sau pentru a transmite evenimente de funnel către un data warehouse.

Acestea înlocuiesc API-ul retras de webhook-uri doar pentru linkuri (/v1/link-webhooks). Integrările noi trebuie să folosească exclusiv /v1/webhooks/*.

Configurarea unui webhook

Urmează acești pași pentru a ajunge de la zero la un endpoint verificat, pregătit pentru producție.

1. Pregătește URL-ul receptorului

Serverul tău are nevoie de un URL public HTTPS care acceptă cereri POST cu corp JSON. În producție, URL-urile de callback trebuie să folosească HTTPS. HTTP este permis doar în dezvoltarea locală.

Handler-ul tău ar trebui să:

  • Citească corpul brut al cererii ca string sau buffer (nu parsa JSON-ul înainte de a verifica semnătura).
  • Citească header-ele Styrar-Signature, Styrar-Event-Id și Styrar-Event-Type.
  • Verifice semnătura (vezi secțiunea Verificarea semnăturilor de mai jos).
  • Returneze rapid un cod 2xx după ce evenimentul este acceptat. Pune procesarea costisitoare în coadă, asincron.

Exemplu de path: https://your-app.com/webhooks/styrar

Styrar nu poate livra către adrese private sau loopback precum localhost.

2. Obține acces la API (pentru configurare programatică)

Dacă creezi endpoint-uri prin API în loc de dashboard, ai nevoie de un token Bearer cu:

ScopAcces
webhooks:readListează endpoint-uri, livrări și tipuri de evenimente
webhooks:writeCreează, actualizează, șterge, testează, rotește secretul, reîncearcă livrările

Creează o cheie API în Setări → Chei API cu webhooks:read și webhooks:write, sau folosește un token de acces OAuth dintr-o aplicație conectată cu aceleași scopuri.

Pentru spațiile de lucru ale companiilor, transmite companyId la cererile de listare și creare.

3. Creează un endpoint din dashboard

  1. Conectează-te și deschide Setări → Webhook-uri (/settings/webhooks).
  2. La secțiunea Endpoint-uri, introdu URL-ul endpoint-ului (callback-ul public HTTPS).
  3. Opțional, adaugă o Descriere (de exemplu „Sincronizare CRM producție").
  4. Selectează Evenimentele dorite (este necesar cel puțin unul). Alegeri comune:
    • link.clicked pentru urmărirea click-urilor
    • link.created / link.updated / link.deleted pentru ciclul de viață al linkurilor
    • post.published și campaign.created pentru evenimente de funnel
  5. Apasă Adaugă endpoint.

După creare, Styrar îți arată secretul de semnare (whsec_live_… sau whsec_test_…). Copiază-l imediat și stochează-l în managerul tău de secrete. Este afișat o singură dată. Dacă îl pierzi, folosește Rotește secretul pe endpoint și actualizează-ți serverul.

4. Creează un endpoint prin API

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

Stochează secret imediat. Nu este returnat la cererile GET ulterioare. Un array eventTypes gol abonează la toate tipurile de evenimente; dashboard-ul necesită cel puțin un eveniment explicit.

5. Trimite un eveniment de test

Confirmă că serverul tău este accesibil înainte de a te baza pe trafic live.

Dashboard: Pe rândul endpoint-ului, apasă Trimite test. Styrar livrează un eveniment webhook.test. Ar trebui să vezi un toast de succes cu codul de stare HTTP.

API:

RequestPOST
Authorization: Bearer sty_live_your_api_key
Response200 OK
{
  "ok": true,
  "statusCode": 200
}

Când testul are succes, serverul tău ar fi trebuit să primească un eveniment webhook.test (vezi Primirea unui eveniment pe serverul tău de mai jos).

Verifică Livrările recente din dashboard (sau GET /v1/webhooks/deliveries) dacă testul eșuează. Probleme frecvente:

ProblemăCe să verifici
Conexiune refuzată / timeoutURL-ul este public HTTPS, firewall-ul permite Styrar, tunelul este activ
HTTP 4xx/5xx de la aplicația taHandler-ul returnează 2xx după verificare; loghează corpul brut și header-ele
Verificarea semnăturii eșueazăFolosește corpul brut, secretul corect, comparație în timp constant
Endpoint dezactivatReactivează din dashboard sau remediază problema care a cauzat HTTP 410

6. Verifică și procesează livrările

La fiecare POST către URL-ul tău:

  1. Parsează Styrar-Signature în timestamp-ul t și digest-ul v1.
  2. Respinge dacă t este mai vechi decât toleranța ta (se recomandă 5 minute).
  3. Calculează HMAC-SHA256 pentru "{t}.{raw_body}" cu secretul endpoint-ului tău.
  4. Compară cu v1 folosind o verificare în timp constant.
  5. Parsează JSON-ul doar după ce verificarea a avut succes.
  6. Deduplică pe baza Styrar-Event-Id (livrare cel-puțin-o-dată).

Vezi secțiunea Verificarea semnăturilor de mai jos pentru un exemplu Node.js.

7. Lansează în producție

  • Păstrează endpoint-ul Activat în dashboard.
  • Abonează-te doar la evenimentele de care ai nevoie (reduce zgomotul și costul).
  • Monitorizează Livrările recente pentru eșecuri și folosește Reîncearcă când e nevoie.
  • Rotește secretul dacă există suspiciunea că a fost expus (Rotește secretul din dashboard sau POST …/rotate-secret).

Start rapid

  1. Obține acces la API cu scopurile webhooks:read și webhooks:write (cheie API sau OAuth).
  2. Expune un receptor public HTTPS și creează un endpoint din Setări → Webhook-uri sau prin POST /v1/webhooks/endpoints.
  3. Copiază secretul de semnare când este afișat și stochează-l în siguranță.
  4. Trimite un eveniment de test și confirmă că serverul tău verifică Styrar-Signature și returnează 2xx.
  5. Procesează evenimentele live cu deduplicare pe baza Styrar-Event-Id.

Autentificare

Gestionarea webhook-urilor necesită un token Bearer cu scopurile listate la pasul 2 de mai sus. Cheile API cu scopuri corespunzătoare funcționează la fel ca token-urile de acces OAuth.

Tipuri de evenimente

Apelează GET /v1/webhooks/event-types pentru catalogul live din mediul tău. Abonează-te la tipuri specifice per endpoint, sau transmite un array eventTypes gol pentru a primi toate evenimentele.

webhook.test este trimis doar când apeși Trimite test; nu este emis de activitatea normală a spațiului de lucru.

Plicul evenimentului

Fiecare livrare folosește aceeași structură la nivel superior. Câmpurile specifice evenimentului se află sub data.object:

CâmpTipDescriere
idstringID unic al evenimentului. Aceeași valoare ca header-ul Styrar-Event-Id.
typestringNumele evenimentului (de exemplu link.clicked).
api_versionstringVersiunea schemei payload-ului (2026-06-22).
created_atstringTimestamp ISO 8601 la care a fost creat evenimentul.
workspace.typestringcompany sau personal.
workspace.company_idstring | nullID-ul companiei când workspace.type este company.
data.objectobjectPayload-ul evenimentului (documentat per eveniment mai jos).

Exemplu complet de livrare (plic + payload link.created):

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

Pentru spațiile de lucru personale, workspace.type este personal și workspace.company_id este null. Payload-ul poate include company_id: null pe obiectele de tip link.

Sumarul evenimentelor

EvenimentCând se declanșeazăVolum tipic
link.createdEste creat un short link (POST /v1/links, creare în masă sau upsert în masă)Scăzut
link.updatedUn short link este actualizat (PATCH /v1/links/:id, actualizare în masă sau upsert în masă)Scăzut
link.deletedUn short link este șters logic (DELETE /v1/links/:id sau ștergere în masă)Scăzut
link.clickedEste înregistrat un click urmărit (asincron, după redirect, prin worker-ul de click-uri)Ridicat
link.experiment.completedUn experiment A/B pe un link este finalizat și este selectat un câștigătorScăzut
campaign.createdEste creată o campanie (POST /v1/campaigns)Scăzut
post.publishedO postare socială este publicată cu succes pe toate platformele țintăScăzut
webhook.testRulezi Trimite test pe un endpointLa cerere

link.created

Se declanșează când un short link nou este salvat în spațiul tău de lucru.

Utilizări comune: Sincronizarea linkurilor noi cu un CRM, declanșarea automatizărilor de bun venit sau indexarea linkurilor într-un motor de căutare extern.

Câmpurile data.object:

CâmpTipDescriere
idstringID-ul link-ului
short_codestringCodul scurt public
titlestring | nullTitlul link-ului
original_urlstring | nullURL-ul de destinație
is_activebooleanDacă link-ul este activ
company_idstring | nullCompania deținătoare (spațiu de lucru companie)
campaign_idstring | nullCampania asociată, dacă există
link_group_idstring | nullID-ul folderului/grupului, dacă există
created_atstringOra creării în format ISO 8601
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

Se declanșează când un short link existent este modificat (destinație, titlu, etichete, folder, setări UTM etc.).

Utilizări comune: Păstrarea sincronizării sistemelor externe atunci când un link se schimbă, fără a-l recrea.

Câmpurile data.object: Identice cu link.created (vezi mai sus).

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

Se declanșează când un short link este șters logic (isActive setat la false). Link-ul nu mai redirecționează public, dar rămâne în istoricul spațiului tău de lucru.

Utilizări comune: Eliminarea linkurilor din cataloagele externe sau întreruperea automatizărilor asociate codului scurt respectiv.

Câmpurile data.object: Aceeași structură ca link.created, cu is_active mereu false.

CâmpTipDescriere
idstringID-ul link-ului
short_codestringCodul scurt la momentul ștergerii
titlestring | nullTitlul link-ului
original_urlstring | nullURL-ul de destinație
is_activebooleanfalse după ștergerea logică
company_idstring | nullCompania deținătoare
campaign_idstring | nullCampania asociată, dacă există
link_group_idstring | nullID-ul folderului/grupului, dacă există
created_atstringOra creării originale
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

Se declanșează după ce un click al unui vizitator este persistat. Livrat asincron (nu pe calea redirecționării), așa că poate exista o întârziere ușoară sub sarcină mare.

Utilizări comune: Analiză a click-urilor în timp real, rutare bazată pe geo în stack-ul tău, fluxuri de activitate CRM sau ingestie în data warehouse.

Confidențialitate: Payload-urile includ dimensiuni geo și de dispozitiv, nu adrese IP brute.

Câmpurile data.object:

CâmpTipDescriere
idstringID-ul link-ului
link_idstringIdentic cu id
short_codestringCodul scurt care a fost accesat
titlestring | nullTitlul link-ului
countrystring | nullCodul ISO al țării (geo lookup)
citystring | nullOrașul din geo lookup
devicestring | nullClasa de dispozitiv (de exemplu mobile, desktop)
browserstring | nullNumele browser-ului
osstring | nullSistemul de operare
platformstring | nullIndicație de platformă din analiza user agent
referrerstring | nullURL-ul de referință, când e disponibil
routing_rule_idstring | nullRegula de rutare geo/dispozitiv potrivită, dacă există
experiment_variant_idstring | nullID-ul variantei A/B când click-ul a trecut printr-un experiment
clicked_atstringOra click-ului în format ISO 8601
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

Se declanșează când un test A/B pe un short link este marcat ca finalizat și este selectată o variantă câștigătoare.

Utilizări comune: Înregistrarea rezultatelor experimentelor, actualizarea dashboard-urilor sau declanșarea campaniilor de follow-up în funcție de câștigător.

Câmpurile data.object:

CâmpTipDescriere
idstringID-ul experimentului
link_idstringID-ul link-ului părinte
short_codestring | nullCodul scurt al link-ului părinte
statusstringcompleted
assignment_modestring | nullModul în care a fost împărțit traficul (de exemplu ponderat)
winning_variant_idstring | nullID-ul variantei câștigătoare
ended_atstring | nullOra finalizării în format ISO 8601
variantsarrayRezultate per variantă (vezi mai jos)

Fiecare element din variants:

CâmpTipDescriere
idstringID-ul variantei
labelstringEtichetă lizibilă pentru oameni
weightnumberPonderea de trafic folosită în test
destination_urlstringURL-ul către care această variantă a trimis traficul
clicksnumberNumărul total de click-uri înregistrate pentru această variantă
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

Se declanșează când este creată o campanie nouă în spațiul tău de lucru.

Utilizări comune: Provizionarea folderelor de campanie în instrumente externe, începerea fluxurilor de atribuire sau notificarea managerilor de cont.

Câmpurile data.object:

CâmpTipDescriere
idstringID-ul campaniei
namestringNumele campaniei
descriptionstring | nullDescrierea campaniei
company_idstring | nullCompania deținătoare
user_idstring | nullUtilizatorul care a creat (campanii personale)
brand_idstring | nullDomeniul de brand, când este setat
created_atstringOra creării în format ISO 8601
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

Se declanșează când o postare programată sau imediată este publicată cu succes pe toate platformele țintă configurate.

Utilizări comune: Notificarea sistemelor din aval că conținutul este live, închiderea ciclului automatizărilor de campanie sau sincronizarea stării publicării cu un data warehouse.

Notă: Este emis doar când toate țintele au succes. Eșecurile parțiale nu emit acest eveniment momentan.

Câmpurile data.object:

CâmpTipDescriere
idstringID-ul postării
post_idstringIdentic cu id
statusstringPUBLISHED
company_idstring | nullCompania deținătoare
user_idstring | nullUtilizatorul deținător (spațiu de lucru personal)
brand_idstring | nullDomeniul de brand, când este setat
campaign_idstring | nullCampania asociată, când este setată
published_atstringOra publicării în format ISO 8601
platform_countnumberNumărul de platforme țintă din postare
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

Ping sintetic trimis doar când apeși Trimite test în dashboard sau apelezi POST /v1/webhooks/endpoints/:id/test.

Utilizări comune: Validarea URL-ului receptorului tău, TLS, verificarea semnăturii și gestionarea răspunsului înainte de lansarea în producție.

Câmpurile data.object:

CâmpTipDescriere
endpoint_idstringEndpoint-ul care a primit testul
messagestringMesaj fix: Styrar webhook test ping
JSON
{
  "endpoint_id": "wh_ep_abc123",
  "message": "Styrar webhook test ping"
}

Exemple API

Listarea endpoint-urilor

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

Primirea unui eveniment pe serverul tău

Când un eveniment abonat se declanșează, Styrar face POST către URL-ul endpoint-ului tău. Acesta este payload-ul pe care îl primește handler-ul tău:

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
}

Corpul răspunsului tău poate fi gol. Returnează orice cod 2xx după ce verifici semnătura și accepți evenimentul. Styrar reîncearcă la răspunsuri non-2xx.

Payload-ul ping-ului de test

O acțiune Trimite test livrează webhook.test cu un payload mai mic:

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

Listarea livrărilor recente

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

Gestionarea endpoint-urilor

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
CâmpDescriere
urlURL de callback HTTPS (HTTP permis doar în dezvoltare locală)
descriptionEtichetă opțională
eventTypesArray de tipuri de evenimente abonate; gol înseamnă toate evenimentele
enabledImplicit true

Secretul de semnare (whsec_live_… / whsec_test_…) este returnat o singură dată la creare și la rotate-secret. Poți înregistra până la 25 de endpoint-uri per domeniu de spațiu de lucru.

Livrări

Plain text
GET  /v1/webhooks/deliveries?endpointId={id}
GET  /v1/webhooks/deliveries/:id
POST /v1/webhooks/deliveries/:id/retry

Fiecare rând de livrare urmărește status, attemptCount, nextAttemptAt, lastStatusCode, lastError și deliveredAt. Inspectează eșecurile din dashboard la Setări → Webhook-uri sau reîncearcă prin API.

Formatul livrării

Fiecare eveniment live este o cerere HTTP POST către URL-ul tău de callback. Header-ele și corpul sunt prezentate în secțiunea Primirea unui eveniment pe serverul tău de mai sus.

HeaderDescriere
Styrar-Event-IdID unic al evenimentului (folosit pentru deduplicare)
Styrar-Event-Typede exemplu link.clicked
Styrar-Signaturet={unix_ts},v1={hmac_sha256_hex}
User-AgentStyrar-Webhooks/1.0

Câmpurile plicului:

CâmpDescriere
idID-ul evenimentului (corespunde cu Styrar-Event-Id)
typeString-ul tipului de eveniment
api_versionVersiunea schemei payload-ului
created_atTimestamp ISO 8601
workspaceDomeniu company sau personal
data.objectPayload specific evenimentului

Payload-urile pentru click-uri includ dimensiuni geo și de dispozitiv, nu adrese IP brute.

Verificarea semnăturilor

Semnătura urmează o schemă similară celei de la Stripe. Semnează "{timestamp}.{raw_body}" cu HMAC-SHA256 folosind secretul endpoint-ului tău.

  1. Parsează Styrar-Signature în t (timestamp unix) și v1 (digest hex).
  2. Respinge dacă t este în afara toleranței tale (recomandăm 5 minute).
  3. Calculează digest-ul așteptat și compară-l cu v1 folosind o verificare în timp constant.
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
  }
}

Verifică mereu folosind corpul brut al cererii înainte de a parsa JSON-ul.

Fiabilitate

  • Livrările sunt cel-puțin-o-dată. Deduplică folosind Styrar-Event-Id sau id-ul din plic.
  • Livrările eșuate reîncearcă cu backoff exponențial până la succes sau numărul maxim de încercări.
  • Dacă serverul tău returnează HTTP 410, Styrar dezactivează automat endpoint-ul.
  • Răspunde cu 2xx imediat ce ai acceptat evenimentul; procesează sarcinile costisitoare asincron.

Limite

  • HTTPS este obligatoriu în producție pentru URL-urile de callback.
  • 25 de endpoint-uri per domeniu de spațiu de lucru personal sau companie.
  • Secretele de semnare sunt afișate o singură dată la creare și rotire; stochează-le în managerul tău de secrete.

Conexe

Join the beta waitlist

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