Audiences
This page is for developers and integrators who read or change audiences from their own code: the calls that build an audience, manage its members, read the list a send uses, or ask which audiences a contact is in.
An audience is a named set of contacts that a mailing or a journey is pointed at. It is stored, not evaluated at send time. An active audience is defined by a tree of rules and recomputed on a schedule or on demand. A static audience holds an import, a hand-picked list, or a frozen copy of an active audience. Every member is a contact; the app keeps no second contact table, but a membership row keeps copies of a few contact fields (see below).
Rules read the contact, they never copy it. A question that cannot be answered for a contact excludes that contact. Every join and leave is recorded in the member history.
The main objects
| Object | What it holds |
|---|---|
| Audience | id, code, labels (text per locale), description, kind (active or static), rule_match (all or any), active, member_count, eligible_count, recompute_state, recompute_error, frozen_from_id, created_at, updated_at. |
| Rule | One node of an audience's rule tree. A group (match_mode all or any, negate) holds further rules; a question names a subject, a field, an operator, a value or value_to, an optional window_days and channel, and a position among its siblings. |
| Membership | One row per contact per audience: audience_id, contact_id, organization_id, email, locale, market, eligible, source, joined_at. The organization_id, email, locale and market values are copies of the contact's fields, kept on the row so a send can read them without loading the contact. The app refreshes them when the contact changes, without a leave and a join. |
| Member event | One join or leave: audience_id, contact_id, kind, source, reason, at. |
| Import | One upload into a static audience: filename, rows, accepted, rejected, errors, state. |
The allowed values of a rule's subject, operator and related fields are closed lists. Read them from GET /v1/audiences/vocabularies and GET /v1/audiences/vocabularies/{name} before you build a rule.
Shared behaviour
X-Revenexx-Market(optional header) sets the market of an audience that is created with it. Only audiences take the market from the header; rules, members, imports and events ignore it. An audience created without it is unassigned and visible in every market. A membership'smarketis a copy of the contact's market, not a header value. On a read, the header does not filter the result.- Trash.
DELETEon an audience moves it to the trash; a secondDELETEdeletes it for good. Restore it withPOST .../restore. - Trash and sync fields. The audience record carries
deleted_at,deleted_by,deleted_by_id,trash_batch,purge_atandpurge_blocked_reason, and the list takestrashed. It also carries the sync fieldsexternal_id,external_refs,source_dataandsource_synced_at, writable on create and update. Shared rules: Trash; Sync fields. - An audience in the trash is read-only. Every write, preview, recompute, freeze, import and member read on it answers
409within_trashand arestorepath. Only the record read, the timeline and the composition still answer for it. Its code stays reserved: creating another audience with that code answers409within_trash, notcode_taken. - Chunked passes. Recompute and freeze work in budgeted steps. An unfinished pass answers
done: falseand continues on the next call. - Scheduled recompute. The tenant setting
recompute_schedulesets the cadence:hourly(the default),dailyoroff. The schedule runs at 25 minutes past every hour. Onhourlyevery active, switched-on audience is recomputed on each run. Ondailyall of them are recomputed on the run in the 03:00 UTC hour, and the other hourly runs take only the audiences whose rules changed (dirty_at) or whose pass is unfinished.offstops the schedule. A run takes at most 200 audiences (the same limit asrecompute-all). The same engine runs when you callrecompute-all.
Audiences
| Operation | What it does |
|---|---|
GET /v1/audiences/audiences | Lists the audiences, active and static, with member counts. Filters include kind, active, trashed, code and the count fields. Paged with limit (default 50, max 200) and offset. |
POST /v1/audiences/audiences | Creates an audience. Requires code, which is fixed once created. Sets kind (active or static, also fixed once created), labels, description and rule_match. 400 for a missing code or an unknown value; 409 with code_taken, or with in_trash when a trashed audience holds the code. |
GET /v1/audiences/audiences/{id} | Reads one audience, also when it is in the trash. 404 if it does not exist. |
PUT /v1/audiences/audiences/{id} | Corrects an audience. kind and code cannot change: 409 with kind_immutable or code_immutable. 400 with empty_update for an empty body; 400 names what is wrong otherwise. |
DELETE /v1/audiences/audiences/{id} | Moves the audience to the trash. Refused with 409 and trash_blocked when another audience asks for its members (blocker audience_in_use, with audience_ids). A DELETE on an audience already in the trash deletes it for good, or refuses with 409 and delete_blocked. A storefront contact cannot delete for good. |
POST /v1/audiences/audiences/{id}/restore | Takes an audience back out of the trash. 409 with not_in_trash if it is not there. |
POST /v1/audiences/audiences/trash/empty | Deletes every audience in the trash for good. An audience that something still blocks stays in the trash, with its reason in the response. 403 with purge_not_allowed for a storefront contact. |
POST /v1/audiences/audiences/trash/purge | Deletes the audiences that have been in the trash longer than the tenant's trash retention (90 days unless the tenant set another). Same rules as a second DELETE. If the retention cannot be read, nothing is deleted. 403 with purge_not_allowed for a storefront contact. The nightly purge runs the same operation. |
POST /v1/audiences/audiences/{id}/preview | Counts the contacts that match the stored rules, or a candidate tree sent in rules, without writing. Bounded by a short budget; when the population does not fit, the answer says how many were examined and estimates the rest. 409 with static_audience for a static audience sent without a candidate rules tree. |
POST /v1/audiences/audiences/{id}/recompute | Evaluates the rules and writes who joined and who left, within the tenant's budget. Body: restart (boolean) drops an unfinished pass and starts over. 422 with audience_inactive; 409 with static_audience. |
POST /v1/audiences/audiences/recompute-all | Recomputes every active audience under one shared budget. Unfinished passes go first. Audiences with active: false are skipped. One audience failing does not stop the others: its result carries error and status. An audience the budget did not reach comes back with skipped: true and done: false, and the answer's done is false. |
POST /v1/audiences/audiences/{id}/freeze | Creates a static audience with exactly the members the active audience has now. Body: code (required on the first call), labels, description, and target_id to continue an unfinished freeze. The answer carries done. 409 with static_audience or code_taken. A trashed audience that holds the target code also answers code_taken, not in_trash. |
GET /v1/audiences/audiences/{id}/composition | Members by market and by source (rule, import, manual), each with its share. At most eight markets are named; the rest are summed. Labels follow Accept-Language (de or en), and the source labels come from the member-sources vocabulary. |
GET /v1/audiences/audiences/{id}/timeline | Joins and leaves per day in Europe/Berlin, for at most the latest 90 days. capped is true when the history is longer than one read covers. |
Members
The member read works for every active or static audience that is not in the trash (see Shared behaviour). The writes below are for static audiences only.
| Operation | What it does |
|---|---|
GET /v1/audiences/audiences/{id}/members | Reads the members in contact-id order, keyset-paged. Pass cursor and limit (default 200, max 500); hand next_cursor back until done. Each item carries contact_id, email, locale and market. A page never repeats or skips a member who stays, even while a recompute runs. This is the read a send uses. |
POST /v1/audiences/audiences/{id}/members | Adds contacts by hand. Body: contact_ids (array of UUIDs, at most 500 per call), or contact_id for one. A contact that does not exist is refused with its reason. 409 with active_audience for an active audience. |
DELETE /v1/audiences/audiences/{id}/members/{contact_id} | Removes one contact from a static audience. 409 with active_audience for an active audience. |
POST /v1/audiences/audiences/{id}/import | Imports rows into a static audience. Body: filename, and either rows (each with an email or a contact_id UUID) or emails (addresses separated by lines, commas or semicolons). At most 1000 rows per call. Each row that matches no contact is refused with its reason. 409 with active_audience for an active audience. |
Rules
Rules belong to an active audience. A static audience has no rules: 409 with static_audience.
| Operation | What it does |
|---|---|
GET /v1/audiences/rules | Lists the rule rows of the active audiences, which are the only ones with rules. Filter with audience_id for one tree. Rules of an audience in the trash are listed too. |
POST /v1/audiences/rules | Adds a rule or a group. Requires audience_id. Sets kind (group or question), parent_id (a group of the same audience; empty at the top level), and the question fields. 400 for a value outside the vocabulary or a parent that is not a group of the same audience. 400 with too_deep past four levels, and with self_reference for a rule that asks for the audience's own members. |
GET /v1/audiences/rules/{id} | Reads one rule. |
PUT /v1/audiences/rules/{id} | Corrects a rule. 400 names what is wrong. |
DELETE /v1/audiences/rules/{id} | Removes a rule. For a group, the rules it holds go with it. |
Membership and history (read-only)
| Operation | What it does |
|---|---|
GET /v1/audiences/members | Lists membership rows across audiences. Filter with audience_id, audience_code, contact_id, organization_id, email, locale, market, eligible or source. To read the members for a send, use the audience's own members route. |
GET /v1/audiences/members/{id} | Reads one membership row. |
GET /v1/audiences/member-events | Lists joins and leaves. Filter with audience_id, contact_id, kind, source or reason. |
GET /v1/audiences/member-events/{id} | Reads one join or leave. |
GET /v1/audiences/imports | Lists imports with their counts and refused rows. Filter with audience_id, filename, state and the count fields. |
GET /v1/audiences/imports/{id} | Reads one import. |
Storefront
| Operation | What it does |
|---|---|
POST /v1/audiences/memberships | Returns the audiences one logged-in contact belongs to, by id and by code, for personalisation. Both active and static audiences are returned. An audience switched off with active: false is left out, and so is one in the trash. Body: contact_id (required); 400 with invalid_request without it. |
The spec and the App source do not name an API key scope or key type for this route (as of 2026-10), so this page does not state one.
Vocabularies
| Operation | What it does |
|---|---|
GET /v1/audiences/vocabularies | Lists the closed lists the app enforces. |
GET /v1/audiences/vocabularies/{name} | Reads one closed list with its values and labels. 404 for an unknown name. |
Related
- Segments are a different thing: groups of organizations or contacts used in the customers area. An audience rule can read an organization's segments as one of its subjects; the audience does not keep the segments themselves.