@wix/agent-skills 1.15.0 → 1.15.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 (61) hide show
  1. package/README.md +8 -0
  2. package/package.json +1 -1
  3. package/skills/wix-app/references/DATA_COLLECTION.md +5 -3
  4. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +2 -0
  5. package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
  6. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -0
  7. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
  8. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +57 -0
  9. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +6 -0
  10. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +3 -1
  11. package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
  12. package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
  13. package/skills/wix-app/references/stores/INVENTORY.md +37 -0
  14. package/skills/wix-app/references/stores/QUERY.md +87 -0
  15. package/skills/wix-app/references/stores/WRITE.md +55 -0
  16. package/skills/wix-headless/SKILL.md +6 -1
  17. package/skills/wix-headless/entry/bootstrap.mjs +2 -2
  18. package/skills/wix-headless/entry/skill.md +0 -2
  19. package/skills/wix-headless/references/CAPABILITIES.md +6 -4
  20. package/skills/wix-headless/references/DISCOVERY.md +1 -1
  21. package/skills/wix-headless/references/FEEDBACK.md +16 -5
  22. package/skills/wix-headless/references/SDK_HANDOFF.md +2 -0
  23. package/skills/wix-headless/references/SEED.md +3 -1
  24. package/skills/wix-headless/references/SETUP.md +1 -1
  25. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +42 -2
  26. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +5 -1
  27. package/skills/wix-headless/references/inline-recipes/setup-forms.md +1 -1
  28. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +21 -5
  29. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +3 -1
  30. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
  31. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +38 -2
  32. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +20 -1
  33. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +1 -1
  34. package/skills/wix-headless/references/managed/CONNECT.md +1 -1
  35. package/skills/wix-headless/references/managed/CREATE.md +1 -1
  36. package/skills/wix-headless/references/managed/DEPLOYMENT.md +22 -0
  37. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +8 -0
  38. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +8 -0
  39. package/skills/wix-manage/SKILL.md +8 -5
  40. package/skills/wix-manage/references/analytics/query-site-analytics.md +66 -9
  41. package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
  42. package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
  43. package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
  44. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +76 -42
  45. package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
  46. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
  47. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +225 -144
  48. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +19 -10
  49. package/skills/wix-manage/references/sites/site-import.md +270 -0
  50. package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
  51. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +16 -10
  52. package/skills/wix-vibe-headless/SKILL.md +6 -0
  53. package/skills/wix-vibe-headless/platforms/README.md +22 -0
  54. package/skills/wix-vibe-headless/platforms/base44.md +142 -0
  55. package/skills/wix-vibe-headless/platforms/generic.md +87 -0
  56. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +5 -1
  57. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +3 -1
  58. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +2 -1
  59. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +84 -55
  60. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +11 -2
  61. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +5 -1
@@ -22,10 +22,10 @@ A concise checklist for preparing any new Wix site that uses the Online Stores a
22
22
 
23
23
  ### STEP 1: Clean the store — remove the default sample products
24
24
 
25
- A freshly provisioned Wix Stores app comes pre-seeded with demo/sample products. Remove them **before** creating yours, so the storefront shows only your catalog. Do this **first** — cleaning before you create guarantees the ids you delete are the install's samples, never your own products.
25
+ A freshly provisioned Wix Stores app comes pre-seeded with demo/sample products. **Only remove products that are obviously the install's own demo/sample data on a fresh install.** Do **not** assume the existing products are samples: the site may already hold the owner's **real catalog** (a connect/iterate run, or an owner-populated store). If what's there isn't obviously install demo data, or you're unsure, **do not delete it — ask the user first** (`SEED.md`: seeding is additive; deleting real content needs the owner's approval). When they clearly are the install's samples, remove them **before** creating yours so the storefront shows only the intended catalog.
26
26
 
27
27
  1. **List the existing products** — `POST https://www.wixapis.com/stores/v3/products/query` with body `{"query": {"paging": {"limit": 50}}}`. Collect every `product.id` from the response.
28
- 2. **Bulk-delete them in one call** — `POST https://www.wixapis.com/stores/v3/bulk/products/delete` with body `{"productIds": ["<id1>", "<id2>", …]}` (the ids from step 1; up to 100 per call). The query on a fresh install returns the sample products; delete exactly those ids.
28
+ 2. **Bulk-delete them in one call** — `POST https://www.wixapis.com/stores/v3/bulk/products/delete` with body `{"productIds": ["<id1>", "<id2>", …]}` (the ids from step 1; up to 100 per call). On a fresh install the query returns only the install's sample products — delete those. **If it returns anything that could be the owner's real catalog, stop and ask first** (above).
29
29
 
30
30
  ### STEP 2: Bulk-create the products (with options)
31
31
 
@@ -160,9 +160,25 @@ The request body is `items` (each with the product's `catalogItemId` and the Sto
160
160
 
161
161
  **Only when `imagery` is on** (`SEED.md` § "Entity images"). Products were created text-only above; this pass-2 step writes a generated brand image onto each. Generate + import per `references/IMAGE_GENERATION.md`, keep `file.url`, then PATCH the product.
162
162
 
163
- - The field that persists is **`media.itemsInfo.items`**, NOT `media.main`. `PATCH /stores/v3/products/{id}` setting `"media": { "itemsInfo": { "items": [ { "url": "<static.wixstatic.com/…>", "altText": "…" } ] } }`; the image may be referenced by a `static.wixstatic.com` **`url`** or by its Wix Media **`id`** (`<hash>~mv2.png`), either works.
164
- - **⚠️ `media.main` set ALONE silently no-ops** — a `200` comes back but re-query shows no image; that is the trap. The server promotes the first `itemsInfo.items` entry to `media.main` for you, so **verify success by reading `media.main`** (`media.main.image.url` resolves to a `static.wixstatic.com` URL). Conversely `media.itemsInfo` is **write-only** — it comes back `null` on read, so never assert on it.
165
- - **428 prevention:** first `GET /stores/v3/products/{id}` for its **`revision` + `options` + `variantsInfo`** and echo all three back in the PATCH body — do **not** send a field mask (the validator runs before masking).
163
+ **The exact working call — do this per product (getting it right first time avoids a multi-round debug loop):**
164
+
165
+ 1. **`GET https://www.wixapis.com/stores/v3/products/{id}`** → read `revision`, `options`, and `variantsInfo` from **`response.product.*`**. The product is **nested under a `product` key** — the GET response is *not* flat.
166
+ 2. **`PATCH https://www.wixapis.com/stores/v3/products/{id}`** with the body below. **⚠️ Everything nests under a `product` wrapper.** Putting `id` / `revision` / `media` at the **root** fails `400 "product is invalid: revision must not be empty"` — the #1 cause of the loop here. **Do not send a field mask** (the validator runs before masking → `428`):
167
+
168
+ ```json
169
+ {
170
+ "product": {
171
+ "id": "<product id>",
172
+ "revision": "<revision from the GET — required; omitting it is the 400 above>",
173
+ "media": { "itemsInfo": { "items": [ { "url": "<static.wixstatic.com/…>", "altText": "…" } ] } },
174
+ "options": "<echo the GET's product.options, unchanged>",
175
+ "variantsInfo": "<echo the GET's product.variantsInfo, unchanged>"
176
+ }
177
+ }
178
+ ```
179
+ The image may be referenced by a `static.wixstatic.com` **`url`** or by its Wix Media **`id`** (`<hash>~mv2.png`) — either works.
180
+
181
+ - The field that persists is **`media.itemsInfo.items`**, NOT `media.main`. **⚠️ `media.main` set ALONE silently no-ops** — a `200` comes back but re-query shows no image; that is the trap. The server promotes the first `itemsInfo.items` entry to `media.main` for you, so **verify success by reading `media.main.image.url`** — it must resolve to a `static.wixstatic.com` URL. Check that nested string, **not** the presence of `media.main` (which can be a non-null object with no image and makes a failed attach look successful). Conversely `media.itemsInfo` is **write-only** — it comes back `null` on read, so never assert on it.
166
182
  - Send **one image per product** (primary); a larger gallery is out of scope for the seed.
167
183
  - **Never block on image failure** — skip and leave the product text-only.
168
184
 
@@ -13,6 +13,8 @@ A concise checklist for preparing any new Wix site that uses the Wix Portfolio a
13
13
 
14
14
  > **API surfaces:** everything is the Portfolio **v1** API on `https://www.wixapis.com/portfolio/v1/...` — `/collections` and `/projects`. Use the **public `/portfolio/v1/...`** form; the method pages' schema headers show an internal `/portfolio/collections/api/v1/...` / `/portfolio/projects/projects/api/v1/...` URL — **do not** call those.
15
15
 
16
+ > **This recipe seeds content and assumes the Portfolio app is already installed.** Installing apps is a separate, earlier step in the run (the **Setup** step — `SETUP.md` — runs before any seeding); this recipe only *creates content* on an already-installed app. So if a call returns **`428` / `APP_NOT_INSTALLED`**, it means the Setup step was skipped for Portfolio — **fail loudly with the response verbatim** and let Setup handle the install. Do **not** fabricate a limitation ("I can't install apps / do it in your Wix dashboard") — installing is not this recipe's job, but it *is* something the skill does, in Setup.
17
+
16
18
  > **Visibility is `hidden`, not `visible` — and it defaults to visible.** Portfolio's polarity is the **inverse** of Stores/Restaurants: entities carry a `hidden` boolean that **defaults to `false` (shown)**. So a collection/project created with no `hidden` field **appears on the live site** — you do **not** need to set anything to make it visible. Only set `"hidden": true` to hide one. (Send a plain boolean — `"hidden": false` — never a `{"value": …}` wrapper.) **Note:** when you omit `hidden` (or send `false`), the field is **absent** from the create/list response — proto3 drops false defaults — so *absent* reads as *false* reads as *shown*; `hidden` appears in the response only when you explicitly sent `true`. Don't treat a missing `hidden` as an error.
17
19
 
18
20
  ---
@@ -24,7 +26,7 @@ A concise checklist for preparing any new Wix site that uses the Wix Portfolio a
24
26
 
25
27
  ### STEP 0: Clean the portfolio — remove the default sample data
26
28
 
27
- A freshly installed Wix Portfolio app comes pre-seeded with **one sample collection ("My Portfolio") and several sample projects** ("Editorial Portraits", "Seasonal Lookbook", …), all assigned to that collection. Remove them **before** creating yours, so the site shows only your content. Do this **first** — cleaning before you create guarantees the ids you delete are the install's samples, never your own.
29
+ A freshly installed Wix Portfolio app comes pre-seeded with **one sample collection ("My Portfolio") and several sample projects** ("Editorial Portraits", "Seasonal Lookbook", …), all assigned to that collection. **Only remove data that is obviously the install's own sample content on a fresh install** (that "My Portfolio" collection and its sample projects). Do **not** assume existing collections/projects are samples: the site may already hold the owner's **real content** (a connect/iterate run, or an owner-populated portfolio). If what's there isn't obviously install sample data, or you're unsure, **do not delete it — ask the user first** (`SEED.md`: seeding is additive; deleting real content needs the owner's approval). When it clearly is the install's samples, remove it **before** creating yours so the site shows only the intended content.
28
30
 
