Документация

Webhooks

Получайте события доставки в реальном времени: delivered, bounced, complained, opened, clicked.

Настройка

В dashboard укажите URL, на который мы будем слать POST-запросы с событиями. При первой конфигурации автоматически генерируется секрет для HMAC-подписи — сохраните его, он показывается только один раз.

Формат события

json
{
  "id": "ev_a1b2c3...",
  "type": "email.delivered",
  "created_at": "2026-04-24T10:15:32Z",
  "data": {
    "email_id": "em_abc123...",
    "message_id": "<abc123@yourdomain.ru>",
    "to": ["user@example.com"],
    "tags": ["orders"],
    "ts": "2026-04-24T10:15:32Z"
  }
}

Типы событий

  • email.sent — передано SMTP-серверу получателя
  • email.delivered — SMTP-сервер получателя подтвердил приём
  • email.bounced — жёсткий отказ: ящика не существует. Адрес попадает в стоп-лист
  • email.rejected — почтовая служба получателя отклонила письмо по своей политике или репутации отправителя. Ящик существует, адрес НЕ попадает в стоп-лист, удалять его из своей базы не нужно
  • email.complained — получатель нажал «Это спам»
  • email.opened — письмо открыто
  • email.clicked — клик по ссылке
  • email.failed — отправка не удалась (домен не подтверждён, все адреса в стоп-листе и т.п.)

Верификация HMAC

Каждый запрос содержит заголовки X-Synapsea-Timestamp (unix-секунды) и X-Synapsea-Signature: v1=<hex> — HMAC-SHA256 от строки `${timestamp}.${body}` с вашим секретом. Всегда проверяйте подпись — это защита от подделки событий, а timestamp — от replay-атак.

Node.js (Express)

webhook.js
javascript
import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/webhooks/mail", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-synapsea-signature"];
  const timestamp = req.headers["x-synapsea-timestamp"];

  // Replay protection: reject events older than 5 minutes
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
    return res.status(401).send("timestamp too old");
  }

  const expected = "v1=" + crypto
    .createHmac("sha256", process.env.SYNAPSEA_WEBHOOK_SECRET)
    .update(timestamp + "." + req.body)
    .digest("hex");

  if (signature !== expected) return res.status(401).send("invalid signature");

  const event = JSON.parse(req.body.toString());
  console.log(event.type, event.data.email_id);
  res.json({ received: true });
});

Python (Flask)

webhook.py
python
import hmac, hashlib, os, time
from flask import Flask, request, abort

app = Flask(__name__)

@app.post("/webhooks/mail")
def webhook():
    signature = request.headers.get("X-Synapsea-Signature", "")
    timestamp = request.headers.get("X-Synapsea-Timestamp", "0")
    if abs(time.time() - int(timestamp)) > 300:
        abort(401)
    secret = os.environ["SYNAPSEA_WEBHOOK_SECRET"].encode()
    signed = f"{timestamp}.".encode() + request.data
    expected = "v1=" + hmac.new(secret, signed, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(signature, expected):
        abort(401)
    event = request.json
    print(event["type"], event["data"]["email_id"])
    return {"received": True}

Retry-политика

Если ваш endpoint вернул HTTP 2xx — событие считается доставленным. Если 4xx/5xx или таймаут (15 сек) — мы делаем retry до 8 раз с exponential backoff, начиная с 15 секунд (15с, 30с, 1 мин, 2 мин, … до ~30 мин). После исчерпания попыток событие остаётся в статусе failed — историю видно в dashboard → Webhooks.

Тестирование

Для локальной разработки используйте ngrok или webhook.site. История запросов доступна в dashboard → Webhooks.