Webhookok
Webhookok
A webhookok segítségével valós idejű értesítéseket kaphatsz, ha események történnek a Workspace-edben — például amikor egy QR-kódot beolvasnak.
Webhook létrehozása
POST /v1/webhooks{ "url": "https://example.com/webhooks/qr3", "events": ["qr.scanned", "qr.created"], "secret": "my-secret-key-min-16-chars"}Válasz
{ "id": "wh_abc123", "url": "https://example.com/webhooks/qr3", "events": ["qr.scanned", "qr.created"], "is_active": true, "secret": "my-secret-key-min-16-chars", "secret_hint": "my-s…", "created_at": "2026-03-15T10:00:00.000Z"}Eseménytípusok
| Esemény | Leírás |
|---|---|
* | Minden esemény |
qr.created | QR-kód létrehozva |
qr.updated | QR-kód frissítve (URL, állapot, címkék) |
qr.deleted | QR-kód törölve |
qr.scanned | QR-kód beolvasva |
qr.flagged | QR-kód nem biztonságosként megjelölve |
webhook.ping | Teszt ping (a /ping végponton keresztül) |
Payload-formátum
Minden webhook payload az alábbi formátummal rendelkezik:
{ "id": "evt_4f0c2a91d7e84b3c9a1f", "type": "qr.scanned", "created": "2026-03-15T10:00:00.000Z", "data": { "code_id": "qr_abc123", "short_code": "r7f3Kx", "scanned_at": "2026-03-15T10:00:00.000Z", "country": "AT", "device_type": "mobile", "os": "iOS", "browser": "Safari", "language": "de", "ab_variant": null, "redirected_to": "https://example.com/landing" }}Kódesemények: qr.created és qr.updated
Mindkét esemény esetében a data pontosan úgy tartalmazza a kódot, ahogyan azt a GET /v1/codes/:id végpont visszaadja. Ide tartozik az appearance is, amely a kódhoz mentett színeket jelöli (a null az alapértelmezett fekete, illetve fehér színt jelenti):
{ "id": "evt_9b2e41c07a5d4f18b3c6", "type": "qr.updated", "created": "2026-09-28T10:00:00.000Z", "data": { "id": "qr_abc123", "short_code": "r7f3Kx", "status": "live", "appearance": { "foreground_color": "#1F4E79", "background_color": null }, "updated_at": "2026-09-28T10:00:00.000Z" }}A példa rövidített. Minden mező megtalálható a Codes API-ban.
Aláírás-ellenőrzés
A qr3.app minden webhook kérést HMAC-SHA256 használatával ír alá:
X-QR3-Signature: sha256=a1b2c3d4e5f6...X-QR3-Event: qr.scannedX-QR3-Delivery: wdl_abc123X-QR3-Attempt: 1User-Agent: qr3-webhooks/1.0Ellenőrzés implementálása
import crypto from "crypto";
function verifySignature( payload: string, signature: string | undefined, secret: string): boolean { const expected = crypto .createHmac("sha256", secret) .update(payload) .digest("hex"); const expectedBytes = Buffer.from(`sha256=${expected}`); const receivedBytes = Buffer.from(signature ?? ""); return ( expectedBytes.length === receivedBytes.length && crypto.timingSafeEqual(expectedBytes, receivedBytes) );}
// In Express: verify the raw body — re-serialised JSON breaks the signature.app.post( "/webhooks/qr3", express.raw({ type: "application/json" }), (req, res) => { const raw = req.body.toString("utf8"); const signature = req.headers["x-qr3-signature"] as string | undefined; if (!verifySignature(raw, signature, process.env.QR3_WEBHOOK_SECRET!)) { return res.status(401).json({ error: "Invalid signature" }); } const event = JSON.parse(raw); console.log(event.type, event.data); res.json({ received: true }); },);import hmacimport hashlib
def verify_signature(payload: str, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode(), payload.encode(), hashlib.sha256 ).hexdigest() received = (signature or "").removeprefix("sha256=") return hmac.compare_digest(expected, received)Újrapróbálkozási logika
Hibák esetén (HTTP >= 300 vagy időtúllépés) a qr3.app megkísérli az újbóli kézbesítést:
| Kísérlet | Késleltetés |
|---|---|
| 1 | Azonnal |
| 2 | 1 perc |
| 3 | 5 perc |
10 egymást követő hiba után a webhook automatikusan deaktiválásra kerül.
Kézbesítési garancia
A qr3.app legalább egyszer kézbesít, nem pontosan egyszer. Ugyanaz az esemény ezért többször is megérkezhet a végpontodra: hiba utáni újrapróbálkozásként vagy egy már elküldött kísérlet megismétlésként.
- A duplikátumokat a payload
idmezője alapján szűrd ki (evt_…). Az eseményhez tartozik, és minden kísérletnél és minden ismétlésnél ugyanaz marad. AzX-QR3-Deliveryaz egyes kísérletet azonosítja: egy kísérlet ismétlése ugyanazt az értéket viseli, egy új kísérlet újat. A már ismertid-ra 2xx-es állapotkóddal válaszolj, ne 4xx-szel: a 4xx sikertelen kísérletnek számít, és 10 egymást követő hiba után a webhook automatikusan deaktiválásra kerül. - Az a kísérlet, amely a létrehozása után egy órával még mindig
pending(például mert az üzenete elveszett a sorban), egy napi takarítás soránfailedállapotba kerül; astatus_codeés aresponse_time_msnullmarad. Ez nem a végpontod hibája, és nem számít bele a 10 egymást követő hibába. A napló erre aDelivery was not completed in time — closed by cleanupszöveget tárolja; a kézbesítési naplók végpontja nem adja vissza. A lezárás nem végleges: ha az üzenet mégis megérkezik, kézbesítésre kerül, és a tényleges eredménye lecseréli ezt a szöveget.
Kézbesítési naplók
GET /v1/webhooks/:id/deliveries{ "data": [ { "id": "wdl_abc123", "event_type": "qr.scanned", "status": "success", "status_code": 200, "response_time_ms": 145, "attempt": 1, "created_at": "2026-03-15T10:00:00.000Z" } ]}A kézbesítési kísérletek 90 napig maradnak meg (az aktuális UTC-nap és az azt megelőző 89 nap), utána automatikusan törlődnek. A megjelenített mezőkön túl a napló tárolja az esemény payloadját és a végpont válaszának első 1 KB-ját; ez a végpont egyiket sem adja vissza.
Teszt ping
Tesztelj egy webhookot azonnal:
POST /v1/webhooks/:id/pingEz egy webhook.ping eseményt küld a végpontod URL-jére.