@picked/client 0.0.70 → 0.0.71-next.2
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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/api-B0ydpQBE.js +38 -0
- package/dist/api-CObostrU.cjs +1 -0
- package/dist/cjs/form/index.js +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/plugins/dom-observer/index.js +1 -1
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/server/index.js +1 -1
- package/dist/docs/explanation/conditions-and-the-logic-engine.md +94 -0
- package/dist/docs/explanation/feature-flag-allocation.md +176 -0
- package/dist/docs/explanation/how-picked-works.md +102 -0
- package/dist/docs/explanation/how-pickedql-works.md +76 -0
- package/dist/docs/explanation/pages-and-flows.md +64 -0
- package/dist/docs/explanation/the-content-model.md +77 -0
- package/dist/docs/explanation/the-two-apis.md +68 -0
- package/dist/docs/explanation/why-server-side-rendering.md +76 -0
- package/dist/docs/explanation/workspaces-and-versions.md +71 -0
- package/dist/docs/how-to/add-a-step-field-or-action.md +43 -0
- package/dist/docs/{pickedql/examples.md → how-to/analytics/index.md} +19 -6
- package/dist/docs/how-to/archive-or-delete-a-workspace.md +46 -0
- package/dist/docs/how-to/authenticate-with-a-magic-link.md +40 -0
- package/dist/docs/how-to/authenticate-with-a-password.md +51 -0
- package/dist/docs/how-to/authenticate-with-an-api-key.md +51 -0
- package/dist/docs/how-to/authenticate-with-oauth.md +40 -0
- package/dist/docs/{flow-builder/building-flows.md → how-to/build-a-flow.md} +13 -9
- package/dist/docs/how-to/connect-the-mcp-server.md +38 -0
- package/dist/docs/how-to/create-a-project.md +29 -0
- package/dist/docs/how-to/create-an-experience.md +42 -0
- package/dist/docs/how-to/create-and-revoke-an-api-key.md +37 -0
- package/dist/docs/how-to/debug-with-the-chrome-extension.md +57 -0
- package/dist/docs/how-to/define-a-page-layout.md +39 -0
- package/dist/docs/how-to/fill-and-publish-a-page.md +50 -0
- package/dist/docs/{gtm-template.md → how-to/install-the-gtm-template.md} +9 -3
- package/dist/docs/how-to/install-the-sdk.md +68 -0
- package/dist/docs/how-to/install-the-vite-plugin.md +56 -0
- package/dist/docs/how-to/invite-and-remove-members.md +34 -0
- package/dist/docs/how-to/preview-a-workspace.md +28 -0
- package/dist/docs/how-to/publish-a-workspace.md +30 -0
- package/dist/docs/how-to/read-a-feature-flag.md +101 -0
- package/dist/docs/how-to/register-a-component.md +55 -0
- package/dist/docs/how-to/register-an-action-handler.md +67 -0
- package/dist/docs/how-to/render-a-step-form.md +132 -0
- package/dist/docs/how-to/render-an-unpublished-preview.md +41 -0
- package/dist/docs/how-to/reuse-a-section.md +37 -0
- package/dist/docs/how-to/reuse-a-step-field-or-action.md +28 -0
- package/dist/docs/how-to/roll-back-a-published-version.md +26 -0
- package/dist/docs/how-to/run-an-ab-test.md +49 -0
- package/dist/docs/how-to/run-code-on-a-form-submission.md +125 -0
- package/dist/docs/how-to/server-render-a-page.md +81 -0
- package/dist/docs/how-to/set-a-content-security-policy.md +32 -0
- package/dist/docs/how-to/set-up-mfa.md +69 -0
- package/dist/docs/how-to/upload-and-use-an-asset.mdx +73 -0
- package/dist/docs/how-to/validate-a-field.md +52 -0
- package/dist/docs/how-to/write-a-condition.md +45 -0
- package/dist/docs/how-to/write-a-predicate.md +60 -0
- package/dist/docs/index.md +41 -70
- package/dist/docs/reference/dashboard/actions.mdx +53 -0
- package/dist/docs/{api-keys.mdx → reference/dashboard/api-keys.mdx} +7 -15
- package/dist/docs/reference/dashboard/conditions.mdx +42 -0
- package/dist/docs/reference/dashboard/content/asset-library.mdx +63 -0
- package/dist/docs/{content → reference/dashboard/content}/content-entries.mdx +11 -3
- package/dist/docs/{content → reference/dashboard/content}/layouts.mdx +11 -35
- package/dist/docs/reference/dashboard/content/pages.mdx +35 -0
- package/dist/docs/reference/dashboard/content/sections.mdx +28 -0
- package/dist/docs/{experiences.mdx → reference/dashboard/experiences.mdx} +9 -16
- package/dist/docs/{features.mdx → reference/dashboard/features.mdx} +26 -41
- package/dist/docs/{fields.mdx → reference/dashboard/fields.mdx} +26 -20
- package/dist/docs/{flow-builder/overview.mdx → reference/dashboard/flow-builder/canvas.mdx} +8 -13
- package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/condition-builder.md +5 -13
- package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/keyboard-shortcuts.md +7 -1
- package/dist/docs/{flow-builder → reference/dashboard/flow-builder}/nodes.md +10 -3
- package/dist/docs/reference/dashboard/projects.mdx +44 -0
- package/dist/docs/reference/dashboard/reports.mdx +43 -0
- package/dist/docs/reference/dashboard/steps.mdx +37 -0
- package/dist/docs/{team-management.md → reference/dashboard/team-management.md} +5 -22
- package/dist/docs/{workspace-versioning.mdx → reference/dashboard/workspace-versioning.mdx} +6 -10
- package/dist/docs/reference/dashboard/workspaces.mdx +76 -0
- package/dist/docs/{delivery-api → reference/delivery-api}/experience-content.md +11 -4
- package/dist/docs/reference/delivery-api/index.md +45 -0
- package/dist/docs/{delivery-api → reference/delivery-api}/preview-caching-crawling.md +12 -5
- package/dist/docs/{delivery-api → reference/delivery-api}/project-config.md +11 -4
- package/dist/docs/reference/glossary.md +173 -0
- package/dist/docs/reference/management-api/authentication.md +74 -0
- package/dist/docs/{api → reference/management-api}/authorization-and-scopes.md +10 -3
- package/dist/docs/{api → reference/management-api}/error-handling.md +9 -3
- package/dist/docs/{api → reference/management-api}/pagination.md +6 -0
- package/dist/docs/{pickedql → reference/pickedql}/data-sources.md +16 -9
- package/dist/docs/{pickedql → reference/pickedql}/dimensions.md +7 -0
- package/dist/docs/{pickedql → reference/pickedql}/filtering.md +7 -0
- package/dist/docs/reference/pickedql/index.md +18 -0
- package/dist/docs/{pickedql → reference/pickedql}/schema.md +13 -6
- package/dist/docs/{pickedql → reference/pickedql}/virtual-functions.md +7 -0
- package/dist/docs/reference/sdk/actions.md +30 -0
- package/dist/docs/{sdk → reference/sdk}/conditions.md +8 -1
- package/dist/docs/reference/sdk/content-security-policy.md +21 -0
- package/dist/docs/reference/sdk/experiences.md +51 -0
- package/dist/docs/{sdk → reference/sdk}/features.md +30 -117
- package/dist/docs/{sdk → reference/sdk}/fields.md +10 -88
- package/dist/docs/reference/sdk/index.md +68 -0
- package/dist/docs/{sdk → reference/sdk}/plugins/create-experience.md +10 -22
- package/dist/docs/reference/sdk/plugins/dynamic-experiences.md +26 -0
- package/dist/docs/reference/sdk/plugins/index.md +29 -0
- package/dist/docs/{sdk → reference/sdk}/plugins/window-object.md +6 -0
- package/dist/docs/reference/sdk/predicates.md +43 -0
- package/dist/docs/reference/sdk/ssr/index.md +33 -0
- package/dist/docs/{sdk → reference/sdk}/ssr/rendering-and-hydration.md +4 -63
- package/dist/docs/{sdk → reference/sdk}/ssr/serialize.md +6 -33
- package/dist/docs/{sdk → reference/sdk}/ssr/slots-and-placements.md +7 -1
- package/dist/docs/reference/sdk/steps.md +87 -0
- package/dist/docs/reference/tools/chrome-extension.md +16 -0
- package/dist/docs/{image-optimisation-service.md → reference/tools/image-optimisation-service.md} +8 -0
- package/dist/docs/{mcp.md → reference/tools/mcp.md} +5 -45
- package/dist/docs/{vite-plugin.md → reference/tools/vite-plugin.md} +8 -38
- package/dist/docs/{api/index.md → tutorials/first-api-call.md} +27 -15
- package/dist/docs/tutorials/first-experience-in-the-dashboard.mdx +143 -0
- package/dist/docs/tutorials/first-experience.mdx +255 -0
- package/dist/docs/tutorials/first-server-rendered-page.mdx +186 -0
- package/dist/{dom-BwmTHeT7.js → dom-BqcToNL0.js} +1 -3
- package/dist/{dom-CDrHc_j9.cjs → dom-DlLH3FxV.cjs} +1 -1
- package/dist/esm/form/index.js +2 -4
- package/dist/esm/index.js +306 -298
- package/dist/esm/plugins/dom-observer/index.js +1 -1
- package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
- package/dist/esm/preview/index.js +1 -1
- package/dist/esm/server/index.js +12 -10
- package/dist/{experience-instance-DOJShRk5.js → experience-instance-DGk11d8a.js} +3 -3
- package/dist/experience-instance-Dej3P1sE.cjs +1 -0
- package/dist/plugin-CNna0UjB.cjs +1 -0
- package/dist/{plugin-ChPkTj__.js → plugin-DUOJpHaq.js} +2 -2
- package/dist/{preview-channel-DW9J8t4B.js → preview-channel-CSr96XAo.js} +1 -1
- package/dist/{protocol-DSqOlW2r.js → protocol-C5KkkiZc.js} +1 -1
- package/dist/types/client.d.ts +7 -6
- package/dist/types/entities/action/action.d.ts +5 -4
- package/dist/types/entities/action/action.d.ts.map +1 -1
- package/dist/types/entities/component/component.d.ts +6 -5
- package/dist/types/entities/component/component.d.ts.map +1 -1
- package/dist/types/entities/condition/condition.d.ts +5 -5
- package/dist/types/entities/content/content.d.ts +4 -3
- package/dist/types/entities/experience/experience.d.ts +11 -10
- package/dist/types/entities/experience/experience.d.ts.map +1 -1
- package/dist/types/entities/experience-instance/experience-instance.d.ts +10 -9
- package/dist/types/entities/feature/feature.d.ts +19 -2
- package/dist/types/entities/feature/feature.d.ts.map +1 -1
- package/dist/types/entities/field/field.d.ts +7 -6
- package/dist/types/entities/framework/framework.d.ts +7 -6
- package/dist/types/entities/framework/framework.d.ts.map +1 -1
- package/dist/types/entities/history/history.d.ts +6 -6
- package/dist/types/entities/plugin/plugin.d.ts +3 -2
- package/dist/types/entities/plugin/plugin.d.ts.map +1 -1
- package/dist/types/entities/predicate/predicate.d.ts +4 -3
- package/dist/types/entities/predicate/predicate.d.ts.map +1 -1
- package/dist/types/entities/response/response-validation.d.ts +3 -2
- package/dist/types/entities/response/response.d.ts +12 -11
- package/dist/types/entities/session/session.d.ts +2 -1
- package/dist/types/entities/step/step.d.ts +6 -5
- package/dist/types/exports.d.ts +2 -1
- package/dist/types/exports.d.ts.map +1 -1
- package/dist/types/form/build-responses.d.ts +2 -1
- package/dist/types/form/create-field-evaluator.d.ts +4 -3
- package/dist/types/form/create-field-validation.d.ts +5 -4
- package/dist/types/form/create-form.d.ts +7 -6
- package/dist/types/form/create-submit-pipeline.d.ts +7 -6
- package/dist/types/form/get-step-fields.d.ts +3 -2
- package/dist/types/lifecycle/condition-selection.d.ts +14 -4
- package/dist/types/lifecycle/lifecycle.d.ts +4 -3
- package/dist/types/lifecycle/rules-logic.d.ts +4 -3
- package/dist/types/lifecycle/state-snapshot.d.ts +4 -3
- package/dist/types/picked.d.ts +64 -62
- package/dist/types/picked.d.ts.map +1 -1
- package/dist/types/ping.d.ts +1 -1
- package/dist/types/ping.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/create-experience.d.ts +9 -8
- package/dist/types/plugins/create-experience/create-experience.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/plugin.d.ts +3 -2
- package/dist/types/plugins/create-experience/plugin.d.ts.map +1 -1
- package/dist/types/plugins/create-experience/serialization.d.ts.map +1 -1
- package/dist/types/plugins/dom-observer/plugin.d.ts +1 -1
- package/dist/types/plugins/dynamic-experiences/plugin.d.ts +3 -3
- package/dist/types/plugins/dynamic-experiences/types.d.ts +8 -5
- package/dist/types/plugins/experience-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/experience-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/history-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/history-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-browser-storage/plugin.d.ts +1 -1
- package/dist/types/plugins/responses-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-indexeddb/plugin.d.ts +1 -1
- package/dist/types/plugins/single-page-app/plugin.d.ts +1 -1
- package/dist/types/plugins/single-page-app/plugin.d.ts.map +1 -1
- package/dist/types/plugins/web-components/plugin.d.ts +2 -1
- package/dist/types/plugins/window-object/plugin.d.ts +2 -2
- package/dist/types/preview-channel.d.ts +5 -1
- package/dist/types/preview-receiver/index.d.ts +18 -1
- package/dist/types/preview-receiver/protocol.d.ts +10 -1
- package/dist/types/server/index.d.ts +3 -3
- package/dist/types/server/serialize.d.ts +2 -2
- package/dist/types/utils/dom.d.ts +2 -1
- package/dist/types/utils/events.d.ts +6 -6
- package/dist/types/utils/events.d.ts.map +1 -1
- package/dist/types/utils/global-obj.d.ts +1 -1
- package/dist/types/utils/global-obj.d.ts.map +1 -1
- package/dist/types/utils/id.d.ts.map +1 -1
- package/dist/types/utils/logger.d.ts +2 -1
- package/dist/types/utils/logger.d.ts.map +1 -1
- package/dist/types/utils/node-tree/get-next-node-from-action.d.ts +2 -1
- package/dist/types/utils/node-tree/get-next-node-from-condition.d.ts +2 -1
- package/dist/types/utils/node-tree/get-next-node.d.ts +1 -1
- package/dist/types/utils/node-tree/get-next-node.d.ts.map +1 -1
- package/dist/types/utils/node-tree/get-origin-node.d.ts +1 -1
- package/dist/types/utils/node-tree/get-origin-node.d.ts.map +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-leaf.d.ts.map +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts +1 -1
- package/dist/types/utils/node-tree/min-max-steps-to-origin.d.ts.map +1 -1
- package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts +1 -1
- package/dist/types/utils/node-tree/next-reachable-step-nodes.d.ts.map +1 -1
- package/dist/types/utils/nodes.d.ts +1 -1
- package/dist/types/utils/picked-config.d.ts +2 -1
- package/dist/types/utils/rules-logic.d.ts +6 -2
- package/dist/types/utils/storage.d.ts +1 -1
- package/dist/types/utils/storage.d.ts.map +1 -1
- package/package.json +22 -23
- package/dist/api-CPy9G49C.js +0 -40
- package/dist/api-Ce6xYI--.cjs +0 -1
- package/dist/docs/actions.mdx +0 -98
- package/dist/docs/api/authentication.md +0 -403
- package/dist/docs/chrome-extension.md +0 -68
- package/dist/docs/conditions.mdx +0 -72
- package/dist/docs/content/asset-library.mdx +0 -124
- package/dist/docs/content/pages.mdx +0 -82
- package/dist/docs/content/sections.mdx +0 -44
- package/dist/docs/delivery-api/index.md +0 -53
- package/dist/docs/guides/build-a-personalized-page.mdx +0 -161
- package/dist/docs/pickedql/index.md +0 -54
- package/dist/docs/projects.mdx +0 -44
- package/dist/docs/reports.mdx +0 -20
- package/dist/docs/sdk/actions.md +0 -82
- package/dist/docs/sdk/content-security-policy.md +0 -33
- package/dist/docs/sdk/experiences.md +0 -64
- package/dist/docs/sdk/feature-allocation.md +0 -161
- package/dist/docs/sdk/index.md +0 -98
- package/dist/docs/sdk/plugins/dynamic-experiences.md +0 -84
- package/dist/docs/sdk/plugins/index.md +0 -9
- package/dist/docs/sdk/predicates.md +0 -128
- package/dist/docs/sdk/ssr/index.md +0 -79
- package/dist/docs/sdk/steps.md +0 -479
- package/dist/docs/steps.mdx +0 -53
- package/dist/docs/workspaces.mdx +0 -133
- package/dist/experience-instance-DurC3pE-.cjs +0 -1
- package/dist/plugin-DHn11gn6.cjs +0 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to render an unpublished preview
|
|
7
|
+
|
|
8
|
+
This guide shows you how to render draft content on your server. Your server cannot read
|
|
9
|
+
the query string of the browser, so you must pass the token yourself.
|
|
10
|
+
|
|
11
|
+
## 1. Get the token
|
|
12
|
+
|
|
13
|
+
The management API holds the preview token of a workspace in the `previewToken` field. The
|
|
14
|
+
preview action in the dashboard also makes one for you.
|
|
15
|
+
|
|
16
|
+
## 2. Read the config with the token
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
curl "https://api.picked.app/api/v1/projects/proj_123?preview_token=<token>"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
A preview response returns `versionId: null`. An invalid token returns `401`.
|
|
23
|
+
|
|
24
|
+
## 3. Pass the same token to `serialize()`
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const snapshot = await serialize(client, {
|
|
28
|
+
projectConfig,
|
|
29
|
+
attributes: { location: { pathname: '/promo' } },
|
|
30
|
+
previewToken,
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use the same token for both requests. The configuration and the content then come from the same
|
|
35
|
+
preview.
|
|
36
|
+
|
|
37
|
+
## Related
|
|
38
|
+
|
|
39
|
+
- [About the two APIs](/explanation/the-two-apis) — how a preview stays out of the search
|
|
40
|
+
index.
|
|
41
|
+
- [Preview, cache, and crawlers](/reference/delivery-api/preview-caching-crawling).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to reuse a section
|
|
7
|
+
|
|
8
|
+
This guide shows you how to define a common structure one time and use it in more than
|
|
9
|
+
one layout.
|
|
10
|
+
|
|
11
|
+
## Create a section
|
|
12
|
+
|
|
13
|
+
1. Open your workspace.
|
|
14
|
+
2. Click **Pages** in the sidebar, then **Sections**.
|
|
15
|
+
3. Click **New block**.
|
|
16
|
+
4. Enter a **Name** and a **Key**.
|
|
17
|
+
5. Add your fields.
|
|
18
|
+
6. Click **Create block**.
|
|
19
|
+
|
|
20
|
+
## Use it in a layout
|
|
21
|
+
|
|
22
|
+
1. Open a page layout or a step layout.
|
|
23
|
+
2. Add a **Section** field.
|
|
24
|
+
3. Point that field at your section.
|
|
25
|
+
|
|
26
|
+
The layout now carries the fields of the section at every place where that field appears.
|
|
27
|
+
|
|
28
|
+
:::warning
|
|
29
|
+
An edit to a section does not migrate the entries that use it. Check each affected content
|
|
30
|
+
entry again, and correct every field error before you publish.
|
|
31
|
+
:::
|
|
32
|
+
|
|
33
|
+
## Related
|
|
34
|
+
|
|
35
|
+
- [About the content model](/explanation/the-content-model) — why a schema change does not
|
|
36
|
+
migrate your data.
|
|
37
|
+
- [Sections](/reference/dashboard/content/sections) — the reference.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to reuse a step, a field, or an action
|
|
7
|
+
|
|
8
|
+
This guide shows you how to use one step, field, action, or condition in more than one
|
|
9
|
+
flow. All four belong to the project, and not to the experience that you made them in.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. Open the flow builder for the second experience.
|
|
14
|
+
2. Find the step, the action, or the condition in the library on the left.
|
|
15
|
+
3. Drag it onto the canvas, or onto a step node for a field.
|
|
16
|
+
|
|
17
|
+
The same one now appears in both flows.
|
|
18
|
+
|
|
19
|
+
:::warning
|
|
20
|
+
A change to the options applies at every place that uses it. Check the other flows before
|
|
21
|
+
you change a shared step, field, action, or condition.
|
|
22
|
+
:::
|
|
23
|
+
|
|
24
|
+
## Related
|
|
25
|
+
|
|
26
|
+
- [Steps](/reference/dashboard/steps) — the step options.
|
|
27
|
+
- [Fields](/reference/dashboard/fields) — the field options.
|
|
28
|
+
- [Actions](/reference/dashboard/actions) — the action options.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to roll back a published version
|
|
7
|
+
|
|
8
|
+
This guide shows you how to make an earlier published version live again. Your current
|
|
9
|
+
draft does not change.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. Open **Publish history** from the project sidebar.
|
|
14
|
+
2. Find the version that you want to restore.
|
|
15
|
+
3. Click **Restore**.
|
|
16
|
+
4. Read the diff preview. It compares the current live version against the one that you
|
|
17
|
+
chose.
|
|
18
|
+
5. Click **Restore this version** to confirm.
|
|
19
|
+
|
|
20
|
+
The restored version is live immediately.
|
|
21
|
+
|
|
22
|
+
## Related
|
|
23
|
+
|
|
24
|
+
- [About workspaces and versions](/explanation/workspaces-and-versions) — why a roll-back
|
|
25
|
+
changes no data.
|
|
26
|
+
- [Publish history](/reference/dashboard/workspace-versioning) — the version list.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to run an A/B test
|
|
7
|
+
|
|
8
|
+
This guide shows you how to split your traffic with a feature flag and read the result in
|
|
9
|
+
your application.
|
|
10
|
+
|
|
11
|
+
Read [About feature flag allocation](/explanation/feature-flag-allocation) first. The
|
|
12
|
+
point at which you read the feature flag decides when a visitor enters the test.
|
|
13
|
+
|
|
14
|
+
## 1. Create the feature flag
|
|
15
|
+
|
|
16
|
+
1. Open your project.
|
|
17
|
+
2. Click **Feature flags** in the sidebar.
|
|
18
|
+
3. Click **New feature flag**.
|
|
19
|
+
4. Enter a name.
|
|
20
|
+
5. Click **Create feature flag**.
|
|
21
|
+
|
|
22
|
+
You cannot change the key after you create the feature flag.
|
|
23
|
+
|
|
24
|
+
## 2. Set the allocation
|
|
25
|
+
|
|
26
|
+
Open the detail page of the feature flag and set these controls:
|
|
27
|
+
|
|
28
|
+
1. Turn **Enabled** on. A feature flag that is off allocates nobody.
|
|
29
|
+
2. Set **Weight** to the percentage of visitors that you want in the test.
|
|
30
|
+
3. Enter a **Description** that says what the feature flag tests.
|
|
31
|
+
|
|
32
|
+
## 3. Read the feature flag in your application
|
|
33
|
+
|
|
34
|
+
See [How to read a feature flag](/how-to/read-a-feature-flag). Read it at the point where
|
|
35
|
+
the decision matters, and not at startup.
|
|
36
|
+
|
|
37
|
+
## 4. Change the weight later
|
|
38
|
+
|
|
39
|
+
1. Change **Weight** on the detail page.
|
|
40
|
+
2. Increment the version.
|
|
41
|
+
|
|
42
|
+
The new weight then applies to a visitor with a cached decision. Leave the version as it
|
|
43
|
+
is to apply the new weight only to a visitor who is not allocated yet.
|
|
44
|
+
|
|
45
|
+
## Related
|
|
46
|
+
|
|
47
|
+
- [About feature flag allocation](/explanation/feature-flag-allocation) — why two feature
|
|
48
|
+
flags at the same weight give four groups, not two.
|
|
49
|
+
- [Feature flags](/reference/dashboard/features) — the controls.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to run code on a form submission
|
|
7
|
+
|
|
8
|
+
This guide shows you how to act on a step submission. Pass your callbacks to `useStep()`
|
|
9
|
+
in React, or to `getStep()` in Svelte.
|
|
10
|
+
|
|
11
|
+
## What the SDK does on submit
|
|
12
|
+
|
|
13
|
+
1. It collects the field values from the HTML form.
|
|
14
|
+
2. It runs each `onBeforeSubmit` callback, one after another.
|
|
15
|
+
3. It checks each required field and each validation rule.
|
|
16
|
+
4. It commits the responses to the experience state.
|
|
17
|
+
5. It fires `form_submit_success` or `form_submit_error` on the event bus.
|
|
18
|
+
6. It runs your `onSuccess` or `onError` callback.
|
|
19
|
+
7. It moves to the next step.
|
|
20
|
+
|
|
21
|
+
## Change the data before it is stored
|
|
22
|
+
|
|
23
|
+
:::code-group
|
|
24
|
+
|
|
25
|
+
```tsx [React]
|
|
26
|
+
const { form } = useStep({
|
|
27
|
+
onBeforeSubmit: async (data) => ({
|
|
28
|
+
...data,
|
|
29
|
+
'email-address': String(data['email-address']).toLowerCase(),
|
|
30
|
+
}),
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```svelte [Svelte]
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
const { form } = getStep({
|
|
37
|
+
onBeforeSubmit: async (data) => ({
|
|
38
|
+
...data,
|
|
39
|
+
'email-address': String(data['email-address']).toLowerCase(),
|
|
40
|
+
}),
|
|
41
|
+
})
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
:::
|
|
46
|
+
|
|
47
|
+
:::warning
|
|
48
|
+
An `onBeforeSubmit` callback that throws aborts the submit before the SDK commits
|
|
49
|
+
anything, and the step does not advance. Use it for a transform and for a check. Put a
|
|
50
|
+
side effect, such as an analytics call, in `onSuccess`.
|
|
51
|
+
:::
|
|
52
|
+
|
|
53
|
+
## Act on the outcome
|
|
54
|
+
|
|
55
|
+
:::code-group
|
|
56
|
+
|
|
57
|
+
```tsx [React]
|
|
58
|
+
const { form } = useStep({
|
|
59
|
+
onSuccess: ({ data }) => analytics.track('lead_captured', data),
|
|
60
|
+
onError: (errors) => console.error('Validation errors:', errors),
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```svelte [Svelte]
|
|
65
|
+
<script lang="ts">
|
|
66
|
+
const { form } = getStep({
|
|
67
|
+
onSuccess: ({ data }) => analytics.track('lead_captured', data),
|
|
68
|
+
onError: (errors) => console.error('Validation errors:', errors),
|
|
69
|
+
})
|
|
70
|
+
</script>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
:::
|
|
74
|
+
|
|
75
|
+
A callback belongs to the component that passes it, and the SDK releases it when that
|
|
76
|
+
component unmounts. An `onSuccess` callback that throws cannot stop the flow. The SDK
|
|
77
|
+
reports the failure as `form_submit_callback_error`.
|
|
78
|
+
|
|
79
|
+
## Choose the next step yourself
|
|
80
|
+
|
|
81
|
+
Call `stopNavigation()` inside `onSuccess`, then move where you want:
|
|
82
|
+
|
|
83
|
+
:::code-group
|
|
84
|
+
|
|
85
|
+
```tsx [React]
|
|
86
|
+
const experience = useExperience()
|
|
87
|
+
|
|
88
|
+
const { form } = useStep({
|
|
89
|
+
onSuccess: ({ data, stopNavigation }) => {
|
|
90
|
+
stopNavigation()
|
|
91
|
+
if (data['plan-type'] === 'enterprise') {
|
|
92
|
+
experience.goToStepById('enterprise-contact')
|
|
93
|
+
} else {
|
|
94
|
+
experience.goToNextStep()
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```svelte [Svelte]
|
|
101
|
+
<script lang="ts">
|
|
102
|
+
const experience = getExperience()
|
|
103
|
+
|
|
104
|
+
const { form } = getStep({
|
|
105
|
+
onSuccess: ({ data, stopNavigation }) => {
|
|
106
|
+
stopNavigation()
|
|
107
|
+
if (data['plan-type'] === 'enterprise') {
|
|
108
|
+
experience.goToStepById('enterprise-contact')
|
|
109
|
+
} else {
|
|
110
|
+
experience.goToNextStep()
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
</script>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
:::
|
|
118
|
+
|
|
119
|
+
`goToStepById()` takes a step `id`. `goToStepByKey()` takes the key of the step. When no
|
|
120
|
+
step matches, the call does nothing.
|
|
121
|
+
|
|
122
|
+
## Related
|
|
123
|
+
|
|
124
|
+
- [How to render a step form](/how-to/render-a-step-form).
|
|
125
|
+
- [SDK steps](/reference/sdk/steps) — the `form` object.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to server-render a page
|
|
7
|
+
|
|
8
|
+
This guide shows you how to render a Picked Page on your server and hydrate it in the
|
|
9
|
+
browser. You need a published Page and a server that renders React or Svelte.
|
|
10
|
+
|
|
11
|
+
## 1. Make the snapshot
|
|
12
|
+
|
|
13
|
+
Create one client for each request, then call `serialize()`:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createClient } from '@picked/client'
|
|
17
|
+
import { serialize } from '@picked/client/server'
|
|
18
|
+
|
|
19
|
+
const client = createClient(options)
|
|
20
|
+
|
|
21
|
+
const snapshot = await serialize(client, {
|
|
22
|
+
projectConfig,
|
|
23
|
+
attributes: { location: { pathname: url.pathname } },
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
:::warning
|
|
28
|
+
Give `serialize()` one client for each request. It does not support two calls at the same
|
|
29
|
+
time on one client.
|
|
30
|
+
:::
|
|
31
|
+
|
|
32
|
+
## 2. Embed the snapshot
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
const html = `
|
|
36
|
+
<div id="root">${appHtml}</div>
|
|
37
|
+
<script>window.__PICKED__ = ${JSON.stringify(snapshot)}</script>
|
|
38
|
+
`
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 3. Seed the client and render the slot
|
|
42
|
+
|
|
43
|
+
:::code-group
|
|
44
|
+
|
|
45
|
+
```tsx [React]
|
|
46
|
+
import { PickedProvider, ExperienceSlot } from '@picked/react'
|
|
47
|
+
|
|
48
|
+
export function Page({ snapshot }) {
|
|
49
|
+
return (
|
|
50
|
+
<PickedProvider options={options} snapshot={snapshot}>
|
|
51
|
+
<ExperienceSlot name="page" />
|
|
52
|
+
</PickedProvider>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```svelte [Svelte]
|
|
58
|
+
<script lang="ts">
|
|
59
|
+
import { ExperienceSlot, setPickedContext } from '@picked/svelte'
|
|
60
|
+
|
|
61
|
+
const { snapshot } = $props()
|
|
62
|
+
setPickedContext({ options, snapshot })
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<ExperienceSlot name="page" />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
:::
|
|
69
|
+
|
|
70
|
+
The provider seeds the client at once, so the server HTML and the first paint match.
|
|
71
|
+
|
|
72
|
+
## 4. Point the experience at the slot
|
|
73
|
+
|
|
74
|
+
Set the target selector of your Page experience to `.picked-slot-page`, then publish.
|
|
75
|
+
|
|
76
|
+
## Related
|
|
77
|
+
|
|
78
|
+
- [About server-side rendering](/explanation/why-server-side-rendering) — why the
|
|
79
|
+
snapshot carries condition results.
|
|
80
|
+
- [Server snapshot](/reference/sdk/ssr/serialize) — every parameter.
|
|
81
|
+
- [Your first server-rendered page](/tutorials/first-server-rendered-page).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to set a Content Security Policy
|
|
7
|
+
|
|
8
|
+
This guide shows you how to let the Picked SDK load its resources under a CSP.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
Add the Picked domains to your policy.
|
|
13
|
+
|
|
14
|
+
As an HTTP header:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Content-Security-Policy: connect-src 'self' https://api.picked.app https://evnts.pckd.dev; img-src 'self' https://img.picked-content.com;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or as a `<meta>` tag:
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<meta
|
|
24
|
+
http-equiv="Content-Security-Policy"
|
|
25
|
+
content="connect-src 'self' https://api.picked.app https://evnts.pckd.dev; img-src 'self' https://img.picked-content.com;"
|
|
26
|
+
/>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Related
|
|
30
|
+
|
|
31
|
+
- [Content Security Policy](/reference/sdk/content-security-policy) — what each domain is
|
|
32
|
+
for.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to set up MFA
|
|
7
|
+
|
|
8
|
+
This guide shows you how to add a second factor to an account. Every MFA mutation needs
|
|
9
|
+
authentication.
|
|
10
|
+
|
|
11
|
+
## 1. Add a factor
|
|
12
|
+
|
|
13
|
+
```graphql
|
|
14
|
+
mutation {
|
|
15
|
+
mfaFactorEnroll(input: {
|
|
16
|
+
factorType: PHONE
|
|
17
|
+
phone: "+15551234567"
|
|
18
|
+
friendlyName: "My Phone"
|
|
19
|
+
}) {
|
|
20
|
+
factor { id factorType status }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
A new factor has the status `UNVERIFIED`.
|
|
26
|
+
|
|
27
|
+
## 2. Send a challenge
|
|
28
|
+
|
|
29
|
+
```graphql
|
|
30
|
+
mutation {
|
|
31
|
+
mfaChallengeSend(input: { factorId: "factor-id" }) {
|
|
32
|
+
challengeId
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This sends a code to the telephone number of the factor.
|
|
38
|
+
|
|
39
|
+
## 3. Verify the challenge
|
|
40
|
+
|
|
41
|
+
```graphql
|
|
42
|
+
mutation {
|
|
43
|
+
mfaChallengeVerify(input: {
|
|
44
|
+
factorId: "factor-id"
|
|
45
|
+
challengeId: "challenge-id"
|
|
46
|
+
code: "123456"
|
|
47
|
+
}) {
|
|
48
|
+
session { accessToken refreshToken aal }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The session moves to `AAL2`, and the factor status changes to `VERIFIED`.
|
|
54
|
+
|
|
55
|
+
## Remove a factor
|
|
56
|
+
|
|
57
|
+
```graphql
|
|
58
|
+
mutation {
|
|
59
|
+
mfaFactorUnenroll(input: { factorId: "factor-id" }) {
|
|
60
|
+
success
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Related
|
|
66
|
+
|
|
67
|
+
- [Authorization and scopes](/reference/management-api/authorization-and-scopes) — which
|
|
68
|
+
operations need `AAL2`.
|
|
69
|
+
- [Authentication](/reference/management-api/authentication) — every mutation.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
import { Screenshot } from '../../../components/Screenshot'
|
|
7
|
+
|
|
8
|
+
# How to upload and use an asset
|
|
9
|
+
|
|
10
|
+
This guide shows you how to put a file in the asset library and point at it from your
|
|
11
|
+
content.
|
|
12
|
+
|
|
13
|
+
## 1. Add the field to a layout
|
|
14
|
+
|
|
15
|
+
Two field types point at the library:
|
|
16
|
+
|
|
17
|
+
- An **Asset** field takes an image, a video, or a document.
|
|
18
|
+
- An **HTML** field takes a `.html` file.
|
|
19
|
+
|
|
20
|
+
Add one of them to a page layout or a step layout.
|
|
21
|
+
|
|
22
|
+
## 2. Choose a file
|
|
23
|
+
|
|
24
|
+
1. Open the content entry.
|
|
25
|
+
2. Click **Choose file** on the asset field. On a field that holds a value already, click
|
|
26
|
+
**Replace**.
|
|
27
|
+
3. Open the **Upload** tab to add a new file.
|
|
28
|
+
4. Open the **Library** tab to use a file that is there already.
|
|
29
|
+
5. Choose your asset.
|
|
30
|
+
|
|
31
|
+
<Screenshot
|
|
32
|
+
route="/{orgSlug}/{projectId}/design-systems/new"
|
|
33
|
+
wait='button:has-text("Upload file")'
|
|
34
|
+
click='button:has-text("Upload file")'
|
|
35
|
+
selector="[role=dialog]"
|
|
36
|
+
alt="The asset picker modal on the Picked dashboard, with the Library tab, its search box, its type filter, and its Sort menu"
|
|
37
|
+
renderWidth={1072}
|
|
38
|
+
renderHeight={790}
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<Screenshot
|
|
42
|
+
route="/{orgSlug}/{projectId}/design-systems/new"
|
|
43
|
+
wait='button:has-text("Upload file")'
|
|
44
|
+
click='button:has-text("Upload file")'
|
|
45
|
+
selector="[role=dialog]"
|
|
46
|
+
alt="The asset picker modal on the Picked dashboard, with the Library tab, its search box, its type filter, and its Sort menu"
|
|
47
|
+
renderWidth={1072}
|
|
48
|
+
renderHeight={790}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
The detail panel on the right shows the kind, the dimensions, the size, and the upload
|
|
52
|
+
date.
|
|
53
|
+
|
|
54
|
+
## 3. Find an asset in a large library
|
|
55
|
+
|
|
56
|
+
The **Library** tab has three controls, and the server applies each one across the whole
|
|
57
|
+
library:
|
|
58
|
+
|
|
59
|
+
- **Search by name** narrows the list by file name while you type.
|
|
60
|
+
- The type filter narrows the list to one media type. **All types** is the default.
|
|
61
|
+
- **Sort** orders the list by **Name**, **Date created**, or **Date updated**.
|
|
62
|
+
|
|
63
|
+
Click **Load more** at the bottom to read the next page of results.
|
|
64
|
+
|
|
65
|
+
:::tip
|
|
66
|
+
Each row has an **Open in new tab** control. Use it to check an asset again before you
|
|
67
|
+
choose it. Your choice in the picker stays as it is.
|
|
68
|
+
:::
|
|
69
|
+
|
|
70
|
+
## Related
|
|
71
|
+
|
|
72
|
+
- [Asset library](/reference/dashboard/content/asset-library) — the limits, the file
|
|
73
|
+
types, and how Picked serves an uploaded file.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to validate a field
|
|
7
|
+
|
|
8
|
+
This guide shows you how to check what a visitor enters. Write the rule in JSON Logic. The
|
|
9
|
+
`$input` variable holds the current value of the field.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
Add a `validation` rule to `createField()`:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createField } from '@picked/client/plugins/create-experience'
|
|
17
|
+
|
|
18
|
+
const quantityField = createField({
|
|
19
|
+
id: 'quantity',
|
|
20
|
+
name: 'quantity',
|
|
21
|
+
type: 'number',
|
|
22
|
+
label: 'Quantity',
|
|
23
|
+
multiple: false,
|
|
24
|
+
required: true,
|
|
25
|
+
validation: {
|
|
26
|
+
catch: {
|
|
27
|
+
or: [
|
|
28
|
+
{
|
|
29
|
+
and: [
|
|
30
|
+
{ '>': [{ var: '$input' }, 0] },
|
|
31
|
+
{ '<=': [{ var: '$input' }, 999] },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{ throw: 'Please enter a value between 1 and 999' },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
:::tip
|
|
42
|
+
The `catch` and `throw` pattern checks that the condition gives `true`. When it does not,
|
|
43
|
+
the SDK shows the `throw` message to your visitor.
|
|
44
|
+
:::
|
|
45
|
+
|
|
46
|
+
The SDK runs the rule on blur and on submit.
|
|
47
|
+
|
|
48
|
+
## Related
|
|
49
|
+
|
|
50
|
+
- [SDK fields](/reference/sdk/fields) — every field option.
|
|
51
|
+
- [About conditions and the logic engine](/explanation/conditions-and-the-logic-engine) —
|
|
52
|
+
the rule format.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to write a condition
|
|
7
|
+
|
|
8
|
+
This guide shows you how to build a rule in the visual condition builder. The builder
|
|
9
|
+
appears on a condition node, on an experience condition, on an initialize-open condition,
|
|
10
|
+
and on a field option.
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. Choose the node or the field that takes the condition.
|
|
15
|
+
2. Find the **Logic** section in the inspector.
|
|
16
|
+
3. Choose a data source: **Responses**, **Actions**, **Feature flags**, **Conditions**,
|
|
17
|
+
or **Window Location**.
|
|
18
|
+
4. Choose an operator. The choices include equals, not equals, greater than, and
|
|
19
|
+
contains.
|
|
20
|
+
5. Enter a value.
|
|
21
|
+
|
|
22
|
+
## Join more than one rule
|
|
23
|
+
|
|
24
|
+
1. Create an AND group or an OR group.
|
|
25
|
+
2. Add your rules inside it.
|
|
26
|
+
|
|
27
|
+
With **AND** every rule must match. With **OR** one rule must match. Put a group inside
|
|
28
|
+
another group to build a larger rule.
|
|
29
|
+
|
|
30
|
+
## Example
|
|
31
|
+
|
|
32
|
+
To show a step only to a visitor who is on one page and who accepted marketing:
|
|
33
|
+
|
|
34
|
+
1. Create an AND group.
|
|
35
|
+
2. Add a rule: **Window Location → pathname** equals `/pricing`.
|
|
36
|
+
3. Add a rule: **Responses → `marketing_opt_in`** equals `true`.
|
|
37
|
+
|
|
38
|
+
The group matches only when both rules are true.
|
|
39
|
+
|
|
40
|
+
## Related
|
|
41
|
+
|
|
42
|
+
- [About conditions and the logic engine](/explanation/conditions-and-the-logic-engine) —
|
|
43
|
+
what a rule can read.
|
|
44
|
+
- [Condition builder](/reference/dashboard/flow-builder/condition-builder) — the rule
|
|
45
|
+
structure.
|