@picked/client 0.0.70-next.5 → 0.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/{api-DQ1DqG_F.js → api-CPy9G49C.js} +1 -1
  2. package/dist/api-Ce6xYI--.cjs +1 -0
  3. package/dist/cjs/index.js +2 -2
  4. package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
  5. package/dist/cjs/server/index.js +1 -1
  6. package/dist/docs/actions.mdx +38 -18
  7. package/dist/docs/api/authentication.md +75 -48
  8. package/dist/docs/api/authorization-and-scopes.md +63 -47
  9. package/dist/docs/api/error-handling.md +70 -54
  10. package/dist/docs/api/index.md +41 -22
  11. package/dist/docs/api/pagination.md +30 -20
  12. package/dist/docs/api-keys.mdx +11 -6
  13. package/dist/docs/chrome-extension.md +22 -5
  14. package/dist/docs/conditions.mdx +32 -19
  15. package/dist/docs/content/asset-library.mdx +65 -34
  16. package/dist/docs/content/content-entries.mdx +8 -2
  17. package/dist/docs/content/layouts.mdx +45 -29
  18. package/dist/docs/content/pages.mdx +29 -14
  19. package/dist/docs/content/sections.mdx +19 -8
  20. package/dist/docs/delivery-api/experience-content.md +10 -5
  21. package/dist/docs/delivery-api/index.md +19 -12
  22. package/dist/docs/delivery-api/preview-caching-crawling.md +27 -14
  23. package/dist/docs/delivery-api/project-config.md +6 -4
  24. package/dist/docs/experiences.mdx +26 -10
  25. package/dist/docs/features.mdx +75 -20
  26. package/dist/docs/fields.mdx +42 -27
  27. package/dist/docs/flow-builder/building-flows.md +38 -20
  28. package/dist/docs/flow-builder/condition-builder.md +32 -21
  29. package/dist/docs/flow-builder/keyboard-shortcuts.md +18 -18
  30. package/dist/docs/flow-builder/nodes.md +26 -13
  31. package/dist/docs/flow-builder/overview.mdx +28 -15
  32. package/dist/docs/gtm-template.md +35 -26
  33. package/dist/docs/guides/build-a-personalized-page.mdx +36 -18
  34. package/dist/docs/image-optimisation-service.md +33 -17
  35. package/dist/docs/index.md +28 -17
  36. package/dist/docs/mcp.md +46 -38
  37. package/dist/docs/pickedql/data-sources.md +65 -43
  38. package/dist/docs/pickedql/dimensions.md +27 -12
  39. package/dist/docs/pickedql/examples.md +38 -26
  40. package/dist/docs/pickedql/filtering.md +10 -6
  41. package/dist/docs/pickedql/index.md +32 -14
  42. package/dist/docs/pickedql/schema.md +85 -76
  43. package/dist/docs/pickedql/virtual-functions.md +62 -36
  44. package/dist/docs/projects.mdx +14 -7
  45. package/dist/docs/reports.mdx +5 -3
  46. package/dist/docs/sdk/actions.md +33 -17
  47. package/dist/docs/sdk/conditions.md +8 -5
  48. package/dist/docs/sdk/content-security-policy.md +5 -4
  49. package/dist/docs/sdk/experiences.md +29 -11
  50. package/dist/docs/sdk/feature-allocation.md +79 -47
  51. package/dist/docs/sdk/features.md +130 -76
  52. package/dist/docs/sdk/fields.md +66 -42
  53. package/dist/docs/sdk/index.md +22 -13
  54. package/dist/docs/sdk/plugins/create-experience.md +18 -15
  55. package/dist/docs/sdk/plugins/dynamic-experiences.md +42 -16
  56. package/dist/docs/sdk/predicates.md +53 -37
  57. package/dist/docs/sdk/ssr/index.md +25 -8
  58. package/dist/docs/sdk/ssr/rendering-and-hydration.md +37 -16
  59. package/dist/docs/sdk/ssr/serialize.md +38 -20
  60. package/dist/docs/sdk/ssr/slots-and-placements.md +28 -10
  61. package/dist/docs/sdk/steps.md +100 -55
  62. package/dist/docs/steps.mdx +20 -11
  63. package/dist/docs/team-management.md +17 -11
  64. package/dist/docs/vite-plugin.md +26 -19
  65. package/dist/docs/workspace-versioning.mdx +14 -8
  66. package/dist/docs/workspaces.mdx +55 -24
  67. package/dist/esm/index.js +257 -238
  68. package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
  69. package/dist/esm/server/index.js +1 -1
  70. package/dist/{plugin-EPx6sE_l.js → plugin-ChPkTj__.js} +58 -53
  71. package/dist/plugin-DHn11gn6.cjs +1 -0
  72. package/dist/types/entities/feature/feature.d.ts.map +1 -1
  73. package/dist/types/plugins/dynamic-experiences/hash.d.ts +40 -5
  74. package/dist/types/plugins/dynamic-experiences/hash.d.ts.map +1 -1
  75. package/dist/types/plugins/dynamic-experiences/plugin.d.ts.map +1 -1
  76. package/package.json +1 -1
  77. package/dist/api-B9koex39.cjs +0 -1
  78. package/dist/plugin-YbSSGrM7.cjs +0 -1
