Agent skill

Iterable Webhooks Skill

Receive and authenticate Iterable system webhooks. Use when setting up an Iterable webhook endpoint, checking the Authorization header (Bearer or Basic), debugging 401s, or handling Iterable events such as emailSend, emailOpen, emailClick, emailBounce and emailUnSubscribe.

Install this skill

npx skills add hookdeck/webhook-skills --skill iterable-webhooks


Iterable (marketing automation) sends system webhooks to your server whenever an event occurs anywhere in a project: email sends, opens, clicks and bounces, SMS, push, in-app, journey exits and more. Payloads are not signed. The only authentication is the Authorization header, if you choose Basic or OAuth 2.0 on the webhook.

When to Use This Skill

  • How do I receive Iterable webhooks?
  • How do I verify the Iterable webhook Authorization header (Bearer or Basic)?
  • Does Iterable sign webhook payloads? (No: static credential header only)
  • Why does my Iterable webhook endpoint return 401?
  • How do I handle emailSend, emailOpen, emailClick, emailBounce or emailUnSubscribe?

Verification (core)

On the system webhook, set Authentication to Basic or OAuth 2.0 and fill in Authentication Token. Iterable then sends the credential as the Authorization header:

  • OAuth 2.0: Authorization: Bearer <token> (you enter only the token)
  • Basic: exactly the text you typed, e.g. Authorization: Basic abc123 (not necessarily base64 user:pass)

Store the whole header value, prefix included, as ITERABLE_AUTHORIZATION and compare the whole Authorization header to it. Don't strip or parse the prefix and never decode a Basic value:

const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyIterableRequest(headers, expected = process.env.ITERABLE_AUTHORIZATION) {
  if (!expected) return false;                  // never accept when unconfigured
  const received = headers['authorization'];    // full value, e.g. "Bearer abc123"
  return Boolean(received) && safeEqual(received, expected);
}

Return 401 when this fails. Python uses hmac.compare_digest on bytes (encode both sides; str raises on non-ASCII). Read the body raw (express.raw({ type: '*/*' })) and parse JSON only after the check. See references/verification.md.

A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS (Iterable requires it) and rotate the credential if it leaks.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Event Types

The body has eventName, email (or userId) and a dataFields object. Names are case-sensitive and exactly as Iterable sends them, including emailUnSubscribe:

eventNameFires when
emailSendA blast or triggered email is sent
emailOpenA recipient opens an email
emailClickA recipient clicks a link
emailBounceAn email bounces (dataFields.recipientState)
emailComplaintA recipient reports spam
emailSubscribe / emailUnSubscribeSubscription changes (universal to all channels)
smsSend, pushSend, inAppOpen, webPushSend, whatsAppSend, rcsSendOther channels
journeyExitA user exits a journey

The full list is in references/overview.md.

Delivery

2-second timeout; retries up to 3 times only on 429, 502, 503, 504 (other 4xx/5xx, including 401, are not retried). Delivery is not guaranteed, order is not guaranteed and duplicates are possible: respond fast, queue the work, and deduplicate.

Environment Variables

ITERABLE_AUTHORIZATION=Bearer 00000000-0000-0000-0000-000000000000   # full header value

Local Development

npx hookdeck-cli listen 3000 iterable --path /webhooks/iterable

No account required. Use the printed URL as the Endpoint URL (Iterable requires https://).

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 3, 2026

View on GitHub →