Ранний доступЧасть функций, включая ИИ-черновики, ещё включается.Что уже работает

Инструкция · Данные наружу

Приём подписанных вебхуков

Sendalto может пересылать выбранные события — из Events API или синхронизации HubSpot — на ваш адрес. Каждый запрос подписан, так что вы можете убедиться, что он пришёл от Sendalto.

Сверено с Sendalto

1. Добавьте подписку

  1. В разделе «Настройки → API и приватность» добавьте вебхук: адрес и точные названия событий для пересылки, например trial_started или crm.contact.changed (до 50).
  2. Скопируйте секрет подписи. Он показывается один раз — сохраните его на своём сервере.
  3. Доставляются только события, записанные после создания подписки; история не пересылается.

Адрес должен быть публичным https:// на порту 443 с доменным именем — без IP-адресов, частных сетей, логина в адресе и #фрагмента. Перенаправления не выполняются.

2. Как выглядит доставка

Запрос
POST /sendalto-webhook HTTP/1.1
content-type: application/json
x-sendalto-signature: t=1791279000,v1=5f0c…e9a1
x-sendalto-delivery: 0b9d7c52-…

{
  "id": "6c7e1d3a-…",
  "type": "trial_started",
  "occurredAt": "2026-10-06T09:30:00.000Z",
  "data": {
    "email": "[email protected]",
    "properties": { "plan": "team", "seats": 5 }
  }
}
  • id — ID события, type — его название, occurredAt — время события; в data — адрес человека и свойства события, поэтому обращайтесь с доставками как с персональными данными.
  • x-sendalto-delivery обозначает эту доставку и не меняется при повторах.
  • x-sendalto-signature имеет вид t=<секунды unix>,v1=<HMAC в hex>.

3. Проверьте подпись

  1. Прочитайте сырое тело запроса как байты, до разбора JSON.
  2. Разделите заголовок на t и v1. Отклоните запрос, если t расходится с вашими часами больше чем примерно на 5 минут.
  3. Вычислите HMAC-SHA256 секретом подписи от строки <t>.<сырое тело> и закодируйте в hex.
  4. Сравните результат с v1 за постоянное время. При несовпадении отклоните запрос.
  5. Пропустите доставки, чей x-sendalto-delivery вы уже обработали, и ответьте 2xx.
Node.js (Express)
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const SECRET = process.env.SENDALTO_WEBHOOK_SECRET;
const TOLERANCE_SECONDS = 300;
const app = express();

// Verify against the raw bytes: parsing and re-serialising JSON changes the body.
app.post('/sendalto-webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  const header = req.get('x-sendalto-signature') ?? '';
  const parts = Object.fromEntries(header.split(',').map(part => part.trim().split('=', 2)));
  const timestamp = Number(parts.t);
  if (!Number.isInteger(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) {
    return res.sendStatus(400); // stale or missing timestamp
  }
  const expected = createHmac('sha256', SECRET).update(`${parts.t}.`).update(req.body).digest();
  const received = Buffer.from(parts.v1 ?? '', 'hex');
  if (received.length !== expected.length || !timingSafeEqual(received, expected)) {
    return res.sendStatus(401); // not signed with your secret
  }

  // Delivery is at least once: remember each x-sendalto-delivery and skip repeats.
  const deliveryId = req.get('x-sendalto-delivery');
  if (await alreadyProcessed(deliveryId)) return res.sendStatus(200);

  const event = JSON.parse(req.body.toString('utf8'));
  await handleEvent(event); // event.id, event.type, event.occurredAt, event.data.email, event.data.properties
  await markProcessed(deliveryId);
  res.sendStatus(200);
});

4. Повторы

  • Доставленной считается любая 2xx за 10 секунд. Отвечайте быстро, а долгую работу делайте после ответа.
  • Тайм-ауты, сетевые ошибки, 408, 425, 429 и 5xx повторяются с растущими паузами — примерно 1, 2, 4, 8, 16, 32 и 64 минуты, всего до восьми попыток.
  • Остальные ответы 4xx сразу прекращают повторы, поэтому возвращайте 4xx только для запросов, которые вы не примете никогда.
  • Доставка «как минимум один раз»: повтор может прийти, когда событие уже обработано, в том числе после перезапуска обработчика Sendalto. Убирайте дубли по x-sendalto-delivery.
  • Неудачные доставки видны в «Настройки → API и приватность», там же их можно повторить. На время переезда приёмника подписку можно приостановить.

5. Смена секрета

При смене показывается новый секрет — один раз; им подписываются все следующие попытки, включая повторы прежних событий. Обновите секрет в приёмнике в тот же момент или приостановите подписку на время замены.