message.comDevelopers

Verify outgoing webhook signatures

Verify Message outgoing events using the timestamp and exact request bytes.

Message outgoing signature

The X-Message-Signature header is t=<Unix seconds>,v1=<lowercase hex HMAC>. Sign the timestamp, a period and the raw request body using HMAC-SHA256. Use the endpoint’s whsec_ secret as the literal key; do not base64-decode it or substitute an API key.

Capture the raw bytes before a JSON parser runs. The example checks a five-minute replay window and rejects malformed or incorrectly sized signatures without throwing.

Message outgoing signature
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyOutgoing(rawBody, header, secret, now = Date.now()) {
  if (!Buffer.isBuffer(rawBody) || typeof header !== 'string') return false;
  const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header);
  if (!match) return false;
  const timestamp = Number(match[1]);
  if (!Number.isSafeInteger(timestamp) || timestamp <= 0) return false;
  if (Math.abs(Math.floor(now / 1000) - timestamp) > 300) return false;
  const expected = createHmac('sha256', secret)
    .update(match[1] + '.')
    .update(rawBody)
    .digest();
  const received = Buffer.from(match[2], 'hex');
  return received.length === expected.length && timingSafeEqual(received, expected);
}

Connect verification to your receiver

Verify before parsing or processing. Persist an event before acknowledging it so a process crash does not silently lose work. The durable receiver example is in the retries tutorial.

Secret lifecycle

Save the secret when the product’s create response returns it. There is no shared 24-hour dual-signing contract or generic rotation endpoint. Follow the product-specific delete/create or credential-management flow, and test the new endpoint before retiring the previous receiver.

Provider callbacks are different

Email and phone providers send callbacks to Message using their provider-specific authentication. Those are not Message events sent to your application. Do not apply a provider’s plain-HMAC or Svix scheme to X-Message-Signature, and do not assume an X-Timestamp header exists on every callback.