Skip to main content

Delivery Events

After you submit an email message to Email API via the /send endpoint, the message is queued for delivery within MailChannels' extensive cloud infrastructure. When something happens to your message, Email API can send you a webhook callback allowing you to track message delivery attempts and failures (i.e. bounces), user interactions such as opens and clicks, and more.

Webhook Management

Webhooks are managed via the /webhook endpoint. The HTTP request method specifies whether you are creating, retrieving, or removing a webhook:

  1. POST: Create a webhook to receive delivery events
  2. GET: Retrieve an existing webhook configuration
  3. DELETE: Remove an existing webhook configuration

Creating a Webhook

To create a webhook that will receive delivery events from Email API, send a POST request to /webhook specifying the URL at which you wish to receive webhook requests from Email API:

curl -X POST 'https://api.mailchannels.net/tx/v1/webhook?endpoint=YOUR_ENDPOINT_URL' \
-H 'X-Api-Key: YOUR_API_KEY'

Replace YOUR_ENDPOINT_URL with your webhook receiver URL and YOUR_API_KEY with your MailChannels API key.

Event Notification Format

Once configured, MailChannels will send event notifications to your webhook in the following format:

{
"email": "sender@example.com",
"customer_handle": "abc123",
"timestamp": 1625097600,
"event": "processed"
}

See the Appendix below for a JSON schema definition.

Event Fields

  • email: Sender email address
  • customer_handle: the account ID that you can find in the upper-right corner of the MailChannels Console
  • timestamp: Unix timestamp of the event
  • event: Event type, such as delivered or bounced (see Supported Event Types below)

The customer_handle is set so that you can have a single webhook receiver that receives webhooks for multiple MailChannels accounts. For instance, if your account ID is exampleuser then the customer_handle field will be set to exampleuser for webhooks related to emails sent by that MailChannels account.

tip

To improve your webhook security, we highly recommend verifying that the customer_handle value matches your account ID.

info

To find your account ID, go to the upper-right corner of the MailChannels Console

Retrieving Webhook Configuration

To view your current webhook configuration:

curl -X GET 'https://api.mailchannels.net/tx/v1/webhook' \
-H 'X-Api-Key: YOUR_API_KEY'

Deleting a Webhook

To stop receiving event notifications:

curl -X DELETE 'https://api.mailchannels.net/tx/v1/webhook' \
-H 'X-Api-Key: YOUR_API_KEY'

Supported Event Types

Event TypeDescription
processedEmail received and being processed for delivery
deliveredEmail successfully delivered to recipient's server
droppedAn error occurred when processing the email
bouncedEmail could not be delivered to recipient's server
opened*Recipient opened the email
clicked*Recipient clicked a link in the email
complained*Recipient filed a spam complaint about the email
unsubscribed*Recipient indicated they wish to unsubscribe from the email

Note: If a dropped event is generated, it means the API request also generated an error. So if your event-processing code sees dropped, you can safely assume that the original API call to send the message also failed in your client code.

info

opened and clicked events require tracking to be enabled, which is not available in our free tier.

info

complained and unsubscribed are reserved for future use.

Best Practices

  1. Ensure your webhook endpoint can handle concurrent requests.
  2. Authenticate our requests by checking that each request contains your account ID in the customer_handle field.
  3. Process events asynchronously to avoid blocking the webhook receiver.
  4. Implement retry logic in case of temporary failures.
  5. Store raw event data before processing to allow for reprocessing if needed.

By leveraging delivery events, you can gain real-time insights into email delivery status, improve deliverability, and enhance your email sending strategies.

Appendix: Sample Webhook Code

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)

const (
ExpectedCustomerHandle = "myaccountid"
)

type WebhookPayload struct {
Email string `json:"email"`
CustomerHandle string `json:"customer_handle"`
Timestamp int64 `json:"timestamp"`
Event string `json:"event"`
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusInternalServerError)
return
}
defer r.Body.Close()

var payload WebhookPayload
err = json.Unmarshal(body, &payload)
if err != nil {
http.Error(w, "Error parsing JSON", http.StatusBadRequest)
return
}

// Validate required fields
// Note: payload.Email is not present in all notifications
if payload.CustomerHandle == "" || payload.Timestamp == 0 || payload.Event == "" {
http.Error(w, "Missing required fields", http.StatusBadRequest)
return
}

// Verify customer handle
if payload.CustomerHandle != ExpectedCustomerHandle {
http.Error(w, "Invalid customer handle", http.StatusForbidden)
return
}

// Validate event type
validEvents := map[string]bool{
"processed": true,
"delivered": true,
"dropped": true,
"opened": true,
"clicked": true,
"bounced": true,
"complained": true,
"unsubscribed": true,
}
if !validEvents[payload.Event] {
http.Error(w, "Invalid event type", http.StatusBadRequest)
return
}

// Process the webhook payload
fmt.Printf("Received webhook: Email: %s, Customer: %s, Event: %s, Time: %s\n",
payload.Email,
payload.CustomerHandle,
payload.Event,
time.Unix(payload.Timestamp, 0).Format(time.RFC3339))

w.WriteHeader(http.StatusOK)
w.Write([]byte("Webhook received successfully"))
}

func main() {
http.HandleFunc("/webhook", webhookHandler)
fmt.Println("Starting server on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}

Appendix: JSON Schema

The following JSON Schema describes the data structure that will be sent to your webhook by Email API.

info

The event types complained and unsubscribed are reserved for future use.

{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"description": "The sender's email address"
},
"customer_handle": {
"type": "string",
"description": "The MailChannels account ID that generated the webhook"
},
"timestamp": {
"type": "integer",
"description": "The Unix timestamp (in seconds) when the event occurred; the timezone is always UTC"
},
"event": {
"type": "string",
"enum": ["processed", "delivered", "opened", "clicked", "bounced", "complained", "unsubscribed"],
"description": "The type of event that occurred"
},
"url": {
"type": "string",
"description": "For click events, the URL that was clicked by the recipient"
},
"user_agent": {
"type": "string",
"description": "For click and open events, the User-Agent header given when the recipient clicked a link or opened a message"
},
"smtp_id": {
"type": "string",
"description": "For click and open events, the Message-Id of the message that generated the event"
},
"ip": {
"type": "string",
"description": "For click and open events, the IP address of the host that made the HTTP request"
}
},
"required": ["customer_handle", "timestamp", "event"],
"additionalProperties": false
}