Audiences

The Audiences app that stores the named sets of contacts a mailing or a journey is pointed at, defined by rules or held as static lists, with their members, imports and join and leave history.

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

ObjectWhat it holds
Audienceid, 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.
RuleOne 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.
MembershipOne 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 eventOne join or leave: audience_id, contact_id, kind, source, reason, at.
ImportOne 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's market is a copy of the contact's market, not a header value. On a read, the header does not filter the result.
  • Trash. DELETE on an audience moves it to the trash; a second DELETE deletes it for good. Restore it with POST .../restore.
  • Trash and sync fields. The audience record carries deleted_at, deleted_by, deleted_by_id, trash_batch, purge_at and purge_blocked_reason, and the list takes trashed. It also carries the sync fields external_id, external_refs, source_data and source_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 409 with in_trash and a restore path. Only the record read, the timeline and the composition still answer for it. Its code stays reserved: creating another audience with that code answers 409 with in_trash, not code_taken.
  • Chunked passes. Recompute and freeze work in budgeted steps. An unfinished pass answers done: false and continues on the next call.
  • Scheduled recompute. The tenant setting recompute_schedule sets the cadence: hourly (the default), daily or off. The schedule runs at 25 minutes past every hour. On hourly every active, switched-on audience is recomputed on each run. On daily all 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. off stops the schedule. A run takes at most 200 audiences (the same limit as recompute-all). The same engine runs when you call recompute-all.

Audiences

OperationWhat it does
GET /v1/audiences/audiencesLists 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/audiencesCreates 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}/restoreTakes an audience back out of the trash. 409 with not_in_trash if it is not there.
POST /v1/audiences/audiences/trash/emptyDeletes 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/purgeDeletes 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}/previewCounts 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}/recomputeEvaluates 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-allRecomputes 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}/freezeCreates 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}/compositionMembers 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}/timelineJoins 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.

OperationWhat it does
GET /v1/audiences/audiences/{id}/membersReads 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}/membersAdds 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}/importImports 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.

OperationWhat it does
GET /v1/audiences/rulesLists 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/rulesAdds 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)

OperationWhat it does
GET /v1/audiences/membersLists 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-eventsLists 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/importsLists 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

OperationWhat it does
POST /v1/audiences/membershipsReturns 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

OperationWhat it does
GET /v1/audiences/vocabulariesLists 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.
  • 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.
Was this page helpful?