People API — Mimeo docs
People API
Look someone up: their fields, tags, suppression state, money and where they are in every flow and sequence.
GET /api/v1/people/:id_or_email
Addressed by numeric id or email address — email is the universal key here, lowercased, and it's what imports and events upsert on.
GET /api/v1/people/ada@example.com
{ "person": {
"id": 41, "email": "ada@example.com", "name": "Ada Lovelace",
"unsubscribed": false,
"subscribed_on": "2023-04-11",
"fields": { "plan": "trial" },
"tags": ["trial"],
"attribution": { "first_landing_page": "…", "utm_source": "…" },
"lifetime_spend_cents": 29900,
"subscriptions": [\
{ "external_id": "sub_311", "product": "pro-plan", "status": "active",\
"plan": "pro", "interval": "yearly",\
"renewal_amount_cents": 29900, "currency": "usd",\
"next_renewal_at": "2027-08-06T00:00:00Z",\
"cancels_at": null, "cancelled_at": null }\
],
"active_flow_runs": [ { "flow": "trial_journey", "step": "trial_journey.7-wait-for-pricing" } ],
"recent_events": [ … ] } }
Each subscription is keyed the way money events are: product is the product key and external_id is what subscription events update against. A non-null cancels_at is a scheduled cancellation — the subscription is still live until that date, and cancelled_at stays null until it actually ends.
Writing to a person
There is no person-update endpoint, and that's deliberate: record an event instead. An event upserts the person, sets fields and tags, captures first-touch attribution, records money — and leaves a timeline entry explaining why any of it changed. A silent write leaves no such account.
POST /api/v1/events
{ "email": "ada@example.com", "name": "plan_changed",
"details": { "from": "trial", "to": "pro" },
"person": { "fields": { "plan": "pro" }, "tags": ["customer"] } }
The subscribe date
Every person has subscribed_on, a calendar date (YYYY-MM-DD) recording when they joined the list. People created through Mimeo get the day they arrived; people carried over from another tool get whatever date you supply. It is never null, so a question like "who joined before June?" is one comparison rather than a null check plus a fallback.
Set it explicitly under person when you record an event:
POST /api/v1/events
{ "email": "ada@example.com", "name": "subscribed",
"person": { "subscribed_on": "2023-04-11" } }
Only an earlier date replaces a stored one, and the event's occurred_at is never used for it. Both rules exist for the same reason: an event carries a timestamp whether or not it has anything to do with joining a list, so backfilling years of purchase history would otherwise rewrite everyone's subscribe date to their first purchase. Because earlier wins, those old timestamps are exactly the ones that would land.
The practical effect is that a repeat opt-in can't overwrite a real join date, and re-sending the same event is idempotent. To move a date forward, edit it on the person's profile — that's the one write that sets it outright.
Suppression
Unsubscribing is the person's, through the tokenized subscription endpoints — which attribute it to the exact email it came from. Suppression state appears here read-only: whether they're unsubscribed, when, why, and how it happened (one_click, api, import, provider_sync, provider_webhook, manual).
"It's people, not subscribers" is the rule the data model follows: someone who unsubscribes is still a person, with their history intact.
The queue
GET /api/v1/queue?person=ada@example.com&status=held
What's scheduled for them, what's holding and why, and what was dropped. ?status= takes pending, held, dropped, canceled or sent; ?limit= caps at 200.
See also: Events API · Subscriptions API · People, for humans