Webhooks

Webhook events

The email.sent, email.opened, and email.clicked events — envelope and per-event payload shapes, generated from the live fan-out's schemas.

Webhook events

Batohi delivers three event types. The envelope is the same for every event; the data payload varies by type. The schemas below are generated from the same WEBHOOK_PAYLOAD_SCHEMAS the live fan-out validates against, so they cannot drift from what is delivered.

Envelope

Every delivery is a JSON body with this shape:

Envelope

FieldTypeRequiredDescription
idstringyesDeterministic delivery id — use for idempotent dedup.
typestringyesThe event type.
timestampstringyesDelivery-attempt time (ISO 8601). NOT the original event time — that is in data.*At.
dataobjectyesEvent-specific payload. See the per-event schemas.

timestamp is the delivery-attempt time, not the event time. It is recomputed on each retry. The original engagement-event time lives only inside data.openedAt / data.clickedAt / data.sentAt. Do not treat the envelope timestamp as when the open/click/send happened.

email.sent

Fired when a message is sent.

data — email.sent

FieldTypeRequiredDescription
trackIdstringyesThe tracked message identifier.
recipientCountinteger | nullyesNumber of recipients, or null if unknown.
sentAtstring | nullyesISO 8601 send timestamp, or null.

email.opened

Fired only for genuine opens. A proxy prefetch, self-open, or scanner does not fire this event.

data — email.opened

FieldTypeRequiredDescription
trackIdstringyesThe tracked message identifier.
openedAtstringyesISO 8601 timestamp of the genuine open.

email.clicked

Fired when a tracked link is clicked. linkId is null when the click can't be attributed to a specific rewritten link.

data — email.clicked

FieldTypeRequiredDescription
trackIdstringyesThe tracked message identifier.
linkIdstring | nullyesThe clicked link identifier, or null if unavailable.
clickedAtstringyesISO 8601 timestamp of the click.

Idempotency

The id field is deterministic: ${outboxEventId}_${endpointId}. A redelivered outbox event never double-queues — use id as your dedup key.

On this page