/v1/account/webhook-endpoints
account/webhook-endpoints
Webhookok: hová küldi a Monitoring az új Signals-t, és hogyan ellenőrzöd őket.
A webhook-végpont a szervered HTTPS-címe, ahová a monitoraid új jelzéseit küldjük. Minden üzenet alá van írva, így ellenőrizheted, hogy tőlünk származik.
Költségek és limitek
| Mi | Kredit |
|---|---|
| Webhook-végpontok és kézbesítések kezelése | ingyenes |
Limit: percenként 30 kérés. Létrehozás, módosítás és törlés: percenként 10. Ellenőrzés, teszt és új secret: percenként 6.
Fiókonként legfeljebb 10 webhook-végpont lehet; az archiváltak nem számítanak. Minden webhook-végpont legfeljebb 10 egyéni headert hordozhat.
Admin token vagy API-kulcs
Az admin tokenre van szükséged az aláíró secret létrehozásához, valamint webhook-végpontok engedélyezéséhez, letiltásához és archiválásához. A Fiók › API-kulcsok alatt hozod létre, a „Fejlesztőknek: kulcsok kezelése API-n keresztül” részben. Egy évig érvényes, és egy új felváltja a régit.
Az admin tokent Authorization: Bearer AZ_ADMIN_TOKENED formában küldd. Minden más hívás az API-kulccsal is működik.
Ahogy az account/monitors esetén, minden írási híváshoz kell az Idempotency-Key header.
Végpontok
| Metódus | Útvonal | Cél | Hozzáférés |
|---|---|---|---|
| GET |
/v1/account/webhook-endpoints |
Az összes webhook-végpont, archiváltak nélkül. | API-kulcs vagy admin token |
| POST |
/v1/account/webhook-endpoints |
Webhook-végpont létrehozása. | Admin token |
| POST |
/v1/account/webhook-endpoints/{id}/verify |
Ellenőrzés. | API-kulcs vagy admin token |
| POST |
/v1/account/webhook-endpoints/{id}/test |
Tesztüzenet küldése. | API-kulcs vagy admin token |
| POST |
/v1/account/webhook-endpoints/{id}/rotate-secret |
Új aláíró secret. | Admin token |
| POST |
/v1/account/webhook-endpoints/{id}/enable |
Engedélyezés. | Admin token |
| POST |
/v1/account/webhook-endpoints/{id}/disable |
Letiltás. | Admin token |
| DELETE |
/v1/account/webhook-endpoints/{id} |
Archiválás. | Admin token |
| GET |
/v1/account/webhook-deliveries |
Az utolsó 50 kézbesítés. | API-kulcs vagy admin token |
| POST |
/v1/account/webhook-deliveries/{id}/retry |
Egy sikertelen kézbesítés újraküldése. | API-kulcs vagy admin token |
| GET |
/v1/account/webhook-events |
Az utolsó 50 esemény. | API-kulcs vagy admin token |
Webhook-végpont létrehozása
curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints" \ -H "Authorization: Bearer AZ_ADMIN_TOKENED" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "name": "CRM-Sync", "url": "https://hooks.example.com/handelsregister", "headers": { "X-Receiver-Token": "A_FOGADO_TOKENED" } }'
import uuid import requests response = requests.post( "https://handelsregister.ai/api/v1/account/webhook-endpoints", headers={ "Authorization": "Bearer AZ_ADMIN_TOKENED", "Idempotency-Key": str(uuid.uuid4()), }, json={ "name": "CRM-Sync", "url": "https://hooks.example.com/handelsregister", "headers": { "X-Receiver-Token": "A_FOGADO_TOKENED" } }, ) data = response.json()
const response = await fetch("https://handelsregister.ai/api/v1/account/webhook-endpoints", { method: "POST", headers: { "Authorization": "Bearer AZ_ADMIN_TOKENED", "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json", }, body: JSON.stringify({ "name": "CRM-Sync", "url": "https://hooks.example.com/handelsregister", "headers": { "X-Receiver-Token": "A_FOGADO_TOKENED" } }), }); const data = await response.json();
| Név | Típus | Leírás | Példa |
|---|---|---|---|
| Idempotency-KeyHeader | stringkötelező | Műveletenként egy egyedi érték, legjobb egy UUID. Lásd Idempotency-Key.Limitek: 1–128 karakter |
req-20260802-01 |
| nameBody | stringkötelező | Egy név a saját tájékozódásodra.Limitek: 1–120 karakter |
CRM-Sync |
| urlBody | string (uri)kötelező |
A szervered címe: nyilvánosan elérhető, https a 443-as porton, IP-cím helyett hostnévvel.Limitek: 1–2 048 karakter |
https://hooks.example.com/handelsregister |
| headersBody | objectopcionális | Minden üzenettel küldött egyéni headerek, például egy token a szerverednek.Limitek: legfeljebb 10 elem |
{"X-Receiver-Token":"A_FOGADO_TOKENED"} |
A headernevek nem kezdődhetnek webhook- előtaggal, és nem írhatják felül a szabványos headereket, például a Content-Type vagy a Host headert. Az értékek csak írhatók; később az API csak a neveket mutatja.
A válasz tartalmazza a whsec_… aláíró secretet. Csak ez egyszer mutatjuk meg. Azonnal mentsd el.
1{2 "endpoint": {3 "id": "wep_01m6b3j1g0cdz98hw8zdefbf52",4 "name": "CRM-Sync",5 "url": "hooks.example.com/…",6 "status": "pending_verification",7 "verified_at": null,8 "header_names": [9 "X-Receiver-Token"10 ],11 "consecutive_failures": 0,12 "created_at": "2026-11-01T08:28:01+00:00"13 },14 "signing_secret": "whsec_…",15 "meta": {16 "message": "Endpoint created. Store the signing secret now — it is shown only once.",17 "request_credit_cost": 0,18 "credits_remaining": 14519 }20}
Egy új webhook-végpont pending_verification státuszú, és csak az ellenőrzése után kap üzeneteket. A név, az URL és a headerek később nem módosíthatók. Új címhez hozz létre új webhook-végpontot.
A válaszokban az url csak a hostot mutatja; az útvonalat /… formára rövidítjük.
Ellenőrzés
A POST …/{id}/verify hívással egy endpoint.verification típusú, aláírt üzenetet küldünk az URL-edre. A data.challenge egy véletlen értéket tartalmaz.
A szervered 15 másodpercen belül 2xx státusszal válaszol, és az értéket a webhook-verification válaszheaderben adja vissza. A webhook-végpont ezután active lesz.
1{2 "id": "msg_01m6b3jsqq7ntwzkdj099ayraz",3 "type": "endpoint.verification",4 "timestamp": "2026-11-01T08:28:26.743Z",5 "schema_version": 1,6 "data": {7 "challenge": "189fb34b2af819bb13cb47d9806dbf30"8 }9}
curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/verify" \ -H "x-api-key: AZ_API_KULCSOD" \ -H "Idempotency-Key: $(uuidgen)"
import uuid import requests response = requests.post( "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/verify", headers={ "x-api-key": "AZ_API_KULCSOD", "Idempotency-Key": str(uuid.uuid4()), }, ) data = response.json()
const response = await fetch("https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/verify", { method: "POST", headers: { "x-api-key": "AZ_API_KULCSOD", "Idempotency-Key": crypto.randomUUID(), }, }); const data = await response.json();
Ha sikerül, az API "verified": true értékkel válaszol, különben 422-vel és "verified": false értékkel. Új kísérlethez új Idempotency-Key kell.
Webhook-végpontonként óránként 6 kísérlet lehetséges.
1{2 "verified": true,3 "endpoint": {4 "id": "wep_01m6b3j1g0cdz98hw8zdefbf52",5 "name": "CRM-Sync",6 "url": "hooks.example.com/…",7 "status": "active",8 "verified_at": "2026-11-01T08:28:26+00:00",9 "header_names": [10 "X-Receiver-Token"11 ],12 "consecutive_failures": 0,13 "created_at": "2026-11-01T08:28:01+00:00"14 },15 "meta": {16 "request_credit_cost": 0,17 "credits_remaining": 14518 }19}
Ezután a POST …/{id}/test bármikor küld egy endpoint.test típusú, aláírt tesztüzenetet. A válasz a kézbesítés eredményét mutatja.
1{2 "delivery": {3 "id": "del_01m6b3jsyrnc3sz1mt31dq6107",4 "message_id": "msg_01m6b3jsynxzhvagm7n2nq7mpv",5 "event_id": "evt_01m6b3jsynxzhvagm7n2nq7mpt",6 "event_type": "endpoint.test",7 "endpoint_id": "wep_01m6b3j1g0cdz98hw8zdefbf52",8 "status": "succeeded",9 "attempts": 1,10 "last_status_code": 200,11 "last_error_category": null,12 "next_attempt_at": "2026-11-01T08:28:26+00:00",13 "succeeded_at": "2026-11-01T08:28:26+00:00",14 "created_at": "2026-11-01T08:28:26+00:00"15 },16 "meta": {17 "request_credit_cost": 0,18 "credits_remaining": 14519 }20}
Az aláírás ellenőrzése
Minden üzenetet a nyílt Standard Webhooks szabvány szerint írunk alá. Ellenőrizd az aláírást, mielőtt feldolgoznál egy üzenetet.
| Header | Jelentés |
|---|---|
| webhook-id |
Az üzenet azonosítója (msg_…). Újrapróbáláskor ugyanaz marad. Ezzel ismered fel az ismétlődéseket.
|
| webhook-timestamp | Ennek a kézbesítési kísérletnek az ideje Unix-másodpercben. |
| webhook-signature |
Egy vagy több v1,<Base64> alakú aláírás, szóközzel elválasztva.
|
Így ellenőrzöl egy üzenetet:
- Vedd a secret
whsec_utáni részét, és dekódold Base64-ből. Ez a kulcs. - Állítsd össze a
webhook-id.webhook-timestamp.Bodyszöveget a két headerből és a változatlan bodyból. - Számíts belőle HMAC-SHA256-ot a kulccsal, és kódold az eredményt Base64-be.
- Hasonlítsd össze az eredményt konstans idő alatt a header minden
v1aláírásával. Ha egy egyezik, az üzenet valódi.
A bodyt pontosan úgy használd, ahogy megérkezik, ne újraszerializált JSON-ként. Utasítsd el azokat az üzeneteket, amelyek időbélyege több mint öt perccel eltér.
import base64, hashlib, hmac, time def verify_webhook(secret: str, headers: dict, body: bytes) -> bool: msg_id = headers["webhook-id"] timestamp = headers["webhook-timestamp"] # Az öt percnél régebbi üzenetek elutasítása if abs(time.time() - int(timestamp)) > 300: return False key = base64.b64decode(secret.removeprefix("whsec_")) signed = f"{msg_id}.{timestamp}.".encode() + body expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode() # Secret-rotáció közben két aláírás van a headerben for part in headers["webhook-signature"].split(" "): version, _, signature = part.partition(",") if version == "v1" and hmac.compare_digest(signature, expected): return True return False
import crypto from "node:crypto"; export function verifyWebhook(secret, headers, rawBody) { const msgId = headers["webhook-id"]; const timestamp = headers["webhook-timestamp"]; // Az öt percnél régebbi üzenetek elutasítása if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64"); const expected = crypto .createHmac("sha256", key) .update(`${msgId}.${timestamp}.${rawBody}`) .digest(); // Secret-rotáció közben két aláírás van a headerben return headers["webhook-signature"].split(" ").some((part) => { const [version, signature] = part.split(","); const given = Buffer.from(signature ?? "", "base64"); return version === "v1" && given.length === expected.length && crypto.timingSafeEqual(given, expected); }); }
Saját kód írása helyett használhatod a hivatalos Standard Webhooks könyvtárakat, például a standardwebhooks csomagot Pythonhoz és JavaScripthez.
A POST …/{id}/rotate-secret hívással új secretet kapsz, egyszer, a válaszban. 7 napig az üzenetek két aláírást hordoznak, egyet az új és egyet a régi secrettel. Így leállás nélkül állsz át.
Payload
Minden üzenet ugyanolyan borítékú JSON-objektum. Hogy mi alkotja az eseményt, az a data alatt van.
| Mező | Jelentés |
|---|---|
| id |
Az üzenet azonosítója, megegyezik a webhook-id headerrel.
|
| event_id |
Az esemény azonosítója (evt_…).
|
| type | Az esemény típusa, lásd lent. |
| timestamp | Mikor észleltük az eseményt, UTC-ben. |
| schema_version |
A felépítés verziója, jelenleg 1.
|
| data | Az esemény adatai. |
| Eseménytípus | Jelentés |
|---|---|
| organization.signal.detected |
Egy monitor új jelzést talált. A data.signal felépítése ugyanaz, mint egy jelzésé a signals végpontban, plusz a data.monitor és a data.links.signal alatt a jelzés linkje az API-ban.
|
| organization.signal.sample |
Példa a cég valódi előzményeiből, a Fiók › Monitoring alatt küldve. Ugyanaz a felépítés, mint fent, plusz data.sample: true.
|
| endpoint.test |
Tesztüzenet a /test révén.
|
| endpoint.verification | Challenge az ellenőrzéskor. |
1{2 "id": "msg_01m65pqg9x0176cteqvtxw68sa",3 "event_id": "evt_01m65pqg9sx7mwmw0edc1mfbbe",4 "type": "organization.signal.sample",5 "timestamp": "2026-10-30T06:07:37.000Z",6 "schema_version": 1,7 "data": {8 "monitor": {9 "id": "mon_01m65pn3vm11sjjp1j076cbvec",10 "type": "organization.signals"11 },12 "sample": true,13 "signal": {14 "event": {15 "id": "36c7c8fddf99bb0299d6815bcaa01013",16 "topic": "TRANSFORMATIONS",17 "topic_name": {18 "de": "Umwandlungen & M&A",19 "en": "Transformations & M&A"20 },21 "occurred_on": "2022-12-22",22 "announced_on": "2023-08-10",23 "date_basis": "SOURCE_EVENT"24 },25 "organization": {26 "type": "ORGANIZATION",27 "entity_id": "94eb910fef13f94585c48c263fa446d4",28 "current_profile": {29 "name": "Hetzner Online GmbH",30 "registration": {31 "register_type": "HRB",32 "register_number": "6089",33 "court": "Ansbach",34 "register_locality": "Ansbach",35 "registration_authority_id": "D3201",36 "register_entity_name": "Ansbach HRB 6089"37 },38 "legal_form": {39 "name": {40 "de": "Gesellschaft mit beschränkter Haftung"41 },42 "short": {43 "de": "GmbH"44 }45 },46 "seat": {47 "city": "Gunzenhausen"48 },49 "status": {50 "code": "ACTIVE"51 }52 }53 },54 "parties": {55 "counterparty": {56 "entity": {57 "type": "ORGANIZATION",58 "entity_id": "dd4d2d6854b2dbd1ffce74e669f3852e",59 "current_profile": {60 "name": "Headtrip GmbH",61 "registration": {62 "register_type": "HRB",63 "register_number": "33432",64 "court": "Nürnberg",65 "register_locality": "Nuremberg",66 "registration_authority_id": "D3310",67 "register_entity_name": "Nürnberg HRB 33432"68 },69 "legal_form": {70 "name": {71 "de": "Gesellschaft mit beschränkter Haftung"72 },73 "short": {74 "de": "GmbH"75 }76 },77 "seat": {78 "city": "Nürnberg"79 },80 "status": {81 "code": "INACTIVE"82 }83 }84 },85 "relationship": "TRANSFERRING_COMPANY"86 }87 },88 "register_entry": {89 "entry_number": "3",90 "entry_date": "2023-08-10",91 "description_text": "Verschmelzung zur Aufnahme der Headtrip GmbH",92 "context": "PRIMARY",93 "flags": []94 },95 "source": {96 "kind": "COMMERCIAL_REGISTER"97 },98 "details": {99 "schema": "TRANSFORMATION",100 "kind": "MERGER",101 "specifics": {102 "merger_target": "ABSORPTION"103 },104 "direction": "INCOMING",105 "is_cross_border": false,106 "resolution_date": "2022-12-22",107 "agreement_date": "2022-12-22"108 }109 },110 "links": {111 "signal": "https://handelsregister.ai/api/v1/signals/36c7c8fddf99bb0299d6815bcaa01013"112 }113 }114}
Egy üzenet legfeljebb 20 KB. Ha egy jelzés nagyobb lesz, a details és a register_entry kimarad, és a data.truncated értéke true. Ilyenkor a teljes jelzést a linken keresztül kéred le.
Hagyd figyelmen kívül az általad nem ismert eseménytípusokat, és ennek ellenére válaszolj 2xx-szel. Így a szervered stabil marad, amikor új típusok jönnek.
Kézbesítések és újrapróbálások
Egy kézbesítés akkor számít sikeresnek, ha a szervered 15 másodpercen belül 2xx státusszal válaszol. Átirányításokat nem követünk.
Különben újra próbálkozunk: legfeljebb 10 kísérlettel, növekvő szünetekkel nagyjából három napon át. Ha a szervered Retry-After headert küld, azt követjük.
Ha a szervered 410-zel válaszol, leállítjuk a kézbesítést, és letiltjuk a webhook-végpontot.
Ha 5 egymást követő kézbesítés végleg sikertelen, a webhook-végpontot disabled_failures okkal letiltjuk. A POST …/{id}/enable hívással újra bekapcsolod.
webhook-id alapján ismerd fel. Válaszolj gyorsan, és csak utána dolgozd fel az üzenetet.
A GET /v1/account/webhook-deliveries az utolsó 50 kézbesítést mutatja, a ?endpoint=wep_… paraméterrel egy webhook-végpontra. A GET /v1/account/webhook-events az utolsó 50 eseményt mutatja.
Egy végleg sikertelen kézbesítést a POST /v1/account/webhook-deliveries/{id}/retry hívással küldesz újra, ugyanazzal a webhook-id-val és ugyanazzal a bodyval. Ez 30 napig működik; ennyi ideig őrizzük az üzeneteket.
| Státusz | Jelentés |
|---|---|
| withheld | Vár, amíg az ellenőrzést elszámoljuk. |
| pending | Küldésre vár. |
| in_flight | Éppen küldés alatt. |
| retry_wait | A következő kísérletre vár. |
| succeeded | Kézbesítve. |
| exhausted | Minden kísérlet sikertelen volt. |
| blocked_endpoint | A webhook-végpont jelenleg nem tud fogadni. |
| cancelled | Megszakítva, például mert az üzenet lejárt. |
Engedélyezés, letiltás, archiválás
A POST …/{id}/disable egyelőre leállítja a kézbesítést. A más webhook-végpont nélküli monitorok ekkor paused_configuration okkal szünetelnek.
A POST …/{id}/enable újra bekapcsol egy ellenőrzött webhook-végpontot. Ezután folytasd a szüneteltetett monitorokat a resume hívással.
A DELETE …/{id} véglegesen archiválja a webhook-végpontot. Ha csak egy időre akarod leállítani, inkább tiltsd le.
curl -X POST "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/disable" \ -H "Authorization: Bearer AZ_ADMIN_TOKENED" \ -H "Idempotency-Key: $(uuidgen)"
import uuid import requests response = requests.post( "https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/disable", headers={ "Authorization": "Bearer AZ_ADMIN_TOKENED", "Idempotency-Key": str(uuid.uuid4()), }, ) data = response.json()
const response = await fetch("https://handelsregister.ai/api/v1/account/webhook-endpoints/wep_01m6b3j1g0cdz98hw8zdefbf52/disable", { method: "POST", headers: { "Authorization": "Bearer AZ_ADMIN_TOKENED", "Idempotency-Key": crypto.randomUUID(), }, }); const data = await response.json();
Hibák
A hibák felépítése ugyanaz, mint az account/monitors esetén: egy kód az error, a magyarázat a meta.message mezőben. A hibák nem kerülnek semmibe.
| Státusz | Ok | Mit tehetsz |
|---|---|---|
| 400 |
Az api_key az URL-ben van. |
Küldd a kulcsot az x-api-key headerben. |
| 400 |
Az URL nem engedélyezett célként, egy header nem megengedett, elérted a maximális számot, vagy a webhook-végpont nem aktív. A meta.message megadja az okot. |
Javítsd az értéket, vagy előbb ellenőrizd a végpontot. |
| 400 |
Az Idempotency-Key formátuma érvénytelen. |
Javítsd a formátumot, például használj UUID-t. |
| 401 | Nincs API-kulcs vagy token, vagy érvénytelen. |
Küldd a kulcsot az x-api-key headerben. |
| 403 |
A bearer tokenből hiányzik az account:read jogosultság. |
Használd az API-kulcsot vagy egy account:read jogosultságú tokent. |
| 403 | A fiókod e-mail-címe még nincs megerősítve, vagy a fiók zárolva van. | Nyisd meg a megerősítő e-mailben lévő linket. Ha a fiók zárolva van, a support segít. |
| 403 | Ehhez a híváshoz az admin token kell. Egy API-kulcs nem elég, és más token sem. |
Küldd Authorization: Bearer headerrel és az admin tokennel. |
| 403 |
A bearer tokenből hiányzik a monitoring:manage jogosultság. |
Használd az API-kulcsot vagy az admin tokent. |
| 404 | Nincs ilyen azonosítójú webhook-végpont vagy kézbesítés. | Ellenőrizd az azonosítót. |
| 409 |
Az Idempotency-Key kulcsot már más adatokkal használták, 24 óránál régebbi, vagy még feldolgozás alatt áll. |
Használj új kulcsot, vagy várj egy kicsit. |
| 422 |
Egy érték hiányzik vagy érvénytelen, például a name vagy az url. Az errors megnevezi a mezőt. |
Javítsd az értéket, és próbáld újra új kulccsal. |
| 422 | Az ellenőrzés sikertelen. | Ellenőrizd a challenge headert és a 2xx státuszt, aztán próbáld újra új kulccsal. |
| 428 |
Hiányzik az Idempotency-Key header. |
Küldd el a headert. |
| 429 | Túl sok kérés. |
Próbáld újra a Retry-After után. |
| 503 | Az ellenőrzés, a tesztelés és az újraküldés átmenetileg nem érhető el. | Próbáld újra kicsit később. |
1{2 "error": "invalid_request",3 "meta": {4 "message": "The endpoint is not active.",5 "request_credit_cost": 0,6 "credits_remaining": 1457 }8}