Skip to content
Back to skills

Source 490de0ef71d04eee57933b31

ASecurity

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).

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
developmentpython

Works with

  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add gutsofpratik/OpenClaude-Skills --skill source-490de0ef71d04eee57933b31 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Source 490de0ef71d04eee57933b31?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Source 490de0ef71d04eee57933b31
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gutsofpratik-source-490de0ef71d04eee57933b31/badge)](https://www.skillsdirectory.com/skills/gutsofpratik-source-490de0ef71d04eee57933b31)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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. 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](#fields) below, including:

- Product and duration — `latestProduct`, `allPurchasedProductIds`, `latestPurchasedOffering`,
  entitlements, offers
- Spend and renewals — `totalSpent` (filter, and `sort_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.

1. Fetch project-specific values with `get-audience-filter-options` for any field marked
   project-specific that the question uses (`project_id`, `fields`).
2. Call `preview-audience` with `project_id` and `body.rules` in the [filter rule](#the-filter-rule)
   shape (for a saved audience from `list-audiences`, pass `body.audience_uuid` instead). It is
   read-only and saves nothing. Do not `create-audience` just to inspect a segment.
3. To rank or name a superlative, pass `sort_by` (`status`, `latest_auto_renew_intent`,
   `first_seen_at`, `last_seen_at`, `total_spent`) and `sort_direction` (`asc` default, or `desc`)
   as top-level parameters, not inside `body`. `sort_by: total_spent` with `sort_direction: desc`
   returns the highest spenders first, so the first `customer_sample` row is the top spender. The
   sample is capped (typically 50): it is the top or bottom matches, not every customer.
4. Answer "how many" from `stats` (`total_customers`, `active_subscriptions`, `active_trials`,
   `total_revenue` in `currency`), never from the sample size. When `stats.is_approximate` is true,
   give counts and revenue as approximate ("about 6,400 customers").
5. Name customers only from fields `customer_sample` returned (`app_user_id`, `email`, `status`,
   `total_spent`, `latest_product_name`, first/last seen). Without `sort_by` the sample is
   unordered: say it is a sample of matches, not a ranking.
6. `totalRenewals` is a filter only, not a `sort_by` value or a sample column. Filter on a high
   threshold and report the matches; do not invent a renewal count or claim a unique maximum.
7. Share the dashboard link. The `preview-audience` result for `body.rules` includes a line
   starting "Dashboard URL for this result"; link that exact URL. Build one yourself
   ([constructing a link](#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`.

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

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:

```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\"}"
        }
      ]
    }
  ]
}
```

Files in this skill

  • PROVENANCE.json239 B
  • SKILL.md16.9 KB
  • UPSTREAM-LICENSE.txt1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.