> For the complete documentation index, see [llms.txt](https://docs.intelligems.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.intelligems.io/developer-resources/external-api/intelligems-external-api/filters-reference.md).

# Filters Reference

Analytics endpoints (`POST /analytics/resource/{experienceId}` and the `POST /analytics/sitewide/*` endpoints) accept a `filters` parameter to narrow the result data as a **JSON object** in the request body.

The deprecated `GET /analytics/resource/{experienceId}` endpoint does not accept the new filters contract. It remains available only during the beta for the original query-parameter shape and will be removed when the beta version ends.

All filter fields are optional. Omit a field or set it to `null` to skip that filter.

#### Dimension Filters

| Field                   | Type      | Description                                   |
| ----------------------- | --------- | --------------------------------------------- |
| `deviceType`            | string    | `"any"`, `"mobile"`, or `"desktop"`           |
| `visitorType`           | string    | `"any"`, `"new"`, or `"returning"`            |
| `countryCodes`          | string\[] | ISO 3166-1 alpha-2 codes, e.g. `["US", "GB"]` |
| `sourceSitesOrChannels` | string\[] | Traffic channel names (see list below)        |

**Available traffic channels:** `Instagram`, `Facebook`, `Google`, `Twitter`, `TikTok`, `Youtube`, `Klaviyo`, `Attentive`, `Postscript`, `Paid Shopping`, `Paid Search`, `Paid Social`, `Paid Video`, `Display`, `Paid Other`, `Organic Shopping`, `Organic Social`, `Organic Video`, `Organic Search`, `Other`, `Referral`, `Affiliate`, `SMS`, `Email`, `Direct`

These 25 values are the complete accepted set; any other value is rejected with a `400`. Omit `sourceSitesOrChannels` or pass an empty array to include all channels — there is no `any` sentinel.

#### Page & URL Filters

| Field                        | Type   | Description                                                                                                                                                                                      |
| ---------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `landingPageFilters`         | array  | **Preferred landing-page filter.** Array of `{ filterType: operator, values: string[] }` items. Values within one item are OR'd; items are combined per `landingPageFiltersOperator`.            |
| `landingPageFiltersOperator` | string | How `landingPageFilters` items are combined: `or` (default — matches any item) or `and` (must match every item).                                                                                 |
| `landingPage`                | object | **Deprecated — use `landingPageFilters`.** Still accepted for backwards compatibility. `{ landingPageUrlPath: string, landingPageUrlPathFilterType: operator }`                                  |
| `landingPageTypes`           | array  | Restrict to sessions whose landing page is one of these page types: `"PDP"`, `"Collection"`, `"Content"`, `"Blog"`, `"Homepage"`, `"Search"`, `"Cart"`, `"Other"`. Omit or `null` for all types. |
| `urlParam`                   | object | `{ queryParam: string, queryParamValue: string, queryParamFilterType: operator }`                                                                                                                |

**Match operators** for `landingPageFilters[].filterType` and the deprecated `landingPageUrlPathFilterType`: `equals`, `contains`, `startsWith`, `endsWith`, `doesNotEqual`, `doesNotContain`, `doesNotStartWith`, `doesNotEndWith`, `isNull`, `isNotNull` (omit `values` for `isNull`/`isNotNull`)

**Match operators** for `queryParamFilterType`: the same set as above (set `queryParamValue` to `null` for `isNull`/`isNotNull`)

**Operator / value pairing**

Each operator either takes a value or matches on its absence, and sending a mismatched pair is a **`400` validation error** — it is not silently ignored:

| Filter                     | With `equals` and the other 7 value operators                                                        | With `isNull` / `isNotNull`                                          |
| -------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `landingPageFilters[]`     | `values` must hold at least one non-empty string. Omitted, `[]`, or blank-only entries are rejected. | Omit `values`. An empty array is accepted; real values are rejected. |
| `landingPage` (deprecated) | `landingPageUrlPath` must be a non-empty string.                                                     | Leave `landingPageUrlPath` as `null`.                                |
| `urlParam`                 | `queryParam` **and** `queryParamValue` must both be non-empty.                                       | `queryParam` must be non-empty; leave `queryParamValue` as `null`.   |

A value with no operator is also rejected — e.g. `landingPageUrlPath` set while `landingPageUrlPathFilterType` is `null`, or any `urlParam` field set while `queryParamFilterType` is `null`.

To apply no filter at all, omit the whole object (or leave every field `null`). That remains valid.

These pairings are enforced at request validation but cannot be expressed in the OpenAPI schema, which describes each field independently — so rely on this table rather than on the generated field types.

#### User Behavior Filters

| Field          | Type   | Description                                                                                                        |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------ |
| `userBehavior` | object | `{ viewedCheckout?: boolean, addedToCart?: boolean, viewedProductPage?: boolean, viewedCollectionPage?: boolean }` |

The `reachedCheckoutStage` filter and its allowed values are documented per-endpoint in each endpoint's request schema (auto-generated from the source enum), so they are not duplicated here.

#### Revenue & Order Filters

| Field                     | Type           | Description                                                                                     |
| ------------------------- | -------------- | ----------------------------------------------------------------------------------------------- |
| `minNetRevenue`           | number         | Minimum order net revenue (inclusive)                                                           |
| `maxNetRevenue`           | number         | Maximum order net revenue (inclusive)                                                           |
| `minShippingRevenue`      | number         | Minimum shipping revenue (inclusive)                                                            |
| `maxShippingRevenue`      | number         | Maximum shipping revenue (inclusive)                                                            |
| `shippingMethods`         | string\[]      | Shipping method names to include                                                                |
| `currencyCodes`           | string\[]      | ISO 4217 order currency codes, e.g. `["USD", "EUR"]`. Sitewide analytics only — see note below. |
| `currencyCodesFilterType` | string         | `"include"` (default) or `"exclude"` — how `currencyCodes` is matched. Sitewide analytics only. |
| `maxZScore`               | number \| null | Outlier Z-score threshold; `null` disables filtering                                            |

**Currency filters are not supported on experience analytics.** The experience analytics endpoints do not accept `currencyCodes` or `currencyCodesFilterType`, but they do not reject them either: the fields are silently stripped, and the response covers every currency. Only the `/analytics/sitewide/*` endpoints apply them.

#### Custom Events Filters

| Field                    | Type      | Description                                                                                                                                                 |
| ------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `triggeredCustomEvents`  | string\[] | Custom event identifiers. You can find the id for your custom event in the `GET https://api.intelligems.io/v25-10-beta/experiences/{experienceId}` payload. |
| `customEventsFilterType` | string    | `"any"` (at least one), `"all"` (every event), `"none"` (none of the events)                                                                                |

**Built-in identifiers.** Three identifiers are always valid `triggeredCustomEvents` values, with no Events Manager setup required — they're synthesized server-side rather than looked up from a merchant-defined custom event:

* `checkout_block_viewed` — a checkout extension block was rendered to the visitor
* `onsite_upsell_viewed` — an onsite upsell offer was viewed
* `post_purchase_offer_viewed` — a post-purchase offer was viewed

In the analytics app's filter drawer, these three are shown grouped visually alongside the checkout funnel-stage options under "Reached Checkout Stage." Despite that grouping, they are **not** `reachedCheckoutStage` values — on the wire (and in this API) they are sent as `triggeredCustomEvents` entries, paired with `customEventsFilterType`:

```json
{
  "filters": {
    "triggeredCustomEvents": ["checkout_block_viewed"],
    "customEventsFilterType": "any"
  }
}
```

#### Product Filters

| Field                    | Type      | Description                                                                                                                                                                                    |
| ------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `experimentProductsOnly` | boolean   | Only include products in the experiment. Experience analytics only. The default depends on the endpoint — see below. Pass it explicitly when the population matters.                           |
| `onlyProductIds`         | string\[] | Restrict to orders containing at least one of these Shopify product IDs (numeric strings). Available for both experience and sitewide analytics.                                               |
| `onlyProductHandles`     | string\[] | Shopify product handles. Experience analytics only. Scopes the "viewed product page" signals, not orders or revenue; send it together with `onlyProductIds` for the same products — see below. |

**`experimentProductsOnly` default.** When the field is omitted:

* On `POST /analytics/resource/{experienceId}`, it is `true` only when the effective `analyticsViewType` is `test_orders_only`, and `false` for every other value. The effective view type is the request's `analyticsViewType` if sent, else the experience's saved setting; the schema default when neither is set is `none`, which gives `false`.
* On `POST /analytics/experience/{experienceId}/timeseries`, it defaults to `false`.
* For experiences that test checkout blocks, it is always treated as `false`.

**Do not rely on this default — pass the field.** Which branch you land on is a per-experience setting, not a property of the API, and it flips the order population between target-products-only and store-wide. On one production store the saved view type split 47% `test_orders_only` / 52% `sitewide_orders` / 1% `none`, so roughly half of those experiences default to `true` and half to `false`; for experiments specifically only about a third defaulted to `true`. Read the experience's `experienceAnalysis.analyticsViewType` from `GET /experiences/{experienceId}` if you need to predict it.

**Explicit product filters override it.** Setting either explicit product filter (`onlyProductIds` or `onlyProductHandles`) clears `experimentProductsOnly`, because the analytics backend rejects an explicit product filter combined with the experiment-products flag.

**`onlyProductHandles` scopes product-page views, not orders — send it with `onlyProductIds`.** Orders, revenue, conversion, and add-to-cart/checkout signals are narrowed by `onlyProductIds` (or `experimentProductsOnly`) only, because orders carry product IDs. Page views carry only the URL, so `onlyProductHandles` scopes the "viewed product page" signals instead: the `view_product_page_rate` metric and the `userBehavior.viewedProductPage` filter. The two fields are the order half and the page-view half of one product filter. The Intelligems dashboard always sends both for the same products, and API callers should do the same. Sent alone, `onlyProductHandles` does not restrict orders. Because any explicit product filter also clears `experimentProductsOnly`, a request that sets only `onlyProductHandles` *widens* the order population to all products.

#### Advanced Targeting

| Field                                  | Type  | Description                                        |
| -------------------------------------- | ----- | -------------------------------------------------- |
| `pageTargeting`                        | array | Expression groups for page-level targeting         |
| `productTargeting`                     | array | Expression groups for product-level targeting      |
| `applyExperienceTargetingExperienceId` | UUID  | Reuse targeting config from an existing experience |

**Page targeting** — each group has `type` (one of: `utm`, `url`, `urlPath`, `device`, `visitor`, `trafficSource`, `country`, `referrer`, `cookie`, `klaviyo`, `landingPage`) and `queries` array. Each query: `{ key: string, value: string, filter: matchOperator, type: filterType }`.

**Product targeting** — each group has `type` (one of: `productId`, `collection`, `tag`, `price`, `inventory`) and `queries` array. Each query: `{ key: productAttribute, value: string | number, filter: productOperator, type: productAttribute }`. Product operators: `includes`, `doesNotInclude`, `greaterThan`, `lessThan`.

#### Example Filter Object

```json
{
  "deviceType": "mobile",
  "countryCodes": ["US", "CA"],
  "currencyCodes": ["USD", "CAD"],
  "onlyProductIds": ["1234567890"],
  "sourceSitesOrChannels": ["Paid Search", "Email"],
  "landingPageFilters": [
    { "filterType": "startsWith", "values": ["/collections/sale"] }
  ],
  "landingPageFiltersOperator": "or",
  "userBehavior": {
    "addedToCart": true
  },
  "minNetRevenue": 10,
  "maxNetRevenue": 500,
  "maxZScore": 3
}
```

For the resource and sitewide POST endpoints, send the filter object directly in the JSON body under `filters`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.intelligems.io/developer-resources/external-api/intelligems-external-api/filters-reference.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
