Webhooks
Have MailMoo call your systems when something happens: a reply arrives, a lead moves, a video finishes.
A webhook is a URL MailMoo posts to when something happens in your workspace. It is the push half of the API: rather than polling for replies, you give MailMoo an endpoint and it tells you.
Set them up in Workspace settings.
What you can subscribe to
| Event | Fires when |
|---|---|
| Videos | A personalized video finishes rendering, or fails |
| Analytics | A lead opens or watches your content |
| Social sending steps | A sequence step goes out on a social account |
| Account disconnected | A connected social account stops working |
| Message received | A lead replies on a social account |
| Email received | A lead replies by email |
| Pipeline | A lead moves into a pipeline stage |
Most events can be narrowed when you create the subscription: videos by render status, analytics by watch percentage, social sending steps by step type, and pipeline by the stage category the lead moved into. Filtering on the destination is deliberate: subscribers care about "tell me when something is Won", not where it came from.
A subscription belongs to the workspace, and can optionally be scoped to specific campaigns. Leave it unscoped and it covers all of them.
What MailMoo sends
A POST with a JSON body containing the subscription and the event:
{
"hook": { "id": "...", "targetUrl": "https://example.com/hooks/mailmoo" },
"payload": {
"workspaceId": "...",
"workspaceName": "Acme",
"campaignId": "...",
"campaignName": "Heads of Sales, Germany",
"leadId": "...",
"lead": { "firstName": "Dana", "companyName": "Northwind" },
"type": "message-received"
}
}lead is a flat map of your variable names to that lead's values, so whatever
you enriched or generated is available without a second call. Fields that do
not apply are omitted: campaignId and leadId are absent on workspace-wide
events like an account disconnecting.
Webhooks created before 20 February 2026 are sent without a Content-Type
header. That is deliberate, to avoid breaking integrations built against the
original behaviour. New subscriptions send Content-Type: application/json.
If your framework needs the header, recreate the subscription.
Delivery
One attempt per event, with no retries. If your endpoint is down when an event fires, that event is not resent. Two consequences worth designing around:
- Acknowledge fast, work later. Return a 2xx as soon as you have the payload and do the real work afterwards. An endpoint that finishes a slow job before responding will eventually time out and lose events.
- Do not treat webhooks as your source of truth. For anything that must be complete, reconcile periodically against the API.
Every attempt is recorded, with the response status and body, in the delivery log beside the subscription. That log is the first place to look when an integration goes quiet.
Returning 410 Gone deletes the subscription. It is the intended way for an integration to unsubscribe itself, so do not return it for ordinary errors: use a 4xx or 5xx, which fails the delivery and leaves the subscription alone.
Choosing between webhooks and the API
Webhooks are for reacting; the API is for asking. Use a webhook when something should happen in your system the moment a lead replies or moves. Use the API when you want the current state of things, or to change it.
Reply notifications pair well: take the webhook as the trigger, then call the API to read the full conversation and decide what to do.

