Webhooky

Ověření a zpracování webhooků

Kdykoli nastane vybraná událost, QRflex pošle HTTP POST na vaši adresu. Každý požadavek je podepsaný — ověříte si tak, že přišel opravdu od nás a cestou se nezměnil. Webhooky zapnete v Nastavení → Webhooky.

Formát

Obálka a hlavičky

Tělo každého požadavku má stejnou obálku; metadata pro ověření jsou v hlavičkách.

Obálka (tělo POST)

{
  "id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
  "event": "scan.created",
  "createdAt": "2026-10-12T10:09:50+02:00",
  "data": {
    "qrCodeId": "0f8e...",
    "deviceType": "mobile",
    "countryCode": "CZ"
  }
}

Hlavičky požadavku

webhook-idJednoznačné ID doručení. Použijte ho pro deduplikaci (idempotenci).
webhook-timestampČas odeslání (unixové sekundy). Použijte ho pro ochranu proti replay útokům.
webhook-signaturePodpis ve formátu v1,<base64>. Může obsahovat víc podpisů oddělených mezerou.
Content-TypeVždy application/json.
Události

Jaké události přijdou

Vyberte, které události chcete odebírat. Obsah pole data se liší podle typu události.

eventPopisdata
scan.createdQR kód byl naskenován.qrCodeId, deviceType, countryCode
conversion.createdNávštěvník provedl konverzi (kliknutí na CTA nebo odeslání formuláře).qrCodeId, eventType, sessionToken, pageUrl, occurredAt
qr.createdByl vytvořen nový QR kód.id, name, type
qr.limit_warningQR kód se blíží limitu skenů.used, limit, percent
campaign.publishedKampaň byla publikována.id, name
bot.detectedSken byl vyhodnocen jako bot.qrCodeId, botName, botCategory
webhook.testTestovací událost z tlačítka Test v nastavení.message
Bezpečnost

Jak ověřit podpis

Z hlaviček složte řetězec {webhook-id}.{webhook-timestamp}.{tělo} a spočtěte jeho HMAC-SHA256 se svým secretem — ze secretu nejdřív odřízněte prefix whsec_ a zbytek dekódujte z base64. Výsledek pak v konstantním čase porovnejte s hodnotou v hlavičce webhook-signature (bez prefixu v1,).

function verify(string $secret, array $headers, string $rawBody): bool {
    $id  = $headers['webhook-id'] ?? '';
    $ts  = $headers['webhook-timestamp'] ?? '';
    $hdr = $headers['webhook-signature'] ?? '';

    if (abs(time() - (int) $ts) > 300) return false;           // replay

    $key      = base64_decode(substr($secret, strlen('whsec_')));
    $signed   = $id . '.' . $ts . '.' . $rawBody;
    $expected = base64_encode(hash_hmac('sha256', $signed, $key, true));

    foreach (explode(' ', $hdr) as $part) {                    // "v1,xxx v1,yyy"
        [$ver, $sig] = array_pad(explode(',', $part, 2), 2, '');
        if ($ver === 'v1' && hash_equals($expected, $sig)) return true;
    }
    return false;
}

Ověřujte vždy proti hodnotám z hlaviček, ne z těla. Požadavky starší než pár minut odmítejte (ochrana proti replay útoku) a případné duplicity poznáte podle webhook-id.

Doručování

Opakování a vypnutí

Pokud váš server nevrátí 2xx, doručení opakujeme.

Až 5 pokusů s prodlevami 5 minut, 1 hodina, 6 hodin a 12 hodin.

Po 10 selháních v řadě webhook automaticky vypneme (status disabled).

Historii doručení uchováváme 30 dní.

Doručení si vyzkoušejte tlačítkem Test v nastavení; pro rychlou kontrolu bez vlastního serveru použijte webhook.site.