Event contract — Mimeo docs

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": "ada@example.com",
  "name": "signed_up",
  "details": { "plan": "trial", "source": "ads" },
  "occurred_at": "2026-07-29T10:00:00Z"
}

The fields

Field Notes
email Required. The universal key — lowercased, and the person is created if they're new.
name Required. Your own vocabulary. Triggers match on it exactly.
details Any JSON object. Trigger matches and event_occurred conditions read these.
occurred_at Optional. Defaults to now. Timeline ordering.
idempotency_key Optional. A repeat of the same key is recognised as a duplicate and does nothing, which makes an at-least-once sender safe.
person Optional. first_name, last_name, fields, tags — upserted alongside the event.
attribution Optional, 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:

Event When
tag_added · tag_removed A tag changes, by hand or by a flow. Details carry the tag.
field_changed A custom field changes. Details carry the key, the old value and the new one.
unsubscribed · resubscribed Suppression state changes. Details carry the reason and how it happened.
sequence_completed Someone 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