Webhooks
Webhooks
Webhooks võimaldavad sul vastu võtta reaalajas teavitusi, kui sinu töölaual (workspace) toimuvad sündmused — näiteks kui skannitakse QR-koodi.
Webhooki loomine
POST /v1/webhooks{ "url": "https://example.com/webhooks/qr3", "events": ["qr.scanned", "qr.created"], "secret": "my-secret-key-min-16-chars"}Response
{ "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"}Sündmuste tüübid
| Sündmus | Kirjeldus |
|---|---|
* | Kõik sündmused |
qr.created | QR-kood loodud |
qr.updated | QR-kood uuendatud (URL, staatus, sildid) |
qr.deleted | QR-kood kustutatud |
qr.scanned | QR-kood skannitud |
qr.flagged | QR-kood märgitud ebaturvaliseks |
webhook.ping | Test-ping (läbi /ping otspunkti) |
Payload-vorming
Kõik webhooki payloadid on järgmises vormingus:
{ "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" }}Koodisündmused: qr.created ja qr.updated
Nende mõlema sündmuse puhul on data kood täpselt selline, nagu GET /v1/codes/:id seda tagastab. Selle hulka kuulub appearance ehk koodi juurde salvestatud värvid (null tähistab vaikimisi musta või valget):
{ "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" }}Näide on lühendatud. Kõik väljad on loetletud koodide API-s.
Allkirja kontrollimine
qr3.app allkirjastab iga webhooki päringu kasutades HMAC-SHA256 algoritmi:
X-QR3-Signature: sha256=a1b2c3d4e5f6...X-QR3-Event: qr.scannedX-QR3-Delivery: wdl_abc123X-QR3-Attempt: 1User-Agent: qr3-webhooks/1.0Verifitseerimise rakendamine
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)Uuesti proovimise loogika
Tõrgete korral (HTTP >= 300 või ajalõpp) proovib qr3.app kohaletoimetamist uuesti:
| Katse | Viivitus |
|---|---|
| 1 | Kohe |
| 2 | 1 minut |
| 3 | 5 minutit |
Pärast 10 järjestikust tõrget lülitatakse webhook automaatselt välja.
Kohaletoimetamise garantii
qr3.app toimetab kohale vähemalt üks kord, mitte täpselt üks kord. Sama sündmus võib seetõttu jõuda sinu otspunkti rohkem kui üks kord: korduskatsena pärast tõrget või juba saadetud katse kordusena.
- Dubleerimise tuvastamiseks kasuta payload’i välja
id(evt_…). See kuulub sündmuse juurde ja jääb iga katse ja iga korduse puhul samaks.X-QR3-Deliverytähistab üksikut katset: selle katse kordus kannab sama väärtust, uus katse uut. Kinnita juba tuntudidvastusega 2xx, mitte 4xx: 4xx loetakse nurjunud katseks ning pärast 10 järjestikust tõrget lülitatakse webhook automaatselt välja. - Katse, mis on tund aega pärast loomist endiselt
pending(näiteks seetõttu, et selle järjekorrasõnum läks kaduma), seatakse igapäevase puhastuse käigus olekussefailed;status_codejaresponse_time_msjäävad väärtuselenull. See ei ole sinu otspunkti tõrge ega lähe arvesse 10 järjestikuse tõrke hulka. Logi salvestab selle kohta tekstiDelivery was not completed in time — closed by cleanup; kohaletoimetamise logide lõpp-punkt seda ei tagasta. Sulgemine ei ole lõplik: kui sõnum siiski saabub, toimetatakse see kohale ja selle tegelik tulemus asendab selle teksti.
Kohaletoimetamise logid
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" } ]}Kohaletoimetamiskatseid säilitatakse 90 päeva (jooksev UTC-päev ja 89 eelnevat päeva), seejärel kustutatakse need automaatselt. Lisaks näidatud väljadele salvestab logi sündmuse payload’i ja lõpp-punkti vastuse esimese kilobaidi (1 KB); see lõpp-punkt ei tagasta kumbagi.
Test-ping
Testi webhooki kohe:
POST /v1/webhooks/:id/pingSee saadab sinu otspunkti URL-ile sündmuse webhook.ping.