Webhook events
Receive real-time notifications when subscribers open emails, click links, bounce, unsubscribe, and more. Webhooks push data to your endpoint so you do not have to poll.
What webhooks are in Mally
Mally webhooks are outbound HTTP POST requests that your platform sends to a URL you configure whenever a subscriber event occurs. Your server receives the event in real time and can react — update a CRM record, trigger a Zapier workflow, log to a database, or fire a follow-up action.
Webhooks are configured per endpoint — you specify a URL and which event types to send to it. You can have multiple endpoints receiving different event types.
Available events
bounceA campaign email to a subscriber was hard-bounced (permanent delivery failure). The subscriber is automatically suppressed after this event.complaintA subscriber marked the email as spam via their mail client. Triggered via SNS from AWS SES. The subscriber is immediately suppressed.openA subscriber opened a campaign email. Tracked via a 1×1 pixel. Note: Apple MPP causes inflated open events for Apple Mail users.clickA subscriber clicked a tracked link in a campaign. Includes the destination URL and campaign ID.subscribeA new subscriber was added to a list — via form, API, or import.unsubscribeA subscriber clicked the unsubscribe link in an email or was manually unsubscribed via the dashboard.
Setting up a webhook endpoint
Go to Settings → Webhooks → Add endpoint. Fill in:
- Endpoint URL: The HTTPS URL on your server that will receive the POST requests. Must use HTTPS.
- Events: Select which event types to send to this endpoint. You can send all events to one URL or route different events to different endpoints.
- Description: Optional. A label for your own reference.
After saving, Mally sends a test ping to your endpoint. Your server should respond with HTTP 200 within 5 seconds. If it does not, check that your server is accessible from the internet and not behind a firewall that blocks Mally's IP.
You can view delivery attempts and response codes for each webhook in Settings → Webhooks → [endpoint] → Activity log.
Webhook payload format
All webhook payloads are JSON with a consistent top-level structure:
{
"event": "open",
"timestamp": "2025-09-01T14:32:11Z",
"account_id": "acct_01hx...",
"campaign_id": "cmp_02ab...",
"subscriber": {
"id": "sub_09cd...",
"email": "alice@example.com",
"first_name": "Alice",
"tags": ["customer", "active"]
},
"data": {
"user_agent": "Mozilla/5.0 ...",
"ip": "203.0.113.42"
}
}The data object contains event-specific fields:
clickurl (destination), link_idbouncebounce_type (hard/soft), bounce_subtype, diagnostic_codecomplaintfeedback_typesubscribelist_id, source (form/api/import), referrer (for form subscribes)unsubscribelist_id, method (link/api/manual)
Verifying webhook signatures
Each webhook request includes a signature header X-Mally-Signature that you can use to verify the request came from Mally and was not tampered with.
The signature is an HMAC-SHA256 hex digest of the raw request body, signed with your webhook endpoint's secret. You can find the secret in Settings → Webhooks → [endpoint] → Signing secret.
To verify (Python example):
import hmac
import hashlib
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# In your request handler:
body = request.get_data()
sig = request.headers.get("X-Mally-Signature", "")
if not verify_signature(body, sig, YOUR_WEBHOOK_SECRET):
return 401Always verify signatures on incoming webhooks to prevent spoofed requests from third parties.
Retry policy
If your endpoint does not return HTTP 2xx within 10 seconds, Mally considers the delivery failed and retries:
- Attempt 1: immediately
- Attempt 2: 5 minutes later
- Attempt 3: 30 minutes later
After three failed attempts, the event is marked as failed and no further retries are made. You can view failed events in the webhook activity log and manually replay them if needed.
Respond quickly, process asynchronously
Your endpoint should return HTTP 200 immediately after receiving the request, then process the payload asynchronously. If your handler takes more than 10 seconds (e.g. waiting for a CRM API), Mally will retry even though you received the event.