Skip to main content
The client.audience resource manages everything campaigns send to. Each kind is a sub-resource:
All list() methods return paginated data with a pagination object (total, per_page, current_page, last_page).

Contacts

Contact status is "subscribed", "unsubscribed", "bounced", "complained", or "unverified".

API Reference

GET /audience/contacts

Double opt-in

Pass double_opt_in to create the contact as unverified and send a confirmation email — they become subscribed after clicking the link:

Bulk create contacts

bulkCreate() takes up to 1000 contacts per request in one of two shapes. The flat emails shape gives every address the same lists, properties and topics:
The contacts shape addresses each contact individually. Row-level list_ids and topics are applied on top of the batch-wide ones, and a row’s properties key overrides the batch-wide value for that key:
Pass emails or contacts, not both — the TypeScript types enforce it.
subscription: "opt_out" suppresses a topic for that contact in the same request. Useful when a topic’s default_subscription is opt_out, which auto-subscribes newly created contacts — a row-level opt_out cancels that instead of needing a second call. A row-level opt_out also beats a batch-level opt_in.
update_existing defaults to false, which leaves existing contacts’ properties alone (they are still attached to the requested lists). Set it to true to merge properties — submitted keys overwrite, absent keys are preserved. It governs properties only: a row-level opt_out drops an existing subscription either way.

Handling the result

error === null does not mean every row landed. Rows that fail validation are skipped and reported in data.errors while the rest of the batch commits — the API still returns 201. Always check data.errors.length.
error_code is one of missing_email, invalid_email, invalid_property_value, unknown_property_key, unknown_list, unknown_topic, or invalid_topic_subscription.
already_existed and updated overlap by design — “was the address already in the audience?” versus “did this request change the contact?” — so they don’t sum to the row count. A contact that already existed and got attached to a list is counted in both.

Bulk membership

data.contacts from a bulkCreate() feeds straight into the bulk membership calls, so no id lookup is needed in between:
Both topic calls process every contact_ids × topic_ids combination, up to 1000 contacts × 50 topics. Unsubscribing ignores pairs that don’t exist, so unsubscribed can be lower than total_pairs.
bulkUnsubscribeTopics() and bulkDetachLists() issue a DELETE with a request body. fetch handles that fine, but a proxy in front of your app may not.

API Reference

POST /audience/contacts/bulk

Lists

API Reference

GET /audience/lists

Segments

A segment is a dynamic group defined by conditions. Groups are joined by OR; conditions within a group by AND.
Operators include equals, not_equals, contains, starts_with, ends_with, is_true, is_false, and others.

API Reference

GET /audience/segments

Topics

API Reference

GET /audience/topics

Properties

Custom contact properties have an immutable name and type; only the fallback can be updated.

API Reference

GET /audience/properties

What’s Next

Campaigns

Send to your audience

API Reference

Full audience API reference