Усі статті
Як тестувати вебхуки Postmark локально через HTTPS
Postmarkemail webhookslocalhostwebhook security

Як тестувати вебхуки Postmark локально через HTTPS

Запустіть локальний handler, відкрийте порт через npx portpreview PORT і вкажіть HTTPS-маршрут у потрібному Message Stream. Захистіть Basic Authentication або секретним header, перевіряйте JSON, зберігайте ідемпотентно та швидко відповідайте HTTP 200.

Що Postmark надсилає у вебхук

Postmark надсилає HTTP POST для Delivery, Bounce, Open, Click, Spam Complaint, Subscription Change та вхідної пошти. Розподіляйте за RecordType. Delivery означає приймання сервером, але не inbox. Bounce містить Type, TypeCode, Inactive, CanActivate. офіційний огляд вебхуків довідник bounce-вебхука

Невеликий локальний приймач Express

Express на порту 3000 спершу перевіряє Basic Auth, потім мінімальну схему та надійно записує ключ дедуплікації. Замініть helpers транзакцією або чергою. Обмежте розмір і не логуйте повні листи з персональними даними, посиланнями та вкладеннями.

import crypto from 'node:crypto';
import express from 'express';

const app = express();
app.use('/webhooks/postmark', express.json({ limit: '2mb' }));

function safeEqual(actual, expected) {
  const a = Buffer.from(actual);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function authorized(req) {
  const value = req.get('authorization') ?? '';
  if (!value.startsWith('Basic ')) return false;
  const decoded = Buffer.from(value.slice(6), 'base64').toString('utf8');
  const separator = decoded.indexOf(':');
  if (separator < 0) return false;
  return safeEqual(decoded.slice(0, separator), process.env.POSTMARK_WEBHOOK_USER ?? '') &&
    safeEqual(decoded.slice(separator + 1), process.env.POSTMARK_WEBHOOK_PASSWORD ?? '');
}

app.post('/webhooks/postmark', async (req, res) => {
  if (!authorized(req)) return res.sendStatus(401);

  const event = req.body;
  if (typeof event?.RecordType !== 'string' ||
      typeof event?.MessageID !== 'string') {
    return res.status(400).json({ error: 'Invalid Postmark event' });
  }

  const deliveryKey = `${event.RecordType}:${event.MessageID}:${event.ID ?? ''}`;
  await saveWebhookOnce({ provider: 'postmark', deliveryKey, event });
  res.sendStatus(200);
});

app.listen(3000);

Публічна HTTPS-адреса для localhost

Перевірте локальний порт, виконайте npx portpreview 3000 і додайте маршрут https://YOUR-TUNNEL.portpreview.dev/webhooks/postmark. Тунель дає HTTPS і доступність, але не автентифікує Postmark. посібник із безпеки localhost-тунелів

Налаштування правильного вебхука Postmark

Для outbound оберіть правильні Server і Message Stream, додайте URL і потрібні triggers. Inbound Message Stream має власний URL. API підтримує HttpAuth, HttpHeaders; X-Postmark-Server-Token потрібен для API й не надсилається приймачу. Webhooks API

Автентифікація Postmark — не криптографічний підпис

Postmark не підтримує HMAC-підпис вебхуків: немає signing secret і X-Postmark-Signature. Basic Authentication, IP allowlist і секретний header підтверджують спільний секрет, але не пов’язують його криптографічно з body. Використовуйте HTTPS, валідацію та актуальні IP. Надавайте перевагу HttpAuth; для https://username:[email protected]/path потрібні окремі стійкі, правильно закодовані дані. Не використовуйте Server API token.

Обробка delivery і bounce за типом

Винесіть бізнес-логіку у worker та обробляйте збережені події ідемпотентно. Не встановлюйте постійне блокування за одним полем; враховуйте актуальну класифікацію Bounce. Spam Complaint і Subscription Change — окремі типи, Open і Click повторюються.

async function processPostmarkEvent(event) {
  switch (event.RecordType) {
    case 'Delivery':
      await markAcceptedByRecipientServer({
        messageId: event.MessageID,
        deliveredAt: event.DeliveredAt
      });
      break;
    case 'Bounce':
      await recordBounce({
        bounceId: String(event.ID),
        messageId: event.MessageID,
        type: event.Type,
        inactive: event.Inactive,
        canActivate: event.CanActivate
      });
      break;
    default:
      await recordUnhandledPostmarkType(event.RecordType);
  }
}

Проєктування з урахуванням повторів і дублів

Без HTTP 200 Postmark повторює запит; Bounce і Inbound довше за Click, Open, Delivery і Subscription Change, а 403 зупиняє повтори. Дубль після commit і timeout є легітимним. Створіть unique key із MessageID, а для змішаного endpoint додайте RecordType та ID. Відповідайте 200 після надійної передачі, зовнішні API викликайте асинхронно. повтори та ідемпотентність вебхуків

Безпечна перевірка справжніх подій

Спершу надішліть curl POST, потім лист на власну адресу для Delivery. Bounce перевіряйте офіційними засобами Postmark, зокрема black-hole test domain, якщо доступний. Збережіть MessageID і двічі надішліть очищений fixture, очікуючи один ефект.

Діагностика типових збоїв вебхука Postmark

Запит не доходить до endpoint

Перевірте тунель, маршрут і порт. Для 401 звірте credentials, перезапустіть застосунок і перевірте, чи proxy не видаляє Authorization, не виводячи значення. Для повторів перевірте публічні status і latency: потрібен 200. Для іншого payload звірте RecordType, trigger і stream.

Усі запити повертають HTTP 401

посібник щодо помилок 401/403

Повтори після успішної обробки

Перевірте тунель, маршрут і порт. Для 401 звірте credentials, перезапустіть застосунок і перевірте, чи proxy не видаляє Authorization, не виводячи значення. Для повторів перевірте публічні status і latency: потрібен 200. Для іншого payload звірте RecordType, trigger і stream.

Payload не збігається зі зразком

Перевірте тунель, маршрут і порт. Для 401 звірте credentials, перезапустіть застосунок і перевірте, чи proxy не видаляє Authorization, не виводячи значення. Для повторів перевірте публічні status і latency: потрібен 200. Для іншого payload звірте RecordType, trigger і stream.

Чекліст безпеки для production

Використовуйте HTTPS і окремі стійкі credentials; розділяйте API tokens; робіть ротацію після тесту; видаляйте старі URL; перевіряйте тип, розмір і поля; приховуйте поштові дані та секрети; застосовуйте least privilege; стежте за помилками, lag, дублями й dead letters.

посібник із локального налагодження вебхуків

Поширені запитання

Як перевірити вебхук Postmark на localhost?
Запустіть handler, відкрийте порт через npx portpreview PORT, додайте маршрут до HTTPS URL і налаштуйте його в потрібному Message Stream.
Чи підписує Postmark вебхуки через HMAC?
Ні. HMAC-підписи не підтримуються. Використовуйте HTTPS із Basic Authentication, за потреби актуальний IP allowlist, і перевіряйте payload.
Чому Postmark повторює вебхук?
Якщо немає HTTP 200, запит повторюється. Зберігайте стабільний ключ під unique constraint.
Чи означає Delivery, що лист прочитано?
Ні. Це лише приймання сервером призначення, а не гарантія inbox, відкриття чи прочитання.