Theme

Event contract

Events are the trigger stream — the one way the outside world tells Mimeo that something happened. Everything the automation engine does starts here.

An event is a name, a person, and whatever details you want to carry. There is no schema and there never will be: your app's vocabulary is your data, not our product vocabulary.

POST /api/v1/events
Authorization: Bearer mm_your_token
Content-Type: application/json

{
  "email": "[email protected]",
  "name": "signed_up",
  "details": { "plan": "trial", "source": "ads" },
  "occurred_at": "2026-07-29T10:00:00Z"
}

The fields

FieldNotes
emailRequired. The universal key — lowercased, and the person is created if they're new.
nameRequired. Your own vocabulary. Triggers match on it exactly. One name is reserved: internal_note is refused here — internal notes go through the notes endpoint, which never triggers automation.
detailsAny JSON object. Trigger matches and event_occurred conditions read these.
occurred_atOptional. Defaults to now. Timeline ordering.
idempotency_keyOptional. A repeat of the same key is recognised as a duplicate and does nothing, which makes an at-least-once sender safe.
personOptional. first_name, last_name, fields, tags — upserted alongside the event.
attributionOptional, first touch only: landing page, referrer, UTMs, device, country.

Batching

Send up to 100 at once as { "events": [ … ] }. The response carries a per-event result, so a partial failure tells you exactly which ones.

{ "results": [
    { "status": "created",   "person_id": 41, "event_id": 902 },
    { "status": "duplicate", "person_id": 41, "event_id": 887 },
    { "status": "invalid",   "error": "email is required" } ] }

Events Mimeo writes itself

Your events aren't the only ones in the stream. State changes fire events too, so a flow can trigger on them exactly as it would on yours:

EventWhen
tag_added · tag_removedA tag changes, by hand or by a flow. Details carry the tag.
field_changedA custom field changes. Details carry the key, the old value and the new one.
unsubscribed · resubscribedSuppression state changes. Details carry the reason and how it happened.
sequence_completedSomeone reaches the end of a sequence.

A flow's fire_event step writes one too — which is how one flow hands off to another. The validator warns (self_trigger) if a flow fires the event that triggers itself.

Money is events

Purchases, subscriptions and payments are recorded from events with the right names and details — Mimeo never integrates a payment processor directly, because your systems already know and one source of truth beats two. See People for the shapes.

What events are not

Opens and clicks are not events. They're telemetry, in their own tables, with bot filtering — deliberately not part of the trigger stream, because a flow branching on "opened" is a flow branching on a number that Apple Mail Privacy Protection inflates. Use the opened_email and clicked_email conditions if you want them, knowing that.

Tags don't decide. Tags describe. Anything a flow branches on should be real state — a field, a purchase, a subscription, an event that actually happened.

Every name registers itself

The first time a name arrives — from your systems, from the engine, or from an import — it registers itself in the event registry: one row per name, carrying its occurrence count, sources, first/last seen, and a merged shape of every payload ever sent under it. Nothing is declared ahead of time, so the registry is exactly what has actually happened — the live vocabulary triggers, gates and event_occurred conditions draw from. Read it in the UI under Events, over GET /api/v1/event_types, or with the MCP list_event_types tool — and describe_schema returns the names as event_names. The feed of individual occurrences is readable too: GET /api/v1/events.

Designing your event names

See also: Events API · Flow schema