Email Webhooks

Email Webhooks

See webhook actions to send an API request to a specific API

Email Webhooks send email performance data from to a URL you specify in order to receive information about events as they occur in real time. sends the information as JSON in an HTTP POST.


To begin receiving HTTP calls from

  • Log in and choose Integrations from the left panel.
  • Select the Data Management category.
  • Find and select Email Activity Webhooks.
  • Enter the URL for the Webhook Endpoint at which you would like to receive the event notifications. The URL can be either an HTTP or HTTPs URL (HTTPs is generally preferred for protecting customer information).
  • Select the event notifications you want to monitor and click Save Changes at the bottom right of the page.



You may use a service like to inspect HTTP requests before pointing them at your own servers. Note the following warning:

Adding a URL to any endpoint will cause us to start sending potentially sensitive data to that URL right away. The information we send comes directly from the data you store in the same workspace as the settings you are updating. Testing tools provided by untrusted third parties should only be used with dummy/test data that is stored in a test/sandbox workspace in order to prevent leaking your real customer data to the providers of those services.


To send a test event to your webhook endpoint, press Send Test. Once sent you will see a green checkmark to the right of the button.


By registering a URL with, you can be notified when the following events occur:

  • customer_subscribed: customer has been re-subscribed.
  • customer_unsubscribed: customer has been unsubscribed.
  • email_attempted: we've attempted to send this but weren't successful. Retrying…
  • email_bounced: receiving server for email could not or would not accept the email
  • email_clicked: customer clicked through a link in the email
  • email_converted: customer entered/exited the segment set as conversion segment
  • email_delivered: email successfully delivered to the receiving server
  • email_drafted: email you asked for approval before sending is ready for approval
  • email_dropped: email was suppressed because customer bounced or reported your emails as spam previously
  • email_failed: email had an error as we tried to render it for delivery. A common scenario is the matching customer is missing an attribute you've used in the email content.
  • email_opened: email was opened by one of your customers
  • email_sent: email has been sent from our servers
  • email_spammed: email was marked as spam by the customer
  • email_unsubscribed: the customer clicked the unsubscribe link on this email

To only receive specific events, select just those that interest you:


If you have a specific request for an event not listed here that you would like to be notified of, please contact us.

Format Webhooks are HTTP POST requests encoded in JSON. The requests have a User Agent header containing " Web Hooks x.x" where "x.x" is the version number.

The JSON body contains a general top-level section included in all webhook requests, as well as a "data" attribute, which contains data specific to the type of event.

Below is an example of an HTTP request for any of the email related events:

User-Agent: Web Hooks 1.0
Content-Type: application/json
Accept-Encoding: gzip
X-Request-Id: 	7e6f46cd-480e-4354-93ce-74b770015c7f
Connect-Time: 1
Content-Length: 1100
Cf-Visitor: {"scheme":"http"}
Total-Route-Time: 0
Cf-Ipcountry: CA
Cf-Ray: 2b62237a83a12507-ORD
Connection: close
Via: 1.1 vegur


