Webhookok fogadása
A webhook azonnal értesíti a rendszeredet a Sursában történt változásokról: egy kártyát közzétettek, ellenőriztek
vagy archiváltak, egy kérdés megválaszolatlan maradt. A Sursa egy HTTPS POST kérést küld az eseménnyel az általad
regisztrált címre. Ez az útmutató olyan fogadót épít, amely újrapróbálkozások, sorrendcserék és ismétlődések mellett is
helyesen működik.
Hogy az egyes események mit jelentenek és mit tartalmaznak, azt A webhookok áttekintése útmutató és az API-referencia eseménykatalógusa írja le.
1. Webhook regisztrálása
A webhookokat egy adminisztrátor regisztrálja a Sursában, az Administration → Integrations → Webhooks and API
(adminisztráció → integrációk → webhookok és API) alatt, vagy a POST /v1/webhooks
végponttal, admin szerepkörű és webhooks:manage hatókörű kulccsal:
curl -X POST https://api.sursa.com/v1/webhooks \
-H "Authorization: Bearer $SURSA_ADMIN_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/sursa",
"description": "Sync verified cards to the help center",
"events": ["card.published", "card.updated", "card.verified", "card.archived"]
}'A válasz a secret mezőben tartalmazza az aláíró titkos kulcsot, amely whsec_ előtaggal kezdődik. Ez egyszer
jelenik meg. Tárold a fogadód mellett, például a SURSA_WEBHOOK_SECRET környezeti változóban.
A címnek https sémát kell használnia, és nyilvános IP-címekre kell feloldódnia: a magán-, a visszacsatolási és más
fenntartott hálózatok címeit a Sursa a regisztráláskor és minden kézbesítéskor is elutasítja. Ha a saját gépeden
fejlesztesz, tedd elérhetővé a helyi fogadódat egy alagútszolgáltatáson keresztül, amely nyilvános HTTPS-címet ad
neki.
Tesztesemény bármikor küldhető a POST /v1/webhooks/{webhook_id}/test
végponttal. A válasz megmutatja, mit felelt a fogadód.
2. Hogyan néz ki egy kézbesítés
POST /sursa HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
webhook-id: evt_0123456789abcdef0123456789abcdef
webhook-timestamp: 1791194400
webhook-signature: v1,l1LLl/aEl4dQq7Geb383KbyNeZgs7VZpiw6iuoJgunI=
X-KB-Signature: t=1791194400,v1=82aa6b27378d89d7d880d17c3fe96268598443ae0e64d059c0c41e94e5d3ffbe
X-KB-Event-Id: evt_0123456789abcdef0123456789abcdef
X-KB-Event: card.verified
X-KB-Delivery: 99812
X-KB-Attempt: 1
X-KB-Sequence: 1042
{"id":"evt_0123456789abcdef0123456789abcdef","type":"card.verified","created_at":"2026-10-05T10:00:00Z","org_id":1,"api_version":"1.60.0","sequence":1042,"object":{"type":"card","id":"c1"},"payload":"full","data":{"card":{"id":"c1"}},"test":false}
| Fejléc | Jelentése |
|---|---|
webhook-id | Az esemény azonosítója. Minden újrapróbálkozáskor ugyanaz. Ezzel szűrd ki az ismétlődéseket. |
webhook-timestamp | Mikor írták alá ezt a kísérletet, Unix-másodpercben. |
webhook-signature | Az aláírás Standard Webhooks formátumban. |
X-KB-Signature | Ugyanez a kézbesítés a korábbi Sursa-formátumban aláírva. |
X-KB-Event | Az eseménytípus. |
X-KB-Attempt | A kísérlet sorszáma, 1-től kezdve. |
X-KB-Sequence | Ennek a kézbesítésnek a helye a webhook sorrendjében. |
A fejlécnevekben a kis- és nagybetű nem számít; a keretrendszered X-Kb-Signature vagy x-kb-signature alakban is
mutathatja őket.
A törzs maga az esemény. A data mezője a típustól függ. A thin adattartalomra beállított webhook data nélkül
kapja meg az eseményt; ilyenkor az objektumot a saját kulcsoddal olvasd ki az API-n keresztül.
3. Az aláírás ellenőrzése
Minden kézbesítés aláírását ellenőrizd, mielőtt megbíznál benne. Mindkét formátum a törzs pontos bájtjait írja alá, ezért a nyers törzset olvasd be, mielőtt bármilyen JSON-elemzést végeznél: a JSON újraszerializálása megváltoztatja a bájtokat, és az ellenőrzés meghiúsul.
| Formátum | Aláírt karakterlánc | Kulcs | Aláírás |
|---|---|---|---|
Standard Webhooks (webhook-signature) | <webhook-id>.<webhook-timestamp>.<body> | A titkos kulcs whsec_ utáni része, base64-dekódolva | v1, + a HMAC-SHA256 base64 alakja |
Korábbi formátum (X-KB-Signature) | <t>.<body> | A teljes titkoskulcs-karakterlánc, szövegként | v1= + a HMAC-SHA256 hexadecimális alakja |
Elég az egyiket ellenőrizni. A Standard Webhooks fejlécet részesítsd előnyben: ugyanez az ellenőrzés számos kész
könyvtárban elérhető. A titkos kulcs megújítása alatt minden fejléc érvényes titkos kulcsonként egy aláírást tartalmaz
(a webhook-signature fejlécben szóközzel elválasztva, az X-KB-Signature fejlécben ismételt v1= elemekkel). A
kézbesítés érvényes, ha bármelyik egyezik.
Utasítsd el azt az időbélyeget is, amely 5 percnél többel eltér a saját órádtól. Így senki nem tudja később visszajátszani egy rögzített kézbesítést.
# Check a saved delivery by hand: body.json holds the raw body, the values come from its headers.
SECRET="whsec_…"; WEBHOOK_ID="evt_…"; TIMESTAMP="1791194400"
# Standard Webhooks: compare with the value after "v1," in webhook-signature
KEY=$(printf '%s' "${SECRET#whsec_}" | tr '_-' '/+')
while [ $(( ${#KEY} % 4 )) -ne 0 ]; do KEY="$KEY="; done
KEY_HEX=$(printf '%s' "$KEY" | openssl base64 -d -A | xxd -p | tr -d '\n')
{ printf '%s.%s.' "$WEBHOOK_ID" "$TIMESTAMP"; cat body.json; } \
| openssl dgst -sha256 -mac HMAC -macopt "hexkey:$KEY_HEX" -binary | openssl base64 -A
# Earlier format: compare with the value after "v1=" in X-KB-Signature
{ printf '%s.' "$TIMESTAMP"; cat body.json; } | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1Az aláírás-ellenőrző kód tesztelése
Mielőtt valódi kézbesítésekre kapcsolnád, futtasd le a kódodat ezekkel az értékekkel. Ha az óra az időbélyegre van állítva, mindkét ellenőrzésnek sikerülnie kell, és meg kell hiúsulnia, ha a törzs egyetlen bájtja megváltozik.
| Bemenet | Érték |
|---|---|
| Titkos kulcs | whsec_test_secret_0123456789abcdef |
webhook-id | evt_0123456789abcdef0123456789abcdef |
webhook-timestamp és t | 1791194400 |
| Törzs | {"id":"evt_0123456789abcdef0123456789abcdef","type":"card.verified","created_at":"2026-10-05T10:00:00Z","org_id":1,"data":{"card":{"id":"c1"}}} |
webhook-signature | v1,+xxWSXH6weQJ/MVg4+N78a1Dj4y/AQ4p94+PRNzpECg= |
X-KB-Signature | t=1791194400,v1=ce26a19cb63cd3ae620217bf7688e1df1044fa7d59d9ae148d215f0292b768dc |
4. Gyors válasz, későbbi feldolgozás
A Sursa legfeljebb 10 másodpercig vár a válaszodra. Bármely 2xx állapotkód azt jelenti, hogy a kézbesítés
sikerült; minden más, az átirányítást, az időtúllépést és a lezárt kapcsolatot is beleértve, azt jelenti, hogy
sikertelen volt, és újrapróbálkozás következik.
Ezért a fogadó csak ellenőrizzen, rögzítse az eseményt, és válaszoljon. A tényleges munkát utána végezd el, a saját sorodból.
// Node.js 20+, no framework. verifyStandard and verifyLegacy come from step 3.
import http from "node:http";
const SECRET = process.env.SURSA_WEBHOOK_SECRET;
http
.createServer(async (req, res) => {
const parts = [];
for await (const part of req) parts.push(part);
const body = Buffer.concat(parts); // the raw bytes, before any JSON parsing
const valid = req.headers["webhook-signature"]
? verifyStandard(SECRET, req.headers, body)
: verifyLegacy(SECRET, req.headers["x-kb-signature"], body);
if (!valid) {
res.writeHead(400).end();
return;
}
const event = JSON.parse(body);
await saveEventOnce(event); // insert into your table or queue, keyed by event.id; ignore a duplicate
res.writeHead(204).end();
})
.listen(3000);Csak azután válaszolj 2xx kóddal, hogy az eseményt biztonságosan eltároltad. Ha előbb válaszolsz, és utána leáll a
folyamatod, az esemény elvész számodra, mert a Sursa már kézbesítettnek tekinti.
5. Újrapróbálkozások
A sikertelen kézbesítést a Sursa egyre hosszabb szünetekkel próbálja újra: körülbelül 1 perc, 5 perc, 30 perc, 2 óra, 5 óra, 10 óra, 24 óra és még 24 óra után, összesen kilenc kísérlettel, közel három nap alatt. Minden szünet legfeljebb 20%-kal eltérhet, hogy a sok webhooknak szóló újrapróbálkozások ne ugyanabban a pillanatban érkezzenek.
- A
Retry-Afterfejléces429vagy503válasz legalább ennyivel, legfeljebb 24 órával későbbre tolja a következő kísérletet. Akkor használd, ha a rendszered túlterhelt vagy karbantartás alatt áll. - A
410 Goneválasz letiltja a webhookot: csak akkor küldd, ha erre a címre nem kérsz több kézbesítést. - 10 egymást követő sikertelen kísérlet után a webhook szüneteltetve lesz, és a szervezet adminisztrátorai
értesítést kapnak. Az események közben gyűlnek, és amikor a webhookot újraindítják, az adminisztrációs
felületen vagy a
POST /v1/webhooks/{webhook_id}/resumevégponttal, sorrendben kézbesítődnek. - Az utolsó kísérlet után a kézbesítés
exhaustedjelölést kap. Kézzel újra elküldheted.
A webhook kézbesítéseit, az egyes kísérletekkel, a válaszkódoddal és az időzítéssel együtt, a
GET /v1/webhooks/{webhook_id}/deliveries végpont és az
adminisztrációs felület sorolja fel. A
POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver újra
elküld egy kézbesítést. Az újrakézbesítésnek ugyanaz a webhook-id azonosítója.
6. Sorrend és ismétlődések
- Sorrend. Az egy webhooknak szóló kézbesítések szigorúan egymás után mennek ki, a
sequencesorrendjében. Amíg egy kézbesítés újrapróbálása folyik, a későbbiek várnak. A különböző webhookok nem várnak egymásra. - Ismétlődések. Ugyanaz az esemény többször is megérkezhet, például ha a válaszod a visszaúton elveszett. Minden
feldolgozott esemény
webhook-idazonosítóját (ez a törzsben azidis) tárold el, és hagyd ki azt, amelyet már láttál. - Állapotokban gondolkodás. Ugyanannak az objektumnak a változásainál vesd össze a
created_atvagy asequenceértékét az eltárolttal, és hagyd figyelmen kívül azt az eseményt, amely régebbi a meglévő állapotnál. Kétség esetén olvasd ki az aktuális objektumot az API-n keresztül: az esemény azt mondja meg, hogy valami változott, az API azt, hogy most milyen.
7. Egyeztetés az eseménycsatornával
Minden esemény 30 napig az eseménycsatornában is megmarad, függetlenül attól, hogy feliratkozott-e rá webhook. Ha a
fogadód hosszabb ideig nem működött, mint amennyit az újrapróbálkozások lefednek, vagy ellenőrizni szeretnéd, hogy
semmi nem maradt ki, olvasd be a csatornát egy admin szerepkörű és webhooks:manage hatókörű kulccsal:
curl -G https://api.sursa.com/v1/webhook-events \
-H "Authorization: Bearer $SURSA_API_KEY" \
--data-urlencode "type=card.*" \
--data-urlencode "created_since=2026-10-20T00:00:00Z" \
--data-urlencode "limit=200"A csatornában lévő esemény id értéke és szerkezete ugyanaz, mint a kézbesítésben, csak a sequence, a payload és
a test kézbesítési mező hiányzik belőle. Mivel a fogadód kihagyja a már látott eseményeket, az egyeztetést olyan
gyakran futtathatod, ahogy szeretnéd, például óránként.
A GET /v1/webhook-events/{event_id} egy eseményt olvas be az azonosítója
alapján.
8. A titkos kulcs megújítása
A POST /v1/webhooks/{webhook_id}/rotate-secret új titkos kulcsot ad
ki, az előzőt pedig egy átfedési ideig érvényben hagyja: alapértelmezés szerint 24 óráig, legfeljebb 7 napig. Az
átfedési idő alatt minden kézbesítést mindkét titkos kulccsal aláír.
- Újítsd meg a titkos kulcsot, és tárold az újat a régi mellett a fogadódban.
- Fogadd el a kézbesítést, ha bármelyik titkos kulccsal sikeres az ellenőrzés.
- Az átfedési idő után távolítsd el a régi titkos kulcsot.
Ha a régi titkos kulcs kiszivárgott, ne várj, hanem azonnal zárd le az átfedési időt a
POST /v1/webhooks/{webhook_id}/revoke-previous-secret
végponttal, vagy újítsd meg a titkos kulcsot "overlap_hours": 0 értékkel.
Ellenőrzőlista
- Ellenőrizd az egyik aláírást a nyers törzsön, és ellenőrizd az időbélyeget.
- Tárold el az eseményt a
webhook-idszerint, majd 10 másodpercen belül válaszolj2xxkóddal. - Hagyd ki az ismétlődéseket és az elavult eseményeket; ha számít, olvasd ki az aktuális objektumot az API-n keresztül.
- Válaszolj
Retry-Afterfejléces429vagy503kóddal, ha azt szeretnéd, hogy a Sursa lassítson; soha ne válaszolj véletlenül410kóddal. - Hagyd ki az ismeretlen eseménytípusokat és mezőket.
- Ütemezetten egyeztess az eseménycsatornával.