Data Schema Reference

Data Architecture

NexoMailer is a Headless SDK, which means you have 100% control over your data. This page explains our MongoDB schema, indexing strategy, and how to query your data for custom integrations.


1. Primary Collections

NexoMailer manages two main collections in your MongoDB database.

messages Collection

This is the "Source of Truth" for every email sent. It stores the final state and aggregate metrics.

FieldTypeDescription
messageIdStringUnique Nexo ID (UUID). Used for all tracking links.
recipientStringThe target email address. Indexed for fast lookup.
statusEnumCurrent state: SENT, DELIVERED, OPENED, CLICKED, BOUNCED, FAILED, COMPLAINED.
providerStringThe provider used (e.g., smtp, resend, ses).
subjectStringEmail subject line.
openCountNumberTotal successful open events.
clickCountNumberTotal successful click events.
projectIdStringMulti-tenant identifier for your different apps.
environmentStringEnvironment tag: production, staging, development.
metadataObjectCustom payload including provider-specific IDs.

trackingevents Collection

This is a time-series audit trail of every interaction. Every click or open creates a new document here.

{
  "messageId": "msg_998877",
  "type": "email.opened",
  "ip": "122.161.x.x",
  "userAgent": "Mozilla/5.0...",
  "timestamp": "2026-05-13T09:00:00Z"
}

2. Performance & Indexing

To ensure NexoMailer handles millions of emails without slowing down, the following indexes are automatically created:

  • Unique Nexo ID: db.messages.createIndex({ messageId: 1 }, { unique: true })
  • Recipient Search: db.messages.createIndex({ recipient: 1 })
  • Multi-Tenant Filter: db.messages.createIndex({ projectId: 1, environment: 1 })
  • Event Timeline: db.trackingevents.createIndex({ messageId: 1, timestamp: -1 })

3. Status Lifecycle

The status field in the messages collection follows a strict Priority Ladder. Events from Tracking Middleware and Webhooks are aggregated to move the status forward.

[!TIP] Data Integrity: NexoMailer ensures that once an email is marked as CLICKED, it can never be downgraded to OPENED, even if multiple tracking pixels are triggered.


4. Querying Examples

You can build your own custom queries on top of the NexoMailer schema:

Find all bounced emails for a project

db.messages.find({ 
  projectId: 'my-saas-app', 
  status: 'BOUNCED' 
});

Get most active recipients

db.messages.aggregate([
  { $group: { _id: "$recipient", totalOpens: { $sum: "$openCount" } } },
  { $sort: { totalOpens: -1 } }
]);

Related Documentation