Contacts tools
Create, import, look up and delete the people you send to, and record opt-outs or SMS consent. Properties merge on every update and become template tokens.
- upsert_contact
- list_contacts
- list_contact_fields
- get_contact
- list_enrollments
- delete_contacts
- import_contacts
- import_opt_outs
- grant_sms_consent
upsert_contact
Create or update a contact (properties jsonb-merge).
is_staffboolean- Staff contact flag; defaults to false for new contacts and is preserved when omitted for existing contacts. Staff receive only staff event sequences. Static audience membership, broadcasts and bulk emit exclude staff; dynamic-audience previews still count them.
emailstringnamestringphonestringpropertiesobject (any keys)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_contacts
List contacts for the team.
limitnumberoffsetnumbersearchstringteamstringrequired- 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_contact_fields
Without kind/name: list every {{token}} usable in this team's email/SMS templates — core tokens (first_name, team_name, unsubscribe_url, …) plus auto-captured form fields from contact properties, with contact counts and a sample value. Call before authoring a template so token spellings match what capture actually stored. Also use the no-argument form to discover the exact normalized property key and submitted value for update_team's sms_consent_property/sms_consent_value consent mapping, since capture normalizes field names and form builders vary on checkbox values. WITH kind+name (supply both or neither): the value distribution for one targeting selector — top values by contact count plus distinct_count/total_count/unsupported_count, for authoring audience conditions. PII policy: email/name return counts only (no raw values); phone returns +1AAA area-code prefixes only.
kindenum- field | property
With `name`, return that selector's value distribution. 'field' = the core columns phone/email/name; 'property' = a captured JSON property. Supply kind and name together or neither. namestring- Field or property name. email/name return counts only; phone returns area-code prefixes (e.g. +1919).
limitnumberteamstringrequired- 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_contact
Fetch one contact by email or contact_id, with its merged properties + suppression state. Provide email or contact_id (contact_id wins if both are given).
emailstringcontact_idstringteamstringrequired- 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_enrollments
List which sequences a contact is in (status + current step order), by email or contact_id (contact_id wins if both are given). Answers 'what sequences is this person in?'.
emailstringcontact_idstringteamstringrequired- 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_contacts
Delete 1-100 contacts (ids or emails) — HARD delete, irreversible. Cascades their enrollments, sequence + broadcast send history, events, audience memberships and SMS consent records, so sequence/broadcast stats will shift. Two-step confirm: call WITHOUT confirm first — if there is anything to lose nothing is deleted and you get back requires_confirmation + the impact counts + the resolved contact ids; show the user, then re-call with confirm: true AND THE RETURNED IDS (confirming by email is rejected, because an email can resolve to a different contact than the one you previewed). Contacts with no history delete immediately. Refused (409) while a send is dispatching. NOTE: unsubscribe/bounce/complaint suppressions are keyed by address, not contact — they SURVIVE deletion, so this is not a way to re-email someone who opted out. Deleting also breaks the unsubscribe link in mail already delivered to that address.
contactsstring[]required- Contact ids or emails (1-100). confirm: true requires ids, not emails.
confirmbooleanteamstringrequired- 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.
import_contacts
Bulk import contacts (≤1000/call; chunk larger lists). Upserts by email or normalized phone; tags/properties merge. enroll defaults FALSE (no contact_added triggers); when true, ramp staggers initial sends (e.g. {per_hour:500}).
rowsobject[]required- Contact rows to upsert.
rows[].emailstringrows[].namestringrows[].phonestringrows[].tagsstring[]rows[].propertiesobject (any keys)enrollboolean- Fire contact_added sequences for NEW contacts (default false).
rampobject- Stagger initial sends when enrolling.
ramp.per_hournumberrequiredteamstringrequired- 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.
import_opt_outs
Import opt-outs by address. SMS opt-out has the same effect as STOP and never touches email. Email opt-out behaves like an unsubscribe click and stops the contact's active sequences, including SMS steps. Works before the contact exists; never lifts or downgrades a stronger reason. Addresses matching any staff contact return staff with no writes. Duplicate normalized entries share an outcome; bad addresses return invalid per entry. Chunk lists larger than 1000 entries.
sourcestringrequired- Nonblank import source, 1–200 characters.
entriesobject[]required- 1–1000 entries; chunk larger imports. Duplicate normalized addresses share one outcome.
entries[].channelenumrequired- sms | email
entries[].addressstringrequired- Email or NANP phone; normalized before import. Invalid addresses return invalid per 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.
grant_sms_consent
Record operator-asserted SMS consent for 1–1000 existing contacts in this team. Supply each contact_id and its current E.164 phone, a nonempty basis (what evidence supports consent), and source (e.g. servicetitan-import). Only use when the operator has confirmed consent covering the SMS being sent; importing a customer alone does not establish consent. Atomically sets sms_consent=opted_in and appends API evidence containing the supplied basis/source. Never creates a form submission or enrolls contacts. Skips explicit opt-outs, suppressed/shared-opted-out phones, changed phones, and missing contacts; returns an outcome per entry. Already opted-in contacts are unchanged, making retries safe. This can make contacts eligible for already scheduled SMS. Chunk larger lists into ≤1000 per call.
contactsobject[]requiredcontacts[].contact_idstringrequiredcontacts[].phonestringrequiredbasisstringrequiredsourcestringrequiredteamstringrequired- 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.