Webhooks
Webhooks
Izmantojot Webhooks, jūs saņemat reāllaika paziņojumus, kad jūsu Workspace notiek notikumi — piemēram, kad tiek noskenēts QR kods.
Izveidot webhook
POST /v1/webhooks{ "url": "https://example.com/webhooks/qr3", "events": ["qr.scanned", "qr.created"], "secret": "my-secret-key-min-16-chars"}Atbilde
{ "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"}Notikumu veidi
| Notikums | Apraksts |
|---|---|
* | Visi notikumi |
qr.created | QR kods izveidots |
qr.updated | QR kods atjaunināts (URL, statuss, tagi) |
qr.deleted | QR kods dzēsts |
qr.scanned | QR kods noskenēts |
qr.flagged | QR kods atzīmēts kā nedrošs |
webhook.ping | Testa ping (izmantojot /ping galapunktu) |
Payload formāts
Visiem Webhook payload ir šāds formāts:
{ "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" }}Koda notikumi: qr.created un qr.updated
Šajos abos notikumos data ir kods tieši tāds, kādu to atgriež GET /v1/codes/:id. Tajā ietilpst appearance — kodam saglabātās krāsas (null nozīmē noklusējuma melno vai balto krāsu):
{ "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" }}Piemērs ir saīsināts. Visi lauki ir atrodami Kodu API.
Paraksta verifikācija
qr3.app paraksta katru Webhook pieprasījumu ar HMAC-SHA256:
X-QR3-Signature: sha256=a1b2c3d4e5f6...X-QR3-Event: qr.scannedX-QR3-Delivery: wdl_abc123X-QR3-Attempt: 1User-Agent: qr3-webhooks/1.0Verifikācijas ieviešana
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)Atkārtošanas loģika
Kļūdu gadījumā (HTTP >= 300 vai noilgums) qr3.app mēģina veikt piegādi vēlreiz:
| Mēģinājums | Aizture |
|---|---|
| 1 | Tūlīt |
| 2 | 1 minūte |
| 3 | 5 minūtes |
Pēc 10 pēc kārtas sekojošām kļūdām Webhook tiek automātiski deaktivizēts.
Piegādes garantija
qr3.app piegādā vismaz vienu reizi, nevis tieši vienu reizi. Tāpēc viens un tas pats notikums var sasniegt jūsu galapunktu vairāk nekā vienu reizi: kā atkārtots mēģinājums pēc kļūdas vai kā jau nosūtīta mēģinājuma atkārtojums.
- Dublikātus atpazīstiet pēc
idno payload (evt_…). Tas pieder notikumam un katrā mēģinājumā un katrā atkārtojumā paliek tāds pats.X-QR3-Deliverynorāda uz atsevišķu mēģinājumu: šī mēģinājuma atkārtojumam ir tāda pati vērtība, jaunam mēģinājumam — jauna. Jau zināmuidapstipriniet ar 2xx, nevis 4xx: 4xx tiek uzskatīts par neveiksmīgu mēģinājumu, un pēc 10 secīgām kļūdām Webhook tiek automātiski deaktivizēts. - Mēģinājums, kas stundu pēc izveides joprojām ir
pending(piemēram, tāpēc, ka tā ziņojums rindā ir pazudis), ikdienas tīrīšanā tiek pārslēgts uzfailed;status_codeunresponse_time_mspalieknull. Tā nav jūsu galapunkta kļūda, un tā netiek skaitīta starp 10 secīgajām kļūdām. Žurnāls tam saglabā tekstuDelivery was not completed in time — closed by cleanup; piegādes žurnālu galapunkts to neatgriež. Aizvēršana nav galīga: ja ziņojums tomēr atnāk, tas tiek piegādāts un tā faktiskais rezultāts aizstāj šo tekstu.
Piegādes žurnāli
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" } ]}Piegādes mēģinājumi tiek glabāti 90 dienas (kārtējā UTC diena un 89 iepriekšējās dienas), pēc tam tie tiek automātiski dzēsti. Papildus parādītajiem laukiem žurnāls glabā notikuma payload un galapunkta atbildes pirmo kilobaitu (1 KB); šis galapunkts neatgriež ne vienu, ne otru.
Testa ping
Testējiet Webhook nekavējoties:
POST /v1/webhooks/:id/pingTas nosūta webhook.ping notikumu uz jūsu galapunkta URL.