@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.
Files changed (30) hide show
  1. package/README.md +21 -0
  2. package/package.json +2 -2
  3. package/skills/wix-app/references/DASHBOARD_PAGE.md +1 -1
  4. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +40 -41
  5. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +11 -6
  6. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +2 -3
  7. package/skills/wix-app/references/editor-react-component/DISABLED-INVALID.md +31 -0
  8. package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +14 -1
  9. package/skills/wix-app/references/editor-react-component/PARTS.md +32 -3
  10. package/skills/wix-app/references/editor-react-component/SSR.md +6 -3
  11. package/skills/wix-headless-kit/install/pins.json +1 -1
  12. package/skills/wix-manage/SKILL.md +25 -7
  13. package/skills/wix-manage/references/automations/actions.md +11 -11
  14. package/skills/wix-manage/references/automations/activation-status.md +6 -9
  15. package/skills/wix-manage/references/automations/api-catalog.md +48 -32
  16. package/skills/wix-manage/references/automations/automation-model.md +97 -22
  17. package/skills/wix-manage/references/automations/bracket-expressions.md +7 -7
  18. package/skills/wix-manage/references/automations/build-and-manage-automations.md +140 -309
  19. package/skills/wix-manage/references/automations/conditions.md +187 -0
  20. package/skills/wix-manage/references/automations/schemas-and-scheduling.md +191 -0
  21. package/skills/wix-manage/references/automations/special-actions.md +210 -0
  22. package/skills/wix-manage/references/automations/triggers.md +47 -10
  23. package/skills/wix-manage/references/automations/validation-and-verification.md +320 -0
  24. package/skills/wix-manage/references/custom-apps/use-app-tools.md +124 -0
  25. package/skills/wix-manage/references/events/create-wix-event.md +136 -73
  26. package/skills/wix-manage/references/events/manage-event-guests.md +366 -0
  27. package/skills/wix-manage/references/events/manage-event-tickets.md +293 -0
  28. package/skills/wix-manage/references/events/manage-wix-events.md +313 -55
  29. package/skills/wix-manage/references/stores/update-product-with-options.md +58 -2
  30. package/skills/wix-manage/references/custom-apps/use-app-skills-and-app-tools.md +0 -208
@@ -1,14 +1,21 @@
1
1
  ---
2
2
  name: "Manage Wix Events — Publishing, Cancelling, Cloning and Counting"
3
- description: "Operates on events that already exist with the Wix Events V3 API — finding an event by title, publishing a draft, cancelling, deleting (one or by filter), cloning, updating an event's date or details, and counting events. Use when the user wants to find, publish or cancel an event, duplicate one, move an event's date, delete events in bulk, or count their events. Creating an event, its tickets or a recurring series is a separate recipe."
3
+ description: "Operates on Wix events that already exist with the Wix Events V3 API — finding, updating (date, venue, details, page settings), controlling registration (close, reopen, registration-free, external, members-only, limits), switching between RSVP and ticketed (a new event — ask before creating it), publishing, cancelling, deleting, cloning and counting, including single occurrences of a recurring event. Use to close registration, move or cancel an event, or change how guests register."
4
4
  ---
5
5
 
6
6
  # Manage Wix Events — Publishing, Cancelling, Cloning and Counting
7
7
 
8
8
  ## Goal
9
9
  Operate on events that already exist. Creating an event — including its date, location,
