Skip to main content

Webhooks

Webhooks are how Mailtarget tells your application what happened after a message left the platform. Submit returns a transmissionId. Webhooks return delivery, engagement, and failure events tied to that ID.

If you skip webhooks, you have no visibility into what happened to the email after the API returned a 2xx.

Event types

Mailtarget fires events for the lifecycle of every message.

EventWhen it fires
deliveryThe receiving server accepted the message.
bounceThe receiving server permanently rejected the message (hard bounce).
soft_bounceThe receiving server temporarily rejected the message. Mailtarget retries internally before this event fires as final.
openThe recipient opened the message and the tracking pixel loaded.
clickThe recipient clicked a tracked link.
complaintThe recipient marked the message as spam in their mail client.
unsubscribeThe recipient clicked an unsubscribe link.
out_of_bandThe receiving server reported a delivery state change after the initial accept (rare; happens when an upstream server bounces a message that was already accepted at the gateway).

Consult the dashboard for the complete event catalog and any account-specific events.

Payload shape

Every event payload includes a stable set of fields.

{
"transmissionId": "abc123",
"messageId": "<mailtarget-internal-message-id>",
"event": "delivery",
"timestamp": "2026-05-08T03:14:15Z",
"recipient": "user@example.com",
"campaignId": "<your-metadata-or-null>",
"metadata": { "any": "metadata you set on submit" },
"raw": { "event-specific fields like bounce reason, click URL, etc." }
}

The transmissionId matches the value returned at submit. The metadata object echoes what you set on the request body. Use metadata to carry your own correlation fields (order ID, user ID, campaign code) so you do not have to maintain a separate join table.

The exact field names and the raw shape per event are in the dashboard webhook documentation. Treat the dashboard as the source of truth and tag this page with review_needed: true until the field-level mapping is confirmed.

Signature verification

Mailtarget signs every webhook request. Verify the signature before trusting the payload.

The signature is a header on the HTTP request. Recompute the signature on your end with the webhook secret (visible in the dashboard at webhook configuration time), compare with constant-time equality, and reject the request if the comparison fails.

import hmac, hashlib

def verify(body_bytes, header_signature, secret):
expected = hmac.new(secret.encode(), body_bytes, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header_signature)

Constant-time comparison matters. A regular string equality check leaks the secret to a timing attacker who can spam your endpoint and measure response times.

The exact header name and the canonical message format used to compute the signature are in the dashboard webhook documentation. Tag any divergence as review_needed: true and confirm with the engineering team before shipping verification code to production.

Retries

Mailtarget retries webhooks if your endpoint does not respond with a 2xx status code within a bounded time. Default policy:

  • Retry on any non-2xx response or timeout.
  • Exponential backoff with multiple attempts spread over hours.
  • After the retry window expires, the event is dropped from the active queue and visible in the dashboard delivery log for manual replay.

Two consequences for your endpoint:

  1. Respond fast. Acknowledge receipt with 200 OK immediately, then process asynchronously. If your handler runs synchronously and the work takes 30 seconds, you will time out the webhook and trigger retries you did not want.
  2. Be idempotent. Mailtarget will deliver the same event more than once during a retry storm. Dedupe on transmissionId plus event plus timestamp (or a stable event ID if your dashboard exposes one).

Idempotency

Two events with the same transmissionId and event and timestamp are the same event. Process them once.

A common pattern:

  1. On webhook receipt, compute a key from transmissionId + event + timestamp.
  2. Insert into a dedupe table with that key as the unique constraint.
  3. If the insert fails on uniqueness, the event has already been processed. Return 200 and stop.
  4. If the insert succeeds, do the work.

This pattern survives retry storms and survives the case where your application processes the event but crashes before responding 200. Mailtarget will retry, the dedupe will catch the second delivery, and the application stays consistent.

Configuring webhooks

Webhooks are configured per account in the dashboard. You set:

  • The destination URL.
  • The events you want delivered.
  • The signing secret.
  • Optional filters (per campaign, per metadata field).

Use one URL per logical handler. If you have a service that cares only about delivery and bounce and a separate service that handles open and click, register two webhooks.

Operations

For production webhook reliability (timeout budgets, replay flow, debugging delivery failures), see the Operations section on webhook reliability.