Skip to main content

messageDeleted

The messageDeleted webhook event is triggered when EmailEngine detects that a previously tracked email has been removed from a mailbox folder. This event helps you keep external systems synchronized with mailbox state changes.

When This Event is Triggered

The messageDeleted event fires when:

  • A message is permanently deleted from a folder
  • A message is moved to another folder (the source folder triggers deletion)
  • A message is expunged from the server
  • The IMAP EXPUNGE command is issued for the message
  • Gmail: A message loses all labels or is moved to Trash
  • Outlook: A message is deleted via the Graph API

The event is triggered after EmailEngine confirms the message is no longer present in the monitored folder.

Common Use Cases

  • Database synchronization - Remove or archive records when emails are deleted
  • Search index updates - Remove deleted messages from your search indexes
  • CRM integration - Update ticket or contact records when associated emails are deleted
  • Audit logging - Track message deletions for compliance purposes
  • Storage cleanup - Remove cached attachments or processed data for deleted messages
  • Analytics - Track deletion patterns and user behavior

Payload Schema

Top-Level Fields

FieldTypeRequiredDescription
serviceUrlstringNoThe configured EmailEngine service URL
accountstringYesAccount ID where the message was deleted
datestringYesISO 8601 timestamp when the webhook was generated
pathstringYesMailbox folder path where the message was located (e.g., "INBOX")
specialUsestringNoSpecial use flag of the folder (e.g., "\Inbox", "\Sent", "\Trash")
eventstringYesEvent type, always "messageDeleted" for this event
eventIdstringYesUnique identifier for this webhook delivery
dataobjectYesMessage identification data (see below)

Message Data Fields (data object)

The messageDeleted event includes minimal data since the message content is no longer available:

IMAP Accounts

FieldTypeRequiredDescription
idstringYesEmailEngine's unique message ID (base64url encoded)
uidnumberYesIMAP UID of the deleted message within the folder

Gmail API Accounts

FieldTypeRequiredDescription
idstringYesGmail message ID
threadIdstringNoGmail thread ID the message belonged to
flagsarrayNoLast known flags before deletion
labelsarrayNoLast known Gmail labels before deletion
categorystringNoLast known category (e.g., "primary", "social")

Microsoft Graph (Outlook) Accounts

FieldTypeRequiredDescription
idstringYesOutlook message ID

Example Payloads

IMAP Account

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T06:44:14.660Z",
"path": "INBOX",
"specialUse": "\Inbox",
"event": "messageDeleted",
"eventId": "d47bc20a-69dd-4483-b678-1f13c3d4e590",
"data": {
"id": "AAAADAAABy4",
"uid": 1838
}
}

Gmail API Account

{
"serviceUrl": "https://emailengine.example.com",
"account": "gmail-user",
"date": "2025-10-17T08:15:22.123Z",
"path": "[Gmail]/All Mail",
"event": "messageDeleted",
"eventId": "e58cd31b-7aee-5594-c789-2g24d4e5f6a1",
"data": {
"id": "18b5c7d8e9f01234",
"threadId": "18b5c7d8e9f01234",
"flags": ["\Seen"],
"labels": ["INBOX", "IMPORTANT"],
"category": "primary"
}
}

Microsoft Outlook Account

{
"serviceUrl": "https://emailengine.example.com",
"account": "outlook-user",
"date": "2025-10-17T09:30:45.789Z",
"path": "Inbox",
"event": "messageDeleted",
"eventId": "f69de42c-8bff-6605-d890-3h35e5f6g7b2",
"data": {
"id": "AAMkADI2NGVhZTVlLTI1OGItNDUwZS05ZDVkLWQzN2E2MDUyYzc3YQBGAAAAAAI"
}
}

Handling the Event

Basic Handler

async function handleMessageDeleted(event) {
const { account, path, data } = event;

console.log(`Message deleted from ${account}:`);
console.log(` Message ID: ${data.id}`);
console.log(` Folder: ${path}`);
if (data.uid) {
console.log(` UID: ${data.uid}`);
}

// Remove from your database
await removeMessageFromDatabase(account, data.id);
}

Database Synchronization

async function handleMessageDeleted(event) {
const { account, data } = event;

try {
// Remove from primary database
await db.messages.delete({
where: {
accountId: account,
messageId: data.id
}
});

// Remove from search index
await searchIndex.delete(`${account}:${data.id}`);

// Clean up any cached attachments
await cache.deletePattern(`attachments:${account}:${data.id}:*`);

console.log(`Cleaned up message ${data.id} for account ${account}`);
} catch (err) {
console.error('Failed to clean up deleted message:', err);
throw err; // Retry the webhook
}
}

Audit Logging

async function handleMessageDeleted(event) {
const { account, path, date, data, eventId } = event;

// Log deletion for compliance
await auditLog.create({
eventId,
timestamp: new Date(date),
account,
action: 'message_deleted',
folder: path,
messageId: data.id,
uid: data.uid || null,
metadata: {
threadId: data.threadId,
labels: data.labels,
lastFlags: data.flags
}
});
}

Important Considerations

Message Content is Unavailable

When a messageDeleted event is received, the message content is no longer available on the server. The webhook only provides identification data (id, uid, threadId) to help you locate and remove records in your external systems.

If you need message content for deletion processing (e.g., archiving before deletion), you should:

  1. Store message metadata when handling messageNew events
  2. Reference that stored data when processing deletions

Deletion vs. Move

When a message is moved between folders:

  1. A messageDeleted event fires for the source folder
  2. A messageNew event fires for the destination folder

To distinguish between permanent deletion and folder moves, you can:

  • Check if a messageNew event arrives shortly after with the same messageId
  • Track message IDs across your system to detect moves

Gmail Label Changes

For Gmail API accounts, removing the last label from a message (except for Trash/Spam) triggers a deletion event. The labels field in the payload shows the last known labels before deletion.

Idempotency

Handle deletion events idempotently since webhooks may be retried:

async function handleMessageDeleted(event) {
const { account, data } = event;

// Check if already processed
const exists = await db.messages.findUnique({
where: {
accountId: account,
messageId: data.id
}
});

if (!exists) {
console.log(`Message ${data.id} already deleted, skipping`);
return;
}

await db.messages.delete({
where: {
accountId: account,
messageId: data.id
}
});
}

See Also