Hoppa till innehåll

Webhooks

Webhooks

Med webhooks tar du emot realtidsaviseringar när händelser inträffar i din workspace — t.ex. när en QR-kod skannas.

Skapa webhook

Terminal window
POST /v1/webhooks
{
"url": "https://example.com/webhooks/qr3",
"events": ["qr.scanned", "qr.created"],
"secret": "my-secret-key-min-16-chars"
}

Svar

{
"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"
}

Händelsetyper

HändelseBeskrivning
*Alla händelser
qr.createdQR-kod skapad
qr.updatedQR-kod uppdaterad (URL, status, taggar)
qr.deletedQR-kod raderad
qr.scannedQR-kod skannad
qr.flaggedQR-kod markerad som osäker
webhook.pingTest-ping (via /ping-endpoint)

Payload-format

Alla webhook-payloads har detta format:

{
"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"
}
}

Kodhändelser: qr.created och qr.updated

Vid dessa två händelser är data koden exakt så som GET /v1/codes/:id returnerar den. Detta inkluderar appearance, de färger som sparats på koden (null står för standard-svart respektive -vitt):

{
"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"
}
}

Exemplet är förkortat. Alla fält finns i API:et för koder.

Signaturverifiering

qr3.app signerar varje webhook-begäran med HMAC-SHA256:

X-QR3-Signature: sha256=a1b2c3d4e5f6...
X-QR3-Event: qr.scanned
X-QR3-Delivery: wdl_abc123
X-QR3-Attempt: 1
User-Agent: qr3-webhooks/1.0

Implementera verifiering

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 });
},
);

Retry-logik

Vid fel (HTTP >= 300 eller timeout) försöker qr3.app leverera igen:

FörsökFördröjning
1Omedelbart
21 minut
35 minuter

Efter 10 misslyckade försök i rad inaktiveras webhooken automatiskt.

Leveransgaranti

qr3.app levererar minst en gång, inte exakt en gång. Samma händelse kan därför nå din endpoint mer än en gång: som nytt försök efter ett fel eller som upprepning av ett försök som redan har skickats.

  • Avduplicera med id i payloaden (evt_…). Det hör till händelsen och är detsamma för varje försök och varje upprepning. X-QR3-Delivery anger det enskilda försöket: en upprepning av det försöket har samma värde, ett nytt försök ett nytt. Besvara ett id som du redan känner till med 2xx, inte med 4xx: Ett 4xx räknas som ett misslyckat försök, och efter 10 misslyckade försök i rad inaktiveras webhooken automatiskt.
  • Ett försök som fortfarande är pending en timme efter att det skapades (till exempel för att dess meddelande i kön gick förlorat) sätts till failed av en daglig städning; status_code och response_time_ms förblir null. Det är inget fel hos din endpoint och räknas inte med bland de 10 misslyckade försöken i rad. Loggen lagrar texten Delivery was not completed in time — closed by cleanup för det; endpointen för leveransloggar returnerar den inte. Stängningen är inte slutgiltig: kommer meddelandet ändå fram, levereras det, och dess verkliga resultat ersätter texten.

Leveransloggar

Terminal window
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"
}
]
}

Leveransförsök sparas i 90 dagar (den aktuella UTC-dagen och de 89 föregående dagarna) och raderas därefter automatiskt. Utöver de visade fälten lagrar loggen händelsens payload och de första 1 KB av endpointens svar; denna endpoint returnerar ingetdera.

Test-ping

Testa en webhook direkt:

Terminal window
POST /v1/webhooks/:id/ping

Detta skickar en webhook.ping-händelse till din endpoint-URL.