Webhooks
Receive a JSON POST each time something happens in your Emelia campaigns: email sent, opened, clicked, replied, bounced, LinkedIn invitation accepted, WhatsApp reply, campaign finished.
A webhook is a URL that Emelia calls with a JSON POST each time a selected event happens in a campaign: a reply, an acceptance, a bounce. Use it to update your CRM, alert your team or route replies the moment they arrive.
Register a webhook
Webhooks are registered on a campaign, or on all your campaigns at once. Today they are created from the Make integration of Emelia (the "Watch Campaign Activities" and "Watch Campaign Ending" modules), and listed, tested and deleted in the Emelia app, in Settings, API & Webhooks.
Creating webhooks by API
The registration routes are not part of this reference yet. If you need to register webhooks from your own code, contact contact@emelia.io.
List your webhooks
GET/webhook
curl https://api.emelia.io/webhook \
-H "Authorization: $EMELIA_API_KEY"{
"webhooks": [
{
"id": "6702c1d4e5f6a7b8c9d0e1f2",
"typeOfCampaign": "advanced",
"campaignId": "6651f0c2a1b2c3d4e5f60718",
"campaignName": "Q4 SaaS founders",
"url": "https://hooks.yourcompany.com/emelia",
"events": ["MAIL_REPLIED", "LINKEDIN_REPLIED"],
"status": "active"
}
]
}There is one entry per campaign: a URL registered on all your campaigns appears once for each of them. typeOfCampaign is advanced for the campaigns of the current app, email or linkedin for legacy campaigns.
Events of a campaign
These are the event names of the campaigns of the current app. The payload of each is on its page: Campaign activity and Campaign finished.
| Event | When |
|---|---|
EMAIL_SENT | An email step was sent. The name is EMAIL_SENT, not MAIL_SENT. |
OPENED, CLICKED | The tracking pixel loaded, or a tracked link was clicked. Needs tracking on the campaign. Delivered 30 to 60 seconds later. |
MAIL_REPLIED | First email reply of the contact, with reply, sentiment, sender and messageId. |
BOUNCED | A bounce was detected. No campaign field, step is 0. |
UNSUBSCRIBED | The contact unsubscribed. |
VISITED, INVITED, ACCEPTED, MESSAGE_SENT, INMAIL_SENT, LIKED | LinkedIn actions. MESSAGE_SENT carries the text in content. |
LINKEDIN_REPLIED | LinkedIn reply, with reply and sentiment. |
WHATSAPP_MESSAGE_SENT, WHATSAPP_REPLIED | WhatsApp message sent, reply received. |
CAMPAIGN_ENDING | The campaign was closed by the daily job, see Campaign finished. |
Never sent for these campaigns: SENT, MAIL_SENT, FOLLOWED, TASK_COMPLETED, TASK_FAILED. The events of legacy campaigns have their own pages: Legacy email activity, Legacy LinkedIn activity.
A MAIL_REPLIED delivery looks like this:
{
"event": "MAIL_REPLIED",
"campaign": "Q4 SaaS founders",
"campaignId": "6651f0c2a1b2c3d4e5f60718",
"senderName": "Marie Martin",
"sender": "marie.martin@yourcompany.com",
"contact": {
"id": "6703d2e5f6a7b8c9d0e1f2a3",
"firstName": "Claire",
"lastName": "Fontaine",
"email": "claire.fontaine@nexora.io",
"company": "Nexora"
},
"date": "2026-10-13T10:20:00.000Z",
"step": 0,
"reply": "Hi Marie, sounds interesting. Can we talk on Tuesday?",
"sentiment": { "classification": "INTERESTED", "score": 0.92 },
"messageId": "<CAF7x2y3z@mail.nexora.io>"
}contactstarts with the custom variables of the contact, then the standard fields.companyis the first custom variable whose name contains "company".sentimentis the AI classification of the reply:INTERESTED,NOT_INTERESTED,NEUTRAL,OUT_OF_OFFICE,HOSTILE,DELEGATION,INCOMPREHENSIONorQUESTION_ASK, with ascore. It is present only when reply classification runs on your account.dateis an ISO 8601 date in UTC, usually the time Emelia processed the event.- Absent fields are omitted, never
null.
Receive the events
Answer fast with a 2xx, then do the work in the background.
# Simulate a delivery to test your endpoint
curl -X POST https://hooks.yourcompany.com/emelia \
-H "Content-Type: application/json" \
-d '{"event": "MAIL_REPLIED", "campaign": "Q4 SaaS founders", "contact": {"email": "claire.fontaine@nexora.io"}, "date": "2026-10-13T10:20:00.000Z", "step": 0, "reply": "Can we talk on Tuesday?"}'Delivery rules
Build your handler for these rules:
- No signature. Deliveries carry
Content-Type: application/jsonand nothing else: no secret, no signature header. Use a URL that is hard to guess, and treat the payload as a hint: confirm important facts with the API before acting on them. - No retry on HTTP errors. Your response is ignored. A few events are retried on network errors only. An endpoint that is down misses the events.
- No ordering. Events come from different services: an
OPENEDcan arrive after theMAIL_REPLIED. - No event id. To deduplicate, build a key from
event,campaign, the contact andstep. A URL registered twice on a campaign receives everything twice. - No timeout on Emelia's side, but answer in a few seconds anyway.
The LinkedIn scraper has its own, separate webhooks (FINISHED and ERROR).