Get your API key
API reference

Events

The Events endpoints.

Auth: API key · Base URL: https://api.mailkite.dev

Endpoints

Each row expands to its request and response schema. The address bar follows along, so any endpoint can be linked to directly.

POST /v1/events Record one application-level fact about a user — `user.created`, `trial.expiring`, `payment.failed`. THE primary way a sequence starts on a developer platform: your application already knows when a payment failed, so it says so, and every enabled trigger listening for that name enrolls the contact with the payload as its input. Identify the subject with `email` or `contactId` (never both). Pass a `dedupeKey` to make retries idempotent: a repeat returns the original event with `duplicate: true` rather than enrolling anyone twice.

Record one application-level fact about a user — `user.created`, `trial.expiring`, `payment.failed`. THE primary way a sequence starts on a developer platform: your application already knows when a payment failed, so it says so, and every enabled trigger listening for that name enrolls the contact with the payload as its input. Identify the subject with `email` or `contactId` (never both). Pass a `dedupeKey` to make retries idempotent: a repeat returns the original event with `duplicate: true` rather than enrolling anyone twice.

Request body

send-event-request.json

One application-level fact about one of your users — `user.created`, `trial.expiring`, `payment.failed`. This is the generic trigger for sequences: a sequence whose trigger names this event enrolls the contact when it arrives, and an enrollment parked on `wait_for_event` wakes on it. Distinct from the delivery timeline at GET /api/events, which records what MailKite did with a message; this records what YOUR user did.

FieldTypeNotes
name req stringEvent name: 1-64 characters of letters, digits, dot, dash, or underscore, starting alphanumeric. Names beginning `mailkite.`, `mailkite:`, or `email.` are reserved for platform events.
eventstringAlias for `name`, accepted so a payload written for another provider works unchanged. `name` wins when both are given.
emailstringThe address this event is about. Give either this or `contactId`, never both. An address we don't hold a contact for still records — the event is an observation, not a subscription, so no contact is created as a side effect.
contactIdstringA contact you own (ctc_…), as an alternative to `email`. Give either this or `email`, never both.
payloadobjectFree-form context carried with the event. Sequence steps read it as {{event.*}} for merge tags and, from conditions on, for branching.
dedupeKeystringIdempotency key. A second POST with the same key returns the ORIGINAL event and `duplicate: true` instead of recording a second one — so a retried webhook can never enroll the same contact twice.

Response body

send-event-response.json

Acknowledgement that the event was recorded. Answered 202: the row is written synchronously, but everything the event sets in motion (sequence enrollment, waking a parked enrollment) is not.

FieldTypeNotes
id req stringEvent id (cev_…).
object req stringAlways "event".
name req stringThe recorded event name.
email req stringThe address the event is about, normalized to lowercase.
duplicate req booleantrue when a `dedupeKey` matched an event already recorded — nothing new was written and `id` is the original's.
GET /v1/events List recorded events, newest first — the surface for confirming a POST landed and for debugging a sequence that did not trigger. Filter by `name` and/or `email`.

List recorded events, newest first — the surface for confirming a POST landed and for debugging a sequence that did not trigger. Filter by `name` and/or `email`.

Response body

list-events-response.json
FieldTypeNotes
events req custom-event[]Recorded events, newest first.
events[].id req stringEvent id (cev_…).
events[].name req stringEvent name as posted.
events[].email req stringThe address the event is about, normalized to lowercase.
events[].contactIdstring · nullThe contact this resolved to, when the address is one we hold.
events[].payloadobject · nullThe context posted with the event.
events[].dedupeKeystring · nullThe idempotency key posted with the event.
events[].createdAt req numberWhen the event was recorded, in ms epoch.
GET /v1/events/names List the distinct event names this account works with, so an editor can offer them instead of asking you to remember one. Returns both events actually posted (with a count and when one last arrived) and events your sequences already trigger on or wait for but that may never have been sent — sequences are routinely written before the application emits the event, so a list of only-what-you-have-sent would be a trap.

List the distinct event names this account works with, so an editor can offer them instead of asking you to remember one. Returns both events actually posted (with a count and when one last arrived) and events your sequences already trigger on or wait for but that may never have been sent — sequences are routinely written before the application emits the event, so a list of only-what-you-have-sent would be a trap.

Response body

list-event-names-response.json
FieldTypeNotes
events req event-name[]Most-recently-seen first, then names only a sequence declares (alphabetical — they have no recency to sort by).
events[].name req string
events[].count req numberHow many of these have been recorded. 0 for a name only a sequence declares.
events[].lastSeenAtnumber · nullWhen one last arrived, in ms epoch. Null if never.
events[].usedBy req string[]Names of the sequences that trigger on or wait for this event.