Why
Some sends should depend on what a lead has done, not just which audience they’re in:- “Don’t send the May 17 ‘introducing Mastery’ message to anyone who already purchased it.”
- “For the +2hr ‘you can still join’ reminder, skip phones currently in the live event.”
mastery_purchase or webinar_attended, each with a timestamp. At send-fire time, the campaign send filters its audience based on those events.
Real-time vs bulk
The endpoint accepts either pattern — both produce the same rows, the filter logic doesn’t care.- Real-time stream — POST one event at a time as it happens (a purchase completes, a phone joins the live event). Best when you want the freshest possible data without a polling loop.
- Bulk batch — POST up to 50,000 events in one call. Best when your source system aggregates and you’d rather push periodically than per-event.
(phoneE164, eventType, occurredAt) more than once, the duplicate is silently dropped. Retries are safe.
Configuring a send to consume events
When adding a send to your campaign sequence, enable Event filter and pick:- Mode —
exclude(skip leads that have the event) orinclude(only send to leads that have the event). - Event type — must match the
eventTypestrings you POST (case-sensitive). - Within last (minutes, optional) — only consider events whose
occurredAtis within this many minutes of the send firing.
Sticky vs windowed events
Whether to use the time window depends on the event’s meaning:
Without a window, “ever happened” matches. With a window, only events whose
occurredAt falls inside the last N minutes from send-fire time match.
Common patterns
Exclude prior purchasers across a multi-day campaign:Combining with audience filtering
A send can have both an event filter (this page) and an audience filter configured. Both must keep a recipient for them to receive the message — the filters are ANDed.Phone normalization
phoneE164 values you POST go through libphonenumber. Both +15551234567 and 15551234567 parse to the same canonical E.164. Numbers that don’t parse are reported per-row in the response’s rejects[] rather than failing the whole batch.
