> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getzenstep.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Targeting Rules Reference

> Complete reference for every targeting rule type — operators, values, and examples.

## url\_path

Match the current page's URL **pathname** (everything after the domain, before the query string).

```json theme={null}
{
  "type": "url_path",
  "operator": "starts_with",
  "value": "/dashboard"
}
```

### Operators

| Operator      | Description        | Example value  | Matches                                                     |
| ------------- | ------------------ | -------------- | ----------------------------------------------------------- |
| `equals`      | Exact match        | `/pricing`     | Only `/pricing` — not `/pricing/`                           |
| `contains`    | Substring match    | `analytics`    | `/dashboard/analytics`, `/analytics/export`                 |
| `starts_with` | Prefix match       | `/dashboard`   | `/dashboard`, `/dashboard/analytics`, `/dashboard/settings` |
| `regex`       | Regular expression | `^/guide/\d+$` | `/guide/42`, `/guide/1` — not `/guide/abc`                  |

<Note>
  `url_path` matches against the pathname only — not the full URL. Query
  parameters and hash fragments are ignored. Use the
  [`query_param`](#query-parameter) rule type to match on query strings.
</Note>

***

## user\_attribute

Match a value from the `attributes` object passed to `identify()`.

```json theme={null}
{
  "type": "user_attribute",
  "attribute": "plan",
  "operator": "equals",
  "value": "free"
}
```

Requires `identify()` to have been called with the attribute. If the attribute is not set on the current user, the rule evaluates to `false`.

### Operators

| Operator     | Description                         |
| ------------ | ----------------------------------- |
| `equals`     | Exact string match                  |
| `not_equals` | Does not match                      |
| `contains`   | Attribute value contains the string |

<Tip>
  All attribute values are compared as strings. Numeric attributes (`seats: 5`)
  are cast to strings before comparison.
</Tip>

***

## device

Match the current user's device type.

```json theme={null}
{
  "type": "device",
  "device": "mobile"
}
```

### Values

| Value     | Description               |
| --------- | ------------------------- |
| `desktop` | Viewport width ≥ 1024px   |
| `tablet`  | Viewport width 768–1023px |
| `mobile`  | Viewport width \< 768px   |

***

## query\_param

Match a URL query parameter.

```json theme={null}
{
  "type": "query_param",
  "key": "ref",
  "value": "email"
}
```

Evaluates to `true` if the URL contains `?ref=email` (exact value match).

***

## always

Show to everyone, always. Useful as a catch-all or when you want a flow to appear on every page load.

```json theme={null}
{
  "type": "always"
}
```

***

## behavior

Match based on how many times the user has performed a `track()` event. Requires the **Grow** plan.

```json theme={null}
{
  "type": "behavior",
  "event": "export_clicked",
  "operator": "eq",
  "value": 0,
  "windowDays": 7
}
```

Show the "Try CSV Export" tooltip to users who have never clicked export in the last 7 days.

### Operators

| Operator | Description                                   |
| -------- | --------------------------------------------- |
| `gt`     | Event count is greater than value             |
| `gte`    | Event count is greater than or equal to value |
| `lt`     | Event count is less than value                |
| `lte`    | Event count is less than or equal to value    |
| `eq`     | Event count equals value                      |

### windowDays

Optional. If set, only counts events within the last N days. If omitted, counts all-time events.

***

## segment

Reference a saved **Segment** — a reusable named targeting configuration. Requires the **Grow** plan.

```json theme={null}
{
  "type": "segment",
  "segmentId": "seg_abc123"
}
```

Segments are created in **Settings → Targeting → Segments**. Using segments keeps your targeting rules DRY — update the segment once and all flows using it pick up the change automatically.

***

## audience\_membership

Match users who are members of a GA4 or Microsoft Clarity imported audience. Requires the **Scale** plan.

```json theme={null}
{
  "type": "audience_membership",
  "audienceId": "aud_xyz789"
}
```

Audiences are synced from GA4 or Clarity via the **Settings → Targeting → Audience Sources** integration.

***

## group

Nest rules with their own logical operator inside a parent rule set.

```json theme={null}
{
  "type": "group",
  "operator": "or",
  "rules": [
    {
      "type": "user_attribute",
      "attribute": "role",
      "operator": "equals",
      "value": "admin"
    },
    {
      "type": "user_attribute",
      "attribute": "role",
      "operator": "equals",
      "value": "owner"
    }
  ]
}
```

Groups can be nested up to **5 levels deep**.

***

## Combining rules — full example

Show a "Upgrade to unlock analytics" flow only to:

* Free-plan users
* On the `/analyze` page
* Who have viewed at least 3 analytics pages in the last 30 days

```json theme={null}
{
  "operator": "and",
  "rules": [
    {
      "type": "url_path",
      "operator": "starts_with",
      "value": "/analyze"
    },
    {
      "type": "user_attribute",
      "attribute": "plan",
      "operator": "equals",
      "value": "free"
    },
    {
      "type": "behavior",
      "event": "analytics_page_viewed",
      "operator": "gte",
      "value": 3,
      "windowDays": 30
    }
  ]
}
```
