Audiences
Lists of contacts: create them, add one person or import up to 5,000 at a time with their opt-out status, page through the members, and remove people.
An audience is a list of contacts. Campaigns mail an audience, segments narrow one, and contact_added automations start when someone joins one. A contact exists once per workspace and can be on any number of audiences, so taking someone off a list and deleting the person are different requests.
Importing is where a migration from another provider happens: bring the old provider's unsubscribes, bounces and complaints across with a status before the first campaign, because that list is the only record of who must not be mailed. See Campaigns for the full flow.
The audience object
| Field | Type | Description |
|---|---|---|
id | string | The audience's id, a UUID. Every path and body field that names an audience takes this, not the name. |
name | string | The audience's name. Names are not unique. |
contact_count | integer | How many contacts are on the list, recounted after each write through this API that adds or removes members. |
created_at | string (ISO 8601) | When the audience was created. |
List audiences
GET /v1/audiences · requires contacts:read
Every audience in the workspace, newest first, with its id and contact_count. Paged with limit and cursor as described in Pagination.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Page size, 1 to 100. |
cursor | string | none | The next_cursor from the previous page. |
Example
Response
200 OK
Create an audience
POST /v1/audiences · requires contacts:write
Creates an empty list. Nothing stops two audiences having the same name, so keep the returned id.
Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | 1 to 120 characters. |
Example
Response
201 Created
An audience object.
Errors
| Status | Type | When |
|---|---|---|
| 422 | invalid_request | name fails validation, with details. The message is name is required when it is missing or empty, name must be 120 characters or fewer when it is too long, and name must be a string when it is another type. |
Retrieve an audience
GET /v1/audiences/{id} · requires contacts:read
Returns one audience. Passing the audience's name instead of its id answers 404, and when the name matches an audience in the workspace (case-insensitively) the message gives its id.
Path parameters
| Parameter | Description |
|---|---|
id | The audience's id. |
Example
Response
200 OK
Errors
| Status | Type | When |
|---|---|---|
| 404 | not_found | No audience with that id in this workspace. |
Delete an audience
DELETE /v1/audiences/{id} · requires contacts:write
Deletes the list, not the people on it. Every contact on it loses this membership and keeps everything else: their other audiences, tags, attributes, engagement history, suppressions and topic preferences. Someone who was on no other list stays in the workspace with an empty audience_ids.
Nothing that refers to the audience is changed. A segment built on it stays, and matches nobody from then on.
Path parameters
| Parameter | Description |
|---|---|
id | The audience's id. |
Example
Response
200 OK
Errors
| Status | Type | When |
|---|---|---|
| 404 | not_found | No audience with that id in this workspace. |
List the contacts on an audience
GET /v1/audiences/{id}/contacts · requires contacts:read
The members of one list, as contact objects, sorted by email address A to Z.
This endpoint's cursor is not the opaque timestamp cursor other lists use: next_cursor is the last address on the page, and the next page starts at the first address after it. Pass it back unchanged as cursor while has_more is true. after is accepted as an older name for cursor; when both are sent, cursor wins.
Path parameters
| Parameter | Description |
|---|---|
id | The audience's id. |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Page size, 1 to 100. |
cursor | string | none | The next_cursor from the previous page: an email address. Only addresses that sort after it are returned. |
after | string | none | Alias for cursor. |
Example
Response
200 OK
Errors
| Status | Type | When |
|---|---|---|
| 404 | not_found | No audience with that id in this workspace. |
Add contacts to an audience
POST /v1/audiences/{id}/contacts · requires contacts:write
Adds one contact, or imports many. The shape of the body decides which: a JSON object adds one person and returns the contact, and a JSON array imports up to 5,000 and returns counts. Both go through the same import, so every rule below applies to both. Importing a large list one request per person is how a migration takes a week; send arrays.
Adding is an upsert on the address, and safe to re-run:
- Existing people are updated, not duplicated. An address already in the workspace joins this list and keeps its other memberships. Names are overwritten only by a non-empty value, tags are added to the ones the person already has (none are removed), and attributes are merged key by key.
- Attributes are typed by contact properties. Each value is coerced to its property's type, the same way a contact update coerces it. A key with no declared property creates a
stringproperty, and its values are stored as strings, so declarenumberandbooleanproperties before importing. A key that is empty, contains.or a null character, or starts with$is refused. statuscarries opt-outs across.unsubscribedsets the contact'sunsubscribedflag and writes a marketing-scoped suppression, so the person still gets transactional mail.bouncedsets the flag and writes a hard-bounce suppression for all mail;complainedsets the flag and writes a complaint suppression for all mail.unsubscribed: trueis a synonym forstatus: "unsubscribed", andstatuswins when both are sent. Each suppression's detail readsImported fromfollowed by thesourceparameter.- An opt-out stops what is already queued. For every row with status
unsubscribed,bouncedorcomplained, the address's pending scheduled sends are cancelled and its active automation enrolments end, exactly as an unsubscribe does, so a migrated opt-out does not still receive the rest of a sequence. - An import never resubscribes anyone. A row with
status: "subscribed"(or no status) for someone already unsubscribed leaves them unsubscribed, with their suppression intact. The old provider's file can predate an opt-out made here. Resubscribing is a deliberate contact update. - Automations start only for people new to this audience, or for tags new to the contact, and never for a suppressed address. A single contact always starts the audience's
contact_addedautomations, and thetag_addedautomations for each tag it did not already carry. An array starts them only with?trigger_automations=true.
Leave trigger_automations off when importing an existing list. A migrated list is not a few thousand new signups, and a welcome sequence sent to all of it at once is a cold blast from a domain that has never mailed those people, the one deliverability mistake with no cheap recovery. See Automations.
An array is validated as a whole before anything is written: one invalid row fails the request with 422, and each problem in details has a path that starts with the row's index. Within an array, repeats of the same address (compared trimmed and lower-cased) after the first are skipped for the contact itself: only the first row's names, tags, attributes and status are applied to the contact. A repeat's status is still acted on for suppressions, so an address listed first as subscribed and again as unsubscribed ends up with a marketing suppression while a new or subscribed contact's unsubscribed flag stays false. Request bodies are limited to 5 MB; a larger body is answered with 413 payload_too_large, so split very large imports into several requests.
Path parameters
| Parameter | Description |
|---|---|
id | The audience's id. |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
source | string | the API | Where the list came from, such as Mailchimp. Recorded on each suppression the request writes as Imported from Mailchimp. Cut to 60 characters. An empty value records just Imported. |
trigger_automations | boolean | false | For an array only: true starts contact_added automations for everyone new to this audience, and tag_added automations for every tag a contact did not already carry. Any value other than true counts as false. Ignored for a single contact, which always starts them. |
Body
Either one contact object, or an array of 0 to 5,000 of them. Each contact takes:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
email | string | yes | A valid email address. Stored trimmed and lower-cased. | |
first_name | string | no | Up to 120 characters. | |
last_name | string | no | Up to 120 characters. | |
tags | array of string | no | Up to 50 tags, each 1 to 60 characters. Lower-cased and trimmed, with runs of spaces turned into a hyphen. | |
attributes | object | no | Custom property values by key; each value a string, number or boolean. Keys cannot be empty, contain . or a null character, or start with $. | |
status | "subscribed" | "unsubscribed" | "bounced" | "complained" | no | "subscribed" | Standing with the previous sender. See above. |
unsubscribed | boolean | no | true is a synonym for status: "unsubscribed". Ignored when status is sent. | |
last_active_at | string (ISO 8601) | no | When the person was last active in your product, with a time zone. Kept only if later than the stored value. More than a day in the future is refused. |
Example
One contact:
An import from another provider:
Response
201 Created
For one contact, the contact object as it stands after the write, showing every audience, tag and attribute the person has:
For an array, counts:
| Field | Type | Description |
|---|---|---|
inserted | integer | People new to the workspace. |
updated | integer | People who already existed in the workspace, whether or not they were already on this list. |
skipped | integer | Rows not written to the contact because the same address appeared earlier in the array. |
unsubscribed | integer | Rows with status unsubscribed. Counts rows, so a repeated address counts each time. |
bounced | integer | Rows with status bounced. |
complained | integer | Rows with status complained. |
suppressed | integer | Suppressions this request created. An address that already had a suppression of the same scope is not counted again. |
properties_created | array of string | Keys of contact properties created because a row carried an undeclared attribute. |
automations_started | integer | Automation enrolments started. 0 unless trigger_automations=true. |
contact_count | integer | The audience's contact count after the import. |
Errors
| Status | Type | When |
|---|---|---|
| 404 | not_found | No audience with that id in this workspace. |
| 422 | invalid_request | An attribute key is empty, contains . or a null character, or starts with $. Nothing in the request is written. The message starts attributes: and names the key, with details. |
| 422 | invalid_request | A contact fails validation in any other way, or an array has more than 5,000 entries (message Validation failed, with details). |
| 413 | payload_too_large | The body is larger than 5 MB. |
Remove a contact from an audience
DELETE /v1/audiences/{id}/contacts/{contact_id} · requires contacts:write
Takes one person off this list. The contact still exists and keeps their other audiences, tags, attributes, engagement history, suppressions and topic preferences. To remove the person from the workspace entirely, use Delete a contact.
It does not check that the person was on the list: removing someone who was not a member answers 200 with removed: true all the same. It does not end automation enrolments the person already has.
Path parameters
| Parameter | Description |
|---|---|
id | The audience's id. |
contactId | The contact's id, from GET /v1/contacts?email=. Not the email address. |
Example
Response
200 OK
Errors
| Status | Type | When |
|---|---|---|
| 404 | not_found | No audience with that id in this workspace, or no contact with that contactId. When contactId is an address belonging to a contact, the message gives the contact's id. |