10
- description, guest limit, ticket tiers and recurring occurrences — is
11
- [Create an Event](create-wix-event.md).
10
+ description, guest limit and recurring occurrences — is [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event).
11
+ Ticket types and prices use the [Ticket Definitions V3 API](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/ticket-definitions-v3); guests, RSVPs, orders and
12
+ check-in use the [RSVP V2](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/rsvp-v2) and [Ticketing](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing) APIs —
13
+ this recipe doesn't manage ticket prices, sale periods, guests, RSVPs, orders or check-in (it only
14
+ makes the couple of ticket-definition calls that registration state depends on).
15
+
16
+ **Switching an event between RSVP and ticketed creates a new event, so it is two turns:** explain and
17
+ ask in the first, create only after the user says yes — see
18
+ [Switch an event between RSVP and ticketed](#switch-an-event-between-rsvp-and-ticketed).
12
19
 
13
20
  ## Prerequisite — the Wix Events app
14
21
 
@@ -26,12 +33,13 @@ curl -X POST 'https://www.wixapis.com/apps-installer-service/v1/app-instance/ins
26
33
  ```
27
34
 
28
35
  `appDefId` nests under `appInstance`, not at the root — at the root it fails with
29
- `400 appInstance must not be empty`. See [Install Wix Apps](../app-installation/install-wix-apps.md).
36
+ `400 appInstance must not be empty`. See [Install Wix Apps](https://dev.wix.com/docs/api-reference/business-management/app-installation/skills/install-wix-apps).
30
37
 
31
38
  ## Find an event
32
39
 
33
- Every action below takes an `eventId`. When the user names the event instead, look it up with
34
- `POST /events/v3/events/query` — `title` supports `$eq` and, for several names at once, `$in`; there is
40
+ Every action below takes an `eventId` ([Query Events](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/query-events)
41
+ is the reference for this call). When the user names the event instead, look it up with
42
+ `POST https://www.wixapis.com/events/v3/events/query` — `title` supports `$eq` and, for several names at once, `$in`; there is
35
43
  no contains match:
36
44
 
37
45
  ```bash
@@ -43,67 +51,248 @@ curl -X POST 'https://www.wixapis.com/events/v3/events/query' \
43
51
  "filter": { "title": { "$eq": "Open House" } },
44
52
  "paging": { "limit": 100 }
45
53
  },
46
- "fields": ["DETAILS"],
47
- "includeDrafts": false
54
+ "fields": ["DETAILS", "REGISTRATION"]
48
55
  }'
49
56
  ```
50
57
 
51
- `filter` and `paging` are siblings under `query`; `paging` nested inside `filter` fails `400`. Matches
52
- come back in `events[]` with the id at `events[].id`, and `pagingMetadata.total` is the count.
58
+ - **Send this body as it is** — no extra filter fields and no `sort`; a nested `registration`
59
+ filter or a `sort` inside `filter` fails `400`. `filter` and `paging` are siblings under `query`; `paging` nested
60
+ inside `filter` fails `400`.
61
+ - **Don't add `includeDrafts`.** `true` needs the draft-events permission and fails `403`; the
62
+ default already returns every published event.
63
+ **Always send `paging.limit`** — its default is `0`, which returns no events. Matches come back in
64
+ `events[]` with the id at `events[].id`, and `pagingMetadata.total` is the count. Other filterable
65
+ fields: `status` (`UPCOMING`, `STARTED`, `ENDED`, `CANCELED`, `DRAFT`), `registration.initialType`,
66
+ `dateAndTimeSettings.startDate`, `slug`, `id`.
67
+
68
+ **One title can match several events** — every occurrence of a recurring event shares the title,
69
+ and duplicated events keep it too. When more than one comes back, pick by
70
+ `dateAndTimeSettings.startDate` and `status` if the user named a date; otherwise list the matches
71
+ with their dates and ask which one. Never act on all of them unless the user asked for the series.
72
+
73
+ Only when the user asks about **draft** events, add `"includeDrafts": true`. If that returns
74
+ `403 Requires WIX_EVENTS.READ_DRAFT_EVENTS permission`, say draft events are not readable from this
75
+ connection rather than answering from published events alone.
76
+
77
+ ## Registration settings
78
+
79
+ Change registration with `PATCH https://www.wixapis.com/events/v3/events/{event.id}` and the
80
+ change under `event.registration` — never through ticket sale periods, and never by cancelling.
81
+
82
+ ```bash
83
+ curl -X PATCH 'https://www.wixapis.com/events/v3/events/<EVENT_ID>' \
84
+ -H 'Content-Type: application/json' \
85
+ -H 'Authorization: <AUTH>' \
86
+ --data-binary '{
87
+ "event": { "registration": { "registrationPaused": true } },
88
+ "fieldmask": "registration.registrationPaused",
89
+ "fields": ["REGISTRATION"]
90
+ }'
91
+ ```
53
92
 
54
- > **Keep `includeDrafts: false` unless the user is asking about draft events.** Setting it to `true`
55
- > needs `WIX_EVENTS.READ_DRAFT_EVENTS`, and a caller without it gets
56
- > `403 Requires WIX_EVENTS.READ_DRAFT_EVENTS permission` rather than more results — so do not flip
57
- > it as a precaution on an ordinary lookup, and do not probe for it and fall back. If the user does
58
- > want drafts and the call returns that `403`, say draft events are not readable from this
59
- > connection instead of answering from published events alone.
93
+ **Updates merge: only the fields you send change.** A `fieldmask` — a comma-separated string with
94
+ the dotted path of each field you send (`registration.rsvp.limit,registration.rsvp.waitlistEnabled`) —
95
+ is optional for registration settings: every row of the table below, including the nested `rsvp`
96
+ and `tickets` objects, applies without one; the examples include it anyway. It is **required in two
97
+ cases** only: changing the `location` (see [Update an event](#update-an-event) — without a mask the
98
+ call returns `200` and the location does not change), and **clearing** a field, which you do by
99
+ listing its path and leaving it out of the body.
100
+
101
+ | The user wants | Under `event.registration` |
102
+ | --- | --- |
103
+ | Close / stop / pause registration, keep the event | `"registrationPaused": true` |
104
+ | Reopen registration | `"registrationPaused": false` |
105
+ | No registration — the event is information-only | `"registrationDisabled": true, "registrationPaused": false` |
106
+ | Guests register on another website | `"external": { "url": "https://..." }, "registrationPaused": false` |
107
+ | Back to registering on the site, from information-only (`type` `NONE`) | `"registrationDisabled": false` |
108
+ | Back to registering on the site, from external (`type` `EXTERNAL`) | send `"registration": {}` with `"fieldmask": "registration.external.url"` — `type` returns to `RSVP` or `TICKETING` |
109
+ | Only site members can register | `"allowedGuestTypes": "MEMBER"` (`VISITOR_OR_MEMBER` for everyone) |
110
+ | RSVP guest limit / waitlist | `"rsvp": { "limit": 50, "waitlistEnabled": true }` — the waitlist needs a limit; to remove the limit, mask `registration.rsvp.limit` with no value in the body and also send `"waitlistEnabled": false` (the waitlist flag is not reset for you) |
111
+ | RSVP answers offered | `"rsvp": { "responseType": "YES_ONLY" }` or `"YES_AND_NO"` — an event created without a `responseType` accepts only "yes" (a "no" RSVP fails `UNEXPECTED_RSVP_STATUS`) |
112
+ | RSVP registration opens / closes on dates | `"rsvp": { "startDate": "<ISO>", "endDate": "<ISO>" }` — both together; to remove the dates, mask both with no values |
113
+ | Tickets per order, reservation hold, tax | `"tickets": { "ticketLimitPerOrder": 10, "reservationDurationInMinutes": 15, "taxSettings": { "type": "ADDED_AT_CHECKOUT", "name": "VAT", "rate": "21" } }` |
114
+ | One registration form per ticket holder | `"tickets": { "guestsAssignedSeparately": true }` |
115
+
116
+ Limits: `ticketLimitPerOrder` 0–50 (default 20), `reservationDurationInMinutes` 5–30, tax `rate` a
117
+ decimal string, tax `name` 1–10 characters, tax `type` `INCLUDED_IN_PRICE` or `ADDED_AT_CHECKOUT`.
118
+
119
+ **Before closing registration**, read `status` and `registration` (`"fields": ["REGISTRATION"]`):
120
+ - `status` `ENDED` or `CANCELED` — there is nothing to close; say so.
121
+ - `registration.type` `NONE` or `EXTERNAL` — the event has no on-site registration to close; say so,
122
+ and offer to switch it back to on-site registration first.
123
+ - `registration.status` `CLOSED_AUTOMATICALLY` — registration is already closed (sold out or past);
124
+ say so rather than setting the flag.
125
+ - a ticketed event with no ticket types yet reads `CLOSED_AUTOMATICALLY` too — nobody can register,
126
+ so there is nothing to close. Don't create a ticket to change that. Tell the user, and set
127
+ `registrationPaused: true` so registration stays closed if tickets are added later.
128
+
129
+ **Pause vs disable.** "Close registration", "stop sign-ups" or "no more guests" →
130
+ `registrationPaused: true`. "Guests don't need to register" → `registrationDisabled: true`.
131
+
132
+ **Read back and report the result.** Ask for `registration` in the response with
133
+ `"fields": ["REGISTRATION"]`, as above, and confirm **the flag you sent comes back with the new
134
+ value** — `registration.registrationPaused: true`, `registrationDisabled: true`, or the
135
+ `external.url`. If it doesn't, the change did not apply: say so. Then report the derived fields:
136
+
137
+ | You sent | Expect |
138
+ | --- | --- |
139
+ | `registrationPaused: true` | `type` unchanged (`RSVP` / `TICKETING`); `status` `CLOSED_MANUALLY` — "Closed" in the dashboard's events list |
140
+ | `registrationPaused: false` | `status` back to `OPEN_RSVP`, `OPEN_RSVP_WAITLIST_ONLY` or `OPEN_TICKETS` (`SCHEDULED_RSVP` if RSVP dates are set in the future) |
141
+ | `registrationDisabled: true` | `type` `NONE`, `status` `UNKNOWN_REGISTRATION_STATUS` — "No registration" in the dashboard |
142
+ | `external.url` | `type` `EXTERNAL`, `status` `OPEN_EXTERNAL` |
143
+
144
+ Read `type` first, then `status`: `type` `NONE` means registration-free whatever the `status`.
145
+ Report `CLOSED_MANUALLY` as paused and `CLOSED_AUTOMATICALLY` as sold out, past, or a ticketed event
146
+ with no tickets — go by this table, not the reference's enum note on `CLOSED_MANUALLY`.
147
+
148
+ > **Never write `registration.type`, `registration.status` or `registration.initialType`.** Use the
149
+ > flags in the table. A PATCH that sets `"type": "NONE"` alone fails `400` with an unrelated date
150
+ > message (`startDate.isDefined must be true … getTimeZoneId is not supported`); inside a full event
151
+ > body it is ignored.
152
+
153
+ Ticket currency is a ticket-definition call, not an event field:
154
+ `POST https://www.wixapis.com/events/v3/ticket-definitions/currency` with
155
+ `{ "eventId": "<EVENT_ID>", "currency": "EUR" }` ([Change Currency](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/ticket-definitions-v3/change-currency)).
156
+
157
+ ## Switch an event between RSVP and ticketed
158
+
159
+ `registration.initialType` can't be changed, and ticket calls on an RSVP event fail
160
+ `428 INVALID_EVENT_TYPE`. For "make it free" don't switch — add a free ticket
161
+ (a `fixedPrice` of `"0"` with `feeType` `NO_FEE`, as in [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event#ticket-tiers)).
162
+
163
+ **Turn 1 — explain and stop.** Find the event and confirm its type (one query). Then reply, and
164
+ end your turn with a question. Make **no** create, update or ticket call in this turn — "switch it"
165
+ or "convert it" is not the confirmation, because the result is a different event. Tell the user:
166
+ - the existing event can't be converted, so a new event would be created;
167
+ - RSVPs or orders on the original are **not** carried over;
168
+ - the original stays as it is until they decide to cancel or delete it;
169
+ - for a ticketed copy: which ticket names and prices to create (ask if they didn't say).
170
+
171
+ End the reply with the offer and the question, for example: "I can create a ticketed copy of Gala
172
+ with the same date, venue and description. Which tickets should it have, and at what price? Shall
173
+ I go ahead?"
174
+
175
+ **Turn 2 — only after the user replies yes:**
176
+
177
+ 1. `GET https://www.wixapis.com/events/v3/events/<EVENT_ID>?fields=DETAILS&fields=TEXTS&fields=REGISTRATION`
178
+ — read `title`, `location`, `dateAndTimeSettings`, `shortDescription`, `description`,
179
+ `mainImage` and the `registration` settings.
180
+ 2. `POST https://www.wixapis.com/events/v3/events` with those fields copied and
181
+ `registration.initialType` set to the other type, as in
182
+ [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event#create-the-event). Do not copy `registration.type`,
183
+ `registration.status`, `id`, `slug` or any read-only field.
184
+ 3. For a ticketed copy, create the tickets against the **`event.id` returned by step 2** — not by
185
+ querying the title again, which also matches the original RSVP event.
186
+ 4. If the original had an agenda, policies or categories, copy them with the
187
+ [Schedule Items](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/schedule-items), [Policies V2](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/policies-v2) and [Categories](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/categories) APIs.
188
+ 5. Ask whether to cancel or delete the original. Do neither without a yes.
60
189
 
61
190
  ## Publish, cancel and delete
62
191
 
63
192
  | Action | Call | Resulting `status` |
64
193
  | --- | --- | --- |
65
- | Publish a draft | `POST /events/v3/events/{eventId}/publish` | `UPCOMING` |
66
- | Cancel | `POST /events/v3/events/{eventId}/cancel` | `CANCELED` |
67
- | Delete | `DELETE /events/v3/events/{eventId}` | — |
68
- | Delete several | `POST /events/v3/bulk/events/delete-by-filter` | — |
194
+ | Publish a draft | `POST https://www.wixapis.com/events/v3/events/{eventId}/publish` | `UPCOMING` |
195
+ | Cancel | `POST https://www.wixapis.com/events/v3/events/{eventId}/cancel` | `CANCELED` |
196
+ | Delete | `DELETE https://www.wixapis.com/events/v3/events/{eventId}` | — |
197
+ | Cancel several | `POST https://www.wixapis.com/events/v3/bulk/events/cancel-by-filter` | `CANCELED` |
198
+ | Delete several | `POST https://www.wixapis.com/events/v3/bulk/events/delete-by-filter` | — |
199
+
200
+ Publish and cancel take an empty body, and neither can be undone.
69
201
 
70
- Publish and cancel take an empty body. Publishing is irreversible — a published event cannot
71
- return to `DRAFT`. Cancelling closes registration but keeps the event; deleting removes it.
202
+ **Cancel only when the user says the event is not happening** — it can email every registered
203
+ guest and can't be reopened. When they want registration closed but the event to go ahead, pause
204
+ registration instead (see [Registration settings](#registration-settings)). To bring a cancelled
205
+ event back, clone it ([Clone an event](#clone-an-event)) — the copy comes back live unless the clone
206
+ body sends `"draft": true`, so don't publish it again. Cancelled events stay in Query Events results — filter
207
+ by `status` to leave them out. `publish` on a cancelled event fails `428 EVENT_ALREADY_PUBLISHED`.
72
208
 
73
209
  Publish, cancel, clone and update all return `{ "event": { "id", "title", "slug", "status", ... } }`
74
- — the same event object [Create an Event](create-wix-event.md#create-the-event) shows in full.
75
- Delete returns `{ "eventId": "..." }` and the bulk delete below returns `{}`.
210
+ — the same event object [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event#create-the-event) shows in full.
211
+ Delete returns `{ "eventId": "..." }` and the bulk calls return `{}`.
76
212
 
77
- To delete a set of events in one call, `POST /events/v3/bulk/events/delete-by-filter`:
213
+ To cancel or delete a set of events in one call, send the ids to the bulk endpoint:
78
214
 
79
215
  ```json
80
216
  { "filter": { "filter": { "id": { "$in": ["<EVENT_ID>", "<EVENT_ID>"] } } } }
81
217
  ```
82
218
 
83
- The request's one field, `filter`, takes a whole query-shaped object — the same object Query Events
84
- takes under `query`, with the conditions under its own `filter` key — not a bare condition map. That
85
- is why the key appears twice: `filter.filter.<field>`. A single level,
86
- `{ "filter": { "id": { "$in": [...] } } }`, is not the documented shape. Resolve the ids with the
87
- query above first; one `DELETE` per event also works but costs a call each.
219
+ The conditions go two levels deep, `filter.filter.<field>`; a single level fails
220
+ `400 filter.filter must not be empty`. Resolve the ids with the query above first, and filter by
221
+ `id` rather than by `title`, so only the events you resolved are affected. **A bulk delete removes
222
+ events that have sold tickets or RSVPs, with no further warning** — check the
223
+ events' guests ([Query Event Guests](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/event-guests/query-event-guests)) and get the
224
+ user's confirmation first. Bulk calls
225
+ return `{}` with no per-event result, and a read straight afterwards can still show the events —
226
+ events with many guests or orders can take ten seconds or more. **Query the same ids again** every
227
+ couple of seconds for up to about twenty, then report what is actually gone or cancelled; if any
228
+ remain, delete or cancel those one by one.
88
229
 
89
230
  > **Draft events need the `WIX_EVENTS.READ_DRAFT_EVENTS` permission.** Without it, publishing a
90
231
  > draft fails `403` — as does querying it, fetching it by slug, or adding ticket definitions to
91
232
  > it. If you hit that `403`, the event was still created; the way forward is to create events
92
233
  > already published rather than as drafts, which needs no publish step at all. See
93
- > [Create an Event](create-wix-event.md).
234
+ > [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event).
235
+
236
+ ## Recurring events — one occurrence or the whole series
237
+
238
+ Each occurrence of a recurring event has its own `id`; occurrences share a title and
239
+ `dateAndTimeSettings.recurringEvents.categoryId`.
240
+
241
+ - **One occurrence** ("cancel this Friday's class", "move the 12th to 7pm"): find the occurrence
242
+ whose `dateAndTimeSettings.startDate` matches, and cancel, delete or update **that `id` only**.
243
+ The other occurrences are untouched.
244
+ - **The whole series**: read one occurrence's `dateAndTimeSettings.recurringEvents.categoryId`, then
245
+ list the series with `"filter": { "dateAndTimeSettings.recurringEvents.categoryId": { "$eq": "<CATEGORY_ID>" } }`
246
+ in Query Events (or `GET https://www.wixapis.com/events/v3/events/category/<CATEGORY_ID>?paging.limit=100`).
247
+ Then update each one, or cancel/delete them with the bulk call and `id.$in`. Updates go one call
248
+ per occurrence; on `429`, wait and retry that call rather than moving on, and count the successes
249
+ before reporting.
250
+ - A date or time change on one occurrence sends just that occurrence's `dateAndTimeSettings`.
251
+
252
+ After deleting a whole series, also delete its hidden category (`states` include `RECURRING_EVENT`,
253
+ named after the title)
254
+ (`DELETE https://www.wixapis.com/events/v1/categories/<CATEGORY_ID>`, id from the categories query).
255
+
256
+ Before cancelling or deleting more than one occurrence, list the dates that will be affected and
257
+ which of them have sold tickets or RSVPs ([Query Event Guests](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/event-guests/query-event-guests)), and
258
+ get the user's confirmation.
94
259
 
95
260
  ## Clone an event
96
261
 
97
- `POST /events/v3/events/{eventId}/clone` with an empty body copies the registration form,
98
- notifications, translations and ticket configuration.
262
+ `POST https://www.wixapis.com/events/v3/events/{eventId}/clone` with an empty body copies the registration form,
263
+ notifications, translations, descriptions and ticket configuration.
264
+
265
+ > **The clone is published, and it does not keep the original's date.** Unless the body says
266
+ > `"draft": true` it comes back `UPCOMING` — live on the site — with its start moved to roughly 14
267
+ > days from now (same time of day), and the title gets " (1)" appended. Cloning a cancelled event
268
+ > gives a fresh, open event.
269
+
270
+ To make the copy right in one call, send overrides in the clone body:
271
+
272
+ ```json
273
+ {
274
+ "event": {
275
+ "title": "Spring Fair — second weekend",
276
+ "dateAndTimeSettings": { "startDate": "2030-10-01T16:00:00.000Z", "endDate": "2030-10-01T19:00:00.000Z", "timeZoneId": "Europe/Dublin" }
277
+ }
278
+ }
279
+ ```
280
+
281
+ A title alone, or a title with both dates and `timeZoneId`, works. Overriding `registration` fails
282
+ `400` with a misleading date message — a clone keeps the original's `registration.initialType` and
283
+ cannot switch RSVP and ticketed (see [Switch an event between RSVP and ticketed](#switch-an-event-between-rsvp-and-ticketed)).
284
+ The copy comes back live. Add `"draft": true` only when the user asks for an unpublished copy
285
+ ("don't publish it yet", "so I can review it first") — a draft also needs the draft-events
286
+ permission for every later call on it.
99
287
 
100
- > **The clone does not keep the original's date.** Its start date is reset to roughly 14 days from
101
- > now and it comes back as a `DRAFT`. For a duplicate on a particular date, follow the clone with
102
- > an update — and note the draft permission above.
288
+ Then check the result's local time: `dateAndTimeSettings.formatted.startTime` in the response must
289
+ equal the original's (and the date must be the one asked for). If it doesn't, your UTC conversion
290
+ was off — fix the dates with an update before reporting.
103
291
 
104
292
  ## Update an event
105
293
 
106
- `PATCH /events/v3/events/{event.id}` with the fields to change nested under `event`:
294
+ `PATCH https://www.wixapis.com/events/v3/events/{event.id}` with the fields to change nested under `event`
295
+ ([Update Event](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/update-event) is the reference for this call):
107
296
 
108
297
  ```bash
109
298
  curl -X PATCH 'https://www.wixapis.com/events/v3/events/<EVENT_ID>' \
@@ -120,34 +309,100 @@ curl -X PATCH 'https://www.wixapis.com/events/v3/events/<EVENT_ID>' \
120
309
  }'
121
310
  ```
122
311
 
123
- The Events API takes **no field mask and no `revision`** on update — send only the fields you are
124
- changing. A `revision`, if you send one, is ignored rather than rejected.
312
+ The Events API takes **no `revision`** on update — a `revision`, if you send one, is ignored rather
313
+ than rejected. Send only the fields you are changing: title, descriptions, slug, guest-list and
314
+ display settings, dates and registration settings all apply as sent. The exceptions that need a
315
+ `fieldmask` are the venue and clearing a field (see below and
316
+ [Registration settings](#registration-settings)).
317
+
318
+ **Moving a date: send `startDate` and `endDate` together.** With `startDate` alone, a move to a
319
+ later day puts the start after the stored end and fails `400 event cannot have negative duration`.
320
+ `timeZoneId` is optional here, unlike on create; the stored one is kept. Express the
321
+ times in the event's own time zone before converting to UTC — see
322
+ [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event#date-and-time).
323
+ After the update, the response's `dateAndTimeSettings.formatted.startTime` must still be the original
324
+ local time when the user asked for the same time; if not, correct it before reporting.
325
+
326
+ **Changing the venue needs a `fieldmask`.** The reference schema does not list `fieldmask` — send it
327
+ anyway. Without it a `location` update returns `200` but saves only the name: the address is dropped
328
+ and `locationTbd` stays `true`. Send the venue with this mask:
329
+
330
+ ```bash
331
+ curl -X PATCH 'https://www.wixapis.com/events/v3/events/<EVENT_ID>' \
332
+ -H 'Content-Type: application/json' \
333
+ -H 'Authorization: <AUTH>' \
334
+ --data-binary '{
335
+ "event": {
336
+ "location": {
337
+ "name": "Grand Hall", "type": "VENUE", "locationTbd": false,
338
+ "address": { "country": "IE", "city": "Dublin", "postalCode": "D02 X285",
339
+ "streetAddress": { "number": "1", "name": "Grafton Street" } }
340
+ }
341
+ },
342
+ "fieldmask": "location.locationTbd,location.name,location.type,location.address",
343
+ "fields": ["DETAILS"]
344
+ }'
345
+ ```
346
+
347
+ Read `location` back: `locationTbd` must be `false` and `address.formattedAddress` present. The
348
+ address is geocoded, so `formattedAddress` can differ from what the user typed (a nearby building
349
+ or the street's local-language name) — confirm the city matches, and if it is a different place,
350
+ say so and resend a fuller address (street number, postal code, `subdivision`).
125
351
 
126
- **When moving a date, send `startDate` and `endDate` together.** `dateAndTimeSettings` is replaced
127
- wholesale rather than merged, so a patch carrying only `startDate` leaves the event with no end and
128
- fails `400 event cannot have negative duration` — an error naming neither the missing field nor the
129
- replacement. `timeZoneId` is optional here, unlike on create; the stored one is kept.
352
+ **Event page settings** go through the same `PATCH`, under `event`:
130
353
 
131
- Ticket definitions are the exception — `PATCH /events/v3/ticket-definitions/{ticketDefinition.id}`
132
- *does* require the current `revision`, which increments on every update.
354
+ | The user wants | Send under `event` | Read back with `fields` |
355
+ | --- | --- | --- |
356
+ | A different event URL | `"seoSettings": { "slug": "summer-gala-2027" }` | `SEO_SETTINGS` |
357
+ | Show the guest list to guests | `"guestListSettings": { "displayedPublicly": true }` | — |
358
+ | Hide the "details" button (registration-free events only) | `"eventDisplaySettings": { "hideEventDetailsButton": true }` | — |
359
+ | No event details page | `"eventDisplaySettings": { "hideEventDetailsPage": true }` — with external registration, visitors go straight to the external page | — |
360
+ | Video conferencing for an online event | `"onlineConferencing": { "enabled": true, "type": "MEETING" }` (`WEBINAR` for a webinar) — needs a connected conferencing provider, see below | `ONLINE_CONFERENCING_SESSION` |
361
+
362
+ - The URL slug is `seoSettings.slug` — the top-level `slug` is read-only. Lowercase letters,
363
+ numbers and hyphens only (`400 INVALID_SLUG_FORMAT`), up to 130 characters, unique on the site
364
+ (`428 SLUG_ALREADY_IN_USE`). The old URL redirects unless
365
+ `seoSettings.advancedSeoData.settings.preventAutoRedirect` is `true`.
366
+ - Online conferencing needs a set date — not while the date is TBD — and a connected conferencing
367
+ provider. On a site with none, `enabled: true` is accepted and **not applied**: the response
368
+ still reads `onlineConferencing.enabled: false`, `type` `UNKNOWN_CONFERENCE_TYPE`. Check the
369
+ response; if it is still `false`, tell the user to connect a provider in the event's online
370
+ settings instead of reporting it done. Reading the join links needs the
371
+ `ONLINE_CONFERENCING_SESSION` fieldset and its permission.
372
+
373
+ The agenda, policies and categories use the [Schedule Items](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/schedule-items), [Policies V2](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/policies-v2) and [Categories](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/categories) APIs.
374
+
375
+ Ticket definitions are the exception to the no-`revision` rule — their update *does* require the
376
+ current `revision` ([Update Ticket Definition](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/ticket-definitions-v3/update-ticket-definition)).
133
377
 
134
378
  ## Count events
135
379
 
136
- `POST /events/v3/events/query` and read `pagingMetadata.total`:
380
+ `POST https://www.wixapis.com/events/v3/events/query` and read `pagingMetadata.total`:
137
381
 
138
382
  ```json
139
383
  { "query": { "paging": { "limit": 100 } } }
140
384
  ```
141
385
 
142
- Query Events returns only published events — **drafts are excluded from both the results and the
143
- total**, and including them needs the draft permission above. Create published, or publish first,
144
- if the count is meant to include the event you just made.
386
+ Query Events returns every event that is not a draft — upcoming, started, **ended and cancelled
387
+ ones included** — so the total is not "upcoming events". Add `"filter": { "status": { "$eq": "UPCOMING" } }`
388
+ (or `$in` for several statuses) to count what the user means. **Drafts are excluded from both the
389
+ results and the total**, and including them needs `includeDrafts: true` and the draft permission
390
+ above. Each occurrence of a recurring event counts as one event.
391
+
392
+ To get a breakdown, `POST https://www.wixapis.com/events/v3/events/count-by-status` with a `facet`:
393
+
394
+ ```json
395
+ { "facet": ["status"] }
396
+ ```
145
397
 
146
- `POST /events/v3/events/count-by-status` exists, but an empty request body returns empty `facets`
147
- even when the site has events, so it is not the way to answer "how many events do I have".
398
+ returns `pagingMetadata.total` and `facets.status.counts`, for example
399
+ `{ "UPCOMING": 4, "CANCELED": 3, "ENDED": 1 }`. Without `facet` the `facets` object comes back
400
+ empty. It takes the same `filter` as Query Events.
148
401
 
149
402
  ## Gotchas & troubleshooting
150
403
 
404
+ - **A `200` on update does not prove the change applied** — read-only fields in the body are
405
+ dropped silently. Confirm from the returned event before telling the user it's done.
151
406
  - **An invalid enum value reports as a missing one** — a value outside an enum returns
152
407
  `<field> value is required` rather than "invalid value". If a field you *did* send is reported
153
408
  as required, suspect the value, not its presence.
@@ -155,9 +410,12 @@ even when the site has events, so it is not the way to answer "how many events d
155
410
  [Find an event](#find-an-event). The `WIX_EVENTS.READ_DRAFT_EVENTS` note on the Query Events
156
411
  reference describes the permission; it does not mean the caller has it.
157
412
  - Dates are always ISO-8601 strings, never `{seconds, nanos}`, and `timeZoneId` is required
158
- whenever `dateAndTimeSettings` is sent — see [Create an Event](create-wix-event.md).
413
+ whenever `dateAndTimeSettings` is sent on create — see [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event).
159
414
 
160
415
  ## Related APIs
161
- - **Wix Events V3**: [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3)
162
- - **Event Guests**: `POST /events/v2/guests/query` — who registered
163
- - [Create an Event](create-wix-event.md) — create body, dates, location, capacity, tickets, recurring
416
+ - **Wix Events V3**: [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3) — [Update Event](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/update-event)
417
+ - [Create an Event](https://dev.wix.com/docs/api-reference/business-solutions/events/skills/create-event) — create body, dates, location, capacity, recurring
418
+ - **Ticket Definitions V3**: [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/ticket-definitions-v3) — ticket types, prices, sale periods, currency
419
+ - **RSVP V2** and **Ticketing**: [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/rsvp-v2), [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing) — RSVPs, ticket holders, orders, check-in
420
+ - **Events Form**: [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/form) — form questions and registration messages
421
+ - **Schedule Items**, **Policies V2**, **Categories**: [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/schedule-items), [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/policies-v2), [REST](https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/categories) — agenda, policies, categories
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "Update Product with Options (Catalog V3)"
3
- description: "Start here for existing Catalog V3 product updates, including digital products that fail at add-to-cart with ITEM_NOT_FOUND_IN_CATALOG. Includes name lookup, Get Product, and PATCH in one flow, plus importing a supplied PDF and attaching it to the existing variant. Covers options, variant prices and SKUs, product visibility (hide/unhide through `visible`, never delete), description edits that preserve Rich Content, and revision-based updates. Read another recipe only for a field or operation this flow does not cover."
3
+ description: "Start here for existing Catalog V3 product updates, including digital products that fail at add-to-cart with ITEM_NOT_FOUND_IN_CATALOG. Includes name lookup, Get Product, and PATCH in one flow, plus importing a supplied PDF and attaching it to the existing variant. Covers options, variant prices and SKUs, variant shipping weight in the site's weight unit, product visibility (hide/unhide through `visible`, never delete), description edits that preserve Rich Content, and revision-based updates. Read another recipe only for a field or operation this flow does not cover."
4
4
  ---
5
5
  **RECIPE**: Business Recipe - Updating a Wix Store Product (Catalog V3)
6
6
 
@@ -156,7 +156,7 @@ For prepend, append, and text removal, execute the documented calls directly: th
156
156
  | Get Product | `GET https://www.wixapis.com/stores/v3/products/{productId}?fields=DESCRIPTION` | `product.id`, `product.revision`, and the full `product.description`. |
157
157
  | Update Product | `PATCH https://www.wixapis.com/stores/v3/products/{productId}` with `{ product: { id, revision, description }, fields: ["DESCRIPTION"] }`, using the read ID/revision and complete edited document. | `product.description` verifies the edit; `product.revision` is the new revision. |
158
158
 
159
- The request-level `fields` array projects response data; it is separate from `search.search.fields`, which selects the fields searched. A description-only PATCH needs only the three product fields shown above; variant, option, price, and physical-property fields belong to other update flows.
159
+ The request-level `fields` array projects response data; it is separate from `search.search.fields`, which selects the fields searched. A description-only PATCH needs only the three product fields shown above. Variant, option, and price changes use [Prepare a Price or SKU Update](#prepare-a-price-or-sku-update); shipping weight uses [Set Shipping Weight](#set-shipping-weight).
160
160
 
161
161
  Use a single read-modify-write operation: locate the product, read its description and revision, make the requested change in memory, then PATCH once and verify the response.
162
162
 
@@ -510,6 +510,61 @@ const skuPatchBody = { product: variantPatchProduct };
510
510
 
511
511
  Send the selected body to `PATCH https://www.wixapis.com/stores/v3/products/{productId}` in the same ExecuteWixAPI invocation as the name lookup and Get Product. Its response is `{ product: { id, revision, variantsInfo: { variants } } }`; verify the changed variant by its retained ID and confirm from that response. These examples provide the request fields and response paths for price and SKU changes; proceed directly after reading this recipe. Discover further schemas only for fields not covered here or a documented call that fails.
512
512
 
513
+ ### Set Shipping Weight
514
+
515
+ Shipping weight belongs to the variant: `variantsInfo.variants[].physicalProperties.weight`, a number in the site's weight unit. The unit is a site-wide, read-only setting at `product.physicalProperties.weightMeasurementUnitInfo.weightMeasurementUnit` (`KG` or `LB`). Get Product returns it only when `fields` includes `WEIGHT_MEASUREMENT_UNIT_INFO`; the default read omits it.
516
+
517
+ 1. Use the name lookup above, then Get Product with the preparation read plus the unit: `GET https://www.wixapis.com/stores/v3/products/{productId}?fields=WEIGHT_MEASUREMENT_UNIT_INFO&fields=VARIANT_OPTION_CHOICE_NAMES&fields=MERCHANT_DATA&fields=PRODUCT_CHOICES_MEDIA_REFERENCES&fields=PRODUCT_CHOICES_DISPLAY_IMAGE`.
518
+ 2. Convert the requested value to the returned unit in code, in either direction (1 kg = 2.20462262 lb). A value given without a unit is already in the site's unit.
519
+ 3. Build `variantPatchProduct` as in **Prepare a Price or SKU Update**, select the target variants, and set `physicalProperties.weight` only on them. A named choice (`Size = Large`) selects exactly one variant, as in the SKU example; if it matches none or several, ask the user. Set every variant only when the product has one variant or the user asked for all of them; otherwise ask which variant they mean. The other variants stay in the body unchanged.
520
+ 4. Send the PATCH with top-level `fields: ["WEIGHT_MEASUREMENT_UNIT_INFO"]`, in the same execution as the lookup and read. Do not send `product.physicalProperties`: product-level `physicalProperties` is accepted on create (see [Create Product (Catalog V3)](create-product-catalog-v3.md)) but not on update, where the unit cannot be changed and a PATCH that carries it is rejected with `400 productType cannot be provided with any these fields: physical_properties`.
521
+ 5. Confirm from the PATCH response: `product.variantsInfo.variants[].physicalProperties.weight` on the retained variant IDs, and the unit at `product.physicalProperties.weightMeasurementUnitInfo.weightMeasurementUnit`. Report both value and unit to the user, for example "0.5 kg (stored as 1.1023 lb, the store's unit)".
522
+
523
+ ```javascript
524
+ const unit = getResponse.product.physicalProperties.weightMeasurementUnitInfo.weightMeasurementUnit;
525
+ const requested = { value: 0.5, unit: "KG" }; // requested weight and its unit ("KG" or "LB")
526
+ const factor = requested.unit === unit ? 1 : unit === "LB" ? 2.20462262 : 1 / 2.20462262;
527
+ const weight = Number((requested.value * factor).toFixed(4));
528
+ const choice = { optionName: "Size", choiceName: "Large" }; // null when the user named no variant
529
+ const allVariants = false; // true when the user asked for every variant
530
+ const targets = choice
531
+ ? variants.filter(variant => variant.choices.some(c =>
532
+ c.optionChoiceNames.optionName === choice.optionName && c.optionChoiceNames.choiceName === choice.choiceName))
533
+ : variants;
534
+ if (choice && targets.length !== 1) throw new Error("Clarify which variant should receive the weight");
535
+ if (!choice && !allVariants && variants.length !== 1) throw new Error("Clarify whether every variant should receive the weight");
536
+ for (const variant of targets) variant.physicalProperties = { ...variant.physicalProperties, weight };
537
+ const weightPatchBody = { product: variantPatchProduct, fields: ["WEIGHT_MEASUREMENT_UNIT_INFO"] };
538
+ ```
539
+
540
+ Set `choice` to `null` and `allVariants` to `true` when the user asked for every variant. Send `weightPatchBody`, not a body rebuilt by hand: each variant in `variants` still carries its SKU, barcode, visibility and cost, which a PATCH without them drops.
541
+
542
+ For a simple product (one variant, no options) on a `KG` site whose variant has no SKU, barcode, cost or other state yet, the request is:
543
+
544
+ ```bash
545
+ curl -X PATCH "https://www.wixapis.com/stores/v3/products/{productId}" \
546
+ -H "Content-Type: application/json" \
547
+ -H "Authorization: <AUTH>" \
548
+ -d '{
549
+ "product": {
550
+ "id": "{productId}",
551
+ "revision": "{currentRevision}",
552
+ "options": [],
553
+ "variantsInfo": {
554
+ "variants": [{
555
+ "id": "{existingVariantId}",
556
+ "choices": [],
557
+ "price": { "actualPrice": { "amount": "{existingPrice}" } },
558
+ "physicalProperties": { "weight": 0.5 }
559
+ }]
560
+ }
561
+ },
562
+ "fields": ["WEIGHT_MEASUREMENT_UNIT_INFO"]
563
+ }'
564
+ ```
565
+
566
+ The response returns `product.variantsInfo.variants[0].physicalProperties.weight: 0.5` and `product.physicalProperties.weightMeasurementUnitInfo.weightMeasurementUnit: "KG"`, which confirm the stored weight and its unit.
567
+
513
568
  ### Update Variant Price Only
514
569
 
515
570
  Read `{existingVariantId}` off the Get Product response; a Search or Query Products result does not carry it. The curl below is for a single-variant product without options. For an optioned product, use the complete options/variants body in **Prepare a Price or SKU Update**.
@@ -607,6 +662,7 @@ Copy any other writable variant fields from the read product using the preparati
607
662
  | `price must not be empty` | A variant was sent without a price — including an existing variant rebuilt from only its `id` and the field being changed | Carry `price.actualPrice.amount` on every variant you send, not just new ones; copy it from the Get Product response for variants you are not repricing |
608
663
  | `variantsInfo is invalid: variants has size 0, expected 1 or more` | Variants were read from a Search or Query Products response, which does not return them | Re-read the product with Get Product and send its `variantsInfo.variants` |
609
664
  | `Missing option choices` or `INVALID_DEFAULT_VARIANT` | Product has options but at least one variant has no matching choices | Rebuild `variantsInfo.variants` so every variant includes choices for all product options |
665
+ | `productType cannot be provided with any these fields: physical_properties` | Sent `product.physicalProperties`, for example to set the weight unit | Leave it out; weight goes on `variantsInfo.variants[].physicalProperties.weight` — see [Set Shipping Weight](#set-shipping-weight) |
610
666
  | `DIGITAL_PRODUCT_CANNOT_BE_VISIBLE_IN_POS` | Sent `visibleInPos: true` on a digital product | Digital products can't be visible in POS; leave `visibleInPos` out of the body |
611
667
  | `ITEM_NOT_FOUND_IN_CATALOG` at add-to-cart, product exists | A `DIGITAL` variant has no `digitalProperties.digitalFile` | Attach a file — see [Attach a Digital File](#attach-a-digital-file) |
612
668
  | `exceeds available inventory` at add-to-cart, product exists | The variant has no stock (`DIGITAL` products included) | Update its stock through the Inventory API; inventory is separate from an Update Product PATCH |