Tracking

Retrieve open/click tracking events and aggregate email statistics.

Tracking is automatically enabled for every outbound email. Sendy combines four signals to build a robust picture of email engagement.

Tracking signals

Each email collects up to five lifecycle timestamps. The unified status is derived from these.

  • Open (pixel) — a 1×1 transparent image is injected before </body>. Fires email.opened and sets first_opened_at. Image-blocking clients (Apple Mail Privacy Protection, some Gmail proxies) may suppress this signal.
  • Click<a href> links are rewritten to go through a tracking redirect. Fires email.clicked. The most reliable engagement signal.
  • Delivered (DSN) — Sendy requests a Delivery Status Notification (RFC 3461) and matches the report back via a bounce+{email_id}@{domain} Return-Path. Fires email.delivered and sets delivered_at. Coverage depends on the recipient MTA; Gmail honors success-DSN only partially.
  • Read (MDN) — Sendy adds theDisposition-Notification-To header (RFC 8098). When the recipient client honors the read-receipt prompt, Sendy parses the returning MDN and sets read_at. Most webmail clients ignore MDN — expect <5% coverage in practice.
  • Replied — when a new inbound message containsIn-Reply-To matching a previously sent email, Sendy fires email.replied and sets first_replied_at. Requires a linked IMAP account.

Unified status

Every email exposes a derived unified_status field, computed with the following priority:bounced >replied >read >opened >delivered >sent >sending >queued >failed.

Endpoints

MethodPathPermission
GET
tracking.read
GET
tracking.read

Get Tracking Events

GET /api/v1/tracking/:emailId

Request

Response

Possible type values:open,click,delivered,read,replied.

{
  "success": true,
  "data": [
    {
      "id": "evt-uuid-1",
      "emailId": "email-uuid",
      "type": "delivered",
      "createdAt": "2026-03-18T10:04:12Z"
    },
    {
      "id": "evt-uuid-2",
      "emailId": "email-uuid",
      "type": "open",
      "ipAddress": "203.0.113.42",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
      "createdAt": "2026-03-18T10:05:00Z"
    },
    {
      "id": "evt-uuid-3",
      "emailId": "email-uuid",
      "type": "click",
      "url": "https://example.com/pricing",
      "ipAddress": "203.0.113.42",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
      "createdAt": "2026-03-18T10:06:30Z"
    },
    {
      "id": "evt-uuid-4",
      "emailId": "email-uuid",
      "type": "replied",
      "createdAt": "2026-03-18T11:20:45Z"
    }
  ]
}
json

Get Global Stats

GET /api/v1/stats

Request

Response

All rates are percentages computed against total_sent. The query period defaults to the last 30 days; override with from and to (ISO 8601).

{
  "success": true,
  "data": {
    "total_sent": 1250,
    "total_opened": 654,
    "total_clicked": 189,
    "total_bounced": 15,
    "total_failed": 5,
    "total_queued": 0,
    "total_sending": 0,
    "total_delivered": 1130,
    "total_read": 42,
    "total_replied": 87,
    "open_rate": 52.32,
    "click_rate": 15.12,
    "bounce_rate": 1.2,
    "delivery_rate": 90.4,
    "read_rate": 3.36,
    "reply_rate": 6.96,
    "period": {
      "from": "2026-03-01T00:00:00Z",
      "to": "2026-03-18T23:59:59Z"
    }
  }
}
json