Webhooks
Send campaign opens, clicks and unsubscribes to your own endpoint, call a web address from an automation, and build a receiver that holds up in production.
On this page›
A webhook lets IGSendMail tell your application that something happened, instead of your application asking over and over. IGSendMail has two kinds: webhooks on a campaign, which report opens, clicks and unsubscribes from that campaign, and a step in an automation that calls a web address. This page covers both, what they don't cover, and the requirements a receiving endpoint has to meet before you put it in production.
Before you start
You need somewhere to receive the request: a URL on a server you control, reachable from the public internet over HTTPS. A local development machine is not reachable, so you will need a tunnel or a deployed staging environment to test against.
The model#
Polling means your code runs on a timer, asks for the current state, and works out what changed since last time. It is wasteful when nothing has happened and slow when something has, because the news waits for your next poll.
A webhook inverts that. You register a URL. When an event occurs, an HTTP request arrives at your URL describing it. Your code reacts. Nothing runs in between.
The trade is that you now operate a public endpoint that has to be available, has to answer quickly, and has to be safe against requests that did not come from where you think.
Both kinds are set up in the app, not through the API.
Campaign webhooks#
Campaign webhooks are set up on each campaign. Add a web address for each event you want to hear about, and requests arrive there as that campaign's recipients act:
Opens. Someone opened the campaign. Useful for feeding engagement into a CRM or a sales view, so the people following up know who read the email.
Clicks. Someone clicked a link in the campaign. This is usually the stronger signal of the two, because a click is a deliberate act. Use it to flag a contact for follow-up or to record interest in what the link pointed to.
Unsubscribes. Someone left through that campaign's unsubscribe link. This is the most important one to handle correctly, and the one most often skipped. If your own database still has that person marked as mailable, your next sync re-adds them and you mail someone who asked you to stop. Write the opt-out back to your system of record the moment you hear about it. See Subscribers API for the matching read and write calls.
Because they belong to a campaign, these webhooks only report what happens in that campaign. Set them up again on each new campaign you want covered.
The "call a web address" step in automations#
An automation can include a step that calls a web address. Each time a contact reaches that step, a request goes to the address you set. Put it wherever in the workflow your own system needs to know something happened.
The common use is the one campaign webhooks don't cover: new subscribers. Build an automation that starts when someone joins a list, and make its first step a call to your web address. Your CRM or sales channel hears about each signup as it happens, without a nightly export. Triggers and timing covers the list, tag and date triggers, and Build a workflow covers the editor.
What webhooks don't cover#
There are no webhooks for bounces, spam complaints or sends. Hard-bounced addresses are suppressed on this side automatically, so your mail is protected either way; the gap is only in keeping your other systems informed. If you need that, export the Blacklist on a schedule and update your records from it.
Unsubscribes that don't come through a campaign with a webhook on it, such as someone leaving from an automation email, don't arrive either. A periodic reconciliation job that reads subscriber status through the API closes that gap; see If something goes wrong.
React to what your business actually does something with. An event you receive and ignore is a moving part with no purpose.
Requirements on the receiving side#
Respond fast, with a 200
Answer the request quickly and return HTTP 200. That is the signal that you received it. A slow handler risks timing out, and a request that times out is a request you may never see again.
Do not do the work inside the request. If your handler calls your CRM, updates three tables and sends an internal notification before responding, you are one slow third-party API away from timing out on every event. Write the event to a queue or a table, return 200, and process it separately. The handler's only job is to accept the message and record it.
Make handling idempotent
Assume every event can arrive more than once. The same person can open a campaign five times, click the same link twice, or reach the same automation step again if they re-enter the workflow.
Idempotent means processing the same event twice leaves the same result as processing it once. In practice:
- Decide what makes an event a duplicate for your purposes, for example the same contact, campaign and event type, and record the ones you have handled. If you have seen it, return 200 and stop.
- Prefer operations that are naturally repeatable. Setting a contact to unsubscribed is safe to repeat. Incrementing a counter or appending a row is not.
- Do not assume order. Events can arrive out of sequence, so a handler that only makes sense if it runs after another handler will eventually break.
The failure this prevents is not theoretical. A non-idempotent handler that creates a CRM record whenever someone reaches a "call a web address" step produces duplicate contacts the first time someone passes through twice, and duplicates are far harder to clean up than to avoid.
Verify before you trust
Your endpoint is a public URL. Anyone who finds it can send it anything, and a handler that trusts its input will happily unsubscribe your entire list on request.
Before acting on a request:
- Use HTTPS. Plain HTTP exposes the contents in transit and gives you nothing to authenticate against.
- Make the URL unguessable. Include a long random component in the path or a secret query parameter that you check on arrival. This is a weak control on its own, because the URL travels in logs and headers, but it filters out drive-by traffic.
- Confirm the claim against the API before doing anything expensive or destructive. The strongest available check is to treat the request as a hint rather than a fact: when an event says an address unsubscribed, read that subscriber back through the API and act on what the API returns. That call is authenticated with your
API token, so the answer is trustworthy in a way an inbound request is not. - Validate the shape. Reject anything that is not the structure you expect, before it reaches your business logic.
Never let an inbound request decide something irreversible on its own
Deleting records, refunding money and disabling accounts should not be triggered directly by a webhook body. Verify against the API first, or queue the action for review. An endpoint that deletes on request is an endpoint that deletes on any request.
Testing your receiver#
For a campaign webhook, send a test campaign to a small staging list with the webhook set up, then open it, click a link and unsubscribe from a test address. For the automation step, add a test address to the list that starts the automation. Watch your logs.
Check three things. That the request arrived at all, which tells you the URL and firewall are right. That you returned 200 within a sensible time. That running the same event through your handler twice produces one outcome, not two. The third is the one people skip and later regret.
Keep a log of received events with their timestamps. When somebody asks why a contact's status differs between systems, that log is the only thing that will answer the question.
If something goes wrong#
Nothing arrives. Confirm the URL is reachable from outside your network. For a campaign webhook, confirm it was set up on that campaign, for the event you expected. For the automation step, confirm the automation is switched on and the contact actually reached the step.
Duplicate records appear downstream. Your handler is not idempotent. Add the seen-event check before doing anything else.
Your data drifts out of sync anyway. Webhooks are a live feed, not a guarantee of completeness. If your endpoint was down for an hour, you missed that hour, and some changes, such as bounces, never arrive as webhooks at all. Keep a periodic reconciliation job that reads the current state through the API and repairs differences, and export the Blacklist on a schedule if your other systems need bounces.
Last updated September 25, 2026