fairo.

Pro

Вебхуки

Fairo надсилає події розіграшу на ваш HTTPS-ендпоінт — щоб підключити CRM, Zapier, власну систему видачі призів чи просто сповіщення в Slack. Адреса задається в налаштуваннях розіграшу, у блоці Pro-можливостей.

Події

  • participant.joined

    Хтось подав заявку на участь

    handle
    — нікнейм учасника
    status
    — passed · rejected · review
    note
    — людське пояснення статусу
    botRisk
    — оцінка ризику бота, 0–100
    referredBy
    — хендл реферера або null
  • giveaway.launched

    Збір заявок відкрито (вручну або за розкладом)

    title
    — назва розіграшу
    endsAt
    — ISO-дата фіналу або null
  • draw.completed

    Переможців визначено

    title
    — назва розіграшу
    winners
    — масив хендлів переможців
    backups
    — масив резервних
    prizes
    — призи за місцями (Pro)
    seed
    — розкритий сід — можна перевірити самому
    verifyUrl
    — лінк на сертифікат чесності

Формат запиту

Метод POST, тіло — JSON. Заголовки: x-fairo-event (назва події) і x-fairo-signature (підпис, див. нижче).

{
  "event": "draw.completed",
  "giveawayId": "fa-2214",
  "sentAt": "2026-07-30T12:34:56.789Z",
  "data": {
    "title": "Сертифікат на 2 000 грн",
    "winners": ["@kateryna.art21"],
    "backups": ["@olena.lviv46"],
    "prizes": ["Сертифікат 2 000 грн"],
    "seed": "fairo-8kd02mz1qx83",
    "verifyUrl": "https://fairo.app/verify/fa-2214"
  }
}

Перевірка підпису

Кожен запит підписаний HMAC-SHA256 від сирого тіла, ключ — секрет вашого розіграшу (показується біля поля вебхука). Обовʼязково звіряйте підпис: інакше будь-хто, знаючи вашу адресу, зможе надсилати фейкові події.

import { createHmac, timingSafeEqual } from "node:crypto";

// Секрет розіграшу — на сторінці редагування, у блоці Pro
const SECRET = process.env.FAIRO_WEBHOOK_SECRET;

export function POST(req, rawBody) {
  const signature = req.headers["x-fairo-signature"];
  const expected = createHmac("sha256", SECRET)
    .update(rawBody)        // саме СИРЕ тіло, не JSON.stringify(parsed)
    .digest("hex");

  const ok =
    signature?.length === expected.length &&
    timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  if (!ok) return new Response("bad signature", { status: 401 });

  const { event, giveawayId, data } = JSON.parse(rawBody);
  // …ваша логіка: CRM, видача призу, Slack-повідомлення
  return new Response("ok");
}

Те саме на Python:

import hmac, hashlib, json

SECRET = os.environ["FAIRO_WEBHOOK_SECRET"]

def handle(headers, raw_body: bytes):
    expected = hmac.new(
        SECRET.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(headers["x-fairo-signature"], expected):
        return 401, "bad signature"

    payload = json.loads(raw_body)
    ...
    return 200, "ok"

Підпис рахується від сирого тіла запиту. Якщо спершу розпарсити JSON, а потім зібрати рядок назад, підпис не зійдеться — порядок ключів і пробіли можуть відрізнятися.

Доставка й обмеження

  • · Лише HTTPS і публічні адреси. Локальні й приватні мережі (localhost, 10.x, 192.168.x, 172.16–31.x, 169.254.x) відхиляються — це захист від запитів у внутрішню інфраструктуру.
  • · Таймаут 5 секунд. Відповідайте швидко: важку роботу ставте в чергу вже у себе.
  • · Доставка best-effort, без повторів. Недоступний ендпоінт не блокує участь чи розіграш — подія просто губиться, а помилка лишається в логах Fairo.
  • · Порядок подій не гарантований. Джерело істини — завжди сторінка сертифіката.

Питання про чесність розіграшів — сторінка «Чесність».