# Audiences

**Write tools (mutate state).** Create intent audiences, refine their filters and save a pending destination. These tools cannot push, export or delete an audience.

:::warning{title="Write tool — mutates state"}

New tokens are read-only by default: the console defaults a new endpoint to the read-only tools, so a fresh token cannot call these until someone deliberately grants a write tool. See [Tool scoping](/tool-scoping) and [Write tools are human-in-the-loop](/tools/overview#write-tools-are-human-in-the-loop).

:::

| Tool | Purpose |
|---|---|
| [`audience_create`](#audience_create) | Create an audience |
| [`audience_update_filters`](#audience_update_filters) | Update audience filters |
| [`audience_add_destination`](#audience_add_destination) | Add an audience destination |

## `audience_create`

**Create an audience**

Build and save an intent audience from a plain-English description. SIDE EFFECT: this creates a feed at the ContactBased intent provider and CONSUMES this organization's Engage audience quota — it is not a free preview, so confirm the query with the user before calling it. There is a per-organization cap on saved audiences; when it is reached the call is refused and no quota is spent. The audience is created unattached: nothing is pushed to any ad platform until a destination is configured and a person runs a push.

**Posture:** mutates state · **Module gate:** none (needs only the agent entitlement) · **REST:** `POST /api/v1/tools/audience_create`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `name` | string | **yes** | Name for the saved audience, shown in the Bellaso app. (length 1–200) |
| `nl_query` | string | **yes** | Plain-English description of the people to reach, e.g. "homeowners shopping for solar panels". This is the intent query the audience is built from. (length 1–500, pattern `\S`) |
| `business_filters` | array of object | no | Firmographic/professional filters, e.g. [{ field: "job_title", operator: "contains", value: "engineer" }]. Unknown fields or operators are dropped silently rather than rejected — read the saved audience back to see what was kept. |
| `consumer_filters` | array of object | no | Consumer filters, e.g. [{ field: "state", operator: "in", value: ["CA","NV"] }]. Unknown fields or operators are dropped silently. |
| `lookback_days` | integer | no | How many days of intent signal to draw on (1–14, default 14). Values outside the range are clamped. (1–14) |
| `output_type` | `consumer` \| `business` | no | Whether the audience is people (consumer, the default) or companies/professionals (business). |
| `similarity_preset` | `low` \| `medium` \| `high` | no | Match precision: high = fewer, closer matches; low = broader reach. Default medium. |

## `audience_update_filters`

**Update audience filters**

Replace a saved audience's structured filters WITHOUT rebuilding it — no ContactBased feed is created and no quota is spent. Only the filter sets you supply are touched; an omitted set is left as it was. Unknown filter fields are dropped by the server, so supplying only unrecognised clauses CLEARS that filter set — read the returned audience to confirm what was kept.

**Posture:** mutates state · **Module gate:** none (needs only the agent entitlement) · **REST:** `POST /api/v1/tools/audience_update_filters`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `audience_id` | string | **yes** | The audience to edit. (length 1–64) |
| `business_filters` | array of object | no | Firmographic/professional filters, e.g. [{ field: "job_title", operator: "contains", value: "engineer" }]. Unknown fields or operators are dropped silently rather than rejected — read the saved audience back to see what was kept. |
| `consumer_filters` | array of object | no | Consumer filters, e.g. [{ field: "state", operator: "in", value: ["CA","NV"] }]. Unknown fields or operators are dropped silently. |

## `audience_add_destination`

**Add an audience destination**

Configure where an audience will be pushed: one ad account on meta, google or linkedin. This only SAVES the destination in a pending state — it does not push anything, and this API cannot push. The ad account must be one this organization has already synced; any other id is refused by name.

**Posture:** mutates state · **Module gate:** none (needs only the agent entitlement) · **REST:** `POST /api/v1/tools/audience_add_destination`

| Argument | Type | Required | Notes |
|---|---|---|---|
| `audience_id` | string | **yes** | The audience to configure a destination for. (length 1–64) |
| `external_account_id` | string | **yes** | The ad account id on that platform, as it appears in this organization's synced accounts (Meta numeric id, Google customer id digits, LinkedIn id). An account this organization has not synced is refused. (length 1–64) |
| `platform` | `meta` \| `google` \| `linkedin` | **yes** | Which ad platform the audience will be pushed to. |
