Audiences tools
Group contacts into static lists or dynamic audiences defined by conditions, and let segment rules move engaged contacts into an audience automatically.
- create_audience
- list_audiences
- update_audience
- preview_audience
- add_to_audience
- remove_from_audience
- delete_audience
- create_segment_rule
- list_segment_rules
- get_segment_rule
- update_segment_rule
- delete_segment_rule
create_audience
Create an audience. Without `condition`: STATIC (membership added manually or by segment rules). With `condition`: DYNAMIC (membership computed from contact data on every read). Same-slug re-create with the same kind+condition is idempotent; a different kind or condition is a 409. Use preview_audience first to see who a condition would match.
namestringrequiredslugstringconditionobject (any keys)[]- AND-list of 1-10 predicates making this a DYNAMIC audience: membership is computed live from contact data (never stored — no manual adds, no audience_added trigger; a contact whose data changes moves in/out automatically). Predicates: {kind:'field', field:'phone'|'email'|'name', op, value} or {kind:'property', prop:'<ingested property name>', op, value}. Ops: eq | in (value: 1-100 strings, matches ANY) | starts_with | ends_with | contains | not_contains | exists | not_exists (exists/not_exists take NO value). starts_with/ends_with/contains ALSO accept a 1-100 value array = matches if ANY needle matches (e.g. Triangle by both area codes: {"kind":"field","field":"phone","op":"starts_with","value":["+1919","+1984"]}); not_contains with an array = contains NONE of them. eq stays single-value — `in` is its any-of form. Values are strings, max 200 chars, matched ASCII-case-insensitively after trimming. Predicates AND together; there is no cross-field OR (compose with `in`/arrays, or create separate audiences). Use list_contact_fields to see which fields/properties exist before building one.
teamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
list_audiences
List the team's audiences: id, name, slug, kind (default | static | dynamic), computed member_count, and — for dynamic audiences — the stored condition. Use this to READ what an audience targets before binding, mirroring, or editing it (there is exactly one default per team; its kind is 'default').
teamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
update_audience
Rename an audience and/or change its condition (slug or id; 'everyone' = the protected default, which rejects condition changes). condition: a new array edits/converts to DYNAMIC (static→dynamic DELETES stored memberships — the response reports members_removed; refused while an audience_added sequence or segment rule references the audience); explicit null converts to STATIC with EMPTY membership (refused while the audience gates a contact_added sequence); omitted preserves. Response includes the computed member_count.
audiencestringrequired- Audience slug or id; 'everyone' targets the team default.
namestringconditionobject (any keys)[] | null- AND-list of 1-10 predicates making this a DYNAMIC audience: membership is computed live from contact data (never stored — no manual adds, no audience_added trigger; a contact whose data changes moves in/out automatically). Predicates: {kind:'field', field:'phone'|'email'|'name', op, value} or {kind:'property', prop:'<ingested property name>', op, value}. Ops: eq | in (value: 1-100 strings, matches ANY) | starts_with | ends_with | contains | not_contains | exists | not_exists (exists/not_exists take NO value). starts_with/ends_with/contains ALSO accept a 1-100 value array = matches if ANY needle matches (e.g. Triangle by both area codes: {"kind":"field","field":"phone","op":"starts_with","value":["+1919","+1984"]}); not_contains with an array = contains NONE of them. eq stays single-value — `in` is its any-of form. Values are strings, max 200 chars, matched ASCII-case-insensitively after trimming. Predicates AND together; there is no cross-field OR (compose with `in`/arrays, or create separate audiences). Use list_contact_fields to see which fields/properties exist before building one. Pass null to convert to static.
teamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
preview_audience
Preview a dynamic-audience condition WITHOUT creating anything: returns the computed match count and up to 5 sample contacts. Use before create_audience/update_audience to sanity-check a condition. 429 if another preview is running for this team (retry shortly); a zero match is a normal 200 with count 0.
conditionobject (any keys)[]required- AND-list of 1-10 predicates making this a DYNAMIC audience: membership is computed live from contact data (never stored — no manual adds, no audience_added trigger; a contact whose data changes moves in/out automatically). Predicates: {kind:'field', field:'phone'|'email'|'name', op, value} or {kind:'property', prop:'<ingested property name>', op, value}. Ops: eq | in (value: 1-100 strings, matches ANY) | starts_with | ends_with | contains | not_contains | exists | not_exists (exists/not_exists take NO value). starts_with/ends_with/contains ALSO accept a 1-100 value array = matches if ANY needle matches (e.g. Triangle by both area codes: {"kind":"field","field":"phone","op":"starts_with","value":["+1919","+1984"]}); not_contains with an array = contains NONE of them. eq stays single-value — `in` is its any-of form. Values are strings, max 200 chars, matched ASCII-case-insensitively after trimming. Predicates AND together; there is no cross-field OR (compose with `in`/arrays, or create separate audiences). Use list_contact_fields to see which fields/properties exist before building one.
teamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
add_to_audience
Add a contact to a STATIC audience by email or phone (fires the audience_added trigger on a new membership). STATIC-ONLY: rule-based (dynamic) audiences compute membership and reject manual adds with a 422; adding to 'everyone' (the default) is a vacuous success (added: false, nothing fires — membership there is implicit).
audiencestringrequiredemailstringphonestringteamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
remove_from_audience
Remove one contact's membership from a STATIC audience (slug or id) by email or phone — dynamic and the Everyone default reject with a 422 (membership is computed, not stored). Does NOT delete the contact and does NOT unenroll them — membership is read only at ingest, so an in-flight sequence keeps running (use unenroll_contact to stop it). If a segment rule feeds this audience the removal will not stick: the next sweep re-adds the contact AND re-fires the audience_added trigger — the response says so.
audiencestringrequiredemailstringphonestringteamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
delete_audience
Delete an audience by id or slug. Refused for the team's Everyone (default) audience. Blocked (409, with counts) while ANY sequence (trigger or gate), segment rule, broadcast (any status — the FK blocks even sent ones), weather watch, or open weather claim references it — re-bind or delete those first. Two-step confirm when it has members: call WITHOUT confirm, then re-call with confirm: true and the returned audience_id (confirming by slug is rejected — a slug can be re-created pointing at a different audience). Memberships are removed; contacts are NOT deleted.
audiencestringrequiredconfirmbooleanteamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
create_segment_rule
Create an engagement segment rule: when a contact first matches the condition they enter the audience (enter-once ratchet), firing audience_added sequences; optionally stop their enrollment in one sequence.
namestringrequiredslugstringconditionobject (any keys)[]required- AND-list of predicates. Kinds: {kind:'opened_count'|'clicked_count', op:'>='|'<=', value:int, sequence_id?:uuid (not slug)} · {kind:'sent_count', op:'>=', value:int, sequence_id?:uuid (not slug)} · {kind:'no_opens_in_days', days:int} (also requires ≥1 send in the window) · {kind:'has_tag', tag:string} · {kind:'property', prop, op:'eq'|'in', value}. All engagement counts in one rule share one sequence scope; max one no_opens_in_days. OR = create multiple rules feeding the same audience.
audiencestringrequired- Audience id or slug the rule feeds.
stop_sequencestring- Sequence (id or slug) to STOP the contact's enrollment in on entry.
teamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
list_segment_rules
List this team's segment rules.
teamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
get_segment_rule
Get one segment rule with its audience member count.
idstringrequiredteamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
update_segment_rule
Update a segment rule (pause/resume, rename, edit condition — future-only: contacts already entered stay; set stop_sequence to null to clear). The audience is immutable.
idstringrequirednamestringstatusenum- active | paused
conditionobject (any keys)[]- AND-list of predicates. Kinds: {kind:'opened_count'|'clicked_count', op:'>='|'<=', value:int, sequence_id?:uuid (not slug)} · {kind:'sent_count', op:'>=', value:int, sequence_id?:uuid (not slug)} · {kind:'no_opens_in_days', days:int} (also requires ≥1 send in the window) · {kind:'has_tag', tag:string} · {kind:'property', prop, op:'eq'|'in', value}. All engagement counts in one rule share one sequence scope; max one no_opens_in_days. OR = create multiple rules feeding the same audience.
stop_sequencestring | nullteamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.
delete_segment_rule
Delete a segment rule. The audience and its members are untouched.
idstringrequiredteamstringrequired- Brand/team to act on (slug or id) → X-Cadence-Team. Required — your account key authorizes every brand you own, so this picks which one.