@wix/agent-skills 1.32.0 → 1.33.1
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/README.md +21 -0
- package/package.json +2 -2
- package/skills/wix-app/references/DASHBOARD_PAGE.md +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +40 -41
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +11 -6
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +2 -3
- package/skills/wix-app/references/editor-react-component/DISABLED-INVALID.md +31 -0
- package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +14 -1
- package/skills/wix-app/references/editor-react-component/PARTS.md +32 -3
- package/skills/wix-app/references/editor-react-component/SSR.md +6 -3
- package/skills/wix-headless-kit/install/pins.json +1 -1
- package/skills/wix-manage/SKILL.md +25 -7
- package/skills/wix-manage/references/automations/actions.md +11 -11
- package/skills/wix-manage/references/automations/activation-status.md +6 -9
- package/skills/wix-manage/references/automations/api-catalog.md +48 -32
- package/skills/wix-manage/references/automations/automation-model.md +97 -22
- package/skills/wix-manage/references/automations/bracket-expressions.md +7 -7
- package/skills/wix-manage/references/automations/build-and-manage-automations.md +140 -309
- package/skills/wix-manage/references/automations/conditions.md +187 -0
- package/skills/wix-manage/references/automations/schemas-and-scheduling.md +191 -0
- package/skills/wix-manage/references/automations/special-actions.md +210 -0
- package/skills/wix-manage/references/automations/triggers.md +47 -10
- package/skills/wix-manage/references/automations/validation-and-verification.md +320 -0
- package/skills/wix-manage/references/custom-apps/use-app-tools.md +124 -0
- package/skills/wix-manage/references/events/create-wix-event.md +136 -73
- package/skills/wix-manage/references/events/manage-event-guests.md +366 -0
- package/skills/wix-manage/references/events/manage-event-tickets.md +293 -0
- package/skills/wix-manage/references/events/manage-wix-events.md +313 -55
- package/skills/wix-manage/references/stores/update-product-with-options.md +58 -2
- package/skills/wix-manage/references/custom-apps/use-app-skills-and-app-tools.md +0 -208
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Automations Conditions"
|
|
3
|
+
description: "Configure visual and code conditions while preserving boolean intent, comparison boundaries and builder editability."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Stage scope:** this stage covers only what [Build and Manage Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/skills/build-and-manage-wix-automations) lists; follow the instruction attached to each guide marked "not yet published". "Stage" and "not yet published" describe how this skill is being rolled out; never mention them to the user. Explain a limit in product terms: what can't be done through the API here and where the user can do it instead (for example, in the automations builder).
|
|
7
|
+
|
|
8
|
+
# Conditions — Visual Conditions and Code Conditions
|
|
9
|
+
|
|
10
|
+
**TL;DR**
|
|
11
|
+
|
|
12
|
+
- `CONDITION` must be something the builder's condition panel can draw: exactly ONE group in `orExpressionGroups`, `operator` `"AND"` or `"OR"`, 1–20 `booleanExpressions`. Each expression is ONE function from §2 (optionally inside `not(…)` — only the negatable ones), with the field as `var()` and a **literal** value.
|
|
13
|
+
- The panel re-writes every expression the moment the user opens the step. A shape outside §2 is dropped, mis-read or silently changed — restructure (§4) or use `CODE_CONDITION` (§5).
|
|
14
|
+
- Entity fields hold ids: `stringContains(["<id>"];var("field"))`, never a display name.
|
|
15
|
+
- String literals are URI-encoded ([Automations Mapping Expressions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-mapping-expressions) §1); date literals may be encoded or not (§2).
|
|
16
|
+
- `truePostActionIds` / `falsePostActionIds`: at most ONE id each; branch children have this condition as their only parent ([Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model)).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Shape
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"id": "<uuid-1>",
|
|
25
|
+
"type": "CONDITION",
|
|
26
|
+
"namespace": "CONDITION-1",
|
|
27
|
+
"displayName": "VIP order?",
|
|
28
|
+
"conditionInfo": {
|
|
29
|
+
"orExpressionGroups": [
|
|
30
|
+
{
|
|
31
|
+
"operator": "AND",
|
|
32
|
+
"booleanExpressions": [
|
|
33
|
+
"{{stringEq(var(\"customer_tier\");\"premium\")}}",
|
|
34
|
+
"{{numberGt(var(\"order_count\");5)}}"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"truePostActionIds": ["<uuid-2>"],
|
|
39
|
+
"falsePostActionIds": []
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
MUST:
|
|
45
|
+
|
|
46
|
+
- Exactly one entry in `orExpressionGroups` with an explicit `operator` (`"AND"` = all hold, `"OR"` = any). Several groups are flattened into one on first open and their logic is lost; a missing `operator` is saved as `OR`.
|
|
47
|
+
- 1–20 expressions — an empty group makes the builder loader hang; the panel allows at most 20 (more than 20 → a code condition).
|
|
48
|
+
- One comparison per entry; never combine with `and(…)`/`or(…)` inside an entry.
|
|
49
|
+
- Every `var()` path exists in THIS step's aggregated schema (trigger + ancestors; [Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) §4). A path the panel can't find marks the step invalid and its canvas label stays empty.
|
|
50
|
+
- No whitespace outside string literals (`{{ stringEq(…` makes the function unreadable).
|
|
51
|
+
- Only `conditionInfo` on the step; both branch arrays present; never both empty (dead end). Set a short `displayName`.
|
|
52
|
+
|
|
53
|
+
## 2. Operator table (the only shapes that round-trip)
|
|
54
|
+
|
|
55
|
+
The builder ships two panel layouts (a dropdown and a newer property picker) and users get either. Everything below survives both; when a user edits a condition, the property picker may rewrite `arraySome/arrayEvery` into `arrayIncludes*` forms — that is outside your control; never write `arrayIncludes*` yourself (see the list below).
|
|
56
|
+
|
|
57
|
+
Classify the field from its schema, in this order: `format: "date"` → date · `format: "date-time"` → date-time · `type: "string"` + `format: "number"` → number · has `enum` (or string/number `oneOf` of consts) → enum / number-enum · `integer` → number · string with `itemsSelectionConfiguration` (`providerKey` or `tag`) → item selection · otherwise its `type`.
|
|
58
|
+
|
|
59
|
+
| Field type | Operator (UI) | Expression (`¬` = may wrap in `not(…)`) |
|
|
60
|
+
| ------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
+
| string | is / is not ¬ | `stringEq(var("f");"v")` |
|
|
62
|
+
| | contains / doesn't contain ¬ | `stringHasSubString(var("f");"v")` |
|
|
63
|
+
| number | equals / not equals ¬ | `numberEq(var("f");5)` |
|
|
64
|
+
| | greater / less than | `numberGt(var("f");5)` · `numberLt(var("f");5)` |
|
|
65
|
+
| date | is / is not ¬ | `dateEq(var("f");"2026-12-25")` |
|
|
66
|
+
| | before / after | `dateBefore(…)` · `dateAfter(…)` |
|
|
67
|
+
| date-time | is / is not ¬ | `dateTimeEq(var("f");"2026-12-25T10:00:00Z")` |
|
|
68
|
+
| | before / after | `dateTimeBefore(…)` · `dateTimeAfter(…)` |
|
|
69
|
+
| boolean | is true / is false | `boolEq(var("f");true)` · `boolEq(var("f");false)` |
|
|
70
|
+
| enum (string) | is / is not ¬ | `stringEq(var("f");"PAID")` |
|
|
71
|
+
| | is one of / is not one of ¬ | `stringContains(["PAID","PENDING"];var("f"))` — list FIRST |
|
|
72
|
+
| number enum | is ¬ / is one of ¬ | `numberEq(var("f");2)` · `numberContains([1,2];var("f"))` |
|
|
73
|
+
| item selection (id) | is one of / is not one of ¬ | `stringContains(["<id-1>","<id-2>"];var("f"))` |
|
|
74
|
+
| array of primitives | includes any of / doesn't ¬ | `arraySome(var("f");contains(["a","b"];$_))` |
|
|
75
|
+
| | includes only | `arrayEvery(var("f");contains(["a","b"];$_))` |
|
|
76
|
+
| | includes all of | one `arraySome(var("f");contains(["a"];$_))` entry per value in an `AND` group (see below) |
|
|
77
|
+
| array of objects | same as array of primitives | `var("f")` → `arrayMap(var("f");"<item.property>")` — ONLY the property named by the array's `defaultConditionedProperty` |
|
|
78
|
+
| any type | is empty / is not empty ¬ | `isEmpty(var("f"))` |
|
|
79
|
+
|
|
80
|
+
NEVER:
|
|
81
|
+
|
|
82
|
+
- `not(…)` around anything not marked ¬ (`numberGt`, `numberLt`, `date*Before/After`, `boolEq`, `arrayEvery`) — the panel loses the operator. Negating `boolEq(…;true)` can use `boolEq(…;false)`. NEVER negate a strict comparison by swapping `Gt`/`Lt` or Before/After: `not(x > N)` means `x <= N`, including equality. Preserve that boundary with comparison + equality in a single `OR` group when the full logic fits (§4), or use CODE_CONDITION. For example, "not more than 100" includes 100; "not before a date" includes that date. Do not invent an inclusive visual operator.
|
|
83
|
+
- `arrayIncludesAnyOf/AllOf/Only(…)` — the dropdown layout reads all three as "includes only" and re-saves them with that meaning.
|
|
84
|
+
- The collection-first `arrayEvery(["a","b"];contains(var("f");$_))` — the property-picker layout cannot read it. For "includes all of" use one `arraySome` entry per value with `operator: "AND"`; if the group must be `OR`, chain conditions (§4) or use code.
|
|
85
|
+
- `arrayMap` to any property other than `defaultConditionedProperty` (or on an array without one) — the dropdown layout re-points it or fails to save. The path is relative to the item (`"service.id"`) and must exist in the item schema; otherwise → `CODE_CONDITION`.
|
|
86
|
+
- Anything around the field (`lower(var("f"))`, `month(var("d"))`), `var()` or `now()` as the value, two fields compared, `eq`/`gt`/`contains` at top level, `isEmptyString`, `startsWith`, `if`, `and`/`or`. These are valid formulas but not drawable.
|
|
87
|
+
|
|
88
|
+
Literal rules (the value inputs reject or rewrite others):
|
|
89
|
+
|
|
90
|
+
| Value | Rule |
|
|
91
|
+
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| string | ≤ 2500 chars; must not contain `"` or `'` (the input flags it) — test a quote-free part with `stringHasSubString`, or use code |
|
|
93
|
+
| number | Unquoted; between −100000000 and 100000000; no decimals below 1 (`0.5`, `.5`, `-0.5`), leading zeros or exponents — they don't parse → use code |
|
|
94
|
+
| boolean | `true`/`false` unquoted (a quoted `"yes"` is re-saved as a broken bare word) |
|
|
95
|
+
| enum | Exactly one of the schema's `enum`/`const` values, same case |
|
|
96
|
+
| date | `"YYYY-MM-DD"`; date-time: full ISO with zone (`"2026-12-25T10:00:00Z"`); never relative (`now()`) — relative windows → code or a set-variables boolean |
|
|
97
|
+
|
|
98
|
+
Date values and time zone: the panel writes a picked date/time as wall-clock time in the **site
|
|
99
|
+
time zone** with its offset (a date → that day's 00:00 site time), and URI-encodes it like every
|
|
100
|
+
literal (`"2026-12-31T23%3A59%3A00%2B02%3A00"`). Its reader decodes first, so an encoded and an
|
|
101
|
+
unencoded ISO literal load the same (a `"YYYY-MM-DD"` literal is unchanged by encoding). Treat a
|
|
102
|
+
calendar date the user names as a site-time-zone date: "valid until Dec 31" → compare with the end
|
|
103
|
+
of that day in the site's zone (Site Properties `timeZone`, [Automations Schemas and Scheduling](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-schemas-and-scheduling) §4).
|
|
104
|
+
|
|
105
|
+
Semantics: `stringEq`/`stringHasSubString`/`stringContains` are case-insensitive. No ≥/≤: for integers `numberGt(var("f");N-1)`; otherwise `numberGt` + `numberEq` in an `OR` group (only if the group may be `OR`) or code. `$_` appears bare, only inside `contains(…)`.
|
|
106
|
+
|
|
107
|
+
## 3. Entity-id fields
|
|
108
|
+
|
|
109
|
+
A field with `itemsSelectionConfiguration` (service, form, product, label, coupon, location, pipeline stage, …) carries an **id or key**. A display name passes validation and silently never matches. Get ids from the vertical's public API or the user (Automations Entity and Provider Configuration (topic guide not yet published; consult the [Automations API reference](https://dev.wix.com/docs/api-reference/business-management/automations))). Ids are whatever that API returns — label keys look like `custom.vip-customers`, not uuids.
|
|
110
|
+
|
|
111
|
+
- Scalar field `service_id`: `{{stringContains(["<service-id>"];var("service_id"))}}`
|
|
112
|
+
- Property in an array of objects `lineItems[].rootCatalogItemId`: `{{arraySome(arrayMap(var("lineItems");"rootCatalogItemId");contains(["<product-id>"];$_))}}` — visual only when that property is the `defaultConditionedProperty`; else code: `(payload.lineItems || []).some(i => ids.includes(i.rootCatalogItemId))`
|
|
113
|
+
- The field is an array of ids `labelKeys.items`: `{{arraySome(var("labelKeys.items");contains(["<label-key>"];$_))}}`
|
|
114
|
+
|
|
115
|
+
Never synthesize `[0]`, `.0`, `firstItem` or a dotted path through an array. "The order contains product X" is a whole-array test — keep it one. If the user needs one specific item, clarify. If you cannot obtain the ids, the entity set is **unknown, not empty**: tell the user and ask which entity to use.
|
|
116
|
+
|
|
117
|
+
## 4. When the logic doesn't fit one group
|
|
118
|
+
|
|
119
|
+
- **`(A AND B) OR C` / mixed AND+OR / anything needing more than one group** → one `CODE_CONDITION` (§5) with exactly that logic — the default; it keeps a single true/false branch. Chaining two visual conditions (`A AND B` → false: condition `C`) is an alternative only when the true branch is one small step, since that step must then be duplicated (tree only — duplicate, never join; [Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model)). Never write several `orExpressionGroups`.
|
|
120
|
+
- **Starts / ends with** → No operator. "contains" (`stringHasSubString`) when close enough (say so), or code for the exact test
|
|
121
|
+
- **Two fields compared, computed values, relative dates, month/day of a date (birthdays), string length, decimals below 1, any §2 NEVER** → `CODE_CONDITION` (§5), or compute a boolean with a set-variables step ([Automations Delays Variables and Branches](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-delays-variables-and-branches)) and test it with `boolEq(var("setVariable.<key>");true)`
|
|
122
|
+
- **Optional field ("if phone is present it must contain 555")** → Two chained conditions (`not(isEmpty(…))`, then the test), or code
|
|
123
|
+
|
|
124
|
+
Prefer a visual `CONDITION` when it expresses the logic exactly — users can keep editing it. Don't force complex logic into it; if the user insists, explain what can't be drawn. Never say the logic is impossible when code can do it.
|
|
125
|
+
|
|
126
|
+
## 5. `CODE_CONDITION`
|
|
127
|
+
|
|
128
|
+
**Before constructing a calendar condition:** obtain the IANA time zone from current tool-returned site metadata for the exact target site, or read Site Properties ([Automations Schemas and Scheduling](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-schemas-and-scheduling) §4) if that metadata is absent. A zone from another site, documentation, memory or an example is not evidence. Validate the returned zone and replace `<SITE_TIME_ZONE>` before saving; a confirmed literal is expected in saved code.
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"id": "<uuid-3>",
|
|
133
|
+
"type": "CODE_CONDITION",
|
|
134
|
+
"namespace": "CONDITION-2",
|
|
135
|
+
"codeConditionInfo": {
|
|
136
|
+
"snippet": {
|
|
137
|
+
"language": "JAVASCRIPT",
|
|
138
|
+
"code": "/**\n * @param {Payload} payload - Automation payload with contact birthdate.\n * @returns {boolean} whether today is the contact's birthday at the site.\n */\nexport default function (payload) {\n const b = payload.contact?.birthdate;\n if (typeof b !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(b)) return false;\n const [y, m, d] = b.split('-').map(Number);\n const days = [31, y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n if (!y || !m || m > 12 || !d || d > days[m - 1]) return false;\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: '<SITE_TIME_ZONE>',\n calendar: 'gregory',\n numberingSystem: 'latn',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(new Date());\n const get = type => parts.find(part => part.type === type)?.value;\n return b.slice(5, 7) === get('month') && b.slice(8, 10) === get('day');\n } catch (e) {\n return false;\n }\n}"
|
|
139
|
+
},
|
|
140
|
+
"dynamicVariableExpressions": ["{{var(\"contact.birthdate\")}}"],
|
|
141
|
+
"truePostActionIds": ["<uuid-4>"],
|
|
142
|
+
"falsePostActionIds": []
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The birthday example requires a confirmed IANA site time zone ([Automations Schemas and Scheduling](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-schemas-and-scheduling) §4).
|
|
148
|
+
A birthdate is a date-only `YYYY-MM-DD`: reject impossible Gregorian dates (including
|
|
149
|
+
February 29 in a non-leap birth year and year `0000`), then compare its literal month/day
|
|
150
|
+
with today's calendar parts in that zone. Do not parse the birthdate as a UTC instant or use the runtime's local
|
|
151
|
+
`getMonth()` / `getDate()`. An unresolved or invalid zone blocks saving; the runtime guard is
|
|
152
|
+
only a fallback. A February 29 birthday matches February 29, not an invented non-leap-year date.
|
|
153
|
+
|
|
154
|
+
Minimal array check (the code reads only the declared array):
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
"displayName": "Check if order has 3+ items",
|
|
158
|
+
"codeConditionInfo": {
|
|
159
|
+
"snippet": { "language": "JAVASCRIPT",
|
|
160
|
+
"code": "/**\n * @param {Payload} payload\n * @returns {boolean}\n */\nexport default function (payload) {\n return (payload.lineItems || []).length >= 3;\n}\n" },
|
|
161
|
+
"dynamicVariableExpressions": ["{{var(\"lineItems\")}}"],
|
|
162
|
+
"truePostActionIds": ["<uuid-2>"], "falsePostActionIds": [] }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
MUST:
|
|
166
|
+
|
|
167
|
+
- `type: "CODE_CONDITION"` with only `codeConditionInfo`; `namespace` follows the condition pattern (`CONDITION-N`, unique).
|
|
168
|
+
- `language` is always `JAVASCRIPT`. The code default-exports a synchronous `function (payload)` with exactly one parameter, starts with the JSDoc block the builder's template has (`/** @param {Payload} payload … @returns {boolean} … */` — required), and returns an explicit boolean. No imports, no network calls. Whole code ≤ 1000 characters; newlines escaped in JSON.
|
|
169
|
+
- Guard every access (`?.`, `|| []`, `|| 0`, `typeof`, `try/catch` for deep paths); a missing field must not throw.
|
|
170
|
+
- Access — the same paths as the aggregated schema ([Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) §4): `payload.field`, `payload['field-with-hyphens']`, action outputs `payload['<namespace>'].field`, `payload.contact.email`, `payload.setVariable['<key>']`. JS may index arrays and use `.some/.every/.filter` and `Date` math; `new Date()` gives the current time for calendar checks.
|
|
171
|
+
- `dynamicVariableExpressions`: one `{{var("<path>")}}` for EVERY field the code reads — the runtime hydrates ONLY these, so an undeclared field is `undefined`. Arrays: declare the array itself (`payload.lineItems.some(…)` → `{{var("lineItems")}}`), never items or indexes; reading an object whole needs the object declared; if a path has no valid `var()`, declare its closest valid parent. Never `payload[computedKey]` or the whole `payload`. `[]` only when the code reads no field. Every path must exist in this node's aggregated schema.
|
|
172
|
+
- Use it only when the visual panel can't express the logic (array indexing, per-element math, string splitting, date parts, regex — §4).
|
|
173
|
+
- Test mentally against a true case, a false case and a payload with the fields missing.
|
|
174
|
+
|
|
175
|
+
Other special steps (delay, rate limit, set variables, split): [Automations Delays Variables and Branches](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-delays-variables-and-branches).
|
|
176
|
+
|
|
177
|
+
## 6. Pre-save checklist
|
|
178
|
+
|
|
179
|
+
- [ ] One group; explicit `AND`/`OR`; 1–20 expressions; no nested `and/or`; no stray whitespace.
|
|
180
|
+
- [ ] Each expression is a §2 row for the field's classified type; `not()` only on ¬ rows; no `arrayIncludes*`, no collection-first `arrayEvery`.
|
|
181
|
+
- [ ] Value literal obeys the literal rules (no quotes in strings, no `0.x` numbers, exact enum values, unquoted booleans).
|
|
182
|
+
- [ ] Every `var()` path exists in this step's aggregated schema; `arrayMap` only onto `defaultConditionedProperty`.
|
|
183
|
+
- [ ] Entity fields compared to ids (§3); no synthetic array paths.
|
|
184
|
+
- [ ] String literals URI-encoded; JSON quotes escaped.
|
|
185
|
+
- [ ] Branch arrays ≤1 id each, children single-parent, not both empty; `namespace` unique.
|
|
186
|
+
- [ ] Anything else → restructured (§4) or a `CODE_CONDITION` meeting §5.
|
|
187
|
+
- [ ] Validate Automation returns no errors for this step.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Automations Schemas and Scheduling"
|
|
3
|
+
description: "Configure dynamic schemas and scheduled automation triggers with correct timezone and calendar behavior."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Stage scope:** this stage covers only what [Build and Manage Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/skills/build-and-manage-wix-automations) lists; follow the instruction attached to each guide marked "not yet published". "Stage" and "not yet published" describe how this skill is being rolled out; never mention them to the user. Explain a limit in product terms: what can't be done through the API here and where the user can do it instead (for example, in the automations builder).
|
|
7
|
+
|
|
8
|
+
# Schemas & Scheduling — Aggregated Schema, Override Schemas, Scheduled and Future-Date Triggers
|
|
9
|
+
|
|
10
|
+
**TL;DR**
|
|
11
|
+
|
|
12
|
+
- Every step can reference only its **aggregated schema** (trigger payload + **ancestor** outputs + `setVariable.*` + identity enrichment) — build it yourself, per step ([Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) §4).
|
|
13
|
+
- Dynamic schemas: trigger payload grows with its filters; an action's inputs grow with `updateSchemaOnChange` fields; its outputs grow with its mapping. Fetch them — never invent fields.
|
|
14
|
+
- Override output schema is allowed ONLY on: triggers `wix_automations-webhook_trigger`, `wix_automations-custom_trigger`; actions `webhooks-action`, `wix_automations-llm_call`, `ai_custom_agent_bm-delegate_to_agent`. Wire fields: `trigger.overrideSchema`, `appDefinedInfo.overrideOutputSchema`.
|
|
15
|
+
- Scheduled trigger: `trigger.automationConfigMapping = { startDate, repetitions? { cronExpression, everyNthDay | everyNthWeek | everyNthMonth, endDate? } }`. No `repetitions` = one run. Use the exact site's IANA time zone from current tool-returned metadata, with Site Properties as fallback (§4). Create and Update support `automationConfigMapping`; include it in the trigger configuration.
|
|
16
|
+
- "N days before the event" = `trigger.scheduledEventOffset` (only for triggers with a `futureDate` field). "After" = a DELAY step ([Automations Delays Variables and Branches](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-delays-variables-and-branches)).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. The aggregated schema
|
|
21
|
+
|
|
22
|
+
Each step may read only its aggregated schema: the trigger payload (override, or static + dynamic merged) + outputs of its ancestor steps under their `namespace` + `setVariable.*` + identity enrichment — never a sibling branch, a descendant or a skipped step. Full recipe, identity-enrichment keys and resolution order: [Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) §4. Only its paths may appear in a step's `var("…")` expressions ([Automations Mapping Expressions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-mapping-expressions)).
|
|
23
|
+
|
|
24
|
+
## 2. Dynamic schemas — markers
|
|
25
|
+
|
|
26
|
+
| Marker | Meaning | Call |
|
|
27
|
+
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | -------------------------------------------------- |
|
|
28
|
+
| trigger `implementedMethods.getDynamicSchema: true` | Payload depends on filter selections (`reevaluateDynamicSchema` filters) | Get Trigger Dynamic Schema ([Automations Trigger Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-trigger-configuration) §5) |
|
|
29
|
+
| action input property `updateSchemaOnChange: true` | Setting it reveals more inputs | Get Action Dynamic Input Schema ([Automations Action Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-action-configuration) §4) |
|
|
30
|
+
| action output depends on mapping (e.g. an email template's placeholders) | Output fields revealed by configuration | Get Action Dynamic Output Schema ([Automations Action Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-action-configuration) §4) |
|
|
31
|
+
|
|
32
|
+
A field you need but cannot see is **unknown until its controlling selection is made** — say which selection reveals it; don't claim the data doesn't exist, and don't invent it. Form answers with no form filter chosen are such fields: use only static fields, or ask which form ([Automations Trigger Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-trigger-configuration) §5).
|
|
33
|
+
|
|
34
|
+
## 3. Override output schema
|
|
35
|
+
|
|
36
|
+
Replaces a component's schema so downstream steps get addressable fields. It exists only for components whose data comes from outside Wix and therefore have no schema of their own.
|
|
37
|
+
|
|
38
|
+
| Component | Wire field |
|
|
39
|
+
| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------- |
|
|
40
|
+
| Trigger `wix_automations-webhook_trigger`, `wix_automations-custom_trigger` (app `139ef4fa-c108-8f9a-c7be-d5f492a2c939`) | `configuration.trigger.overrideSchema` |
|
|
41
|
+
| Action `webhooks-action`, `wix_automations-llm_call`, `ai_custom_agent_bm-delegate_to_agent` | `actions[id].appDefinedInfo.overrideOutputSchema` |
|
|
42
|
+
|
|
43
|
+
NEVER add an override to any other trigger or action — including one with a dynamic output schema (that schema is real; fetch it). If an existing automation already carries an override on another component, leave it untouched.
|
|
44
|
+
|
|
45
|
+
Generate one only when a downstream step consumes the data ("then use…", "based on the result…"). Skip it when the component is the last step or its default schema suffices.
|
|
46
|
+
|
|
47
|
+
Schema requirements (both kinds):
|
|
48
|
+
|
|
49
|
+
- JSON Schema draft-07: `"$schema": "http://json-schema.org/draft-07/schema"`, root `"type": "object"`, `properties`, `required`; standard types only; give each property a `title`.
|
|
50
|
+
- Trigger overrides: one realistic `examples` entry per property.
|
|
51
|
+
- A trigger `overrideSchema` **replaces** the payload schema. An action `overrideOutputSchema` is **merged** over the catalog output schema (its `properties` win on a clash) — catalog output fields stay addressable. Get Automation returns overrides only with `fields: ["OVERRIDE_SCHEMA"]` — use it on read-back, or the override looks lost. For the webhook trigger keep the `webhookId` property in the override if the validator reports it missing. Webhook wiring (id, filter, config mapping) and the custom trigger's `hookId`: [Automations Trigger Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-trigger-configuration) §6.
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
56
|
+
"type": "object",
|
|
57
|
+
"properties": {
|
|
58
|
+
"orderId": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"title": "Order ID",
|
|
61
|
+
"examples": ["order_12345"]
|
|
62
|
+
},
|
|
63
|
+
"customerEmail": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"title": "Customer Email",
|
|
66
|
+
"examples": ["customer@example.com"]
|
|
67
|
+
},
|
|
68
|
+
"totalAmount": {
|
|
69
|
+
"type": "number",
|
|
70
|
+
"title": "Total Order Amount",
|
|
71
|
+
"examples": [89.97]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"required": ["orderId", "customerEmail", "totalAmount"]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Per-action rules:
|
|
79
|
+
|
|
80
|
+
- **LLM (`wix_automations-llm_call`)**: when a property is a decision among known outcomes that a later condition branches on, constrain it with `enum` (e.g. `"location": {"type": "string", "enum": ["inside", "outside"]}`) — the condition can only match values it can predict.
|
|
81
|
+
- **LLM / custom agent — input grounding**: the override describes the result, not the source data. Put the concrete, schema-verified `{{var("…")}}` values into the prompt/instructions input. "Analyze the order" without mapping the order fields is incomplete even if it validates.
|
|
82
|
+
- **Custom agent**: the schema must match the agent's task ("check loyalty eligibility" → `isEligible`, `discountPercentage`, `reason`, not generic `success`). Change the schema when the instructions change.
|
|
83
|
+
- **Webhook action**: model the called API's response (data / status / timestamp).
|
|
84
|
+
|
|
85
|
+
## 4. Scheduled trigger (`wix_automations-scheduled_trigger`, app `139ef4fa-c108-8f9a-c7be-d5f492a2c939`)
|
|
86
|
+
|
|
87
|
+
Timing lives in `configuration.trigger.automationConfigMapping`, supported by Create and Update and required by the builder. Read the trigger's `automationConfigSchema` from Get Trigger By App Id And Key and follow it if it differs. The scheduled trigger carries **no payload data** — only timing.
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
"trigger": {
|
|
91
|
+
"appId": "139ef4fa-c108-8f9a-c7be-d5f492a2c939",
|
|
92
|
+
"triggerKey": "wix_automations-scheduled_trigger",
|
|
93
|
+
"filters": [],
|
|
94
|
+
"automationConfigMapping": {
|
|
95
|
+
"startDate": "2026-10-05T09:00:00-04:00",
|
|
96
|
+
"repetitions": { "cronExpression": "0 9 * * 1", "everyNthWeek": 1,
|
|
97
|
+
"endDate": "2026-12-28T09:00:00-05:00" }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`startDate` (required): `YYYY-MM-DDTHH:mm:00±HH:MM` — seconds `00`, no milliseconds, the site time zone's offset on that date (`Z` only if the site is UTC). Its hour:minute is the time of every run; its date seeds weekly/monthly patterns. The builder reads the **wall-clock digits as site-local time and ignores the offset**, then re-writes the string: NEVER write the UTC instant (`…T13:00:00Z` for 9:00 New York) — the schedule would move to 13:00.
|
|
103
|
+
|
|
104
|
+
Time zone: use the IANA zone in current tool-returned site metadata for the exact target site, if supplied; otherwise read Site Properties — `GET https://www.wixapis.com/site-properties/v4/properties?fields.paths=timeZone` (SDK `siteProperties.getSiteProperties({fields: ["timeZone"]})` from `@wix/business-tools`) → `properties.timeZone` (IANA, e.g. `America/New_York`). Convert it to the offset valid on each date (DST changes the offset; compute `startDate` and `endDate` separately). If neither source supplies a valid zone, ask the user; never use another site’s zone, a documentation example, memory or a silent UTC default.
|
|
105
|
+
|
|
106
|
+
A schedule always runs in the site's time zone. If the user names a different zone ("23:30 Berlin time" on a London site), never write that zone's offset. When the site zone keeps the same offset from theirs all year (for example, both on the same DST calendar), convert the time to site wall-clock time and say which zone the schedule uses. Otherwise a fixed site-time schedule can't match their time all year: ask whether to change the site time zone or run at a site time. Date checks in a CODE_CONDITION may still use the user's zone.
|
|
107
|
+
|
|
108
|
+
One-time: `{"startDate": "…"}` only — the absence of `repetitions` means run once (and no `endDate`).
|
|
109
|
+
|
|
110
|
+
Recurring: `repetitions` with `cronExpression` + **exactly one** of `everyNthDay` (1–99) / `everyNthWeek` (1–52) / `everyNthMonth` (1–12) — the stricter of the builder's input (1–99) and the trigger's config schema (≤ 365 days, 52 weeks, 12 months), optional `endDate` **inside** `repetitions` (same format and time zone, after `startDate`). Cron is `"MM HH DOM MON DOW"` (Sunday = 0); `N#K` = Kth weekday N of the month; `NL` = last weekday N.
|
|
111
|
+
|
|
112
|
+
Builder round-trip (it regenerates the cron from `startDate` when the panel opens — any mismatch is rewritten):
|
|
113
|
+
|
|
114
|
+
- cron `MM HH` = `startDate`'s minute and hour, no leading zeros (`"0 9 …"`, not `"00 09 …"`).
|
|
115
|
+
- Weekly weekdays in ascending order (`1,3,5`). Monthly day-of-month = `startDate`'s day; `N#K`: N = `startDate`'s weekday, K = its week of the month (days 1–7 → `#1`, 8–14 → `#2`, …).
|
|
116
|
+
- Known builder issues (tell the user): opening the schedule panel can rewrite `NL` to `MM HH L * *` and resets an `endDate` to exactly one year after `startDate`; a save after opening it persists that. Ask them to re-check both before saving from the builder.
|
|
117
|
+
|
|
118
|
+
| Intent | cronExpression | param |
|
|
119
|
+
| --------------------------------- | --------------------- | ------------------ |
|
|
120
|
+
| Every day 8:00 | `"0 8 * * *"` | `everyNthDay: 1` |
|
|
121
|
+
| Every 5 days | `"0 10 * * *"` | `everyNthDay: 5` |
|
|
122
|
+
| Every 2 weeks, weekday irrelevant | `"0 9 * * *"` | `everyNthDay: 14` |
|
|
123
|
+
| Every Thursday 10:00 | `"0 10 * * 4"` | `everyNthWeek: 1` |
|
|
124
|
+
| Mon/Wed/Fri 8:00 | `"0 8 * * 1,3,5"` | `everyNthWeek: 1` |
|
|
125
|
+
| Every 2 weeks Sun + Wed | `"12 17 * * 0,3"` | `everyNthWeek: 2` |
|
|
126
|
+
| Weekdays | `"0 9 * * 1,2,3,4,5"` | `everyNthWeek: 1` |
|
|
127
|
+
| 15th of every month | `"0 9 15 * *"` | `everyNthMonth: 1` |
|
|
128
|
+
| First Monday monthly | `"0 10 * * 1#1"` | `everyNthMonth: 1` |
|
|
129
|
+
| Last Friday quarterly | `"0 16 * * 5L"` | `everyNthMonth: 3` |
|
|
130
|
+
|
|
131
|
+
Weekly: pure interval → `everyNthDay: 7/14/21` (runs on `startDate`'s weekday); specific weekday(s) → `everyNthWeek` + cron DOW.
|
|
132
|
+
|
|
133
|
+
Monthly (most restrictive): `startDate` MUST itself be an instance of the pattern (a "15th" schedule starts on a 15th; "first Monday" starts on a first Monday). Only two patterns exist: day-of-month **1–28**, or weekday position `#1`–`#4` / `L`.
|
|
134
|
+
|
|
135
|
+
Not supported — say so and offer an alternative:
|
|
136
|
+
|
|
137
|
+
- Last **day** of month (`L` in day-of-month) — never write it, even though the builder's panel offers a "last day" option when `startDate` is a month's last day: it is not a supported pattern. Offer: a daily schedule + a month-end check (below; the only one that fires on the real last day), day 28 (fires up to 3 days early), a last-weekday position (`5L`), or the 1st.
|
|
138
|
+
- 29th/30th/31st, several days per month, "second-to-last X".
|
|
139
|
+
- More than once a day (hourly/minutes).
|
|
140
|
+
- Two `everyNth*` params together, or `repetitions` with none.
|
|
141
|
+
|
|
142
|
+
**Daily + month-end check** (for "last day of month" or other calendar rules the schedule can't express): a daily schedule (`everyNthDay: 1`) whose root is a CODE_CONDITION ([Automations Conditions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-conditions) §5) with `dynamicVariableExpressions: []` (the scheduled trigger has no payload) and the site time zone written into the code:
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
/**
|
|
146
|
+
* @param {Payload} payload - unused; this check uses the site calendar.
|
|
147
|
+
* @returns {boolean} whether today is the site's last day of the month.
|
|
148
|
+
*/
|
|
149
|
+
export default function (payload) {
|
|
150
|
+
try {
|
|
151
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
152
|
+
timeZone: '<SITE_TIME_ZONE>', // replace with the validated site timeZone
|
|
153
|
+
calendar: 'gregory',
|
|
154
|
+
numberingSystem: 'latn',
|
|
155
|
+
year: 'numeric',
|
|
156
|
+
month: 'numeric',
|
|
157
|
+
day: 'numeric',
|
|
158
|
+
}).formatToParts(new Date());
|
|
159
|
+
const get = type => Number(parts.find(part => part.type === type)?.value);
|
|
160
|
+
const year = get('year'),
|
|
161
|
+
month = get('month'),
|
|
162
|
+
day = get('day');
|
|
163
|
+
return day === new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
164
|
+
} catch (e) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Replace `<SITE_TIME_ZONE>` with the site's validated IANA time zone before saving; do not save the placeholder. Validate the site's IANA time zone before saving; an invalid or unavailable zone is a blocker, never a reason to use UTC. The runtime guard returns false if date formatting fails. Compare calendar dates as above: adding 24 hours is not "tomorrow" across daylight-saving changes. Keep the JSDoc header and the complete snippet within 1000 characters per [Automations Conditions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-conditions) §5; put the real steps on its true branch, `[]` on the false one. Tell the user the check runs in code.
|
|
171
|
+
|
|
172
|
+
Checklist: `startDate` matches the pattern day • cron consistent with the `everyNth*` param • offsets correct for the site time zone on each date • monthly pattern exists in every month • when in doubt prefer daily/weekly over monthly.
|
|
173
|
+
|
|
174
|
+
## 5. Future-date offset ("before the event")
|
|
175
|
+
|
|
176
|
+
For triggers whose payload has a date-time field with `futureDate: true` (e.g. session start), run the automation a fixed time **before** that date:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
"trigger": { "...": "...",
|
|
180
|
+
"scheduledEventOffset": { "preScheduledEventOffsetExpression": "{{2}}",
|
|
181
|
+
"scheduledEventOffsetTimeUnit": "DAYS" } }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- For a **builder-editable** fixed reminder, use a positive integer literal `{{N}}`, unit `MINUTES` | `HOURS` | `DAYS` | `WEEKS` | `MONTHS`. Omit `scheduledEventOffset` entirely when there is none.
|
|
185
|
+
- The [public scheduled-events contract](https://dev.wix.com/docs/api-reference/business-management/automations/automations/automations-v2/configure-your-automation) also supports payload-driven expressions such as `{{var("timeOffset")}}`. This is an API capability, but the current builder's timing panel and canvas label recognize only a numeric expression: a variable will not display as the requested reminder window. Do not silently replace a payload-driven request with a fixed number; explain that editor limitation and ask whether a fixed, editable window is acceptable. Preserve an existing variable expression unchanged during unrelated updates; do not rewrite its timing to make the panel display it.
|
|
186
|
+
- Offsets are always _before_ the event. "2 days after the booking" → no offset; add a DELAY step ([Automations Delays Variables and Branches](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-delays-variables-and-branches)).
|
|
187
|
+
- A trigger without a `futureDate` field cannot take an offset.
|
|
188
|
+
|
|
189
|
+
## 6. Custom API ("site") actions
|
|
190
|
+
|
|
191
|
+
Site actions (`wix_automations-wix_api_integration`) are generated through non-public services and can't be created from public APIs — a limitation (§1 of Automations Feasibility and Planning (topic guide not yet published; consult the [Automations API reference](https://dev.wix.com/docs/api-reference/business-management/automations))). Leave an existing one's `inputMapping` untouched.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Automations Delays Variables and Branches"
|
|
3
|
+
description: "Configure delay, rate-limit, variable and parallel-branch steps, preserving supported existing step metadata."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Stage scope:** this stage covers only what [Build and Manage Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/skills/build-and-manage-wix-automations) lists; follow the instruction attached to each guide marked "not yet published". "Stage" and "not yet published" describe how this skill is being rolled out; never mention them to the user. Explain a limit in product terms: what can't be done through the API here and where the user can do it instead (for example, in the automations builder).
|
|
7
|
+
|
|
8
|
+
# Special Actions — Delay, Rate Limit, Code Condition, Set Variables, Split, Naming
|
|
9
|
+
|
|
10
|
+
**TL;DR**
|
|
11
|
+
|
|
12
|
+
- DELAY: EITHER relative (`offsetExpression: "{{N}}"` number literal + `offsetTimeUnit`) OR absolute (`dueDateExpression`). Never both.
|
|
13
|
+
- RATE_LIMIT: only as THE root action, only one, `maxActivationsExpression: "{{1}}"`, for new steps, identifier = `var()` of a trigger contact/visitor identity field; preserve existing newer identifiers (§2).
|
|
14
|
+
- CODE_CONDITION (full spec [Automations Conditions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-conditions) §5): JavaScript `export default function (payload) { return <boolean>; }`, the fallback for logic the condition panel can't render; `dynamicVariableExpressions` MUST list every payload field the code reads — the runtime supplies ONLY those.
|
|
15
|
+
- SET_VARIABLES and SPLIT are **alpha: not yet in the public API docs, but supported by the builder**. `setVariablesInfo` / `splitInfo` are not in the public schema, so after Create/Update **read the automation back and confirm the node still has its `*Info`** — if the public API dropped it from an automation **you just created in this task**, tell the user and, with their OK, delete that new automation and rebuild it with a fallback (duplicate steps instead of SPLIT; formulas in the consuming fields instead of SET_VARIABLES). On an **update** of an existing automation, never delete or rebuild the user's steps yourself: stop, report what was dropped, and ask.
|
|
16
|
+
- Code variables (`wix_automations-data_manipulation_code`) can't be created through the public API (§4).
|
|
17
|
+
- Number literals in `{{N}}` (delay offsets, rate-limit values): whole numbers without leading zeros or spaces — the builder parser can't read `0.5`, `.5`, `01` or `{{ 3 }}` ([Automations Mapping Expressions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-mapping-expressions) §1).
|
|
18
|
+
- No MERGE. SPLIT paths never re-join.
|
|
19
|
+
- Every node: shape rules from [Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) (own `*Info` only, `[]` not omitted, namespaces per §3 there). Expression syntax: [Automations Mapping Expressions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-mapping-expressions).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. DELAY
|
|
24
|
+
|
|
25
|
+
Pauses the flow before the next step. Place it anywhere except before the root RATE_LIMIT.
|
|
26
|
+
|
|
27
|
+
Relative — wait N units after the previous step finishes:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"id": "<uuid-1>",
|
|
32
|
+
"type": "DELAY",
|
|
33
|
+
"namespace": "DELAY-2",
|
|
34
|
+
"displayName": "Wait 3 days before follow-up",
|
|
35
|
+
"delayInfo": {
|
|
36
|
+
"offsetExpression": "{{3}}",
|
|
37
|
+
"offsetTimeUnit": "DAYS",
|
|
38
|
+
"postActionIds": ["<uuid-2>"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Absolute — wait until a date/time:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"id": "<uuid-1>",
|
|
48
|
+
"type": "DELAY",
|
|
49
|
+
"namespace": "DELAY-2",
|
|
50
|
+
"displayName": "Wait until 1 day before the session",
|
|
51
|
+
"delayInfo": {
|
|
52
|
+
"dueDateExpression": "{{subtractFromDate(var(\"start_date\");0;0;1)}}",
|
|
53
|
+
"postActionIds": ["<uuid-2>"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
MUST (builder + API):
|
|
59
|
+
|
|
60
|
+
- Exactly one mode. Relative ⇒ omit `dueDateExpression`. Absolute ⇒ omit `offsetExpression` and `offsetTimeUnit`.
|
|
61
|
+
- `offsetExpression` is a whole-number literal in braces (`"{{3}}"`) — no `var()`, no formula, no decimals, never bare `"3"`. Anything else loads in the panel as `1`.
|
|
62
|
+
- `offsetTimeUnit` ∈ `MINUTES | HOURS | DAYS | WEEKS | MONTHS` (no seconds). Never write `UNKNOWN_TIME_UNIT` on a DELAY — but an absolute-mode DELAY reads back with `offsetTimeUnit: "UNKNOWN_TIME_UNIT"` filled in by the server; that is expected, don't "fix" it. Panel limits: MINUTES/HOURS 1–999, DAYS 1–365, WEEKS 1–52, MONTHS 1–12 — convert larger values to a bigger unit.
|
|
63
|
+
- `dueDateExpression` (≤1000 chars) is ONE of: a bare ISO date-time literal with offset, NOT in braces (`"2025-08-25T13:57:00+03:00"` — the builder's own format), `"{{var(\"<date-time field>\")}}"`, or a single date-time formula (`addToDate(date;years;months;days)`, `subtractFromDate(...)`, `now()`). The field must exist in this node's aggregated schema. A due date already in the past runs the next step immediately.
|
|
64
|
+
- `postActionIds` ≤1 id; `[]` ends the flow. To fan out after a delay, point it at a SPLIT.
|
|
65
|
+
|
|
66
|
+
Choosing:
|
|
67
|
+
|
|
68
|
+
- "2 hours after signup" → relative. "24 h before the workshop" → absolute with `subtractFromDate`.
|
|
69
|
+
- "Run the whole automation X BEFORE the event date" is not a delay — it is the trigger's `scheduledEventOffset` ([Automations Schemas and Scheduling](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-schemas-and-scheduling) §5). Delays only move execution LATER.
|
|
70
|
+
- "Send now AND remind before the event" → first step, then an absolute DELAY on the event start (`subtractFromDate(var("start_date");0;0;1)`), then the reminder. A trigger offset would delay the first step too.
|
|
71
|
+
- A delay does not change what data is available, but payload values are captured at trigger time and can go stale across long waits.
|
|
72
|
+
- Delays on duplicated branches: see the delay-arithmetic rule in [Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) §2.
|
|
73
|
+
|
|
74
|
+
## 2. RATE_LIMIT ("run once per … every …")
|
|
75
|
+
|
|
76
|
+
Stops the run when the same identity already triggered it within the window; otherwise continues to its child.
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"id": "<uuid-1>",
|
|
81
|
+
"type": "RATE_LIMIT",
|
|
82
|
+
"namespace": "RATE_LIMIT-1",
|
|
83
|
+
"rateLimitInfo": {
|
|
84
|
+
"maxActivationsExpression": "{{1}}",
|
|
85
|
+
"rateLimitDurationExpression": "{{24}}",
|
|
86
|
+
"rateLimitDurationTimeUnit": "HOURS",
|
|
87
|
+
"uniqueIdentifierExpression": "{{var(\"contactId\")}}",
|
|
88
|
+
"postActionIds": ["<uuid-2>"]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
MUST:
|
|
94
|
+
|
|
95
|
+
1. It is `rootActionIds[0]` and its `postActionIds[0]` is the rest of the flow. Never under a condition, delay, split, or any other step (the builder shows it as the trigger's "frequency" footer; anywhere else the node spins forever). At most one per automation.
|
|
96
|
+
2. `maxActivationsExpression: "{{1}}"`. The builder models only "once every X" and rewrites any other value to `{{1}}` as soon as the user opens the panel — never promise "N times per X".
|
|
97
|
+
3. For a NEW rate-limit step, `uniqueIdentifierExpression` = exactly `{{var("<path>")}}` where `<path>` is a field of the TRIGGER payload schema (any object depth, not inside an array) annotated `identityType: "contact"` or `"visitor"` for compatibility with both frequency panels. Not enriched props (`contact.email`), not action outputs, not computed or static values (the footer disappears). If the trigger has no such field, do not claim rate limiting is impossible: the newer panel also supports string `member`/`user` identity fields and string `format: uuid` fields without an `identityType`. Availability of that creation UI varies; ask the user to configure the frequency in their builder instead of guessing support. On UPDATE, preserve an existing identifier of these newer types and its expression exactly; do not replace it with contact/visitor, remove the RATE_LIMIT, or treat it as invalid merely because it is outside the conservative creation rule. Confirm its path still exists in the trigger schema; a missing path needs resolution before saving.
|
|
98
|
+
4. Duration: `rateLimitDurationExpression: "{{N}}"` (whole number) + `rateLimitDurationTimeUnit` ∈ `MINUTES | HOURS | DAYS | WEEKS | MONTHS`, same per-unit limits as DELAY. "Only ever once" (the panel's unchecked "reset every" box) — only when the user explicitly wants it — is: no `rateLimitDurationExpression` and `rateLimitDurationTimeUnit: "UNKNOWN_TIME_UNIT"`; this is the one place `UNKNOWN_TIME_UNIT` is written (the builder writes exactly this shape). Use `{{24}} HOURS`, not `{{1440}} MINUTES` (the builder rewrites that one on load).
|
|
99
|
+
|
|
100
|
+
Recipes: once per contact per day → `{{1}}`, `{{24}} HOURS`, contact field. Once per contact ever → `{{1}}`, no duration expression, `UNKNOWN_TIME_UNIT`.
|
|
101
|
+
|
|
102
|
+
## 3. CODE_CONDITION
|
|
103
|
+
|
|
104
|
+
The fallback for logic the visual condition panel can't render (array indexing, per-element math, string splitting, date parts, regex). Must-know: `dynamicVariableExpressions` MUST list every payload field the code reads (the runtime supplies ONLY those); the builder's JSDoc header is required; code ≤ 1000 chars. Full spec and examples: [Automations Conditions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-conditions) §5; when to use it: [Automations Conditions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-conditions) §4.
|
|
105
|
+
|
|
106
|
+
## 4. SET_VARIABLES (alpha)
|
|
107
|
+
|
|
108
|
+
Computes named values for later steps.
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"id": "<uuid-1>",
|
|
113
|
+
"type": "SET_VARIABLES",
|
|
114
|
+
"namespace": "setVariable",
|
|
115
|
+
"displayName": "Set customer greeting and tax",
|
|
116
|
+
"setVariablesInfo": {
|
|
117
|
+
"outputMapping": {
|
|
118
|
+
"3f2b8c1e-7a4d-4e9b-9c21-5d6f0a1b2c3d": "Dear {{var(\"contact.name.first\")}},",
|
|
119
|
+
"8a9e4f20-1b3c-4d5e-8f6a-7b8c9d0e1f2a": "{{multiply(toNumber(var(\"totals.subtotal\"));divide(8;100))}}",
|
|
120
|
+
"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f": "{{gt(toNumber(var(\"totals.total\"));1000)}}"
|
|
121
|
+
},
|
|
122
|
+
"outputSchema": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"properties": {
|
|
125
|
+
"3f2b8c1e-7a4d-4e9b-9c21-5d6f0a1b2c3d": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"title": "Customer greeting"
|
|
128
|
+
},
|
|
129
|
+
"8a9e4f20-1b3c-4d5e-8f6a-7b8c9d0e1f2a": {
|
|
130
|
+
"type": "number",
|
|
131
|
+
"title": "Tax amount"
|
|
132
|
+
},
|
|
133
|
+
"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f": {
|
|
134
|
+
"type": "boolean",
|
|
135
|
+
"title": "Premium customer"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"required": [
|
|
139
|
+
"3f2b8c1e-7a4d-4e9b-9c21-5d6f0a1b2c3d",
|
|
140
|
+
"8a9e4f20-1b3c-4d5e-8f6a-7b8c9d0e1f2a",
|
|
141
|
+
"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f"
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
"postActionIds": ["<uuid-2>"]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
MUST:
|
|
150
|
+
|
|
151
|
+
1. `namespace` is the literal `setVariable`. Downstream: `{{var("setVariable.8a9e4f20-1b3c-4d5e-8f6a-7b8c9d0e1f2a")}}` (the tax amount).
|
|
152
|
+
2. `outputMapping` and `outputSchema.properties` have exactly the same keys (a mapping key without a property is dropped on save). Keys are opaque, stable ids: prefer a fresh uuid v4 per key — what the builder writes for new variables; descriptive keys also load and round-trip, but get a "key should be a uuid" warning. Keys must be unique across ALL variable steps. Downstream: `setVariable.<key>`.
|
|
153
|
+
3. For new variables use `{type, title, format?}`: `type` ∈ `string | number | boolean`, `format` ∈ `date | date-time | email | uri`. `title` is REQUIRED (the visible name; a title-less variable is invisible downstream and dropped on save) and MUST be unique across ALL variable and code-variable steps in the automation (the panel refuses duplicate names). Add `"required": [<every key>]` to `outputSchema` — the panel writes it on save; other property fields such as `description` are stripped. Exception on UPDATE: preserve an existing `wixCustomType: "IMAGE_URL"` together with `type: "string"` and `format: "uri"`; it distinguishes an image variable from a general URL. The builder preserves this metadata even when its image-variable picker is unavailable. Do not normalize it away or rename its stable variable key.
|
|
154
|
+
4. An expression reads ONLY this node's aggregated schema — never another variable defined in the same step. Need a derived value twice? Repeat the expression, or compute it in an EARLIER variables step.
|
|
155
|
+
5. Panel-renderable values only: plain text / literals, `{{var(...)}}`, and functions from the builder list in [Automations Mapping Expressions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-mapping-expressions), using `eq/gt/gte/lt/lte` for NUMBERS only (they reject string and boolean arguments — for a boolean use the value itself or `not(...)`, for strings `stringEq`); no unclosed `{{`. A `number`/`boolean` variable holds a JSON literal of that type or exactly one whole `{{…}}` — never plain text. NEVER a formula in a `date-time` variable (the panel replaces it with "now"); a literal date-time is re-saved in the site timezone. Anything else → the §4 alternatives (code variables can't be created publicly).
|
|
156
|
+
6. Place it BEFORE a condition when both branches need the value.
|
|
157
|
+
|
|
158
|
+
**Code variables are not available through the public API.** The builder's code-variable step (`APP_DEFINED` `wix_automations-data_manipulation_code`, `namespace: "setVariable"`) is not in Resolve Actions, and Create/Update refuse an automation containing a new one (PermissionDenied "Unauthorized automation creation or update") even when Validate says VALID. For a value the panel can't express (regex, splitting, day of year, joining a list, time between dates), in this order: a formula in the consuming field or a SET_VARIABLES step, if the §2 function list covers it; a CODE_CONDITION when the value only decides a branch (it creates fine); a "Generate or analyze text" (`wix_automations-llm_call`) step with an override output schema to extract or compute a text value; otherwise save without it and tell the user to add a code variable in the builder. An existing code variable in an automation you update: leave it untouched and keep its keys.
|
|
159
|
+
|
|
160
|
+
Variables are transformation, not extraction: they can't pull one element out of an unindexable array — use an LLM text action or a code condition (§5 of Automations Feasibility and Planning (topic guide not yet published; consult the [Automations API reference](https://dev.wix.com/docs/api-reference/business-management/automations))).
|
|
161
|
+
|
|
162
|
+
## 5. SPLIT (alpha) — parallel paths
|
|
163
|
+
|
|
164
|
+
Every path ALWAYS runs, in parallel. It is not a condition.
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"id": "<uuid-1>",
|
|
169
|
+
"type": "SPLIT",
|
|
170
|
+
"namespace": "SPLIT-2",
|
|
171
|
+
"displayName": "Notify customer and team",
|
|
172
|
+
"splitInfo": {
|
|
173
|
+
"paths": [
|
|
174
|
+
{
|
|
175
|
+
"id": "<uuid-p1>",
|
|
176
|
+
"name": "Notify customer",
|
|
177
|
+
"displayName": "Notify customer",
|
|
178
|
+
"postActionId": "<uuid-2>"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"id": "<uuid-p2>",
|
|
182
|
+
"name": "Notify team",
|
|
183
|
+
"displayName": "Notify team",
|
|
184
|
+
"postActionId": "<uuid-3>"
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
MUST:
|
|
192
|
+
|
|
193
|
+
- 2–10 paths; ≤10 SPLIT nodes per automation. A SPLIT has only `splitInfo` — no `postActionIds`.
|
|
194
|
+
- Path ids: fresh uuid v4, unique against every action id, every other path id, and the trigger key. Paths are NOT entries in `configuration.actions`.
|
|
195
|
+
- `name` 1–100 chars is the label; optional `displayName` ≤60 overrides it (the builder sets it only on rename — same text is fine). `postActionId` = first action of that path, or omitted for an empty path.
|
|
196
|
+
- Each path's chain is ordinary actions with one successor each; no action reachable from two paths.
|
|
197
|
+
- Placement: parallel work right after the trigger → the SPLIT is the root (unless a RATE_LIMIT is root; then the SPLIT is its child). Later → the previous step points to it.
|
|
198
|
+
- No re-join. A step that must run after all paths finish can't exist — put it before the SPLIT or on one path.
|
|
199
|
+
|
|
200
|
+
Editing an existing SPLIT: add a branch by appending one path (new id, new first action); remove one by deleting that path plus every node below it; if one path would remain, delete the SPLIT and connect its parent (or `rootActionIds`) straight to that path's first action — the builder does the same. Never linearize paths into a sequence — that changes behavior. Adding a SPLIT in the builder UI may be feature-gated for the user, but drawing and editing an existing one always works.
|
|
201
|
+
|
|
202
|
+
## 6. Why there is no MERGE
|
|
203
|
+
|
|
204
|
+
The API defines `MERGE` + `conditionInfo.mergeActionId`, but the builder cannot draw them. To let both condition branches continue into shared steps, DUPLICATE those steps into each branch ([Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) §2).
|
|
205
|
+
|
|
206
|
+
## 7. Display names
|
|
207
|
+
|
|
208
|
+
- Sentence case, 1–60 chars, specific to purpose in context: "Send welcome email", not "Send email".
|
|
209
|
+
- APP_DEFINED: action + purpose ("Send welcome email"). DELAY: the wait ("Wait 2 hours before follow-up"). CONDITION / CODE_CONDITION: what's checked ("Check if VIP customer"). SET_VARIABLES: what's stored ("Set customer tier"). SPLIT path: the branch's purpose.
|
|
210
|
+
- Existing site-action (API integration) nodes: don't set or change their `displayName`.
|