@@ -51,7 +51,9 @@ ORDER BY project_local_date
51
51
 
52
52
  ## Custom conversion rate definition
53
53
 
54
- Override the default purchase conversion with your own numerator and denominator. Useful when conversion is defined by a form submission, lead capture, or any custom event.
54
+ Override the default conversion, which counts a `purchase` event, with your own
55
+ numerator and denominator. Use this when a form submission, a lead capture, or
56
+ another event of your own defines a conversion.
55
57
 
56
58
  ```sql
57
59
  -- Lead capture conversion: form submissions / lead-capture step entries
@@ -75,7 +77,7 @@ ORDER BY project_local_date
75
77
  You can also combine multiple events with `OR` and group with parentheses:
76
78
 
77
79
  ```sql
78
- -- Either checkout_complete or order_create counts as a conversion
80
+ -- A `checkout_complete` event or an `order_create` event counts as a conversion.
79
81
  SELECT
80
82
  control_conversion_rate(
81
83
  'event_type=checkout_complete OR event_type=order_create'
@@ -87,12 +89,15 @@ FROM events
87
89
  WHERE project_id = '{{ your project ID }}'
88
90
  ```
89
91
 
90
- ## Custom segment with `control_users` / `variant_users`
92
+ ## Your own segment with `control_users` and `variant_users`
91
93
 
92
- On `events`, `control_users` / `variant_users` / `control_sessions` / `variant_sessions` accept an optional filter string to count only users/sessions matching the given expression in addition to the implicit `variant_type` filter.
94
+ On `events`, four functions take an optional filter string: `control_users`,
95
+ `variant_users`, `control_sessions`, and `variant_sessions`. The filter counts only a
96
+ user or a session that matches your expression. It applies together with the
97
+ `variant_type` filter that PickedQL adds.
93
98
 
94
99
  ```sql
95
- -- Control users who fired app_initialize, broken down by step
100
+ -- The control users who fired an `app_initialize` event, by step.
96
101
  SELECT
97
102
  step_id,
98
103
  control_users('event_type=app_initialize') AS control_initialized,
@@ -113,10 +118,13 @@ WHERE project_id = '{{ your project ID }}'
113
118
 
114
119
  ## Conversion rate from nested virtual functions
115
120
 
116
- When numerator and denominator come from different segments — e.g., Control users who fired a custom event divided by **all** users (not just Control) — pass virtual function calls directly as conversion-rate args:
121
+ The numerator and the denominator can come from different segments. An example is the
122
+ control users who fired an event of your own, divided by **all** users and not by the
123
+ control users alone. For that, pass a virtual function call directly as an argument
124
+ of the conversion rate:
117
125
 
118
126
  ```sql
119
- -- (Control users who fired app_initialize) / (all users, regardless of variant)
127
+ -- The control users who fired `app_initialize`, over all users of every variant.
120
128
  SELECT
121
129
  control_conversion_rate(
122
130
  control_users('event_type=app_initialize'),
@@ -188,9 +196,9 @@ GROUP BY utm_source
188
196
 
189
197
  | utm_source | users | purchasers |
190
198
  | ---------- | ------ | ---------- |
191
- | meta | 12031 | 101 |
192
- | fb | 3656 | 31 |
193
- | an | 100366 | 299 |
199
+ | `meta` | 12031 | 101 |
200
+ | `fb` | 3656 | 31 |
201
+ | `an` | 100366 | 299 |
194
202
 
195
203
  ## Step funnel for variant users
196
204
 
@@ -211,18 +219,20 @@ ORDER BY variant DESC
211
219
 
212
220
  | step_id | variant |
213
221
  | ------------------------------ | ------- |
214
- | who-are-you-protecting-step | 1093 |
215
- | about-yourself-step | 533 |
216
- | date-of-birth-step | 435 |
217
- | employment-status-step | 408 |
218
- | nicotine-usage-step | 407 |
219
- | coverage-motivation-step | 405 |
220
- | lead-capture-step | 398 |
222
+ | `who-are-you-protecting-step` | 1093 |
223
+ | `about-yourself-step` | 533 |
224
+ | `date-of-birth-step` | 435 |
225
+ | `employment-status-step` | 408 |
226
+ | `nicotine-usage-step` | 407 |
227
+ | `coverage-motivation-step` | 405 |
228
+ | `lead-capture-step` | 398 |
221
229
 
222
230
 
223
231
  ## Sankey — overall user journey
224
232
 
225
- Forward window from the starting step. Non-reachers of `targeting_step` still contribute edges up to `num_nodes_after_start`. Mirrors dashboard 1156 card 4129's semantics.
233
+ A forward window from the starting step. A user who never reaches `targeting_step`
234
+ still gives edges, up to `num_nodes_after_start`. This does what card 4129 on
235
+ dashboard 1156 does.
226
236
 
227
237
  ```sql
228
238
  SELECT source, target, users FROM experience_journey
@@ -236,13 +246,15 @@ WHERE project_id = '{{ your project ID }}'
236
246
 
237
247
  | source | target | users |
238
248
  | ------------------------- | ------------------------- | ----- |
239
- | 001. step-start | 002. step-questionnaire-1 | 182 |
240
- | 002. step-questionnaire-1 | 003. step-quote | 144 |
241
- | 003. step-quote | 004. step-target | 121 |
249
+ | `001. step-start` | `002. step-questionnaire-1` | 182 |
250
+ | `002. step-questionnaire-1` | `003. step-quote` | 144 |
251
+ | `003. step-quote` | `004. step-target` | 121 |
242
252
 
243
- ## Sankey — top paths leading to a target step
253
+ ## Sankey — the top paths to a target step
244
254
 
245
- Backward window anchored at the target, plus top-N path ranking. Mirrors dashboard 1156 card 3961 / dashboard 991 card 2740 semantics.
255
+ A backward window anchored at the target. It also ranks the paths and keeps the ones
256
+ with the most users. This does what card 3961 on dashboard 1156 and card 2740 on
257
+ dashboard 991 do.
246
258
 
247
259
  ```sql
248
260
  SELECT source, target, users FROM experience_journey
@@ -284,6 +296,6 @@ WHERE project_id = '{{ your project ID }}'
284
296
 
285
297
  | device_category | user_canonical_id |
286
298
  | --------------- | ------------------------------------ |
287
- | Mobile | 019cc336-5a6a-7c32-b638-cdcc983b3e70 |
288
- | Mobile | 019cc336-5a6a-7d8f-994c-69bc28e5f550 |
289
- | Mobile | 019cc80f-503c-7181-b2c6-986b1f76d496 |
299
+ | Mobile | `019cc336-5a6a-7c32-b638-cdcc983b3e70` |
300
+ | Mobile | `019cc336-5a6a-7d8f-994c-69bc28e5f550` |
301
+ | Mobile | `019cc80f-503c-7181-b2c6-986b1f76d496` |
@@ -2,17 +2,19 @@
2
2
  layout: docs
3
3
  ---
4
4
 
5
- # Filtering
5
+ # Filters
6
6
 
7
- Add `WHERE` clauses as usual. Every query must include a `project_id` filter — PickedQL extracts it and verifies you have permission to access that project. Queries without a `project_id` filter are rejected.
7
+ Add a `WHERE` clause in the usual way. Every query must hold a `project_id` filter.
8
+ PickedQL reads that filter and checks that you have permission for the project.
9
+ PickedQL rejects a query with no `project_id` filter.
8
10
 
9
11
  ```sql
10
- -- Date range
12
+ -- A range of dates.
11
13
  SELECT control_users() FROM metrics
12
14
  WHERE project_id = 'your-project-id'
13
15
  AND project_local_date BETWEEN '2026-03-01' AND '2026-03-31'
14
16
 
15
- -- By experience
17
+ -- By experience.
16
18
  SELECT step_enter_users() FROM events
17
19
  WHERE project_id = 'your-project-id'
18
20
  AND experience_id = 'my-experience'
@@ -30,9 +32,11 @@ WHERE project_id = 'your-project-id'
30
32
  Each query can only filter on a single `project_id`. Queries that reference multiple projects will be rejected.
31
33
  :::
32
34
 
33
- ## Reference tables (per-table tenant filter)
35
+ ## Reference tables, each with its own tenant filter
34
36
 
35
- Queries against the reference tables (`steps`, `experiences`, `projects`) require their own tenant filter so you only see rows belonging to a project you have access to:
37
+ A query against a reference table needs its own tenant filter. The reference tables
38
+ are `steps`, `experiences`, and `projects`. The filter makes sure that you see only
39
+ the rows of a project that you can reach:
36
40
 
37
41
  | Source | Tenant filter |
38
42
  |---|---|
@@ -4,33 +4,51 @@ layout: docs
4
4
 
5
5
  # PickedQL
6
6
 
7
- PickedQL is a SQL query language for querying your Picked analytics data. Write simple SQL using virtual tables and functions — PickedQL expands them into optimized queries with automatic joins and A/B test aggregations.
7
+ PickedQL is a SQL query language for your Picked analytics data. Write short SQL with
8
+ a virtual table and a virtual function. PickedQL expands them into a full query, and
9
+ it adds the joins and the A/B test aggregations for you.
8
10
 
9
11
  ## Quick start
10
12
 
11
13
  ```sql
12
- -- How many users are in the control vs variant group?
14
+ -- How many users are in the control group, and how many in the variant group?
13
15
  SELECT control_users(), variant_users()
14
16
  FROM metrics
15
17
  WHERE project_id = 'your-project-id'
16
18
  ```
17
19
 
18
- PickedQL handles table mapping and dimension joins automatically. You write concise, readable SQL — PickedQL does the rest.
20
+ PickedQL maps each table and adds each dimension join for you. You write short SQL
21
+ that a person can read, and PickedQL does the rest.
19
22
 
20
23
  ## How PickedQL works
21
24
 
22
- 1. **Virtual table expansion**: Table names like `metrics` map to underlying data tables
23
- 2. **Virtual function expansion**: Shorthand functions like `control_users()` expand to full aggregate expressions
24
- 3. **Auto dimension joins**: References to dimension columns (like `device_category` or `country_code`) trigger automatic `LEFT JOIN` clauses — you never write joins yourself
25
- 4. **Tenant isolation**: Every query must include a tenant filter in the `WHERE` clause — usually `project_id`, but `projects` uses `id` (the row's `id` IS the project id). PickedQL extracts the filter and verifies you have permission to access that project
26
- 5. **SQL passthrough**: Standard SQL (including `CASE WHEN`, window functions, and subqueries) works alongside virtual functions
25
+ 1. **Virtual table expansion**: a table name such as `metrics` maps to the data
26
+ tables below it.
27
+ 2. **Virtual function expansion**: a short function such as `control_users()` expands
28
+ to a full aggregate expression.
29
+ 3. **Automatic dimension joins**: PickedQL adds a `LEFT JOIN` when you point at a
30
+ dimension column, such as `device_category` or `country_code`. You write no join.
31
+ 4. **Tenant isolation**: every query must hold a tenant filter in the `WHERE` clause.
32
+ That filter is `project_id` for most tables. The `projects` table uses `id`
33
+ instead, because the `id` of the row is the project ID. PickedQL reads the filter
34
+ and checks that you have permission for that project.
35
+ 5. **SQL passthrough**: standard SQL works next to a virtual function. This includes
36
+ `CASE WHEN`, a window function, and a subquery.
27
37
 
28
38
  ## Best practices
29
39
 
30
- - **Use `metrics` for dashboards**: Pre-aggregated data is significantly faster than raw events for standard reporting
31
- - **Use `events` for deep dives**: Raw event-level data gives full flexibility for custom breakdowns and funnels
32
- - **Use `experience_journey` for sankey / funnel charts**: One line of SQL returns `source, target, users` — the 8-CTE scaffold (dedup, top-N path ranking, lagInFrame) is handled internally; pass parameters via `WHERE`
33
- - **Don't write JOINs**: PickedQL injects them automatically when you reference dimension columns
34
- - **Group by any dimension**: Add dimension columns to `SELECT` and `GROUP BY` freely — required joins are handled for you
35
- - **Reference table metadata for free**: Use `step_title`, `experience_title`, `project_name` directly in `events` queries — PickedQL auto-joins the `steps`, `experiences`, and `projects` reference tables
40
+ - **Use `metrics` for a dashboard**: data that PickedQL aggregates first is much
41
+ faster than raw events for a standard report.
42
+ - **Use `events` to go deeper**: raw event data lets you build any breakdown or
43
+ funnel that you want.
44
+ - **Use `experience_journey` for a sankey chart or a funnel chart**: one line of SQL
45
+ returns `source, target, users`. PickedQL builds the eight common table expressions
46
+ for you. Those remove the duplicates, rank the top paths, and call `lagInFrame`.
47
+ Pass your parameters in the `WHERE` clause.
48
+ - **Write no JOIN**: PickedQL adds each one when you point at a dimension column.
49
+ - **Group by any dimension**: add a dimension column to `SELECT` and to `GROUP BY`.
50
+ PickedQL adds the join that it needs.
51
+ - **Read the reference tables for no cost**: use `step_title`, `experience_title`, and
52
+ `project_name` directly in an `events` query. PickedQL joins the `steps`,
53
+ `experiences`, and `projects` reference tables for you.
36
54
 
@@ -12,23 +12,23 @@ Pre-aggregated daily data. One row per unique combination of dimensions per day.
12
12
 
13
13
  | Column | Type | Description |
14
14
  |---|---|---|
15
- | `project_id` | String | Project identifier (required in `WHERE`) |
16
- | `project_local_date` | Date | Date in project timezone |
17
- | `experience_id` | String | Experience identifier |
18
- | `step_id` | String | Step identifier |
19
- | `event_type` | String | Event type (e.g., `page_view`, `click`) |
20
- | `interaction_type` | String | Interaction type (e.g., `Button Click`) |
21
- | `variant_type` | String | `Control`, `Variant`, or `Unknown` |
22
- | `device_category` | String | Device category (e.g., `Desktop`, `Mobile`) |
23
- | `country_code` | String | ISO country code |
24
- | `utm_source` | String | UTM source |
25
- | `utm_medium` | String | UTM medium |
26
- | `product_name` | String | Product name |
27
- | `product_category` | String | Product category |
28
- | `landing_page_url` | String | Landing page URL |
15
+ | `project_id` | `String` | Project identifier (required in `WHERE`) |
16
+ | `project_local_date` | `Date` | Date in project timezone |
17
+ | `experience_id` | `String` | Experience identifier |
18
+ | `step_id` | `String` | Step identifier |
19
+ | `event_type` | `String` | Event type (for example `page_view`, `click`) |
20
+ | `interaction_type` | `String` | Interaction type (for example `Button Click`) |
21
+ | `variant_type` | `String` | `Control`, `Variant`, or `Unknown` |
22
+ | `device_category` | `String` | Device category (for example `Desktop`, `Mobile`) |
23
+ | `country_code` | `String` | ISO country code |
24
+ | `utm_source` | `String` | UTM source |
25
+ | `utm_medium` | `String` | UTM medium |
26
+ | `product_name` | `String` | Product name |
27
+ | `product_category` | `String` | Product category |
28
+ | `landing_page_url` | `String` | Landing page URL |
29
29
 
30
30
  :::note
31
- Aggregate columns (`users`, `sessions`, `events`, etc.) are not queried directly — use [virtual functions](./virtual-functions) instead.
31
+ Aggregate columns (`users`, `sessions`, `events`, and so on) are not queried directly — use [virtual functions](./virtual-functions) instead.
32
32
  :::
33
33
 
34
34
  ## `events`
@@ -39,22 +39,22 @@ User-level event data. One row per event per user. Dimension columns are joined
39
39
 
40
40
  | Column | Type | Description |
41
41
  |---|---|---|
42
- | `project_id` | String | Project identifier (required in `WHERE`) |
43
- | `event_id` | String | Unique event identifier |
44
- | `user_canonical_id` | String | Canonical user identifier |
45
- | `session_id` | String | Session identifier |
46
- | `experience_id` | String | Experience identifier |
47
- | `step_id` | String | Step identifier |
48
- | `event_type` | String | Event type (e.g., `page_view`, `click`) |
49
- | `interaction_type` | String | Interaction type (e.g., `Button Click`) |
50
- | `client_local_date` | Date | Date in client timezone |
51
- | `is_purchase` | UInt8 | `1` if checkout complete event |
52
- | `is_cart_add` | UInt8 | `1` if cart add event |
53
- | `is_first_session` | UInt8 | `1` if first session for this user |
54
- | `landing_page_url` | String | Landing page URL |
42
+ | `project_id` | `String` | Project identifier (required in `WHERE`) |
43
+ | `event_id` | `String` | Unique event identifier |
44
+ | `user_canonical_id` | `String` | Canonical user identifier |
45
+ | `session_id` | `String` | Session identifier |
46
+ | `experience_id` | `String` | Experience identifier |
47
+ | `step_id` | `String` | Step identifier |
48
+ | `event_type` | `String` | Event type (for example `page_view`, `click`) |
49
+ | `interaction_type` | `String` | Interaction type (for example `Button Click`) |
50
+ | `client_local_date` | `Date` | Date in client timezone |
51
+ | `is_purchase` | `UInt8` | `1` if checkout complete event |
52
+ | `is_cart_add` | `UInt8` | `1` if cart add event |
53
+ | `is_first_session` | `UInt8` | `1` if first session for this user |
54
+ | `landing_page_url` | `String` | Landing page URL |
55
55
  | `features` | Map(String, String) | Feature flag assignments |
56
56
  | `feature_keys` | Map(String, String) | Feature key assignments |
57
- | `event_meta` | String | Event metadata |
57
+ | `event_meta` | `String` | Event metadata |
58
58
 
59
59
  ### Dimension columns (auto-joined)
60
60
 
@@ -62,37 +62,39 @@ These columns are joined automatically when referenced — no `JOIN` syntax need
62
62
 
63
63
  | Column | Source | Description |
64
64
  |---|---|---|
65
- | `variant_type` | dim_variant | `Control`, `Variant`, or `Unknown` |
66
- | `ab_feature_name` | dim_variant | A/B feature name |
67
- | `ab_feature_value` | dim_variant | A/B feature value |
68
- | `device_category` | dim_device | Device category |
69
- | `browser_name` | dim_device | Browser name |
70
- | `browser_version` | dim_device | Browser version |
71
- | `os_name` | dim_device | Operating system name |
72
- | `os_version` | dim_device | Operating system version |
73
- | `platform` | dim_device | Platform |
74
- | `country_code` | dim_geo | ISO country code |
75
- | `country_name` | dim_geo | Country name |
76
- | `region` | dim_geo | Region |
77
- | `city` | dim_geo | City |
78
- | `utm_source` | dim_utm | UTM source |
79
- | `utm_medium` | dim_utm | UTM medium |
80
- | `utm_campaign` | dim_utm | UTM campaign |
81
- | `utm_term` | dim_utm | UTM term |
82
- | `utm_content` | dim_utm | UTM content |
83
- | `product_name` | dim_product | Product name |
84
- | `product_category` | dim_product | Product category |
85
- | `product_page` | dim_product | Product page |
86
- | `step_title` | steps | Step title (joined on `project_id` + `experience_id` + `step_id`) |
87
- | `experience_title` | experiences | Experience title (joined on `project_id` + `experience_id`) |
88
- | `project_name` | projects | Project name |
89
- | `project_timezone` | projects | Project timezone |
65
+ | `variant_type` | `dim_variant` | `Control`, `Variant`, or `Unknown` |
66
+ | `ab_feature_name` | `dim_variant` | A/B feature name |
67
+ | `ab_feature_value` | `dim_variant` | A/B feature value |
68
+ | `device_category` | `dim_device` | Device category |
69
+ | `browser_name` | `dim_device` | Browser name |
70
+ | `browser_version` | `dim_device` | Browser version |
71
+ | `os_name` | `dim_device` | The name of the operating system |
72
+ | `os_version` | `dim_device` | The version of the operating system |
73
+ | `platform` | `dim_device` | Platform |
74
+ | `country_code` | `dim_geo` | ISO country code |
75
+ | `country_name` | `dim_geo` | Country name |
76
+ | `region` | `dim_geo` | Region |
77
+ | `city` | `dim_geo` | City |
78
+ | `utm_source` | `dim_utm` | UTM source |
79
+ | `utm_medium` | `dim_utm` | UTM medium |
80
+ | `utm_campaign` | `dim_utm` | UTM campaign |
81
+ | `utm_term` | `dim_utm` | UTM term |
82
+ | `utm_content` | `dim_utm` | UTM content |
83
+ | `product_name` | `dim_product` | Product name |
84
+ | `product_category` | `dim_product` | Product category |
85
+ | `product_page` | `dim_product` | Product page |
86
+ | `step_title` | `steps` | Step title (joined on `project_id` + `experience_id` + `step_id`) |
87
+ | `experience_title` | `experiences` | Experience title (joined on `project_id` + `experience_id`) |
88
+ | `project_name` | `projects` | Project name |
89
+ | `project_timezone` | `projects` | Project timezone |
90
90
 
91
91
  ## `steps`, `experiences`, `projects`
92
92
 
93
- Reference tables describing your Picked configuration. Auto-joined onto `events` when their analyst-facing columns are referenced; require a per-table tenant filter when queried directly.
93
+ These reference tables hold the options of your Picked project. PickedQL joins them
94
+ onto `events` when you point at one of their analyst-facing columns. Each one needs
95
+ its own tenant filter when you read it directly.
94
96
 
95
- | Source | Real Columns | Analyst Columns (when referenced from `events`) | Tenant filter (when queried directly) |
97
+ | Source | Real columns | Analyst columns, when you point at them from `events` | Tenant filter, when you read the table directly |
96
98
  |---|---|---|---|
97
99
  | `steps` | `id`, `title`, `experience_id`, `project_id`, `organization_id` | `step_title` | `project_id = '…'` |
98
100
  | `experiences` | `id`, `title`, `project_id`, `organization_id` | `experience_title` | `project_id = '…'` |
@@ -116,7 +118,9 @@ GROUP BY experience_title, step_title
116
118
 
117
119
  ## `features`
118
120
 
119
- The `features` table provides [parameterized virtual functions](./virtual-functions#feature-functions-features--events) for querying feature flag assignments:
121
+ The `features` table gives you
122
+ [parameterized virtual functions](./virtual-functions#feature-functions-on-features-and-on-events).
123
+ Use them to read which visitors each feature flag holds:
120
124
 
121
125
  | Function | Description |
122
126
  |---|---|
@@ -126,31 +130,36 @@ The `features` table provides [parameterized virtual functions](./virtual-functi
126
130
 
127
131
  ## `experience_journey`
128
132
 
129
- Aggregated step-transition edges for sankey / funnel charts. Parameters are supplied via the outer `WHERE` clause (PickedQL extracts them and substitutes into the underlying scaffold).
133
+ This table gives one edge for each step transition, and PickedQL aggregates them
134
+ first. Use the edges in a sankey chart or a funnel chart. Give each parameter in the
135
+ outer `WHERE` clause. PickedQL reads them and puts them into the query below.
130
136
 
131
137
  ### Columns
132
138
 
133
139
  | Column | Type | Description |
134
140
  |---|---|---|
135
- | `source` | String | Edge start, formatted as `NN. <step title>` (e.g., `001. Welcome screen`) |
136
- | `target` | String | Edge end, formatted as `NN. <step title>`.
137
- | `users` | UInt64 | Distinct `user_canonical_id` count that traversed this edge. |
141
+ | `source` | `String` | Edge start, formatted as `NN. <step title>` (for example `001. Welcome screen`) |
142
+ | `target` | `String` | Edge end, formatted as `NN. <step title>`.
143
+ | `users` | `UInt64` | Distinct `user_canonical_id` count that traversed this edge. |
138
144
 
139
- ### Parameters (via `WHERE`)
145
+ ### Parameters, in the `WHERE` clause
140
146
 
141
147
  | Key | Type | Required | Purpose |
142
148
  |---|---|---|---|
143
- | `project_id` | String | ✓ | Tenant filter. |
144
- | `experience_id` | String | | Restrict to one `fact_events.experience_id`. |
145
- | `starting_step` | String | | Anchor edges at first occurrence of this `step_id`. Defaults to each user's first step. |
146
- | `targeting_step` | String | | Trim each user's window at their first occurrence of this `step_id`. Non-reachers still contribute their pre-cut journey. |
147
- | `num_nodes_after_start` | Integer | | Forward window from start: keep steps where `seq_pos < start_seq + N` (card 4129 semantics). |
148
- | `num_nodes_before_target` | Integer | | Backward window from target: keep steps where `seq_pos >= target_seq - N` (card 2740 semantics). Requires `targeting_step`. |
149
- | `top_n_path` | Integer | | Keep only the top-N most-walked paths by user count. Defaults to no limit. |
150
- | `user_cohort_event` | String | | Restrict to users who have at least one `fact_events` row with this `event_type`. Implemented as `user_canonical_id IN (SELECT DISTINCT user_canonical_id FROM fact_events WHERE event_type = '…')`. |
151
- | `client_local_date` | Date | | Date range filter, pushed into the underlying scan. Supports `=`, `<`, `<=`, `>`, `>=`, `BETWEEN`. |
152
- | `event_timestamp` | DateTime64 | | Server-side timestamp filter. Same operators as `client_local_date`. |
153
-
154
- When both window params are set, both predicates apply (intersection). Date filters combine via `AND` with the other params.
155
-
156
- See [data sources → experience_journey](./data-sources#experience_journey--sankey-ready-step-transitions) for usage examples.
149
+ | `project_id` | `String` | ✓ | The tenant filter. |
150
+ | `experience_id` | `String` | | Limits the rows to one `fact_events.experience_id`. |
151
+ | `starting_step` | `String` | | Anchors each edge at the first time that a user reaches this `step_id`. The default is the first step of each user. |
152
+ | `targeting_step` | `String` | | Trims the window of each user at the first time that the user reaches this `step_id`. A user who never reaches it still gives the journey up to that point. |
153
+ | `num_nodes_after_start` | Integer | | The forward window from the start. It keeps a step where `seq_pos < start_seq + N`. This is what card 4129 does. |
154
+ | `num_nodes_before_target` | Integer | | The backward window from the target. It keeps a step where `seq_pos >= target_seq - N`. This is what card 2740 does. It needs `targeting_step`. |
155
+ | `top_n_path` | Integer | | Keeps the paths that the most users take, and drops the rest. The default is no limit. |
156
+ | `user_cohort_event` | `String` | | Limits the rows to a user with at least one `fact_events` row of this `event_type`. PickedQL writes it as `user_canonical_id IN (SELECT DISTINCT user_canonical_id FROM fact_events WHERE event_type = '…')`. |
157
+ | `client_local_date` | `Date` | | A filter on a range of dates. PickedQL puts it into the read below. It takes `=`, `<`, `<=`, `>`, `>=`, and `BETWEEN`. |
158
+ | `event_timestamp` | `DateTime64` | | A filter on the timestamp from the server. It takes the same operators as `client_local_date`. |
159
+
160
+ When you set both window parameters, both tests apply, and the result is their
161
+ intersection. A date filter joins the other parameters with `AND`.
162
+
163
+ See
164
+ [data sources, `experience_journey`](./data-sources#experience_journey--step-transitions-for-a-sankey-chart)
165
+ for examples.