MailSequence webhooks

Verify every webhook, then process it as an at-least-once event.

MailSequence signs the exact request body with HMAC-SHA256, supplies a stable event ID for deduplication, retries failures with backoff, and disables persistently failing endpoints.

Reviewed September 8, 202611 minute readProduct behavior checked against current implementation
Authenticate the raw body before parsing it, deduplicate on the event ID before taking a side effect, and return a successful response only after the receiver has safely accepted the event.

1. Register a public HTTPS endpoint

Create the endpoint through POST /api/v1/webhook-endpoints using a workspace key with webhooks:manage. Choose only the event families the receiver needs. The signing secret is returned once. Store it in a secret manager and keep it out of source code and workflow JSON.

Targets are checked against public-host rules, including redirects. Private, loopback, and metadata destinations are rejected to reduce server-side request-forgery risk.

2. Subscribe to emitted events

EventUse
message.sentUpdate an outbound activity record.
email.repliedStart reply routing or CRM handoff.
email.bouncedRecord the delivery failure and investigate patterns.
contact.createdSynchronize a newly created contact.
enrollment.completedTrigger a post-sequence workflow.

Open and click event names are reserved in the catalog but do not have production emitters. Do not build an automation that expects them to fire.

3. Preserve the raw body and headers

X-MailSequence-Signature: sha256=<hex digest>
X-MailSequence-Event-Id: <uuid>
X-MailSequence-Event: email.replied
Content-Type: application/json

The JSON envelope contains id, version, event, occurred_at, and event-specific data. Use the version before assuming a payload shape.

4. Verify before parsing

  1. Read the exact request bytes without re-serializing the JSON.
  2. Compute HMAC-SHA256 over those bytes with the endpoint signing secret.
  3. Prefix the hexadecimal digest with sha256=.
  4. Compare the expected and supplied values in constant time.
  5. Reject the delivery before any side effect when verification fails.
expected = "sha256=" + HMAC_SHA256(signing_secret, raw_body)
valid = constant_time_compare(expected, signature_header)

5. Deduplicate before side effects

Delivery is at least once. Persist X-MailSequence-Event-Id with a unique constraint or equivalent atomic claim before updating a CRM, sending a notification, or starting another workflow. A duplicate should return the same successful acknowledgement without repeating the side effect.

6. Design for retries and disablement

A 2xx response records success. Timeouts, blocked redirects, oversized responses, and non-2xx responses count as failures. MailSequence retries with exponential backoff, marks a delivery failed after five attempts, and automatically disables an endpoint after five consecutive failed attempts across deliveries.

Keep the receiver fast: verify, durably enqueue or record, then acknowledge. Monitor endpoint state and repair the receiver before re-enabling or recreating a disabled endpoint.

An HMAC proves that a body was signed with the shared secret; it does not make downstream processing idempotent. Signature verification and event deduplication solve different problems.

Sources and product basis

Build a receiver that is safe to retry.

Preserve the raw body, verify the signature, claim each event ID once, and acknowledge only after durable acceptance.