Nuntly emits webhook events throughout the sending lifecycle so your application can react in real time. You subscribe to these events through the same webhook configuration used for receiving events.
Available events
| Event | Description |
|---|
email.sent | The email has been accepted for delivery |
email.delivered | The email was successfully delivered to the recipient’s mail server |
email.opened | The recipient opened the email (requires open tracking on your domain) |
email.clicked | The recipient clicked a link in the email (requires click tracking) |
email.bounced | The email was rejected by the recipient’s server |
email.complained | The recipient marked the email as spam |
email.rejected | Nuntly refused to send the email due to a policy or configuration issue |
email.deliveryDelayed | Delivery to the recipient is temporarily delayed |
email.failed | The email could not be delivered |
Open and click tracking events require tracking to be enabled on your sending domain.
Event payloads
All sending events share a common envelope. The type field identifies the event, and the data field contains the event-specific payload along with the base email fields.
Base email fields (present on all sending events):
| Field | Type | Description |
|---|
id | string | Unique event identifier. |
messageId | string | The email message identifier. |
domainName | string | The sending domain. |
from | string | Sender address. |
to | string or array | Recipient address(es). |
subject | string | Email subject line. |
sentAt | string (ISO 8601) | When the email was submitted. |
tags | object | Key-value tags attached to the email (if any). |
email.sent
Emitted when an email is accepted for delivery.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.sent",
"createdAt": "2025-01-15T10:30:00.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "recipient@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z"
}
}
email.delivered
Emitted when the email is successfully delivered to the recipient’s mail server.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.delivered",
"createdAt": "2025-01-15T10:30:05.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "recipient@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z",
"delivery": {
"deliveredAt": "2025-01-15T10:30:05.000Z",
"recipients": ["recipient@example.com"],
"smtpResponse": "250 2.0.0 OK",
"remoteMtaIp": "74.125.130.27",
"reportingMta": "dns; mail.yourdomain.com",
"processingTime": 234
}
}
}
email.opened
Emitted when the recipient opens the email. Requires open tracking to be enabled on your sending domain.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.opened",
"createdAt": "2025-01-15T11:15:00.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "recipient@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z",
"open": {
"openedAt": "2025-01-15T11:15:00.000Z",
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15"
}
}
}
email.clicked
Emitted when the recipient clicks a link in the email. Requires click tracking to be enabled on your sending domain.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.clicked",
"createdAt": "2025-01-15T11:16:00.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "recipient@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z",
"click": {
"clickedAt": "2025-01-15T11:16:00.000Z",
"link": "https://yourdomain.com/getting-started",
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15"
}
}
}
email.bounced
Emitted when the email is rejected by the recipient’s mail server. Hard bounces indicate a permanent failure (for example, the address does not exist). Soft bounces are transient.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.bounced",
"createdAt": "2025-01-15T10:30:10.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "invalid@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z",
"bounce": {
"bounceType": "Permanent",
"bounceSubType": "General",
"bouncedAt": "2025-01-15T10:30:10.000Z",
"feedbackId": "feedback_01kabn43yqyxn2bx4ve84mczd3",
"reportingMta": "dns; mail.example.com",
"bouncedRecipients": [
{
"email": "invalid@example.com",
"action": "failed",
"status": "5.1.1",
"diagnosticCode": "smtp; 550 5.1.1 The email account does not exist"
}
]
}
}
}
email.complained
Emitted when the recipient marks the email as spam.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.complained",
"createdAt": "2025-01-15T12:00:00.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "recipient@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z",
"complaint": {
"complainedAt": "2025-01-15T12:00:00.000Z",
"feedbackId": "feedback_01kabn43yqyxn2bx4ve84mczd3",
"complaintFeedbackType": "abuse",
"complainedRecipients": [
{ "email": "recipient@example.com" }
]
}
}
}
email.rejected
Emitted when Nuntly refuses to send the email due to a policy or configuration issue (for example, the recipient is on a suppression list).
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.rejected",
"createdAt": "2025-01-15T10:30:01.000Z",
"data": {
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"reject": {
"reason": "Address is on the suppression list"
}
}
}
email.deliveryDelayed
Emitted when delivery to the recipient is temporarily delayed. The email may still be delivered after retry attempts.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.deliveryDelayed",
"createdAt": "2025-01-15T10:35:00.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "recipient@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z",
"deliveryDelay": {
"delayType": "RecipientServerError",
"delayedAt": "2025-01-15T10:35:00.000Z",
"deliveryStoppedAt": "2025-01-15T11:35:00.000Z",
"reportingMta": "dns; mail.example.com",
"delayedRecipients": [
{
"email": "recipient@example.com",
"status": "4.4.1",
"diagnosticCode": "smtp; 421 Try again later"
}
]
}
}
}
email.failed
Emitted when the email could not be delivered after all retry attempts.
{
"id": "evt_01kabn43yqyxn2bx4ve84mczd3",
"type": "email.failed",
"createdAt": "2025-01-15T11:30:00.000Z",
"data": {
"id": "email_01kabn43yqyxn2bx4ve84mczd3",
"messageId": "msg_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"from": "hello@yourdomain.com",
"to": "recipient@example.com",
"subject": "Welcome to Nuntly",
"sentAt": "2025-01-15T10:30:00.000Z",
"enqueuedAt": "2025-01-15T10:29:59.000Z",
"failure": {
"error": "Rendering failed: template variable 'user.name' is undefined"
}
}
}
Subscribe to sending events
Add sending events to your webhook configuration through the dashboard or the SDK.
import { Nuntly } from '@nuntly/sdk';
const nuntly = new Nuntly({
apiKey: process.env.NUNTLY_API_KEY,
});
const webhook = await nuntly.webhooks.create({
name: 'Sending listener',
endpointUrl: 'https://yourapp.com/api/webhooks/sending',
status: 'enabled',
events: [
'email.delivered',
'email.bounced',
'email.complained',
'email.opened',
'email.clicked',
],
});
console.log('Webhook ID:', webhook.id);
console.log('Signing secret:', webhook.signingSecret);
The signing secret is displayed only once. Store it securely. You need it to verify incoming webhook requests.
Handle sending events
Use the same signature verification flow described in handle webhook events. Here is a condensed example handling sending events:
// Inside your webhook handler (after signature verification)
const event = JSON.parse(payload);
switch (event.type) {
case 'email.delivered':
console.log(`Delivered to ${event.data.to}`);
break;
case 'email.bounced':
console.log(`Bounced (${event.data.bounce.bounceType}): ${event.data.bounce.bouncedRecipients[0].email}`);
// Remove hard-bounced addresses from your mailing list
break;
case 'email.complained':
console.log(`Complaint from ${event.data.to}`);
// Unsubscribe the recipient
break;
case 'email.opened':
console.log(`Opened at ${event.data.open.openedAt}`);
break;
case 'email.clicked':
console.log(`Clicked link: ${event.data.click.link}`);
break;
}
Learn more