Trigger events API
Trigger events are a named event vocabulary you define — order.completed, trial.started, anything your application knows about. Fire one via the API and every active automation listening for it enrolls the contact, with the fire's payload available inside the flow's emails as merge tags. Requires the triggers scope.
How it fits together
Three pieces cooperate:
- The event — a named definition (
nameis a lowercase slug likeuser.signed_up, immutable once created;display_nameanddescriptionare for humans). Names starting withstripe.orshopify.are reserved for provider event sources. - The listening automation — any flow whose trigger is Custom event bound to the event's
uid, optionally gated bypayload_filters(equals/existsrows over dot-notation paths; all rows must match). - The fire — one
POST /triggers/{uid}/firecall. It resolves the contact, records an audit row (visible in-app under Automations → Trigger events, retained 90 days), and enrolls every matching automation. The payload is snapshotted onto each run, so emails render{{ event.* }}tags from enrollment-time data even if you fire the event again later.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /triggers | Paginated index, newest first, with lifetime fire counts. |
| POST | /triggers | Create: name (slug, required), display_name, description, auto_create_contact, mail_list_uid. Duplicate names return 409. |
| GET | /triggers/{uid} | One event with fire count and last_fired_at. |
| PATCH | /triggers/{uid} | Update everything except name (immutable — your code references it). |
| DELETE | /triggers/{uid} | Delete the event and its fire log. Automations bound to it are kept but never fire again. |
| POST | /triggers/{uid}/fire | Fire for a contact: email or subscriber_uid, optional payload object (≤16 KB of JSON). Returns {fire_id, runs_started}. |
| GET | /triggers/{uid}/fires | Recent fires, newest first, payloads included (90-day retention). |
Firing an event
# Define the event once
curl -X POST https://emailflow.ai/api/v1/triggers \
-H "Authorization: Bearer efa_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "order.completed", "display_name": "Order completed",
"auto_create_contact": true, "mail_list_uid": "ab12cd34ef" }'
# Fire it from your application on every checkout
curl -X POST https://emailflow.ai/api/v1/triggers/{uid}/fire \
-H "Authorization: Bearer efa_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order.completed-alice-1720000000" \
-d '{
"email": "alice@example.com",
"payload": { "order": { "id": "ord_42", "total": 129.9, "currency": "USD" } }
}'
# Response (201)
{
"data": {
"fire_id": "64a91b2c7de31",
"event": "order.completed",
"email": "alice@example.com",
"runs_started": 1,
"created_at": "2026-07-05T13:30:00+00:00"
}
}
Contact resolution. A subscriber_uid resolves directly. An email is looked up in the event's target list (or across your lists when no list is configured). With auto_create_contact on and a mail_list_uid configured, unknown emails are created in that list as subscribed contacts — without starting the list's welcome automations; only the fired event's own automations enroll. Fires that resolve no contact are still logged (with runs_started: 0) so you can audit misses.
Idempotency. Enrollment is idempotent per contact and automation — a second fire for the same contact logs a fire but does not double-enroll. For network-level retry safety send an Idempotency-Key header (<event>-<user>-<timestamp> works well).
Payload merge tags
Inside any email of a flow started by a custom event, the fire's payload is addressable with dot notation — missing paths render empty, and the fallback grammar applies:
Subject: Thanks for order {{ event.order.id }}!
Your total was {{ event.order.total }} {{ event.order.currency | USD }}.
Replaying runs
Terminal runs (completed, failed, cancelled) can be re-enrolled from the flow's entry node with the original payload snapshot — {{ event.* }} tags render the same values as the first pass. The new run references the original via replayed_from. Non-terminal runs return 409, as do inactive automations and unsubscribed contacts. Requires the automations scope.
curl -X POST https://emailflow.ai/api/v1/automation-runs/{run_uid}/replay \
-H "Authorization: Bearer efa_YOUR_KEY"
# Response (201) — a NEW run; the original is untouched
{
"data": {
"uid": "64a91b2c7de99",
"status": "waiting",
"event_payload": { "order": { "id": "ord_42", "total": 129.9, "currency": "USD" } },
"replayed_from": "64a91b2c7de31",
"...": "..."
}
}
Replay is also available in-app from the automation's Contacts view on any completed or failed run.