Audience filters and dashboard links
Use Audiences to filter the user's customers and to share a dashboard link to that set. Filters
select the segment; sort_by / sort_direction on preview-audience order the returned sample.
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 below, including:
- Product and duration —
latestProduct,allPurchasedProductIds,latestPurchasedOffering, entitlements, offers - Spend and renewals —
totalSpent(filter, andsort_by: total_spent),totalRenewals(filter only; there is no renewal 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.
- Fetch project-specific values with
get-audience-filter-optionsfor any field marked project-specific that the question uses (project_id,fields). - Call
preview-audiencewithproject_idandbody.rulesin the filter rule shape (for a saved audience fromlist-audiences, passbody.audience_uuidinstead). It is read-only and saves nothing. Do notcreate-audiencejust to inspect a segment. - To rank or name a superlative, pass
sort_by(status,latest_auto_renew_intent,first_seen_at,last_seen_at,total_spent) andsort_direction(ascdefault, ordesc) as top-level parameters, not insidebody.sort_by: total_spentwithsort_direction: descreturns the highest spenders first, so the firstcustomer_samplerow is the top spender. The sample is capped (typically 50): it is the top or bottom matches, not every customer. - Answer "how many" from
stats(total_customers,active_subscriptions,active_trials,total_revenueincurrency), never from the sample size. Whenstats.is_approximateis true, give counts and revenue as approximate ("about 6,400 customers"). - Name customers only from fields
customer_samplereturned (app_user_id,email,status,total_spent,latest_product_name, first/last seen). Withoutsort_bythe sample is unordered: say it is a sample of matches, not a ranking. totalRenewalsis a filter only, not asort_byvalue or a sample column. Filter on a high threshold and report the matches; do not invent a renewal count or claim a unique maximum.- Share the dashboard link. The
preview-audienceresult forbody.rulesincludes a line starting "Dashboard URL for this result"; link that exact URL. Build one yourself (constructing a link) only when no tool result gave you a URL.
Constructing a link
Use this only when no tool result gave you a "Dashboard URL for this result" line. Two shapes: a
filtered all-customers link for ad-hoc exploration, and a saved-audience link after
create-audience.
- Get the project ID from
list-projects. For dashboard URLs, strip theprojprefix. - Pick fields and operators from the field tables. Do not invent field or operator names.
- For project-specific fields, fetch valid values with
get-audience-filter-optionsfirst. - Assemble the rule JSON: one group per OR-branch, conditions inside a group for AND, values encoded per value formats.
- Serialize and URL-encode the rule with a short script (see encoding the rule) — do not encode by hand.
- Append it as the
filtersquery param onhttps://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 fromlist-projectswithprojstripped.- Filters only work on the
all-customerslist. The unfiltered Audiences home is/projects/{project_id}/customer-lists.
Linking to a saved audience
The create-audience and get-audience results include a "Dashboard URL for this result" line;
link that exact URL. If you only have list-audiences output, link with customer_list_id, the
field returned 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:
{
"groups": [
{
"conditions": [{ "field": "platform", "operator": "is", "value": "android" }]
}
]
}
- Conditions within a group combine with AND.
- Groups combine with OR.
valueis 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).
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
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.
Text fields
Operators: is, isNot, contains, doesNotContain, isEmpty, isNotEmpty.
| Field | Meaning |
|---|---|
customerId |
App user ID |
originalAppUserId |
Original app user ID |
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 |
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 exactlyfromandto:"value": "{\"from\":\"2026-01-01\",\"to\":\"2026-01-31\"}"(from≤to).within— stringified JSON with exactlydirection,value,unit:"value": "{\"direction\":\"last\",\"value\":30,\"unit\":\"days\"}".directionislastornext;unitisminutes,hours, ordays;valueis a non-negative integer.before/beforeOrOn/after/afterOrOnalso accept this relative format (noton).
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, orcustomAttributeto 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):
{
"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".
{
"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\"}"
}
]
}
]
}
