Nuntly emits webhook events for receiving activity so your application can react in real time. You subscribe to these events through the same webhook configuration used for sending events.
Available events
| Event | Description |
|---|
message.received | A new email was received and processed. |
message.security.flagged | A received message failed one or more security checks (SPF, DKIM, spam, or virus). |
message.agent.triggered | A received message was routed to an AI agent inbox. |
message.sent | A message was sent from an inbox. |
Event payloads
All receiving events follow the same envelope structure. The type field identifies the event, and the data field contains the event-specific payload.
message.received
Emitted when a new email is received and processed.
{
"type": "message.received",
"data": {
"messageId": "imsg_01kabn43yqyxn2bx4ve84mczd3",
"threadId": "thr_01kabn43yqyxn2bx4ve84mczd3",
"inboxId": "ibx_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"fromAddress": "sender@example.com",
"fromName": "Jane Doe",
"toAddresses": [
{ "address": "support@yourdomain.com" }
],
"subject": "Question about my account",
"receivedAt": "2025-01-15T10:30:00.000Z",
"attachmentCount": 0,
"spamVerdict": "PASS",
"virusVerdict": "PASS"
}
}
message.security.flagged
Emitted when a received message fails one or more security checks: SPF, DKIM, spam verdict, or virus scan. The message is still stored but flagged.
{
"type": "message.security.flagged",
"data": {
"messageId": "imsg_01kabn43yqyxn2bx4ve84mczd3",
"threadId": "thr_01kabn43yqyxn2bx4ve84mczd3",
"inboxId": "ibx_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"fromAddress": "suspicious@example.com",
"subject": "Suspicious message",
"receivedAt": "2025-01-15T10:30:00.000Z",
"spamVerdict": "FAIL",
"spfVerdict": "FAIL",
"dkimVerdict": "FAIL",
"virusVerdict": "PASS"
}
}
message.agent.triggered
Emitted when a message arrives at an inbox that has an AI agent assigned. This event is sent in addition to the message.received event.
{
"type": "message.agent.triggered",
"data": {
"messageId": "imsg_01kabn43yqyxn2bx4ve84mczd3",
"threadId": "thr_01kabn43yqyxn2bx4ve84mczd3",
"inboxId": "ibx_01kabn43yqyxn2bx4ve84mczd3",
"agentId": "my-support-agent",
"domainName": "yourdomain.com",
"fromAddress": "customer@example.com",
"subject": "Help with my order",
"receivedAt": "2025-01-15T10:30:00.000Z"
}
}
message.sent
Emitted when a message is sent from an inbox (via send, reply, or forward).
{
"type": "message.sent",
"data": {
"messageId": "imsg_01kabn43yqyxn2bx4ve84mczd3",
"threadId": "thr_01kabn43yqyxn2bx4ve84mczd3",
"inboxId": "ibx_01kabn43yqyxn2bx4ve84mczd3",
"domainName": "yourdomain.com",
"fromAddress": "support@yourdomain.com",
"toAddresses": [
{ "address": "customer@example.com", "name": "Jane Doe" }
],
"subject": "Re: Help with my order",
"sentAt": "2025-01-15T10:35:00.000Z"
}
}
Subscribe to receiving events
Add receiving events to your webhook configuration the same way you add sending events. You can subscribe 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: 'Receiving listener',
endpointUrl: 'https://yourapp.com/api/webhooks/receiving',
status: 'enabled',
events: [
'message.received',
'message.security.flagged',
'message.agent.triggered',
'message.sent',
],
});
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 receiving events
Use the same signature verification flow as sending events. See handle webhook events for the full implementation pattern.
Here is a condensed example handling receiving events:
// Inside your webhook handler (after signature verification)
const event = JSON.parse(payload);
switch (event.type) {
case 'message.received':
console.log(`New message from ${event.data.fromAddress}: ${event.data.subject}`);
break;
case 'message.security.flagged':
console.log(`Flagged message from ${event.data.fromAddress} (spam: ${event.data.spamVerdict})`);
break;
case 'message.agent.triggered':
console.log(`Agent ${event.data.agentId} triggered for message ${event.data.messageId}`);
// Trigger your AI agent processing
break;
case 'message.sent':
console.log(`Message sent to ${event.data.toAddresses.map(a => a.address).join(', ')}`);
break;
}
Learn more