"data": {
  "campaign_id": "1000002",
  "campaign_name": "Upgrade to Premium",
  "customer_id": "98513",
  "email_address": "",
  "email_id": "NTE4MzE6FwGLxwJkAAJkABcBIfcaAVVvdGukFUsYV2hY6QFlOjQ4YTZhODljLTM3MjktMTFlNi04MDQwLTYzNGY3NzAzM2NhNjozNDMwMzEA",
  "message_id": "1000013",
  "message_name": "First Upgrade Email",
  "subject": "Have any doubts?",
  "template_id": "343031",
  "variables": {
    "attachments": null,
    "customer": {
      "created_at": 1466453747,
      "email": "",
      "id": 98513,
      "name": "John Doe",
      "plan_name": "free"
    "email_id": "NTE4MzE6FwGLxwJkAAJkABcBIfcaAVVvdGukFUsYV2hY6QFlOjQ4YTZhODljLTM3MjktMTFlNi04MDQwLTYzNGY3NzAzM2NhNjozNDMwMzEA",
    "event": {
      "page": ""
    "event_id": "48a6a89c-3729-11e6-8040-634f77033ca6",
    "event_name": "viewed_pricing_page",
    "from_address": null,
    "recipient": null,
    "reply_to": null
"event_id": "b50cb221c60f87cdf06e",
"event_type": "email_drafted",
"timestamp": 1466456299

An example of an HTTP request for any customer related events:

POST /my_webhook HTTP/1.1
Accept: \*/\*
User-Agent: Web Hooks 1.0
Content-Type: application/json
Content-Length: 226
  "event_type": "customer_unsubscribed",
  "event_id": "5c384328a599913e9fb6",
  "timestamp": 1352005931,
  "data": {
    "customer_id": "568"

List of Webhook Attributes

  • campaign_id and campaign_name: refer to the transactional, segment-triggered or newsletter campaign that generated the email
  • customer_id: user id (can be retrieved from the user profile). Only present if the profile is still active (not included if the profile has been deleted).
  • email_address: "To" email address
  • email_id: unique message id (each individual message sent from has a different "email_id"); can also be found in the unsubscribe link URL
  • event: specific to event-triggered campaigns; includes all the event attributes
  • event_id (data section): specific to event-triggered campaigns; id of the event that generated the message (not visible in the UI)
  • event_id: internal attribute; id associated with the email_type action
  • event_name: specific to event-triggered campaigns; name of the event that powers the campaign
  • event_type: type of event ("email_drafted", "email_sent", etc.)
  • from_address: specific to anonymous event-triggered campaigns; from_address set via the event
  • href and link_id: specific to "email_clicked" events
  • href: first URL clicked by the user
  • link_id: internal attribute (not visible in the UI)
  • message_id: campaign email id; can be found in the campaign URL after emails/ (e.g.
  • message_name: the name of the campaign email
  • reason: specific to the "email_bounced" and "email_dropped" events, mentions the cause of the bounce/suppression (e.g.: Invalid)
  • recipient: specific to anonymous event-triggered campaigns; email address of a user that does not exist inside
  • reply_to: specific to anonymous event-triggered campaigns; reply_to address set via the event
  • subject: email subject
  • template_id: internal attribute, each email inside a campaign can have multiple template ids depending on the changes made over time. You can view it in the UI by filtering for a specific email under Email Log. For example:
  • timestamp: date and time when the event took place in unix (seconds since epoch) format
  • variables:
    • attachments: specific to event-triggered emails with small attachments (e.g. .ics files)
    • customer: all the attributes associated with your user

Timeouts and Failures

We have a 4 second timeout and if we don't get a successful (2xx) response in that period, will retry sending the notification several times with exponential backoff. We keep retrying up to a max of 10 times over a period of approximately 7 hours.

Securely Verifying Requests

For security purposes, every email webhook is delivered with an X-CIO-Signature header. This signature is generated by combining your webhook signing key with the body of webhook request using a standard HMAC-SHA256 hash. You can find the signing key on the Email Activity Webhook integration page in your account settings. (This is the same page where you enter your webhook endpoint.)

To validate a signed request, first you'll need to retrieve the X-CIO-Timestamp header sent with the webhook request, and the body of the request. Combine the version number, timestamp and body delimited by colons to form a string in the form v0:<timestamp>:<body> (the version number is always v0). Using HMAC SHA256, hash the string using your webhook signing secret as the hash key. Compare this value to the value of the X-CIO-Signature header sent with the request to confirm that the request originated with

Here's an example of a validation function in Golang.

import (

func CheckSignature(WebhookSigningSecret, XCIOSignature string, XCIOTimestamp int, RequestBody []byte) (bool, error) {
  signature, err := hex.DecodeString(XCIOSignature)
  if err != nil {
    return false, err

  mac := hmac.New(sha256.New, []byte(WebhookSigningSecret))

  if _, err := mac.Write([]byte("v0:" + strconv.Itoa(XCIOTimestamp) + ":")); err != nil {
    return false, err
  if _, err := mac.Write(RequestBody); err != nil {
    return false, err

  computed := mac.Sum(nil)

  if !hmac.Equal(computed, signature) {
    fmt.Println("Signature didn't match")
    return false, nil

  fmt.Println("Signature matched!")
  return true, nil

Frequently Asked Questions

  1. Do webhooks contain the email body? No, we only send the attributes mentioned above under "List of Webhook Attributes".

  2. How can I secure webhooks? To secure webhooks, you can add basic authentication in the link (e.g.

  3. How do you identify each message that is going out? Each message sent from has an email_id unique identifier that is also part of the default unsubscribe link:
    To view a particular message the UI, go to the Email Log page, open any email and then inside the URL replace the part after email_logs/ with the email_id you're interested in. For example
    The email_id is also displayed under the Metadata details in the right-hand column of the page.

  4. Can I specify which campaign(s) get forwarded to an external webhook? No. If you want to monitor only a specific campaign, you'll need to handle the logic on your end to filter out unwanted webhook events based on their campaign_id.

  5. Can I get a webhook when a customer gets added to a segment? From our end, there isn't a direct way to retrieve segment membership. One solution, though, would be to create a segment-triggered campaign based on the segment you're interested in, set the email inside to "Queue Draft" and then monitor the email_drafted events for that campaign_id on your end.

  6. Do you send an event for each click performed by a user? The email_clicked event is unique, so it only gets triggered the first time someone clicks your email.

  7. Is there a way to rate limit webhooks? No, right now you always get one event per action (sent, opened, clicked, etc.). Unfortunately for larger mailing that can mean thousand of messages pretty quickly.

  8. Is it possible to host a webhooks endpoint on No, for incoming data, you'll need to use our REST API or our Segment integration.

Was this article helpful?