Skip to main content
The audience API manages everything campaigns send to: contact lists, the contacts themselves, subscription topics, custom properties, and dynamic segments. Everything is reached through Lettr::audience():
All list() endpoints are paginated and return a response object exposing the data collection, a pagination object, and a hasMore() helper.

Contacts

A contact is an email address with optional custom properties, optionally attached to a list on creation.
Contact status is one of Subscribed, Unsubscribed, Bounced, Complained, or Unverified.

API Reference

GET /audience/contacts

Double opt-in

Pass a DoubleOptInConfig to create the contact as unverified and trigger a confirmation email — the contact becomes subscribed once they click the link.

Bulk create contacts

bulkCreate() takes up to 1000 contacts per request in one of two shapes. forEmails() builds the flat shape, where every address shares the batch-wide lists, properties and topics:
forContacts() addresses each contact individually. Row-level listIds 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:
AudienceTopicSubscription::optOut() 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.
updateExisting 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 — and to let an opt-out drop an existing subscription.

Handling the result

A call that doesn’t throw does not mean every row landed. Rows that fail validation are skipped and reported in errors while the rest of the batch commits — the API still returns 201. Always check hasErrors().
$error->errorCode is a Lettr\Enums\BulkAudienceContactErrorCode (or a plain string for a code added server-side): missing_email, invalid_email, invalid_property_value, unknown_property_key, unknown_list, unknown_topic, or invalid_topic_subscription.
alreadyExisted 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.

Membership

$result->contactIds() from a bulkCreate() feeds straight into the bulk membership calls, so no id lookup is needed in between:
Every bulk membership call processes the full cartesian product of its ids. Unsubscribing ignores pairs that don’t exist, so unsubscribed can be lower than totalPairs.

API Reference

POST /audience/contacts/bulk

Lists

A list is a static collection of contacts.

API Reference

GET /audience/lists

Segments

A segment is a dynamic group defined by conditions. Groups are joined by OR, and conditions within a group are joined by AND.
Operators that don’t take a value (SegmentOperator::IsTrue, SegmentOperator::IsFalse) report this via requiresValue().

API Reference

GET /audience/segments

Topics

Topics are subscription categories contacts can opt in or out of.

API Reference

GET /audience/topics

Properties

Custom contact properties have an immutable name (must match ^[a-z][a-z0-9_]*$) and an immutable type. Only the fallback value can be updated.

API Reference

GET /audience/properties

What’s Next

Campaigns

Send to your audience

API Reference

Full audience API reference