Email callback payloads

Delivery and engagement webhook JSON — field reference and examples for Connect HTTP send.

Email callback payloads

Reference for the JSON Routee POSTs to your webhook URLs when you configure callback on POST /transactional-email on Connect.

Setup first: Receive transactional email delivery callbacks — how to register statusCallback and eventCallback URLs, retry rules, and firewall notes.


Overview

On https://connect.routee.net/transactional-email, Routee delivers callbacks as a JSON object with batchId, messageId, status, results[], and optional reason (failures) or click (engagement).

results[] differs by callback type:

Callbackresults[] behaviour
Delivery (statusCallback)Accumulating history snapshot (queued → … → delivered / bounced)
Engagement (eventCallback)Always present, latest entry only (max 1) — do not count opens/clicks from it

SMTP relay does not use these webhooks — use Search email tracking or portal logs instead.


Configure on send (reminder)

{
  "from": { "address": "[email protected]" },
  "to": [{ "address": "[email protected]" }],
  "subject": "Order shipped",
  "content": { "html": "<p>Your order is on the way.</p>" },
  "callback": {
    "statusCallback": {
      "strategy": "OnChange",
      "url": "https://partner.example/webhooks/email-status"
    },
    "eventCallback": {
      "onOpen": "https://partner.example/webhooks/email-open",
      "onClick": "https://partner.example/webhooks/email-click"
    }
  }
}
Send-time fieldUsed for
statusCallbackDelivery lifecycle (QUEUED, DELIVERED, BOUNCED, …)
eventCallback.onOpen / onClickEngagement (OPENED, CLICKED)

strategy: OnChange (every transition) or OnCompletion (terminal delivery only). Applies to status callbacks.


Identifiers

FieldWhereMeaning
trackingIdSend responseBatch id for the send — use with Get email tracking (batch summary)
batchIdCallback payloadSame value as trackingId
messageIdCallback payloadPer-recipient id — use with Get email tracking (recipient timeline)

Multi-recipient sends: one trackingId / batchId, many messageId values.


Delivery status (statusCallback.url)

Routee POSTs a JSON object to your status URL. Top-level status values include:

QUEUED, SENT, DEFERRED, DELIVERED, BOUNCED, FAILED, UNDELIVERED

Readers may still see legacy alias DIFFERED on older rows; treat it as DEFERRED.

Common fields

FieldTypeDescription
batchIdstringBatch tracking id (send response trackingId)
messageIdstringPer-recipient message id
createdAt, updatedAtstring (ISO-8601)Timestamps
statusstringCurrent delivery status (see enum above)
from, tostringEnvelope addresses
senderstringFrom address (email)
subjectstringSubject line (max 144 chars when present)
cc, labelstringOptional. label echoes send-time campaign/send tag
results[]arrayDelivery status history snapshot — each item has updatedAt, status
customHeaders[]arrayOptional { name, value } pairs
reasonobjectOptional on failure/bounce — see below

reason (failures & bounces)

Present when diagnostic detail is available:

FieldDescription
descriptionHuman-readable SMTP or processing message
codeProvider or internal code when available
deliveryStatusUnderlying delivery status string
dsnParsed DSN map when available

Example — delivered

{
  "batchId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "messageId": "507f1f77bcf86cd799439011",
  "createdAt": "2026-07-14T08:15:00Z",
  "updatedAt": "2026-07-14T08:16:22Z",
  "status": "DELIVERED",
  "from": "[email protected]",
  "to": "[email protected]",
  "sender": "[email protected]",
  "subject": "Order shipped",
  "results": [
    {
      "updatedAt": "2026-07-14T08:15:01Z",
      "status": "QUEUED"
    },
    {
      "updatedAt": "2026-07-14T08:16:22Z",
      "status": "DELIVERED"
    }
  ],
  "customHeaders": []
}

Example — bounced (with reason)

