---
name: revenuecat-audiences
description: >
  Use before sharing a link to a filtered customer list or audience, and when identifying,
  filtering, or ranking the user's customers (a segment, who they are, a product or duration,
  spend, renewals, status, country, attribution).
---

# Audience filters and dashboard links

Use Audiences to filter the user's customers and to share a dashboard link to that set. The same
filters answer "who" questions — they are not a full leaderboard sort.

There is no first-class CLI command for audiences. Do not tell the user RevenueCat cannot filter
or segment customers.

## Identifying, filtering, or ranking customers

Audiences filter on the [fields](#fields) below, including:

- Product and duration — `latestProduct`, `allPurchasedProductIds`, `latestPurchasedOffering`,
  entitlements, offers
- Spend and renewals — `totalSpent`, `totalRenewals` (thresholds, not a sort)
- Subscription state — `status`, trial, auto-renew intent, ownership
- Store, platform, and country — `platform`, `latestStore`, `country`, `storefront`
- Dates — first seen, purchases, renewals, expiration, trial, cancellation
- Attribution and experiments — media source, campaign, ad group, keywords, price experiment
- Identity and custom attributes — app user ID, email, locale, `customAttribute:{key}`

Map the question onto those fields, filter, then read a sample.

1. Fetch project-specific values with `get-audience-filter-options` for any field marked
   project-specific that the question uses (`project_id`, `fields`).
2. Reuse an existing audience from `list-audiences` if one already matches. Otherwise
   `create-audience` with the same `groups`/`conditions` rule shape as [the filter rule](#the-filter-rule).
   Body is only `name` and `rules`. `create-audience` persists a saved audience, so get explicit
   confirmation first.
3. `get-audience` with `expand: ["customer_sample"]`. Sample rows include `total_spent`, status,
   and latest product — not every filter field. Rank or name customers only from fields the
   sample returned.
4. Number fields like `totalRenewals` are filters, not sample columns. Filter on a high
   threshold and report the sample; do not invent a value that was not returned.
5. Say it is a sample of matches, not an exhaustive ranking of every customer. Filtering and a
   `customer_sample` cannot prove a global superlative (who renewed or spent the most). After you
   try the steps above, say that: you can show high-threshold matches, not name a unique maximum.
6. Share the dashboard link ([constructing a link](#constructing-a-link) or
   [linking to a saved audience](#linking-to-a-saved-audience)).

## Constructing a link

Two shapes: a filtered `all-customers` link for ad-hoc exploration, and a saved-audience link
after `create-audience`.

1. Get the project ID from `list-projects`. For dashboard URLs, **strip the `proj` prefix**.
2. Pick fields and operators from the [field tables](#fields). Do not invent field or operator
   names.
3. For project-specific fields, fetch valid values with `get-audience-filter-options` first.
4. Assemble the [rule JSON](#the-filter-rule): one group per OR-branch, conditions inside a group
   for AND, values encoded per [value formats](#value-formats).
5. Serialize and URL-encode the rule with a short script (see [encoding the rule](#encoding-the-rule))
   — do not encode by hand.
6. Append it as the `filters` query param on
   `https://app.revenuecat.com/projects/{project_id}/customer-lists/all-customers`.

## URL format

```
https://app.revenuecat.com/projects/{project_id}/customer-lists/all-customers?filters={encoded_rule}
```

- `{project_id}` — short hex ID from `list-projects` with `proj` stripped.
- Filters only work on the `all-customers` list. The unfiltered Audiences home is
  `/projects/{project_id}/customer-lists`.

## Linking to a saved audience

Link with `customer_list_id` — the field `create-audience`, `get-audience`, and `list-audiences`
return alongside `id`:

```
https://app.revenuecat.com/projects/{project_id}/customer-lists/{customer_list_id}
```

**An audience's `id` (`aud…`) and its `customer_list_id` (`list…`) are different identifiers.**
The dashboard route only resolves `customer_list_id`; an `aud…` id in that slot renders
"Audience not found".

**Correct:**

```
https://app.revenuecat.com/projects/56965ae1/customer-lists/list7c1f0a2b93
```

**Wrong** (the audience `id` instead of the `customer_list_id`):

```
https://app.revenuecat.com/projects/56965ae1/customer-lists/audf0269cdf3df84dd2
```

Do not add a `filters` param to a saved-audience link — the audience carries its own rules. If
the response has no `customer_list_id`, link to `/customer-lists` and name the audience rather
than guessing an id.

## Offering to save the filtered view

A filtered `all-customers` link is ad-hoc — nothing about it is saved. Say so in one short
sentence when you share one, and offer to save it: "This view isn't saved — want me to save it
as an audience so you can find it later?"

Offer once per conversation. If they accept, call `create-audience` with the same rule you built
for the link, then link to it. Do not offer when the link is already to a saved audience.

## The filter rule

The `filters` value is URL-encoded JSON with this shape:

```json
{
  "groups": [
    {
      "conditions": [{ "field": "platform", "operator": "is", "value": "android" }]
    }
  ]
}
```

- Conditions within a group combine with AND.
- Groups combine with OR.
- `value` is always a JSON string — booleans as `"true"`/`"false"`, numbers as `"42"`, lists as a
  comma-separated string, date ranges and relative dates as stringified JSON (see
  [value formats](#value-formats)).

**Correct** (compact JSON, whole value URL-encoded):

```
?filters=%7B%22groups%22%3A%5B%7B%22conditions%22%3A%5B%7B%22field%22%3A%22platform%22%2C%22operator%22%3A%22is%22%2C%22value%22%3A%22android%22%7D%5D%7D%5D%7D
```

**Wrong** (raw JSON, spaces, unencoded quotes/braces):

```
?filters={"groups": [{"conditions": [...]}]}
```

## Encoding the rule

```python
import json
from urllib.parse import quote

rule = {
    "groups": [
        {"conditions": [{"field": "platform", "operator": "is", "value": "android"}]},
    ]
}

print(quote(json.dumps(rule, separators=(",", ":")), safe=""))
```

## Fields

The Audiences preview table shows only **Customer**, **Subscription Status**,
**Auto-Renewal Status**, **Spent**, and **Latest Purchase**. Other attributes appear in the CSV
from **Export all**, or on a customer's profile.

Use these exact `field` strings. See
[Audiences](https://www.revenuecat.com/docs/dashboard-and-metrics/audiences).

### Text fields

Operators: `is`, `isNot`, `contains`, `doesNotContain`, `isEmpty`, `isNotEmpty`.

| Field                     | Meaning                           |
| ------------------------- | --------------------------------- |
| `customerId`              | App user ID                       |
| `originalAppUserId`       | Original app user ID              |
| `email`                   | Email                             |
| `phoneNumber`             | Phone number                      |
| `locale`                  | Locale                            |
| `appVersion`              | App version                       |
| `sdkVersion`              | SDK version                       |
| `platformVersion`         | Platform (OS) version             |
| `projectId`               | Project ID (e.g. `proj1ab2c3d4`)  |
| `projectName`             | Project name                      |
| `appConfigId`             | App ID (e.g. `app1ab2c3d4`)       |
| `appConfigName`           | App name                          |
| `idfa`                    | IDFA                              |
| `idfv`                    | IDFV                              |
| `gpsAdId`                 | GPS ad ID                         |
| `latestPurchasedOffering` | Latest purchased offering         |
| `latestOffer`             | Latest offer identifier           |
| `latestEntitlements`      | Latest entitlement identifiers    |
| `allPurchasedProductIds`  | All purchased product identifiers |

### Enum fields

Operators: `is`, `isNot`, `isAnyOf`, `isNotAnyOf`, `isEmpty`, `isNotEmpty`.

| Field                    | Values                                                                                                                    |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `platform`               | `iOS`, `android`, `web`, `macOS`, `amazon`, `roku`, `tvOS`, `visionOS`, `watchOS`                                         |
| `status`                 | `active`, `trialing`, `in_grace_period`, `in_billing_retry`, `paused`, `expired`, `incomplete`, `unknown`                 |
| `latestStore`            | `app_store`, `play_store`, `promotional`, `mac_app_store`, `stripe`, `amazon`, `roku`, `rc_billing`, `paddle`, `external` |
| `anyActiveStore`         | Any active store — same store identifiers as `latestStore`                                                                |
| `latestOwnershipType`    | `PURCHASED`, `FAMILY_SHARED`                                                                                              |
| `latestOfferType`        | `no_offer`, `free_trial`, `introductory_offer`, `offer_code`, `promotional_offer`, `win_back_offer`, `unspecified_offer`  |
| `priceExperimentVariant` | `a`, `b`, `c`, `d`                                                                                                        |
| `country`                | Last seen country — ISO 3166-1 alpha-2 codes (e.g. `US`, `DE`)                                                            |
| `latestStoreCountry`     | ISO 3166-1 alpha-2 country codes                                                                                          |
| `storefront`             | Store country — ISO 3166-1 alpha-2 codes                                                                                  |
| `mediaSource`            | project-specific — [fetch valid values](#fetching-project-specific-values)                                                |
| `campaign`               | project-specific                                                                                                          |
| `adGroup`                | project-specific                                                                                                          |
| `ad`                     | project-specific                                                                                                          |
| `keyword`                | project-specific                                                                                                          |
| `creative`               | project-specific                                                                                                          |
| `priceExperimentId`      | project-specific                                                                                                          |
| `latestProduct`          | product IDs of the project — fetch valid values                                                                           |

### Boolean fields

Operators: `is`, `isNot`. Value is exactly `"true"` or `"false"`.

| Field                            | Meaning                                              |
| -------------------------------- | ---------------------------------------------------- |
| `hasMadeSandboxPurchase`         | Has made a sandbox purchase                          |
| `hasMadeNonSubscriptionPurchase` | Has made a non-subscription purchase                 |
| `latestAutoRenewIntent`          | Auto-renewal status (`true` = set to renew)          |
| `isCurrentlyTrialing`            | Currently trialing                                   |
| `isRcPromo`                      | Has been granted an entitlement via RC (promotional) |

### Number fields

Operators: `equal`, `notEqual`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,
`lessThanOrEqual`, `isEmpty`, `isNotEmpty`. Value is a numeric string, e.g. `"50"`.

| Field           | Meaning                  |
| --------------- | ------------------------ |
| `totalSpent`    | Total spent              |
| `totalRenewals` | Total number of renewals |

### Date fields

Operators: `before`, `beforeOrOn`, `on`, `after`, `afterOrOn`, `within`, `between`, `notBetween`,
`isEmpty`, `isNotEmpty`.

| Field                  | Meaning                        |
| ---------------------- | ------------------------------ |
| `firstSeenAt`          | First seen                     |
| `lastSeenAt`           | Last seen                      |
| `firstPurchaseAt`      | First purchase                 |
| `mostRecentPurchaseAt` | Most recent purchase           |
| `mostRecentRenewalAt`  | Most recent renewal            |
| `latestExpirationAt`   | Latest expiration              |
| `trialStartAt`         | Trial start                    |
| `trialEndAt`           | Trial end                      |
| `subscriptionOptOutAt` | Most recent cancellation       |
| `trialOptOutAt`        | Most recent trial cancellation |

### Custom attribute fields

Filter with `customAttribute:{key}` (e.g. `customAttribute:favorite_team`). They use the enum
operators. Fetch known keys and values with `get-audience-filter-options` — never invent a key.

## Value formats

- `isEmpty` / `isNotEmpty` — set `"value": ""` (the value is ignored).
- `isAnyOf` / `isNotAnyOf` — comma-separated string: `"value": "US,CA,MX"`.
- `before`, `beforeOrOn`, `on`, `after`, `afterOrOn` — calendar date `"value": "2026-01-31"`
  (`YYYY-MM-DD`).
- `between` / `notBetween` — stringified JSON with exactly `from` and `to`:
  `"value": "{\"from\":\"2026-01-01\",\"to\":\"2026-01-31\"}"` (`from` ≤ `to`).
- `within` — stringified JSON with exactly `direction`, `value`, `unit`:
  `"value": "{\"direction\":\"last\",\"value\":30,\"unit\":\"days\"}"`. `direction` is `last` or
  `next`; `unit` is `minutes`, `hours`, or `days`; `value` is a non-negative integer.
  `before`/`beforeOrOn`/`after`/`afterOrOn` also accept this relative format (not `on`).

## Fetching project-specific values

Fields marked project-specific (`mediaSource`, `campaign`, `adGroup`, `ad`, `keyword`, `creative`,
`priceExperimentId`, `latestProduct`) and custom attributes only match values that exist in the
project's data. Fetch with `get-audience-filter-options`:

- `project_id` (required)
- `fields` (required, at least one) — any of the eight fields above, `customAttribute:{key}` for
  one custom attribute, or `customAttribute` to list every custom attribute key with its values.

A custom-attribute entry may come back with `cardinality_exceeded: true` — a value the user
stated verbatim can still be valid even if it is not in the list.

Fixed-value fields (`country`, `platform`, `status`, …) are not served by this tool — use the
tables above. Never guess project-specific values — a filter on a non-existent value silently
matches zero customers.

## Example: building a link

User wants: "Android customers acquired through Instagram" in project `proj56965ae1`.

Rule (both conditions in one group — AND):

```json
{
  "groups": [
    {
      "conditions": [
        { "field": "platform", "operator": "is", "value": "android" },
        { "field": "mediaSource", "operator": "is", "value": "Instagram" }
      ]
    }
  ]
}
```

Link:

```
https://app.revenuecat.com/projects/56965ae1/customer-lists/all-customers?filters=%7B%22groups%22%3A%5B%7B%22conditions%22%3A%5B%7B%22field%22%3A%22platform%22%2C%22operator%22%3A%22is%22%2C%22value%22%3A%22android%22%7D%2C%7B%22field%22%3A%22mediaSource%22%2C%22operator%22%3A%22is%22%2C%22value%22%3A%22Instagram%22%7D%5D%7D%5D%7D
```

User wants: "customers on iOS or Android who are currently trialing and were first seen in the
last 30 days".

```json
{
  "groups": [
    {
      "conditions": [
        { "field": "platform", "operator": "isAnyOf", "value": "iOS,android" },
        { "field": "isCurrentlyTrialing", "operator": "is", "value": "true" },
        {
          "field": "firstSeenAt",
          "operator": "within",
          "value": "{\"direction\":\"last\",\"value\":30,\"unit\":\"days\"}"
        }
      ]
    }
  ]
}
```