29
31
  **Delete children before the parent — projects first, then collections.** (Deleting a collection does not clean up the projects that reference it.)
30
32
 
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: "Setup Restaurant Experiences"
3
+ description: Seeds Wix Table Reservations **Experiences** — a reservation that IS a special dining occasion at the restaurant (wine tasting, cheese pairing, chef's table, tasting-menu evening), as opposed to a plain table booking or a Bookings service. Points to the live docs for the full create payload and carries only the earned gotchas the docs don't state (field-shape drift, payment policy, premium gating, when-to-use). The occasions/prices/schedule come from the request.
4
+ ---
5
+ **RECIPE**: Business Recipe – Experiences Setup for Wix Table Reservations (Experiences API)
6
+
7
+ > **Standard call shape (every curl below).** `<AUTH>` = `Authorization: Bearer <TOKEN>` **and** `wix-site-id: <SITE_ID>`. Body-bearing requests also need `Content-Type: application/json`.
8
+
9
+ ## When to use experiences (and when NOT to)
10
+
11
+ An **experience** is a **reservation that is itself a curated dining occasion at the restaurant** — it books seats at the restaurant's own reservation location but overrides the location's defaults with its own name, description, per-guest payment policy, party-size, schedule, and booking form. Reach for it when the brief names a **wine tasting, cheese/wine pairing, chef's table, or tasting-menu evening**.
12
+
13
+ - **Experience vs plain reservation:** a plain reservation just books a table; an experience books a *named, scheduled, often paid occasion*. Same booking mechanism, different intent.
14
+ - **Experience vs `bookings`:** the separate `bookings` vertical is for a **service/appointment/class with a provider** (spa, tutor, studio). If a restaurant *hosts and seats* the occasion, it's an experience — not a booking. (See `CAPABILITIES.md` — "Experiences vs bookings".)
15
+
16
+ ## Prerequisites
17
+
18
+ - The **Table Reservations app** is installed (`SETUP.md` §2). **Experiences are a feature of that app — there is no separate app to install.**
19
+ - A **reservation location exists** — the install auto-provisions the default one (`setup-restaurant-reservations.md` STEP 1). Discover it and take its **`id`**; every experience is created against that `reservationLocationId`. No menu dependency.
20
+
21
+ ## The docs are the source of truth for the payload — read them, don't guess
22
+
23
+ Read these `.md` twins directly (first priority; append `.md` to the article URL). This recipe deliberately does **not** re-inline the full schema — it changes, and the docs carry the complete, current field tree:
24
+
25
+ - **About Experiences** (what they are, how they bind to reservations/locations/time-slots): <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/experiences/introduction.md>
26
+ - **Create Experience** (the full create payload + example): <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/experiences/create-experience.md>
27
+ - **Query / Get Experience**: <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/experiences/query-experiences.md> · <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/experiences/get-experience.md>
28
+ - **Sample flow** (showcase + book an experience end-to-end): <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/experiences/sample-flows.md>
29
+
30
+ Endpoint: `POST https://www.wixapis.com/table-reservations/experiences/v1/experiences`.
31
+
32
+ ## The flow
33
+
34
+ 1. **Discover the reservation location** (`setup-restaurant-reservations.md` STEP 1) → keep `reservationLocationId`.
35
+ 2. **Create one experience per named occasion.** Build the body from the request (occasion name, per-guest price, party size, days/times, duration) per the **Create Experience** doc. Keep each returned `experience.id`. Set `configuration.visible: true` so it shows on the live site.
36
+ 3. Per the "simple seeds" default, create only the occasions the brief names (typically 1–3); don't invent a full calendar.
37
+
38
+ Minimal shape (the doc has the complete tree — this is only the skeleton to orient against):
39
+
40
+ ```jsonc
41
+ { "experience": {
42
+ "reservationLocationId": "<reservationLocationId>",
43
+ "configuration": {
44
+ "displayInfo": { "name": "…", "shortDescription": "…" },
45
+ "paymentPolicy": { "paymentPolicyType": "PER_GUEST", "perGuestOptions": { "price": "55.00" } },
46
+ "onlineReservations": {
47
+ "partySize": { "min": 2, "max": 8 },
48
+ "minimumReservationNotice": { "number": 24, "unit": "HOURS" },
49
+ "maximumReservationNotice": { "number": 60, "unit": "DAYS" },
50
+ "approval": { "mode": "AUTOMATIC" },
51
+ "maxGuests": { "number": 16 },
52
+ "businessSchedule": { "durationInMinutes": 90, "entries": [
53
+ { "recurrence": "WEEKLY", "weeklyOptions": { "startDaysAndTimes": [ { "day": "FRIDAY", "time": "18:30" } ] } }
54
+ ] }
55
+ },
56
+ "visible": true
57
+ }
58
+ } }
59
+ ```
60
+
61
+ ## Earned gotchas (what the docs won't tell you — verified against the live API)
62
+
63
+ - **⚠️ Notice fields are FLAT under `onlineReservations`, not wrapped.** Send `onlineReservations.minimumReservationNotice` and `.maximumReservationNotice` **directly**. The Create-Experience doc *example* nests them inside a `noticePeriod` object — that wrapper is stale; the required-parameters list (same page) shows them flat, and the flat form is what the API accepts. Follow the parameter tree, not the example blob.
64
+ - **Required at create** (creation 400s without them): `reservationLocationId`; `configuration.displayInfo.name`; `configuration.paymentPolicy.paymentPolicyType`; `configuration.onlineReservations.partySize.min/max`, `.approval.mode`, `.minimumReservationNotice.{number,unit}`, `.maximumReservationNotice.{number,unit}`, `.maxGuests.number`. (Confirm against the doc — the list evolves.)
65
+ - **`paymentPolicyType`: `PER_GUEST` or `FREE`.** `PER_GUEST` needs `perGuestOptions.price` (decimal **string**, e.g. `"55.00"`; currency is the site's). `FREE` needs no price. Creating a `PER_GUEST` experience succeeds on a free site, but a guest **completing** a paid experience booking needs the same premium + payment provider that any paid reservation does (below).
66
+ - **`businessSchedule.entries[]`** carries the recurrence: `WEEKLY` with `weeklyOptions.startDaysAndTimes[{ day, time }]` (`day` is `MONDAY`…`SUNDAY`, `time` is `"HH:mm"`), or `ONE_TIME` with `oneTimeOptions`. `durationInMinutes` sits on `businessSchedule`, not per entry.
67
+ - **⚠️ Booking an experience is PREMIUM-GATED**, exactly like turning on online reservations (`setup-restaurant-reservations.md` STEP 3 → `428 PREMIUM_ONLY` on a free site). **Creating** the experience works on a free site; **booking** it at runtime needs the site to be premium with online reservations enabled. Record the precondition in the handoff; **don't fail the seed** over it.
68
+ - **Cover image (opt-in):** `configuration.displayInfo.coverImage` takes a Wix Media image (`{ id, url, … }`) — attach it in the imagery pass (`IMAGE_GENERATION.md`) only when `imagery` is on; otherwise omit and the frontend renders a themed block.
69
+ - **⚠️ Heads-up for the booking UI (not a seed step):** the experiences **sample-flow doc** (linked above) tells the frontend to call `getScheduledTimeSlots` with the experience GUID — **that parameter doesn't exist** (neither the SDK nor REST scopes slots to an experience), so it silently returns the *location's* slots. The frontend must instead project bookable times from the experience's own `businessSchedule`. Full detail: `how-to-code-restaurant-reservations.md` (Gotcha C).
70
+
71
+ ## Keep
72
+
73
+ Nothing crosses into the handoff — experiences are read **live** on the frontend (`queryExperiences`). Track the created `experienceId`(s) only transiently (to confirm success / attach cover images). Frontend booking contract: `how-to-code-restaurant-reservations.md` (Experiences section).
@@ -32,7 +32,42 @@ A checklist for online ordering on a Wix site that already has a **Restaurants M
32
32
  ---
33
33
 
34
34
  ## Article: Steps for Setting Up Wix Restaurants Online Ordering
35
- **YOU MUST** complete all the following steps **in the given order** (1-3) without skipping any and **without requiring additional user input**. Every step is idempotent — it verifies the auto-provisioned state and only writes when the request calls for a change or the state is wrong.
35
+ **YOU MUST** complete all the following steps **in the given order** (0-3) without skipping any and **without requiring additional user input**. Every step is idempotent — it verifies the auto-provisioned state and only writes when the request calls for a change or the state is wrong.
36
+
37
+ ### STEP 0: Configure the business location address (required for ordering to actually work)
38
+
39
+ **⚠️ Online ordering needs a business location with a REAL address — without it, ordering is "testing only" and checkout breaks at runtime.** This is the #1 silent gap for ordering sites: the Wix docs are explicit — *"If locations aren't configured on a site, online ordering functionality is limited to testing only, and in some cases may not work as expected"* (<https://dev.wix.com/docs/api-reference/business-solutions/restaurants/about-business-locations.md>). A site provisions with a **default location that has no street address** (only an auto-defaulted country/city), which counts as *not configured*. Fix it here, before verifying the operation.
40
+
41
+ The **default** business location is the one online ordering reads; its address also **propagates to Site Properties** (the site's business address that the Business Manager shows). Docs: Locations API <https://dev.wix.com/docs/api-reference/business-management/locations/introduction.md> · Update Location <https://dev.wix.com/docs/api-reference/business-management/locations/update-location.md>.
42
+
43
+ 1. **List locations** and take the default (`GET https://www.wixapis.com/locations/v1/locations` → the entry with `default: true`). Keep its **`id`** and **`revision`**. (There is normally exactly one, auto-provisioned.)
44
+ 2. **Overwrite it with a real address** via **Update Location** — `PUT https://www.wixapis.com/locations/v1/locations/<id>`. Use the address from the request; the timezone should match it.
45
+
46
+ ```bash
47
+ curl -sS <AUTH> -X PUT "https://www.wixapis.com/locations/v1/locations/<id>" \
48
+ -d '{
49
+ "location": {
50
+ "id": "<id>", "revision": "<revision>", "default": true,
51
+ "name": "<Business name>", "timeZone": "Europe/Paris",
52
+ "email": "<email>", "phone": "<phone>",
53
+ "address": {
54
+ "country": "FR", "subdivision": "FR-75", "city": "Paris", "postalCode": "75001",
55
+ "streetAddress": { "number": "18", "name": "Rue des Lumières" },
56
+ "formattedAddress": "18 Rue des Lumières, 75001 Paris, France"
57
+ }
58
+ }
59
+ }'
60
+ ```
61
+
62
+ **⚠️ CRITICAL — earned gotchas (verified against the live API):**
63
+ - **Update Location is a FULL OVERRIDE, not a partial update** (the docs say so explicitly). Send the **whole** `location` object, not just the address — omitted fields are wiped. A partial "just the address" body is the classic *"it reported success but nothing changed / it's not in the Business Manager"* failure.
64
+ - **Echo `"default": true`.** The default flag isn't in the documented request params, but omitting it on a full override reads as flipping it off and **400s** with `CHANGE_DEFAULT_FORBIDDEN`. (Good news: this API fails **loudly** — prefer it over a field-mask-less Site-Properties `PATCH`, which silently no-ops.)
65
+ - **`revision` is mandatory** (from STEP 0.1) and increments each update; a stale one 400s.
66
+ - **`address.country` is a 2-letter ISO-3166 code** (`"FR"`, `"US"`), not a full name.
67
+ - **No default location at all?** (a bare Orders-only site can have none — operations then carry `businessLocationId: "none"`.) **Create** one instead: `POST https://www.wixapis.com/locations/v1/locations` with the same `location` body (no `id`/`revision`), then it becomes the site's location and operations auto-bind on first-location-add (see about-business-locations). Docs: <https://dev.wix.com/docs/api-reference/business-management/locations/create-location.md>.
68
+ - **The operation's `businessLocationDetails` is a denormalized snapshot with eventual-consistency lag** — after the update it may briefly still show the old placeholder address (e.g. a "San Francisco" default). That's cosmetic and self-heals; checkout reads the configured location/site address, which is correct immediately. Don't loop trying to "force" the snapshot.
69
+
70
+ **Where the address comes from:** the request. If the brief names the restaurant's address, use it. **If it doesn't, a real address is genuine owner info the skill can't invent** — set a clearly-marked placeholder and **flag prominently in the handoff/summary that the owner must set their real business address before ordering works** (this is exactly the "make it work end-to-end" gap). Pair this with the premium/payment precondition below.
36
71
 
37
72
  ### STEP 1: Verify (and if needed enable) the operation
38
73
 
@@ -147,12 +182,13 @@ Response — one object per menu; match by **`menuId`**:
147
182
  - The settings object's `businessLocationId` (inherited from its menu, `"none"` for a locationless site) must match the operation's `businessLocationId` for ordering to bind — both are `"none"` here, so they match. Don't override it.
148
183
  - To make a menu **display-only** (not orderable), PATCH that one to `onlineOrderingEnabled: false`; leave the rest enabled.
149
184
 
150
- > **⚠️ LIVE CHECKOUT PRECONDITION (note, don't fail).** A visitor can browse the orderable menu and build a cart with just this setup, but **completing a paid order** at runtime needs the site to have a **premium plan and a configured payment method** (checkout rides on Wix eCommerce). That's a site-provisioning concern, not a seed step — surface it in the handoff; do **not** fail the seed over it.
185
+ > **⚠️ LIVE CHECKOUT PRECONDITIONS (note, don't fail).** A visitor can browse the orderable menu and build a cart with just this setup, but **completing an order** end-to-end needs three things beyond the seed: **(1) a configured business location with a real address** (STEP 0 — without it ordering is "testing only"); **(2) a premium plan**; and **(3) a configured payment method** (checkout rides on Wix eCommerce). STEP 0 handles (1) via API; (2) and (3) are **dashboard/premium provisioning** the skill can't do headlessly. Surface all three in the handoff; do **not** fail the seed over them.
151
186
 
152
187
  ---
153
188
 
154
189
  ## Conclusion
155
190
  Following these steps confirms/completes online ordering for a site whose **Menus** backend is already seeded:
191
+ - A **business location with a real address is configured first** (STEP 0, Update Location full-override) — without it ordering is "testing only"; the address propagates to Site Properties. If the brief gives no address, a placeholder is set and the owner is flagged to fix it.
156
192
  - The **Orders-app install already produced a working setup** — an `ENABLED` operation with Pickup + Delivery attached and every menu ordering-enabled; this recipe **verifies** that and **reshapes it to the request**, it does not build it from nothing.
157
193
  - The **operation** is discovered, never created (operations/groups are auto-provisioned); it stays `onlineOrderingStatus: "ENABLED"`.
158
194
  - **Fulfillment methods** are reconciled to the request — auto-created ones customized (decimal-string `fee`/`minOrderPrice`, weekly `availability`), unwanted ones disabled, extra ones created **and attached** to the operation via `fulfillmentIds` (a create does not auto-attach).
@@ -24,7 +24,26 @@ A checklist for turning on **table reservations** for a Wix site.
24
24
  ---
25
25
 
26
26
  ## Article: Steps for Setting Up Wix Table Reservations
27
- **YOU MUST** complete the steps in order (1-3) without requiring additional user input. Every step is idempotent — it verifies the auto-provisioned state and only writes when the request calls for a change.
27
+ **YOU MUST** complete the steps in order (0-3) without requiring additional user input. Every step is idempotent — it verifies the auto-provisioned state and only writes when the request calls for a change.
28
+
29
+ ### STEP 0: Configure the business location address
30
+
31
+ **Set a real street address on the site's default business location.** The reservation location **is** the site's business location (same entity — the reservation location's `location` object is the business `Location`), and a fresh site provisions it with an **empty street address**. A complete restaurant site needs a real address: it's where guests come, it's what the reservation/experience surface and the Business Manager show, and it's the **same** location online ordering requires (so a site with both ordering and reservations does this once). Set it via the **Locations API** — `PUT https://www.wixapis.com/locations/v1/locations/<id>` (Update Location). Docs: <https://dev.wix.com/docs/api-reference/business-management/locations/introduction.md>.
32
+
33
+ ```bash
34
+ # 1) find the default location + its revision
35
+ curl -sS <AUTH> -X GET "https://www.wixapis.com/locations/v1/locations" # take the entry with "default": true
36
+ # 2) overwrite it with a real address (FULL override — send the whole object)
37
+ curl -sS <AUTH> -X PUT "https://www.wixapis.com/locations/v1/locations/<id>" \
38
+ -d '{ "location": {
39
+ "id": "<id>", "revision": "<revision>", "default": true,
40
+ "name": "<Business name>", "timeZone": "Europe/Paris",
41
+ "address": { "country": "FR", "subdivision": "FR-75", "city": "Paris", "postalCode": "75001",
42
+ "streetAddress": { "number": "18", "name": "Rue des Lumières" },
43
+ "formattedAddress": "18 Rue des Lumières, 75001 Paris, France" } } }'
44
+ ```
45
+
46
+ **⚠️ Earned gotchas (identical to `setup-restaurant-orders.md` STEP 0 — the same call):** Update Location is a **full override**, not a partial update (a "just the address" body is the classic *"reported success, nothing changed"* failure); **echo `"default": true`** or it 400s `CHANGE_DEFAULT_FORBIDDEN`; **`revision` is mandatory**; **`country` is a 2-letter ISO code**. The address also propagates to Site Properties. **Where the address comes from:** the request; if the brief names none, set a clearly-marked placeholder and **flag in the handoff that the owner must set their real address**.
28
47
 
29
48
  ### STEP 1: Discover the auto-provisioned default reservation location
30
49
 
@@ -24,7 +24,7 @@ A concise checklist for preparing any new Wix site that uses the Wix Restaurants
24
24
 
25
25
  ### STEP 0: Clean the install — remove the default sample menu
26
26
 
27
- **A freshly installed Wix Restaurants Menus app ships a fully populated default "Dinner Menu"** (roughly one menu, ~4 sections, ~21 items). Remove it **before** creating yours, so the storefront shows only your menu. Do this **first** — cleaning before you create guarantees the ids you delete are the install's samples, never your own entities. Delete children before parents (items → sections → menus):
27
+ **A freshly installed Wix Restaurants Menus app ships a fully populated default "Dinner Menu"** (roughly one menu, ~4 sections, ~21 items). **Only remove the menu when it's obviously the install's own default "Dinner Menu" on a fresh install.** Do **not** assume an existing menu is a sample: the site may already hold the owner's **real menu** (a connect/iterate run, or an owner-populated site). If what's there isn't obviously install sample data, or you're unsure, **do not delete it — ask the user first** (`SEED.md`: seeding is additive; deleting real content needs the owner's approval). When it clearly is the install's sample, remove it **before** creating yours so the storefront shows only the intended menu (children before parents: items → sections → menus):
28
28
 
29
29
  1. **Bulk-delete items** — `GET https://www.wixapis.com/restaurants/menus/v1/items` (collect every `items[].id`), then `DELETE https://www.wixapis.com/restaurants/menus/v1/bulk/items/delete` with body `{"ids": ["<id>", …]}`.
30
30
  2. **Bulk-delete sections** — `GET https://www.wixapis.com/restaurants/menus/v1/sections` (collect every `sections[].id`), then `DELETE https://www.wixapis.com/restaurants/menus/v1/bulk/sections/delete` with body `{"ids": ["<id>", …]}`.
@@ -71,4 +71,4 @@ static-hosting fixes in **`references/managed/DEPLOYMENT.md`** — the entry fil
71
71
  `index.html`, and `outputDirectory` must point at the directory holding it (init's `./dist` default is
72
72
  wrong for static). Then finalize per **`references/managed/DEPLOYMENT.md`** (`npx @wix/cli@latest release` — Wix
73
73
  publishes + registers the origin OOTB). Close with a short summary (apps installed, content seeded, what
74
- was wired, live URL).
74
+ was wired) and **both links — the live site URL and the site dashboard `https://manage.wix.com/dashboard/<SITE_ID>`** (see `managed/DEPLOYMENT.md` § "Give the user both links"; `<SITE_ID>` is the `siteId` in scratch), then run the feedback checkpoint (`managed/DEPLOYMENT.md` § "Before you sign off").
@@ -127,4 +127,4 @@ depends on `frontendFramework` (§0):
127
127
  - **Non-Astro** → the framework's own build (e.g. `npm run build`); a static (no-build) site skips this.
128
128
  `release` publishes whatever `site.outputDirectory` points at (default `./dist`).
129
129
 
130
- Close with a short summary (apps installed, content seeded, pages built, live URL).
130
+ Close with a short summary (apps installed, content seeded, pages built) and **both links — the live site URL and the site dashboard `https://manage.wix.com/dashboard/<SITE_ID>`** (see `managed/DEPLOYMENT.md` § "Give the user both links"; `<SITE_ID>` is the `siteId` in scratch), then run the feedback checkpoint (`managed/DEPLOYMENT.md` § "Before you sign off").
@@ -14,6 +14,28 @@ CI=1 npx @wix/cli@latest release
14
14
  - The deployed origin is registered on the OAuth app automatically — the frontend's visitor SDK calls are accepted from the live URL with no extra step.
15
15
  - The published URL is printed on stdout (`Site published on <url>`).
16
16
 
17
+ ## Give the user both links — the live site **and** the dashboard
18
+
19
+ When you close the run, surface **two** links, not one:
20
+
21
+ 1. **The live site URL** — the `Site published on <url>` value from release above.
22
+ 2. **The site dashboard (Business Manager)** — `https://manage.wix.com/dashboard/<SITE_ID>`, where `<SITE_ID>` is the `siteId` held in scratch (read from `wix.config.json`). This is where the owner manages the site behind the headless frontend — view store orders, edit content, manage members, etc. **Always include it**: a headless site has no editor button, so without this link the owner has no obvious way back into their own backend, and the seeded content/apps look unreachable.
23
+
24
+ Present them plainly, e.g.:
25
+
26
+ ```
27
+ Live site: <published-url>
28
+ Dashboard: https://manage.wix.com/dashboard/<SITE_ID>
29
+ ```
30
+
31
+ ## Before you sign off — feedback checkpoint
32
+
33
+ This is the last step of the run — the natural point to check `../FEEDBACK.md`. Don't wait for the
34
+ user to complain: run a quick self-check over the whole session (not just this final step) — a
35
+ confusing API response, a doc that was wrong/missing, an extra retry, a workaround you had to invent,
36
+ anything that cost more turns than it should have. If anything qualifies, **offer** to relay it to Wix
37
+ as you deliver the links above; send only after an explicit yes.
38
+
17
39
  ## Member login on a **non-Astro** frontend — register the callback URI (post-release)
18
40
 
19
41
  **Only when the run has member login on a non-Astro SPA/static frontend using the Wix login page** (`inline-recipes/how-to-code-members-non-astro.md` — the `getAuthUrl` → `/callback` handshake). Astro's built-in `/api/auth/*` callback shapes are auto-registered; a **non-Astro SPA's own callback path is not** — and **login stays dead (4xx on the login redirect) until you register it**. This is a genuine gap `wix release` does *not* close for you.
@@ -5,3 +5,11 @@ For a **self-managed** project, the deploy/finalize steps are **not yet decided
5
5
  Because hosting is the user's (not Wix's), this will likely mirror the self-hosted finalize pattern — **publish the metasite** and **register the deployed origin** on the OAuth app once the live URL is known (see `../stripe/DEPLOYMENT.md` for that shape) — but the exact mechanism, and how credentials/URL are supplied, are TBD.
6
6
 
7
7
  > **TBD.** Until this is defined, tell the user that `self-managed` deployment isn't wired yet, and that the frontend's visitor calls will be rejected until its origin is registered on the OAuth app.
8
+
9
+ ## Before you sign off — feedback checkpoint
10
+
11
+ This is the last step of the run — the natural point to check `../FEEDBACK.md`. Don't wait for the
12
+ user to complain: run a quick self-check over the whole session — a confusing API response, a doc
13
+ that was wrong/missing, an extra retry, a workaround you had to invent, anything that cost more turns
14
+ than it should have. If anything qualifies, **offer** to relay it to Wix before you close out; send
15
+ only after an explicit yes.
@@ -61,3 +61,11 @@ curl -sS -w "\nHTTP_STATUS:%{http_code}" -X PATCH \
61
61
  If the agent isn't the one deploying — the user will deploy the site themselves — the skill **cannot know the deployed URL**, so it cannot register the origin. (It can still publish the site — step 1 doesn't need the URL.) **Flag the origin step to the user clearly**, e.g.:
62
62
 
63
63
  > *"One required step remains before the frontend can talk to Wix: once your site is live, its URL must be registered on the Wix OAuth app (allowed domains). Give me the deployed URL — or re-run this skill with it — and I'll register it. Until then, Wix SDK calls from the live site will be rejected."*
64
+
65
+ ## Before you sign off — feedback checkpoint
66
+
67
+ This is the last step of the run — the natural point to check `../FEEDBACK.md`. Don't wait for the
68
+ user to complain: run a quick self-check over the whole session (not just this final step) — a
69
+ confusing API response, a doc that was wrong/missing, an extra retry, a workaround you had to invent,
70
+ anything that cost more turns than it should have. If anything qualifies, **offer** to relay it to Wix
71
+ before you close out; send only after an explicit yes.
@@ -60,7 +60,7 @@ These recipes do NOT cover frontend development or SDK usage for displaying data
60
60
  **Technical:** Full CRUD operations for Wix Bookings services using Services API. Covers service types (APPOINTMENT, CLASS, COURSE), pricing configuration, location setup, and schedule management.
61
61
 
62
62
  ### [Create Booking Service from Prompt](references/bookings/create-booking-service-from-prompt.md)
63
- **Technical:** Use when the user wants to create a booking service — e.g. "create a yoga class for $50", "set up consultations", "add a personal training appointment". Routes to the correct type-specific recipe (APPOINTMENT, CLASS, or COURSE), gathers business context, applies defaults, and creates the service.
63
+ **Technical:** Use when the user wants to create a booking service — e.g. "create a yoga class for $50", "set up consultations", "add a personal training appointment", "create a hidden free test course with 8 sessions". Routes to the correct type-specific recipe (APPOINTMENT, CLASS, or COURSE), gathers business context, applies defaults, and creates the service. For COURSE services with session dates/counts, the course is not bookable until separate Calendar Events are created on the returned service schedule.
64
64
 
65
65
  ### [Create Appointment Service](references/bookings/create-appointment-service.md)
66
66
  **Technical:** Use when the user wants to create an appointment/consultation/1-on-1 service — e.g. "set up consultations for $75", "create a meeting service". Handles staff assignment, session duration, and pricing via bulkCreateServices API.
@@ -69,10 +69,10 @@ These recipes do NOT cover frontend development or SDK usage for displaying data
69
69
  **Technical:** Use when the user wants to create a group class — e.g. "create a yoga class for $50", "set up a pilates class". Handles group capacity, recurring sessions, and pricing via bulkCreateServices API.
70
70
 
71
71
  ### [Create Course Service](references/bookings/create-course-service.md)
72
- **Technical:** Use when the user wants to create a multi-session course — e.g. "create a 6-week workshop", "set up a training program for $300". Handles group capacity, full-course pricing, and fixed series via bulkCreateServices API.
72
+ **Technical:** Use when the user wants to create a multi-session COURSE — e.g. "create a 6-week workshop", "set up a training program for $300", "create a hidden free test course with 8 online sessions". Handles group capacity, full-course pricing, `bulkCreateServices`, then creates bookable course session events with Calendar `bulkCreateEvents` using the returned `service.schedule.id`. Never put session dates under `course.sessions` in the Services V2 payload.
73
73
 
74
- ### [Diagnose Bookings Availability Issues](references/bookings/diagnose-availability-issues.md)
75
- **Technical:** Use when an appointment-based service has no bookable time slots / "customers can't book". Runs the DiagnoseAvailability endpoint (`POST /v2/time-slots/diagnose`) for ordered, machine-readable reason codes with suggested owner actions, and falls back to ListAvailabilityTimeSlots when the endpoint is inconclusive.
74
+ ### [Check Bookings Availability (and Diagnose Issues)](references/bookings/diagnose-availability-issues.md)
75
+ **Technical:** Use when someone asks whether an appointment-based service has bookable availability, or why it shows no times / "customers can't book". Reports the current availability status first (via ListAvailabilityTimeSlots); diagnoses the cause only when there's no availability or the owner asks why — ruling out service-level blockers (hidden / online booking off), then running DiagnoseAvailability (`POST /v2/time-slots/diagnose`) for ordered reason codes, with a policy/capacity fallback.
76
76
 
77
77
  ### [End-to-End Booking Flow](references/bookings/end-to-end-booking-flow.md)
78
78
  **Technical:** Complete booking flow from service discovery to payment. Query services, check availability with Time Slots V2, create bookings, and process payment via eCommerce checkout.
@@ -197,7 +197,7 @@ These recipes do NOT cover frontend development or SDK usage for displaying data
197
197
  ## Marketing
198
198
 
199
199
  ### [Create and Publish a Social Media Post (with AI generation)](references/marketing/create-and-publish-social-post.md)
200
- **Technical:** Creates and publishes (or schedules) a social media post to a connected channel (Instagram, Facebook, LinkedIn, TikTok, Pinterest, YouTube, Google Business Profile) via the Publisher API. Optionally generates the whole post from a free-text idea or the site's own assets (products, blog posts, events, bookings, coupons, categories), generates caption/title suggestions, and edits an existing image with AI. Verifies the channel is connected (and runs the OAuth connect flow if not), checks premium publishing quota, creates a draft item, then publishes it immediately or schedules it for a future date. Use when the user wants to create, generate, write, post, or schedule a social post (e.g. "post this to Instagram", "make a post from my product", "write a caption", "schedule a post").
200
+ **Technical:** Creates and publishes (or schedules) a social media post to a connected channel (Instagram, Facebook, LinkedIn, TikTok, Pinterest, YouTube, Google Business Profile) via the Publisher API. Optionally generates the whole post from a free-text idea or the site's own assets (products, blog posts, events, bookings, coupons, categories), generates caption/title suggestions, and edits an existing image with AI. Verifies the channel is connected (and runs the OAuth connect flow if not), checks premium publishing quota, creates a draft item, then publishes it immediately or schedules it for a future date. Use when the user wants to create, generate, write, post, or schedule a social post, wants caption ideas or suggestions, or wants to connect a social channel (e.g. "post this to Instagram", "make a post from my product", "write a caption", "give me caption ideas", "connect my Pinterest", "schedule a post").
201
201
 
202
202
  ### [Generate a Marketing Plan and Schedule Its Posts](references/marketing/generate-and-publish-marketing-plan.md)
203
203
  **Technical:** Generates a site's AI social media marketing plan (a calendar of marketing activities, each with per-channel post drafts) via the Marketing Plan API, then schedules the drafts for publishing. Covers optional marketing settings (goal, channels, tone, frequency, content pillars), asynchronous generation with polling, and generating posts for additional activities. Use for "generate a marketing plan", "create a social media plan/calendar", or "schedule my plan's posts".
@@ -253,6 +253,9 @@ These recipes do NOT cover frontend development or SDK usage for displaying data
253
253
  ### [Query Sites](references/sites/query-sites.md)
254
254
  **Technical:** Lists and queries all sites associated with a Wix account using Sites API. Covers pagination with cursor-based navigation.
255
255
 
256
+ ### [Site Import](references/sites/site-import.md)
257
+ **Technical:** Drives the autonomous Wix Site Import agent over REST (`/site-import/v1/imports`) to migrate a store/site from another platform (Shopify, WooCommerce, Magento, or any URL) into Wix. Covers Start/Poll/Reply/Cancel, relaying agent questions and progress in plain language, handling `DEPLOYED`/`FAILED`/`AUTH_EXPIRED`/`SESSION_EXPIRED` states, and post-deploy follow-up changes. Use when the user wants to import, migrate, or clone an existing store/site into Wix.
258
+
256
259
  ---
257
260
 
258
261
  ## Stores
@@ -32,12 +32,42 @@ Always follow **List → Get → Query**. You cannot construct a valid query wit
32
32
  2. **Get Semantic Model** — inspect a model's `measures`, `dimensions`, and `parameters` to find the exact `name` values to query and their supported filters/sorting.
33
33
  3. **Query Semantic Model Data** — request specific field names for a time `interval`, with optional filters, sorting, paging, and formatting.
34
34
 
35
+ **Run each step as its own separate API call, and stop to read the result before starting the next step.** The inputs to each step do not exist until the previous step returns:
36
+
37
+ - You cannot set `semanticModelId` until **List** returns it. **Never fabricate or guess a model ID** — it must be a GUID copied verbatim from a `List Semantic Models` result. A plausible-looking GUID that List did not return will fail (`SEMANTIC_MODEL_NOT_FOUND`).
38
+ - You cannot set `fields`, `filters[].field`, or `sort.fieldName` until you have read the schema from **Get**.
39
+
40
+ Choosing the model and the field names is a **reasoning** step you perform by reading the returned JSON — not something to automate with string matching. Do **not** chain List, Get, and Query into a single script/execution.
41
+
42
+ ❌ **Don't do this** — one execution that lists, gets, and queries in a single pass, picking fields by pattern-matching their names:
43
+
44
+ ```js
45
+ const models = await wix.request(/* list */);
46
+ const model = models.find(m => /traffic/.test(m.slug)); // guessing the model
47
+ const schema = await wix.request(/* get model.id */);
48
+ const dim = schema.dimensions.find(d => /url|path/i.test(d.name)); // guessing the field
49
+ await wix.request(/* query with guessed fields */);
50
+ ```
51
+
52
+ This guesses field names, ignores each field's `dependencies` (so those fields come back silently empty), and never lets you actually verify the model or schema.
53
+
54
+ ✅ **Do this** — three separate calls, reading each result before composing the next:
55
+
56
+ 1. Call **List**. Read the returned models; pick the `id` whose subject area matches the request.
57
+ 2. Call **Get** with that `id`. Read `measures`/`dimensions`/`parameters`; choose the exact `name`s you need and note each field's `dependencies`.
58
+ 3. Call **Query** with the field names you chose.
59
+
35
60
  ## Before you begin (sharp edges)
36
61
 
37
62
  - **`interval` is required on every query.** There is no way to query without a date range — omitting it fails.
38
- - **`interval` is start-inclusive, end-exclusive (`[start, end)`).** `start` is included, `end` is not. To get all of January 2026, query `start: "2026-01-01..."` and `end: "2026-02-01..."` — you get Jan 1–31, and Feb 1 is excluded. Set `end` to the day *after* the last day you want.
63
+ - **`start`/`end` are absolute UTC instants — not wall-clock in `interval.timezone`.** The trailing `Z` is respected as a real point in time; `interval.timezone` does not reinterpret it, it aligns the range to local day boundaries (and materially changes the result — the same UTC window returned different totals under `UTC` vs `Asia/Jerusalem` vs `America/Los_Angeles` in testing). So to capture a **local calendar range**, send the UTC instants that equal **local midnight** in the site's time zone — *not* `...T00:00:00.000Z`. Example — all of January 2026 for a `America/New_York` site (EST, UTC−5): `start: "2026-01-01T05:00:00.000Z"`, `end: "2026-02-01T05:00:00.000Z"`. Using `...T00:00:00.000Z` would actually start the range at 7 pm on Dec 31 local and misalign every day boundary.
64
+ - **`interval` is start-inclusive, end-exclusive (`[start, end)`).** `start` is included, `end` is not — set `end` to the local-midnight instant of the day *after* the last day you want (e.g. for all of January, `end` is Feb 1 local midnight, in UTC).
65
+ - **Convert local midnight → UTC per date, and watch DST.** Compute each boundary as local-midnight-in-UTC using the site's offset **for that specific date** — the offset changes with daylight saving. `America/New_York` is UTC−4 in summer (June local midnight = `04:00:00.000Z`) but UTC−5 in winter (January local midnight = `05:00:00.000Z`), so a range spanning a DST switch has *different* offsets at its two ends. Don't hardcode one offset across a range.
39
66
  - **Set `interval.timezone` to the site's time zone to match the Wix dashboard.** Analytics in the Wix business manager are bucketed by the site's time zone. If `interval.timezone` is omitted it defaults to **UTC**, so day boundaries shift and your numbers won't match what the owner sees in the dashboard (and the same goes for using a different time zone). Get the site's IANA time zone from `properties.timeZone` via [Get Site Properties](https://dev.wix.com/docs/api-reference/business-management/site-properties/properties/get-site-properties) (`GET https://www.wixapis.com/site-properties/v4/properties`) and pass it through.
40
67
  - **Field names must come from `Get Semantic Model`.** The `fields`, `filters[].field`, and `sort.fieldName` values must exactly match a `name` returned by the model schema (e.g. `traffic.sessions_count`). Do not guess field names.
68
+ - **The field-name prefix is NOT the model slug.** Do not build names as `<slug>.<field>`. A model's fields often use a different prefix — e.g. the model with slug `crm-people-subscribers` exposes its measure as `people.contacts_count`, not `people_subscribers.contacts_count`. Copy the exact `name` strings from `Get Semantic Model`; inferring the prefix from the slug produces a field the model doesn't have.
69
+ - **Read the COMPLETE schema — never sample or truncate it.** When inspecting a model in Step 2, read the full `measures` and `dimensions` lists; do not cap them with `.slice(0, N)` or otherwise return only the first few. Lists are often long and alphabetical, so a cutoff silently hides exactly the field you need (e.g. on `traffic`, a `.slice(0, 25)` drops `traffic.referrer_category_name`, `traffic.referrer_source_name`, and `traffic.visitor_type`). Choosing from a partial list re-introduces guessing — the agent assumes a missing field doesn't exist or fabricates a name. The same applies to a field's full `dependencies` array and to the model list from `List Semantic Models`.
70
+ - **A wrong field name can fail loudly OR fail silently — assume neither.** A `fields`/`filters[].field`/`sort.fieldName` value that isn't a valid model field either (a) rejects the query with a `4XX` error and a self-explanatory string code (e.g. `fieldIsInvalid`), sometimes listing the model's **available field names**, or (b) is **silently dropped** — the query returns `200` and that field just doesn't appear in `results[].fields` (same as a missing dependency). In testing, an unknown field in `fields` was silently omitted, not errored. Never rely on an error to catch a bad field. (See *Handling wrong fields* below.)
41
71
  - **Field dependencies.** A field returns data only if at least one of the field names in its `dependencies` array is also included in the same query; otherwise it's **silently omitted** from results (no error). For example, a measure may only return data when a specific dimension is also requested.
42
72
  - **Sorting a nullable measure — set `sort.nullsLast: true`.** `nullsLast` defaults to `false` and only affects **descending** (`DESC`) order. If a measure can return null and you sort it `DESC`, the null rows sort *first* — so a "top N" query surfaces nulls before your real values. Set `nullsLast: true` to push nulls to the end.
43
73
  - **Result cap: 1,000 rows per query.** `results` is capped at 1,000 rows — paginate with `paging.offset` for larger datasets.
@@ -120,8 +150,8 @@ curl -X POST \
120
150
  -d '{
121
151
  "semanticModelId": "cad7fd34-2c8b-4dda-8296-3f9d47fb484d",
122
152
  "interval": {
123
- "start": "2024-06-01T00:00:00.000Z",
124
- "end": "2025-06-01T00:00:00.000Z",
153
+ "start": "2024-06-01T04:00:00.000Z",
154
+ "end": "2025-06-01T04:00:00.000Z",
125
155
  "timezone": "America/New_York"
126
156
  },
127
157
  "fields": [
@@ -150,12 +180,14 @@ curl -X POST \
150
180
  }'
151
181
  ```
152
182
 
183
+ > The `interval` above uses `04:00:00.000Z`, not `00:00:00.000Z`: June is EDT (UTC−4) in `America/New_York`, so `04:00Z` is local midnight. `start`/`end` are absolute UTC instants — set them to local-midnight-in-UTC for the site's time zone, and recompute the offset per date (DST). See the interval sharp edges above.
184
+
153
185
  ### Request fields
154
186
 
155
187
  | Field | Required | Notes |
156
188
  |---|---|---|
157
189
  | `semanticModelId` | Yes | GUID from List/Get. |
158
- | `interval` | Yes | `{ start, end }` UTC ISO date-times, plus `timezone`. Range is start-inclusive, end-exclusive (`[start, end)`) — set `end` to the day after the last day you want. Set `timezone` to the site's IANA time zone (see Time zone section) so results match the Wix dashboard; defaults to UTC when omitted. |
190
+ | `interval` | Yes | `{ start, end }` absolute UTC ISO instants (trailing `Z`), plus `timezone`. `start`/`end` are real points in time, not wall-clock in `timezone` — to capture a local calendar range set them to local-midnight-in-UTC for the site's time zone (offset varies with DST). Range is start-inclusive, end-exclusive (`[start, end)`) — set `end` to the local midnight after the last day you want. Set `timezone` to the site's IANA time zone (see Time zone section) so results align to the Wix dashboard; defaults to UTC when omitted. |
159
191
  | `fields` | Yes | Up to 60 field `name`s from the model schema. |
160
192
  | `filters` | No | Array of `{ field, values[], condition, prefix }`. `condition` defaults to `EQUAL`, `prefix` defaults to `IS`. For `RANGE_*` conditions provide exactly 2 values. |
161
193
  | `sort` | No | `{ fieldName, order, nullsLast }`. `order` defaults to `ASC`; field must be `sortable`. `nullsLast` (default `false`) applies only to `DESC` order — set it to `true` when the sorted measure can contain nulls, otherwise nulls sort before real values. |
@@ -186,6 +218,11 @@ Each cell in `results[].fields` is a typed value — one of `numericValue`, `str
186
218
 
187
219
  Analytics shown in the Wix business manager are aggregated by the **site's time zone**. To return numbers that match what the site owner sees, pass that time zone in `interval.timezone` on every query. **When `timezone` is omitted, the API defaults to UTC** — which shifts day boundaries and produces totals that don't line up with the dashboard (the same applies to any non-site time zone).
188
220
 
221
+ Two independent things both matter, and both use the time zone:
222
+
223
+ 1. **`interval.timezone`** aligns aggregation to the site's local day boundaries. It is not cosmetic — the same `start`/`end` return different totals under different time zones.
224
+ 2. **`start`/`end` themselves are absolute UTC instants.** To line them up with local calendar days you must send local-midnight-in-UTC — i.e. add the site's UTC offset **for that date** (DST-aware). For `America/New_York`: local midnight is `05:00:00.000Z` in winter (UTC−5) and `04:00:00.000Z` in summer (UTC−4). Sending `...T00:00:00.000Z` with a non-UTC `timezone` starts the range partway through the previous local day and skews the result.
225
+
189
226
  Get the site's time zone from **Get Site Properties**:
190
227
 
191
228
  ```bash
@@ -213,23 +250,43 @@ To retrieve more than 1,000 rows, page through with `offset`:
213
250
  1. Request page 1 with `paging: { limit: 1000, offset: 0 }`.
214
251
  2. Increment `offset` by the page size until `pagingMetadata.count` is less than the requested `limit`.
215
252
 
253
+ ## Handling wrong fields
254
+
255
+ A field can be "wrong" in three ways — guard against each:
256
+
257
+ 1. **Rejected (`4XX`).** The query fails with a self-explanatory string code (e.g. `fieldIsInvalid`) and, when provided, the model's valid field names. Recovery: read them, pick the correct `name`, and re-query — do **not** retry the rejected name or invent a new one. If no field list is returned, fall back to `Get Semantic Model` (Step 2) and re-read the schema.
258
+
259
+ 2. **Silently omitted (`200`).** The query succeeds but a requested field is **absent from every `results[].fields` entry** (and from `totals`). This happens for unknown field names *and* for fields whose `dependencies` weren't included — **no error is raised.** So **after every query, verify that each name in `fields` actually appears in the results.** If one is missing: re-check its exact `name` via `Get Semantic Model`, add any required `dependencies`, and re-query. Treat a silently-missing field as a failure, not as empty data.
260
+
261
+ 3. **Valid but semantically wrong.** The name exists, so you get `200` and real-looking numbers — but it answers the wrong question (e.g. using `traffic.page_url` instead of `traffic.page_url_from` for per-page traffic, or a field whose `description` says *"Do not use it ever"*). This is the most dangerous case because nothing looks broken. Prevent it in **Step 2**: choose fields by reading each field's `description`, honor "do not use" notes, and never pick by name pattern alone. (This is why List → Get → Query are separate steps — see *Decision flow*.)
262
+
263
+ Cases 1 and 2 are caught **after** the query by validating the response; case 3 is prevented **before** the query by reading field descriptions when you choose fields.
264
+
265
+ ## Answer from the site's own models — don't ask or call external sources
266
+
267
+ Analytics questions are answered from the site's **semantic models**, discovered via `List Semantic Models`. This includes data that originates from a third party — e.g. **Google Search / Search Console** metrics (impressions, clicks, CTR, average position, top queries and pages) — which Wix ingests and often enriches with its own site data. So: **do not ask the user whether a source is connected, and do not call the external provider (e.g. Google) directly** — find the matching model and query it. If a query returns no data for the requested period, report that there's no data (the source may not be connected) rather than asking the user to connect first.
268
+
216
269
  ## Common Errors
217
270
 
218
271
  | HTTP | Code | Meaning |
219
272
  |---|---|---|
273
+ | 4XX | `fieldIsInvalid` | A `fields`/`filters[].field`/`sort.fieldName` value doesn't exist in the model, and this query rejected it (some invalid fields are instead silently dropped from a `200` — see *Handling wrong fields*). When present, the error lists the model's **available field names** — pick the correct one and re-query. |
220
274
  | 401 | `NO_ACCOUNT_IDENTITY` / `UNAUTHENTICATED` | Caller isn't authenticated; provide valid credentials. |
221
275
  | 404 | `SEMANTIC_MODEL_NOT_FOUND` | The `semanticModelId` doesn't exist for this site. |
222
276
 
277
+ Error codes are self-explanatory strings (e.g. `fieldIsInvalid`) — read the code and the fields it returns rather than parsing a fixed body shape. Note: not every invalid field errors; some are silently omitted from a `200` response, so also validate the response (see *Handling wrong fields*).
278
+
223
279
  Silent gap (no error): a requested field returns no data because none of its `dependencies` were included in the query — add a dependency field and re-query.
224
280
 
225
281
  ## Best Practices
226
282
 
227
- 1. Always run **List → Get → Query**; never hardcode field names — read them from `Get Semantic Model`.
228
- 2. Pass the site's time zone (`properties.timeZone` from Get Site Properties) in `interval.timezone` so results match the Wix dashboard.
283
+ 1. Always run **List → Get → Query** as three separate calls; read each result before composing the next, and never hardcode or pattern-match field names — read them from `Get Semantic Model`.
284
+ 2. Pass the site's time zone (`properties.timeZone` from Get Site Properties) in `interval.timezone` **and** set `start`/`end` to local-midnight-in-UTC for that zone (DST-aware) so results match the Wix dashboard. `start`/`end` are absolute instants, not wall-clock.
229
285
  3. Include a field's `dependencies` in the query, or expect that field to be silently dropped.
230
- 4. Use `formattingEnabled: true` for anything shown directly to a user; keep raw values for calculations.
231
- 5. Use `totalsIncluded: true` to get period totals alongside a paged breakdown in a single call.
232
- 6. Keep `fields` minimal (projection) and paginate large result sets with `offset`.
286
+ 4. After each query, validate the response — confirm every requested field appears in `results[].fields`. A wrong field either errors (`4XX`, e.g. `fieldIsInvalid`) or is silently dropped from a `200`; a missing field means an unknown name or a missing dependency. Fix and re-query rather than trusting the partial result. Choose fields by reading their `description` (honoring "do not use" notes), never by name pattern.
287
+ 5. Use `formattingEnabled: true` for anything shown directly to a user; keep raw values for calculations.
288
+ 6. Use `totalsIncluded: true` to get period totals alongside a paged breakdown in a single call.
289
+ 7. Keep `fields` minimal (projection) and paginate large result sets with `offset`.
233
290
 
234
291
  ## Related Documentation
235
292
 
@@ -99,6 +99,7 @@ Some common apps:
99
99
  | Wix Bookings | `13d21c63-b5ec-5912-8397-c3a5ddb27a97` |
100
100
  | Wix Blog | `14bcded7-0066-7c35-14d7-466cb3f09103` |
101
101
  | Wix Events | `140603ad-af8d-84a5-2c80-a0f60cb47351` |
102
+ | Wix Multilingual | `14d84998-ae09-1abf-c6fc-3f3cace5bf19` |
102
103
  | Wix Pricing Plans | `1522827f-c56c-a5c9-2ac9-00f9e6ae12d3` |
103
104
  | Wix CMS | `e593b0bd-b783-45b8-97c2-873d42aacaf4` |
104
105
 
@@ -114,6 +115,8 @@ Some common apps:
114
115
  ### App Not Installed Error
115
116
  If you receive an error indicating a required app is not installed, use this recipe to install it before proceeding.
116
117
 
118
+ If Locale Settings or Locales APIs return `428 MULTILINGUAL_NOT_INSTALLED`, install **Wix Multilingual** using appDefId `14d84998-ae09-1abf-c6fc-3f3cace5bf19`, then retry enabling multilingual mode or creating locales. Confirm with the user before installing unless they already explicitly asked you to install Wix Multilingual.
119
+
117
120
  ---
118
121
 
119
122
  ## Next Steps
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: "Create Booking Service from Prompt"
3
- description: "Create a booking service from a user prompt — e.g. 'create a yoga class for $50', 'set up consultations for $75', 'add a personal training appointment', 'create a 6-week photography workshop'. Determines the service type (APPOINTMENT, CLASS, or COURSE) and delegates to the type-specific recipe."
3
+ description: "Create a booking service from a user prompt — e.g. 'create a yoga class for $50', 'set up consultations for $75', 'add a personal training appointment', 'create a 6-week photography workshop', 'create a hidden free test course with 8 online sessions'. Determines the service type (APPOINTMENT, CLASS, or COURSE) and delegates to the type-specific recipe. For COURSE services with session dates/counts, follow the course recipe's separate Calendar bulkCreateEvents step; Services V2 alone does not create bookable course sessions."
4
4
  ---
5
5
 
6
6
  # Create Booking Service from Natural Language Prompt
7
7
 
8
8
  ## When to Use
9
9
 
10
- - User describes a service they want to create using natural language (e.g., "create a yoga class for $50", "set up consultation sessions", "add a personal training appointment")
10
+ - User describes a service they want to create using natural language (e.g., "create a yoga class for $50", "set up consultation sessions", "add a personal training appointment", "create a hidden free test course with 8 online sessions")
11
11
  - The intent is autonomous creation — fill in reasonable defaults rather than asking the user for every field
12
12
 
13
13
  ## Step 1: Determine Service Type
@@ -16,7 +16,7 @@ description: "Create a booking service from a user prompt — e.g. 'create a yog
16
16
  |---|---|---|
17
17
  | consultation, appointment, meeting, 1-on-1, one-on-one, session | `APPOINTMENT` | [Create Appointment Service](./create-appointment-service.md) |
18
18
  | class, yoga, pilates, group session, group workout, bootcamp class | `CLASS` | [Create Class Service](./create-class-service.md) |
19
- | workshop, program, course, training program, multi-session | `COURSE` | [Create Course Service](./create-course-service.md) |
19
+ | workshop, program, course, training program, multi-session, fixed series, 8 sessions | `COURSE` | [Create Course Service](./create-course-service.md) |
20
20
  | (unclear or unspecified) | `APPOINTMENT` | [Create Appointment Service](./create-appointment-service.md) |
21
21
 
22
22
  ## Step 2: Follow the Type-Specific Recipe
@@ -26,5 +26,6 @@ Once the service type is determined, follow the corresponding recipe linked abov
26
26
  1. Gathering business context (staff where required, categories, duplicate check)
27
27
  2. Applying type-specific defaults (pricing, capacity, duration, staff assignment)
28
28
  3. Creating the service via `bulkCreateServices`
29
- 4. Navigating to the service form for user review
30
- 5. Providing a summary of what was created and assumptions made
29
+ 4. For COURSE services with session dates/counts, creating separate Calendar events via `bulkCreateEvents` on the returned `service.schedule.id`
30
+ 5. Navigating to the service form for user review
31
+ 6. Providing a summary of what was created and assumptions made