{
  "batchId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "messageId": "507f1f77bcf86cd799439011",
  "createdAt": "2026-07-14T08:15:00Z",
  "updatedAt": "2026-07-14T08:17:05Z",
  "status": "BOUNCED",
  "from": "[email protected]",
  "to": "[email protected]",
  "sender": "[email protected]",
  "subject": "Order shipped",
  "results": [
    {
      "updatedAt": "2026-07-14T08:17:05Z",
      "status": "BOUNCED"
    }
  ],
  "reason": {
    "description": "550 5.1.1 User unknown",
    "code": "550",
    "deliveryStatus": "BOUNCED",
    "dsn": {
      "action": "failed",
      "status": "5.1.1"
    }
  }
}

Engagement (eventCallback.onOpen / onClick)

Routee POSTs a JSON object to the URL you configured for the event — once per engagement that lands (every open, every click). Top-level status: OPENED or CLICKED.

Source of truth for the event: top-level status and updatedAt. Use the click object only when status is CLICKED. Engagement results[] is always present and contains only the latest entry — consumers may ignore it. Do not count opens/clicks from results[]. Full history is available via Search email tracking / timeline GETs.

Shares the same envelope fields as status callbacks (batchId, messageId, from, to, sender, subject, optional label, slim results[] with at most one entry, …).

click object

Meaningful for CLICKED events. An OPENED payload may still include a leftover click object from an earlier click on the same message — ignore click unless top-level status is CLICKED.

FieldDescription
urlOriginal long URL
shortUrlTracked short URL when present
ip, country, cityGeo / network hints when available
referrer, device, browserClient hints when available
lagMilliseconds from send to click
clickedAtTimestamp string

Example — opened

{
  "batchId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "messageId": "507f1f77bcf86cd799439011",
  "createdAt": "2026-07-14T08:15:00Z",
  "updatedAt": "2026-07-14T08:20:11Z",
  "status": "OPENED",
  "from": "[email protected]",
  "to": "[email protected]",
  "sender": "[email protected]",
  "subject": "Order shipped",
  "results": [
    {
      "updatedAt": "2026-07-14T08:20:11Z",
      "status": "OPENED"
    }
  ]
}

Example — clicked

{
  "batchId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "messageId": "507f1f77bcf86cd799439011",
  "createdAt": "2026-07-14T08:15:00Z",
  "updatedAt": "2026-07-14T08:21:44Z",
  "status": "CLICKED",
  "from": "[email protected]",
  "to": "[email protected]",
  "sender": "[email protected]",
  "subject": "Order shipped",
  "results": [
    {
      "updatedAt": "2026-07-14T08:21:44Z",
      "status": "CLICKED"
    }
  ],
  "click": {
    "url": "https://shop.example.com/orders/12345",
    "shortUrl": "https://w.sends.ms/abc123",
    "ip": "203.0.113.10",
    "country": "GR",
    "city": "Athens",
    "lag": 404000,
    "clickedAt": "2026-07-14T08:21:44Z"
  }
}

Engagement callback frequency

Routee POSTs to your onOpen / onClick URLs for every open and every click that lands — including repeat opens of the same message and repeat clicks on the same tracked link.

EventWebhook behaviourTracking counters
OpenOne POST per open eventEach open increments openedCount in Search email tracking
ClickOne POST per click event (any tracked URL)Each click increments clickCount

Payload shapes are unchanged — see the examples above. Drive event handling from top-level status / updatedAt (and click only when status is CLICKED); do not accumulate counters from results[]. Derive unique opens/clicks in your own analytics if you need them.


Handler checklist

  1. Accept POST with Content-Type: application/json
  2. Return HTTP 200 within 2 seconds (platform retry policy applies — see Receive transactional email delivery callbacks)
  3. Ignore unknown JSON fields for forward compatibility
  4. Correlate using trackingId from your send response — match callback batchId
  5. For per-recipient logic on multi-recipient sends, use callback messageId or poll tracking APIs

Firewall

Whitelist inbound traffic from callbacksallow.routee.net on your callback URLs.


Related