Skip to content

Webhookit

Webhookit

Webhookien avulla vastaanotat reaaliaikaisia ilmoituksia, kun työtilassasi tapahtuu jotain – esimerkiksi kun QR-koodi skannataan.

Luo webhook

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

Vastaus

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

Tapahtumatyypit

TapahtumaKuvaus
*Kaikki tapahtumat
qr.createdQR-koodi luotu
qr.updatedQR-koodi päivitetty (URL, tila, tagit)
qr.deletedQR-koodi poistettu
qr.scannedQR-koodi skannattu
qr.flaggedQR-koodi merkitty epäturvalliseksi
webhook.pingTesti-ping (kautta /ping-päätepisteen)

Payload-muoto

Kaikilla webhook-payloadeilla on seuraava muoto:

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

Kooditapahtumat: qr.created ja qr.updated

Näissä molemmissa tapahtumissa data on koodi täsmälleen sellaisena kuin GET /v1/codes/:id sen palauttaa. Tähän sisältyy appearance eli koodiin tallennetut värit (null tarkoittaa oletusarvoista mustaa tai valkoista):

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

Esimerkkiä on lyhennetty. Kaikki kentät löytyvät koodirajapinnasta.

Allekirjoituksen varmennus

qr3.app allekirjoittaa jokaisen webhook-pyynnön käyttämällä HMAC-SHA256-menetelmää:

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

Toteuta varmennus

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

Uudelleenyrityslogiikka

Virhetilanteissa (HTTP >= 300 tai aikakatkaisu) qr3.app yrittää toimitusta uudelleen:

YritysViive
1Heti
21 minuutti
35 minuuttia

10 peräkkäisen virheen jälkeen webhook poistetaan automaattisesti käytöstä.

Toimitustakuu

qr3.app toimittaa vähintään kerran, ei täsmälleen kerran. Sama tapahtuma voi siksi saapua päätepisteeseesi useammin kuin kerran: uudelleenyrityksenä virheen jälkeen tai jo lähetetyn yrityksen toistona.

  • Poista kaksoiskappaleet payloadin id-kentän avulla (evt_…). Se kuuluu tapahtumalle ja pysyy samana jokaisessa yrityksessä ja jokaisessa toistossa. X-QR3-Delivery yksilöi yksittäisen yrityksen: kyseisen yrityksen toisto kantaa saman arvon, uusi yritys uuden. Vastaa jo tuntemaasi id-arvoon 2xx-tilakoodilla, älä 4xx-tilakoodilla: 4xx lasketaan epäonnistuneeksi yritykseksi, ja 10 peräkkäisen virheen jälkeen webhook poistetaan automaattisesti käytöstä.
  • Yritys, joka on yhä pending tunnin kuluttua luomisestaan (esimerkiksi koska sen jonoviesti katosi), asetetaan päivittäisessä siivouksessa tilaan failed; status_code ja response_time_ms pysyvät arvossa null. Tämä ei ole päätepisteesi virhe, eikä sitä lasketa mukaan 10 peräkkäiseen virheeseen. Loki tallentaa siitä tekstin Delivery was not completed in time — closed by cleanup; toimituslokien päätepiste ei palauta sitä. Sulkeminen ei ole lopullista: jos viesti saapuu vielä, se toimitetaan ja sen todellinen tulos korvaa tämän tekstin.

Toimituslokit

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

Toimitusyritykset säilytetään 90 päivää (kuluva UTC-päivä ja 89 edeltävää päivää), minkä jälkeen ne poistetaan automaattisesti. Näytettyjen kenttien lisäksi loki tallentaa tapahtuman payloadin ja päätepisteen vastauksen ensimmäisen kilotavun (1 KB); tämä päätepiste ei palauta kumpaakaan.

Testi-ping

Testaa webhookia heti:

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

Tämä lähettää webhook.ping-tapahtuman päätepisteen URL-osoitteeseen.