Receive email events via webhooks

Sidemail uses webhooks to notify your application when an event happens in your project. Webhooks are particularly useful for asynchronous events like when an email is delivered, bounces, is opened, when the recipient reports a spam complaint, when your inbound route receives a new email, or when a sending domain is verified or fails verification.

How to set up

  • In your project's API section, add a webhook URL, choose which events it should receive, and get your unique webhook secret.
  • In your webhook handler, compare the value of sidemail-secret header with your webhook secret to verify the request.
  • Respond to the webhook request with 200 status code (response with any other status is recognized as a webhook failure).

Node.js webhook handler example:

function handleSidemailWebhook(req, res, next) {
	// Get your webhook secret in your project's API section
	const WEBHOOK_SECRET = "replace-with-your-webhook-secret";

	// Verify that the request is coming from Sidemail
	if (req.headers["sidemail-secret"] !== WEBHOOK_SECRET) {
		return res.status(401).send();
	}

	// Your custom logic ...
	console.log(req.body.type, req.body.data);

	// Acknowledge the webhook by responding with 200 status code
	return res.status(200).send();
}

Events

Domain events are organization-wide because sending domains are shared across projects. If multiple projects in the organization have webhook URLs configured, each project webhook with that domain event enabled receives it.

Event: email.delivered

{
	"type": "email.delivered",
	"data": {
		"email": {
			"id": "123",
			"templateId": "123",
			"templateName": "Welcome",
			"templateProps": { "name": "John" },
			"fromName": "Example App",
			"fromAddress": "[email protected]",
			"toAddress": "[email protected]",
			"createdAt": "2020-11-07T09:50:09.951Z"
		},
		"time": "2020-11-07T09:50:09.951Z",
		"smtpResponse": "250 2.6.0 Message received"
	}
}

Event: email.open

Triggered on the first open of an email by the recipient. Subsequent opens are not reported.

{
	"type": "email.open",
	"data": {
		"email": {
			"id": "123",
			"templateId": "123",
			"templateName": "Welcome",
			"templateProps": { "name": "John" },
			"fromName": "Example App",
			"fromAddress": "[email protected]",
			"toAddress": "[email protected]",
			"createdAt": "2020-11-07T09:50:09.951Z"
		},
		"time": "2020-11-07T09:50:09.951Z",
		"ipAddress": "203.0.113.10",
		"userAgent": "Mozilla/5.0 ..."
	}
}

Event: email.bounce

{
	"type": "email.bounce",
	"data": {
		"email": {
			"id": "123",
			"templateId": "123",
			"templateName": "Welcome",
			"templateProps": { "name": "John" },
			"fromName": "Example App",
			"fromAddress": "[email protected]",
			"toAddress": "[email protected]",
			"createdAt": "2020-11-07T09:50:09.951Z"
		},
		"time": "2020-11-07T09:50:09.951Z",
		"bounceType": "permanent"
	}
}

Event: email.complaint

{
	"type": "email.complaint",
	"data": {
		"email": {
			"id": "123",
			"templateId": "123",
			"templateName": "Welcome",
			"templateProps": { "name": "John" },
			"fromName": "Example App",
			"fromAddress": "[email protected]",
			"toAddress": "[email protected]",
			"createdAt": "2020-11-07T09:50:09.951Z"
		},
		"time": "2020-11-07T09:50:09.951Z",
		"complaintFeedbackType": "abuse"
	}
}

Event: email.received

Triggered when an inbound route receives an email.

{
	"type": "email.received",
	"data": {
		"email": {
			"id": "email_123456",
			"self": "https://api.sidemail.io/v1/inbound/emails/email_123456",
			"destination": "[email protected]",
			"from": { "email": "[email protected]", "name": "Sender" },
			"to": [{ "email": "[email protected]", "name": "Support Team" }],
			"cc": [{ "email": "[email protected]", "name": null }],
			"replyTo": [{ "email": "[email protected]", "name": "Billing" }],
			"subject": "Example inbound message",
			"text": "Hello, please find invoice attached...",
			"htmlAvailable": true,
			"attachments": [
				{ "name": "attachment.pdf", "contentType": "application/pdf", "size": 84320 }
			],
			"previewHtmlUrl": "https://api.sidemail.io/v1/inbound/emails/preview-html?token=...",
			"rawEmailUrl": "https://storage.example.com/..."
		},
		"spam": {
			"score": 2.4,
			"threshold": 15,
			"isSpam": false,
			"action": "accept",
			"symbols": ["R_SPF_ALLOW", "R_DKIM_ALLOW", "MIME_GOOD"]
		},
		"time": "2026-03-27T12:01:18.921Z"
	}
}

Notes:

  • spam.symbols contains the rule names that contributed to spam scoring.
  • email.self is a stable API URL for fetching full inbound email detail.
  • destination is the SMTP recipient routed in Sidemail. Header recipients are in to and cc.
  • previewHtmlUrl is present only when an HTML part is available and is short-lived (currently 15 minutes).
  • rawEmailUrl is a short-lived signed URL (currently 5 minutes).
  • text is truncated to 10 000 characters in the webhook. Fetch the full body via email.self.
  • attachments contains metadata only (name, content type, size in bytes). Download the raw email via rawEmailUrl to access attachment content.

Event: domain.sending.success

Triggered when a sending domain changes to verified and is ready for sending.

{
	"type": "domain.sending.success",
	"data": {
		"domain": {
			"id": "123",
			"domain": "example.com",
			"status": "success",
			"previousStatus": "pending",
			"mailFromDomain": "out.mail.example.com",
			"mailFromStatus": "success",
			"createdAt": "2026-05-10T09:50:09.951Z"
		},
		"time": "2026-05-10T10:05:09.951Z"
	}
}

Event: domain.sending.failed

Triggered when a sending domain changes to failed.

{
	"type": "domain.sending.failed",
	"data": {
		"domain": {
			"id": "123",
			"domain": "example.com",
			"status": "failed",
			"previousStatus": "pending",
			"mailFromDomain": "out.mail.example.com",
			"mailFromStatus": "failed",
			"createdAt": "2026-05-10T09:50:09.951Z"
		},
		"time": "2026-05-13T09:50:09.951Z"
	}
}