@wix/agent-skills 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -7
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +2 -0
- package/skills/wix-app/references/STORES_VERSIONING.md +1 -0
- package/skills/wix-design-system/SKILL.md +4 -0
- package/skills/wix-headless/SKILL.md +55 -32
- package/skills/wix-headless/references/BUILD-astro.md +246 -0
- package/skills/wix-headless/references/BUILD-own-build.md +167 -0
- package/skills/wix-headless/references/BUILD.md +50 -252
- package/skills/wix-headless/references/DESIGN_SYSTEM.md +94 -34
- package/skills/wix-headless/references/DISCOVERY-connect.md +55 -0
- package/skills/wix-headless/references/DISCOVERY-create.md +238 -0
- package/skills/wix-headless/references/DISCOVERY.md +41 -422
- package/skills/wix-headless/references/PLAN-connect.md +19 -0
- package/skills/wix-headless/references/PLAN-create.md +17 -0
- package/skills/wix-headless/references/PLAN.md +37 -27
- package/skills/wix-headless/references/SEED.md +27 -18
- package/skills/wix-headless/references/SETUP.md +32 -213
- package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +5 -4
- package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +337 -0
- package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +428 -0
- package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +5 -5
- package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +10 -10
- package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +12 -13
- package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +10 -12
- package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +8 -8
- package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +50 -27
- package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +7 -8
- package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +5 -5
- package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +3 -3
- package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +5 -19
- package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +2 -2
- package/skills/wix-headless/references/astro/gift-cards/PAGES.md +1 -1
- package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +6 -6
- package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +4 -4
- package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +3 -3
- package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +3 -3
- package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +9 -9
- package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +5 -4
- package/skills/wix-headless/references/astro/templates/Footer.astro +1 -1
- package/skills/wix-headless/references/astro/templates/Layout.astro +1 -1
- package/skills/wix-headless/references/astro/templates/Navigation.astro +1 -1
- package/skills/wix-headless/references/astro/templates/astro.config.mjs +1 -1
- package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +218 -0
- package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +194 -0
- package/skills/wix-headless/references/astro/templates/bookings/ManageBooking.tsx +84 -0
- package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +25 -0
- package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +50 -0
- package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +45 -0
- package/skills/wix-headless/references/astro/templates/bookings/api/confirm-booking.ts +34 -0
- package/skills/wix-headless/references/astro/templates/bookings/api/waitlist.ts +74 -0
- package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +111 -0
- package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +413 -0
- package/skills/wix-headless/references/astro/templates/bookings/manage-booking.astro +76 -0
- package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +81 -0
- package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +65 -0
- package/skills/wix-headless/references/astro/templates/global.css +2 -2
- package/skills/wix-headless/references/astro/templates/index.astro +1 -1
- package/skills/wix-headless/references/blog/BLOG_CONTENT.md +19 -9
- package/skills/wix-headless/references/blog/INSTRUCTIONS.md +3 -3
- package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +106 -0
- package/skills/wix-headless/references/bookings/SERVICES_DATA.md +295 -0
- package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +4 -2
- package/skills/wix-headless/references/cms/INSTRUCTIONS.md +27 -1
- package/skills/wix-headless/references/commands/known-apps.json +2 -1
- package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +142 -0
- package/skills/wix-headless/references/custom/INSTRUCTIONS.md +71 -21
- package/skills/wix-headless/references/custom/blog/WIRING.md +52 -0
- package/skills/wix-headless/references/custom/bookings/WIRING.md +88 -0
- package/skills/wix-headless/references/custom/cms/WIRING.md +97 -0
- package/skills/wix-headless/references/custom/ecom/WIRING.md +67 -0
- package/skills/wix-headless/references/custom/forms/WIRING.md +101 -0
- package/skills/wix-headless/references/custom/stores/WIRING.md +80 -0
- package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +5 -5
- package/skills/wix-headless/references/forms/INSTRUCTIONS.md +2 -2
- package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +3 -3
- package/skills/wix-headless/references/images/INSTRUCTIONS.md +16 -15
- package/skills/wix-headless/references/seed-recipes.md +2 -2
- package/skills/wix-headless/references/shared/AGENTS_MD.md +47 -0
- package/skills/wix-headless/references/shared/AUTHENTICATION.md +3 -5
- package/skills/wix-headless/references/shared/DESIGN_MD.md +85 -0
- package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +29 -28
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +87 -27
- package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +2 -2
- package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +24 -313
- package/skills/wix-headless/references/shared/STYLING.md +8 -18
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +39 -9
- package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +17 -14
- package/skills/wix-headless/references/verticals/_schema.json +1 -1
- package/skills/wix-headless/references/verticals/_schema.md +3 -3
- package/skills/wix-headless/references/verticals/blog.md +1 -1
- package/skills/wix-headless/references/verticals/bookings.md +41 -0
- package/skills/wix-headless/references/verticals/cms.md +1 -1
- package/skills/wix-headless/references/verticals/ecom.md +2 -2
- package/skills/wix-headless/references/verticals/gift-cards.md +1 -1
- package/skills/wix-headless/references/verticals/stores.md +1 -1
- package/skills/wix-headless/scripts/check-manifest.mjs +143 -6
- package/skills/wix-headless/scripts/compose.mjs +540 -0
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +128 -51
- package/skills/wix-headless/scripts/flatten.sh +61 -0
- package/skills/wix-headless/scripts/generate-images.mjs +242 -0
- package/skills/wix-headless/scripts/scaffold.sh +47 -16
- package/skills/wix-headless/scripts/write-decorative-json.mjs +62 -0
- package/skills/wix-manage/SKILL.md +1 -1
- package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +1 -1
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +1 -1
- package/skills/wix-manage/references/cms/cms-references-and-relationships.md +1 -1
- package/skills/wix-manage/references/cms/cms-schema-management.md +1 -1
- package/skills/wix-manage/references/domains/domain-search-and-purchase.md +10 -7
- package/skills/wix-manage/references/forms/create-form.md +49 -1
- package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +1 -1
- package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +1 -1
- package/skills/wix-headless/references/astro/COMPOSE.md +0 -142
- package/skills/wix-headless/scripts/finalize-run-json.mjs +0 -141
- package/skills/wix-headless/scripts/init-site-json.mjs +0 -101
- package/skills/wix-headless/scripts/preview.sh +0 -45
- package/skills/wix-headless/scripts/release.sh +0 -73
|
@@ -120,9 +120,8 @@ Use these node types to build rich, varied content:
|
|
|
120
120
|
- LIST_ITEM nodes contain PARAGRAPH nodes (not TEXT directly)
|
|
121
121
|
- Mix at least 3 different node types per post for visual variety
|
|
122
122
|
- Create all 3 posts in sequence (one `curl` call per post)
|
|
123
|
-
- **Best practice:** include all fields (title, content, media) in the initial creation call to avoid needing a re-publish
|
|
124
123
|
|
|
125
|
-
> **Re-publish after PATCH:** If you update a published post (e.g.,
|
|
124
|
+
> **Re-publish after PATCH:** If you update a published post (e.g., the image agent adds a cover image via PATCH after creation), it becomes `hasUnpublishedChanges: true`. The agent that PATCHes must call `POST /blog/v3/draft-posts/{draftPostId}/publish` to re-publish. (This seed scope does not attach media — see Step 3.)
|
|
126
125
|
|
|
127
126
|
## Step 4: Verify
|
|
128
127
|
|
|
@@ -139,13 +138,24 @@ body: {
|
|
|
139
138
|
|
|
140
139
|
Confirm 3 posts are returned. Report post titles to the user.
|
|
141
140
|
|
|
142
|
-
|
|
141
|
+
## Step 5: Return Results
|
|
143
142
|
|
|
144
|
-
|
|
143
|
+
Emit a structured JSON block at the end of your completion message per `../shared/RETURN_CONTRACT.md`. Do **not** write a sidecar file. Include the created posts' `id` and `title` so the image agent can attach cover images by ID:
|
|
145
144
|
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"status": "complete",
|
|
148
|
+
"phase": "blog-seed",
|
|
149
|
+
"scope": "seed",
|
|
150
|
+
"summary": "Created and published {n} on-brand blog posts",
|
|
151
|
+
"data": {
|
|
152
|
+
"blogPosts": [
|
|
153
|
+
{ "id": "<uuid>", "title": "<title>" }
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
"files": [],
|
|
157
|
+
"errors": []
|
|
158
|
+
}
|
|
151
159
|
```
|
|
160
|
+
|
|
161
|
+
The JSON block MUST be the last content in your message.
|
|
@@ -5,7 +5,7 @@ description: "Implements blog vertical via Wix Blog app — @wix/blog SDK, @wix/
|
|
|
5
5
|
|
|
6
6
|
# Blog Implementer
|
|
7
7
|
|
|
8
|
-
Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, REST auth + doc lookups, prompt-inlined inputs (
|
|
8
|
+
Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, REST auth + doc lookups, prompt-inlined inputs (read only your `.wix/seeded.json` slice), return contract, style conventions, and common failure modes.
|
|
9
9
|
|
|
10
10
|
## Scope routing
|
|
11
11
|
|
|
@@ -13,13 +13,13 @@ Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routi
|
|
|
13
13
|
|-------|-------|-----------|
|
|
14
14
|
| `seed` | Seed (post seeding via REST with Ricos content) | `./BLOG_CONTENT.md` |
|
|
15
15
|
| `components` | Components (blog service module, RicosViewer, consts, astro.config sitemap) | `../astro/blog/BLOG_SETUP.md` |
|
|
16
|
-
| `pages` | Pages (listing, detail, RSS, BlogPost layout,
|
|
16
|
+
| `pages` | Pages (listing, detail, RSS, BlogPost layout — all under `src/pages/blog/*`; no shared-shell patch, so blog merges into one private build agent) | `../astro/blog/BLOG_PAGES.md` |
|
|
17
17
|
|
|
18
18
|
Cover images are generated by the shared image agent (`image-phase-2-entity` scope) AFTER `seed` completes — not by this agent.
|
|
19
19
|
|
|
20
20
|
## Files this vertical creates / contributes
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
The `components` and `pages` scopes enumerate their own file lists — see `../astro/blog/BLOG_SETUP.md` (components) and `../astro/blog/BLOG_PAGES.md` (pages).
|
|
23
23
|
|
|
24
24
|
## Blog-specific failure modes
|
|
25
25
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bookings-implementer
|
|
3
|
+
description: "Implements Wix Bookings vertical — services catalog, service detail with availability calendar and booking flow, and booking confirmation. Scopes: seed, components, pages. Extends references/shared/IMPLEMENTER.md."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bookings Implementer
|
|
7
|
+
|
|
8
|
+
Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, REST auth + doc lookups, site.json read pattern, return contract, style conventions, and common failure modes.
|
|
9
|
+
|
|
10
|
+
## Scope routing
|
|
11
|
+
|
|
12
|
+
| Scope | Phase | Reference |
|
|
13
|
+
|-------|-------|-----------|
|
|
14
|
+
| `seed` | Seed (service creation via Wix Bookings REST API) | `./SERVICES_DATA.md` |
|
|
15
|
+
| `components` | Components: `ServiceCard.astro`, `AvailabilityCalendar.tsx`, `BookingForm.tsx`, `ServiceBookingFlow.tsx`, `ManageBooking.tsx` (`SeoTags.astro` is pre-copied — see Templates) | `../astro/bookings/COMPONENTS.md` |
|
|
16
|
+
| `pages` | Pages: `/services` listing, `/services/[slug]` detail, `/booking-confirmation`, `/manage-booking` | `../astro/bookings/SERVICES_PAGES.md` |
|
|
17
|
+
|
|
18
|
+
## Files this vertical creates / contributes
|
|
19
|
+
|
|
20
|
+
See `<SKILL_ROOT>/references/verticals/bookings.md` frontmatter.
|
|
21
|
+
|
|
22
|
+
## Pre-return file-existence assertion (pages scope)
|
|
23
|
+
|
|
24
|
+
Before returning `status: "complete"` from the `pages` scope, verify all route files exist on disk:
|
|
25
|
+
- `src/pages/services/index.astro`
|
|
26
|
+
- `src/pages/services/[slug].astro`
|
|
27
|
+
- `src/pages/booking-confirmation.astro`
|
|
28
|
+
- `src/pages/manage-booking.astro`
|
|
29
|
+
|
|
30
|
+
If any declared file is missing, return `status: "partial"` with `errors: [{ code: "PHASE4_FILE_MISSING", path: "<expected path>" }]` rather than claiming success.
|
|
31
|
+
|
|
32
|
+
## Templates
|
|
33
|
+
|
|
34
|
+
Canonical templates live at `<SKILL_ROOT>/references/astro/templates/bookings/`. Your `components` and `pages` scopes **read these and adapt them — don't invent markup or SDK wiring** (the booking/confirm/availability/waitlist wiring is subtle and was verified against a live site; re-authoring from scratch is a common source of API-shape bugs). Adapt brand copy, headings, and styling; keep the SDK calls and the data flow.
|
|
35
|
+
|
|
36
|
+
Components (`components` scope — `.tsx`/`.astro`, no CSS):
|
|
37
|
+
- `…/templates/bookings/ServiceCard.astro`
|
|
38
|
+
- `…/templates/bookings/AvailabilityCalendar.tsx` — branches on `serviceType` (APPOINTMENT → `availabilityTimeSlots`, CLASS → `eventTimeSlots`); capacity + instructor + full→waitlist
|
|
39
|
+
- `…/templates/bookings/BookingForm.tsx` — `createBooking` → `/api/confirm-booking`; party size; waitlist on full
|
|
40
|
+
- `…/templates/bookings/ServiceBookingFlow.tsx` — coordinator (threads `serviceType`)
|
|
41
|
+
- `…/templates/bookings/ManageBooking.tsx` — cancel via anonymous token (used by `manage-booking.astro`)
|
|
42
|
+
|
|
43
|
+
Pages (`pages` scope):
|
|
44
|
+
- `…/templates/bookings/services/index.astro`, `…/services/[slug].astro`
|
|
45
|
+
- `…/templates/bookings/booking-confirmation.astro`, `…/manage-booking.astro`
|
|
46
|
+
|
|
47
|
+
### Pre-copied by the orchestrator (do NOT write these yourself)
|
|
48
|
+
These are mechanical (no brand content) — the orchestrator copies them before dispatch (BUILD-astro.md § Step 4.5). Just rely on them at the listed paths:
|
|
49
|
+
- `src/styles/components-bookings.css` ← `…/templates/bookings/components-bookings.css`
|
|
50
|
+
- `src/components/SeoTags.astro` ← `…/templates/bookings/SeoTags.astro` (renders `service.seoData.tags`; imported by `services/[slug].astro`)
|
|
51
|
+
- `src/pages/api/confirm-booking.ts` ← `…/templates/bookings/api/confirm-booking.ts` (elevated `confirmBooking` — holds the seat)
|
|
52
|
+
- `src/pages/api/waitlist.ts` ← `…/templates/bookings/api/waitlist.ts` (elevated native v1 waitlist register)
|
|
53
|
+
|
|
54
|
+
If a pre-copied file is missing at runtime, that's an orchestrator-side bug — return `status: "partial"` with `errors: [{code: "UTILITY_TEMPLATE_NOT_PRECOPIED", path: "<missing>"}]`; do not author your own version.
|
|
55
|
+
|
|
56
|
+
## CSS ownership — bookings pack
|
|
57
|
+
|
|
58
|
+
Bookings-specific component CSS lives in `src/styles/components-bookings.css` (pre-copied by the orchestrator from the skill template — see above), NOT in the designer's `global.css`. Classes the pack owns:
|
|
59
|
+
|
|
60
|
+
- `.service-card`, `.service-card-image`, `.service-card-meta`, `.service-card-price` — the service card itself, including image containment, price badge, and duration tag.
|
|
61
|
+
- `.service-grid` — the layout that lists service cards on `/services`. Include `display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--spacing-xl);` plus View-Transitions opacity fade.
|
|
62
|
+
- `.availability-calendar` — the date-picker shell wrapping the calendar widget.
|
|
63
|
+
- `.time-slot`, `.time-slot--available`, `.time-slot--selected`, `.time-slot--full` (full CLASS session → waitlist), plus the `.time-slot-time` / `.time-slot-capacity` spans inside each slot button.
|
|
64
|
+
- `.booking-form` (+ `.booking-form-note`) — the booking/waitlist form revealed after a slot is selected.
|
|
65
|
+
- `.manage-booking`, `.manage-booking-summary`, `.manage-booking-status`, `.manage-booking-note`, `.manage-booking-link` — the `/manage-booking` view/cancel UI.
|
|
66
|
+
|
|
67
|
+
If `global.css` ships a partial rule for any class above, flag it in your return JSON's `errors` array (`{code: "GLOBAL_CSS_LEAK", class: "<name>"}`) and override with the complete rule in `components-bookings.css`.
|
|
68
|
+
|
|
69
|
+
## Bookings-specific failure modes
|
|
70
|
+
|
|
71
|
+
| Wrong | Right |
|
|
72
|
+
|-------|-------|
|
|
73
|
+
| Treat `createBooking` as the whole booking — stop after it returns | `createBooking` leaves the booking **`CREATED`**, which **does not hold a seat or block availability** (per the [lifecycle docs](https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/introduction): only `CONFIRMED` blocks availability). Without a follow-up **`confirmBooking`**, the class overbooks and capacity never drops. After createBooking returns `CREATED`, call `bookings.confirmBooking(bookingId, revision, { paymentStatus: "NOT_PAID", flowControlSettings: { checkAvailabilityValidation: true } })`. `confirmBooking` needs Manage Bookings → run it **server-side, elevated** (`auth.elevate`), not from the visitor client. |
|
|
74
|
+
| Build the waitlist on `createBooking` (expecting a `WAITING_LIST` status) | `createBooking` has **no** waitlist option (verified: only `participantNotification`/`sendSmsReminder`); a full session returns `SESSION_CAPACITY_EXCEEDED`. The native waitlist is the **legacy v1** API `POST /bookings/v1/waitlist/register` (there is no v2/v3 waitlist). It needs the **v1-encoded session id** as `waitingResource` (surfaced in the `SESSION_CAPACITY_EXCEEDED` message: *"…on session `<id>`"*) **+ a Wix `contactId`** (resolve via Contacts API by email, create if missing), and the **Manage Bookings** scope → server-side elevated. Class/EVENT only. |
|
|
75
|
+
| Call the v1 waitlist (or any Manage-Bookings REST) with `auth.elevate(async () => fetchWithAuth(...))` | `auth.elevate()` only grants permissions for a real **SDK function** (it reads the method's required-permission metadata); a plain closure isn't one, so it runs **un-elevated (visitor)** and 403s. For raw REST with no SDK wrapper, elevate the authenticated fetch itself: `const ef = auth.elevate(httpClient.fetchWithAuth); await ef(url, init)`. (For calls that *do* have an SDK method — `confirmBooking`, `getAnonymousActionToken` — elevate the method directly.) |
|
|
76
|
+
| Pass the v3 `eventId` as the waitlist `waitingResource` | The v1 waitlist can't decode a v3 GUID (`"id … cannot be decoded"`). Use the **v1-encoded session id** (e.g. `2mmoW0vw…`) from the `SESSION_CAPACITY_EXCEEDED` message. |
|
|
77
|
+
| Use Services V1 API endpoint (`/bookings/v1/catalog/services`) | Services V2 is at `POST https://www.wixapis.com/_api/bookings/v2/services`. V1 has a different (nested `info.*`) payload shape. |
|
|
78
|
+
| Nest service fields under `info` (`info.name`, `info.description`) | V2 uses flat fields: `name`, `description`, `tagLine` at the top level of the `service` object. |
|
|
79
|
+
| Use `payment.fixed.price.amount` | V2 uses `payment.fixed.price.value` (a string like `"75.00"`). `amount` is the V1 field name. |
|
|
80
|
+
| Omit `defaultCapacity` when creating a service | Required in V2. Set to `1` for APPOINTMENT; use participant count for CLASS. |
|
|
81
|
+
| Omit `onlineBooking` when creating a service | Required in V2. At minimum `{ "enabled": true }`. |
|
|
82
|
+
| Omit `sessionDurations` for an APPOINTMENT service | Required for APPOINTMENT: `schedule.availabilityConstraints.sessionDurations: [<minutes as int>]`. Do NOT specify for CLASS. |
|
|
83
|
+
| Use `availability.queryAvailability` or `availability` SDK namespace | Branch by service type: APPOINTMENT → `availabilityTimeSlots.listAvailabilityTimeSlots()`; CLASS → **`eventTimeSlots.listEventTimeSlots()`** (a **different namespace** — `eventTimeSlots`, not `availabilityTimeSlots` — with plural `serviceIds` + `includeNonBookable`). The `availability` namespace does not exist in `@wix/bookings`. |
|
|
84
|
+
| Pass UTC ISO strings to `listAvailabilityTimeSlots` | The method expects **local date strings** in `YYYY-MM-DDTHH:mm:ss` format (no `Z`), with a separate `timeZone` parameter. |
|
|
85
|
+
| Read service slug from `service.slug` | V2 response puts slug at `service.mainSlug.name`. Fall back to `service.supportedSlugs[0].name`, then `service.id`. |
|
|
86
|
+
| Call `queryServices({ query: { filter, paging } })` (object form) | In `@wix/bookings` the object form returns **0 items with no error**. Use the **query builder**: `await services.queryServices().limit(100).find()` → `result.items`. For a slug, fetch the (small) catalog and match `mainSlug.name` in JS, or `.eq("mainSlug.name", slug).find()`. |
|
|
87
|
+
| Build a `createClient`/`OAuthStrategy` in `.astro` SSR frontmatter keyed off `import.meta.env.*_WIX_CLIENT_ID` | The `*_WIX_CLIENT_ID` env vars are client-only in `@wix/astro` and `undefined` at server render → `createClient` throws → HTTP 500 (only in the deployed runtime; dev has `.env.local`). SSR reads use `@wix/essentials` `auth.elevate(services.queryServices)()…` — the same ambient pattern as the CMS pages. |
|
|
88
|
+
| Read the client ID in a browser island via `import.meta.env.*_WIX_CLIENT_ID` | Client islands import `{ WIX_CLIENT_ID } from "astro:env/client"`. `import.meta.env` is `undefined` in the browser bundle → `OAuthStrategy` sends an empty `client_id` → `oauth2/token` `400`. The var name is `WIX_CLIENT_ID` (no `PUBLIC_`). |
|
|
89
|
+
| Read service name from `service.info.name` | V2 uses flat `service.name` (not `service.info.name`). Same for `description` and `tagLine`. |
|
|
90
|
+
| Read duration from `service.schedule.sessions.duration` | V2 puts duration at `service.schedule.availabilityConstraints.sessionDurations[0]` (array of ints). |
|
|
91
|
+
| Read media URL from `service.info.images[0].url` | V2 media: `service.media.mainMedia.image.url`. |
|
|
92
|
+
| Use `import { availability } from '@wix/bookings'` | Use `import { availabilityTimeSlots } from '@wix/bookings'`. |
|
|
93
|
+
| Call `availability.queryAvailability` server-side on every render | Availability is real-time and session-specific — always call from the client-side React island, NOT from `.astro` frontmatter SSR. |
|
|
94
|
+
| Create a booking without `totalParticipants` | `createBooking` requires `totalParticipants: 1` (or `participantsChoices`). |
|
|
95
|
+
| Read booking ID from `result.booking.id` | The booking ID field is `result.booking._id` (underscore prefix). |
|
|
96
|
+
| Redirect to `/thank-you` (ecom route) after booking | Redirect to `/booking-confirmation?bookingId=<_id>`. The ecom thank-you page is unrelated to bookings. |
|
|
97
|
+
| Re-fetch the booking with `getBooking` from the confirmation page | Don't. Pass booking details (bookingId, startDate, service name) as URL query params during the client-side redirect after `createBooking` succeeds, and render from those. (A server re-fetch of someone else's booking is out of scope.) NB: this is **not** because `auth.elevate` is unavailable — see next row. |
|
|
98
|
+
| Assume `@wix/essentials` `auth.elevate` doesn't work in headless | It does. **Two different `elevate`s:** `@wix/essentials` `auth.elevate(fn)` works in `@wix/astro` SSR (it's how the listing/detail/CMS pages read data) — use it. What's unavailable is the **`wixClient.auth.elevate`** instance method on a hand-built `createClient({auth: OAuthStrategy})` (a Velo/Blocks API). Don't conflate them. |
|
|
99
|
+
| Mount `AvailabilityCalendar` without `client:only="react"` | Calendar must be fully client-side — SSR renders before timezone is known and breaks slot display. Always use `client:only="react"`. |
|
|
100
|
+
| Omit try/catch on `createBooking` | Booking creation can fail with 409 (slot taken) between slot fetch and submit. Catch and surface a friendly message; do not crash the page. |
|
|
101
|
+
| Use `service.id` to pass the service ID | The Wix SDK uses `_id` (with underscore) for all entity IDs. The service's ID is `service._id`. Using `service.id` will be `undefined`. |
|
|
102
|
+
| Read `resource._id` from `listAvailabilityTimeSlots` response | `availableResources` is EMPTY by default in list results. Omit `resource` from `createBooking` — Wix auto-assigns a resource during confirmation. |
|
|
103
|
+
| Use `timeSlot.slot.startDate` to read slot time | Slot fields are at the TOP LEVEL — there is no nested `slot` on the list response. APPOINTMENT slots: `timeSlot.localStartDate`, `timeSlot.localEndDate`, `timeSlot.scheduleId`. |
|
|
104
|
+
| Treat CLASS (event) slots like APPOINTMENT slots — guard clicks on `scheduleId`, book with `{scheduleId, startDate}` | CLASS event slots carry their session id at **`timeSlot.eventInfo.eventId`** and have **no `scheduleId`** (and no top-level `startDate`). Guarding the slot-click on `scheduleId` drops every CLASS click; the appointment-shaped `createBooking` payload doesn't apply. For CLASS: guard on `eventId`, and book with `bookedEntity.slot = { serviceId, eventId }` (Wix derives start/end/timezone/resource/location from the event). Keep `{ scheduleId, startDate, endDate, timezone, location }` for APPOINTMENT only. |
|
|
105
|
+
| Pass `hidden: true` services to the listing | Always filter with `{ hidden: false }` in your `queryServices` filter to exclude hidden services from the public listing. |
|
|
106
|
+
| Use `../../../` for imports in `[slug].astro` | `src/pages/services/[slug].astro` is two directories deep inside `src/`. Import Layout and components with `../../layouts/Layout.astro` and `../../components/…` — not three `../` which would escape `src/`. |
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Bookings Seed — Services Data
|
|
2
|
+
|
|
3
|
+
You are seeding Wix Bookings services for a headless site. Your job:
|
|
4
|
+
1. Mint a site-scoped token once.
|
|
5
|
+
2. **If `intent.bookings.hasStaff` is `true`** — create staff members FIRST (Step 3 below), then pass their `resourceId`s on each service. **Every APPOINTMENT service needs a non-empty `staffMemberIds`** or it's rejected with `MISSING_APPOINTMENT_RESOURCES` — even when `hasStaff` is false you must pass the default **Business Owner** `resourceId` (see § "When hasStaff is false"). (CLASS services don't need a resource at create time.)
|
|
6
|
+
3. Create `intent.bookings.serviceCount` services via the Bookings V2 REST API. When staff exist, pass their `resourceId` values via `staffMemberIds`.
|
|
7
|
+
4. Return all created IDs in the standard return contract.
|
|
8
|
+
|
|
9
|
+
> **Order matters.** Steps 3 and 4 in this file are presented in the order you should execute them: staff first (when applicable), then services. The old "services-first then staff-optionally" order fails for APPOINTMENT with `MISSING_APPOINTMENT_RESOURCES`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Step 1 — Mint the token
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
TOKEN=$(npx @wix/cli@latest token --site "<siteId>")
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Cache in subagent scratch. Every subsequent `curl` reuses it. Every call carries:
|
|
20
|
+
```
|
|
21
|
+
Authorization: Bearer $TOKEN
|
|
22
|
+
wix-site-id: <siteId>
|
|
23
|
+
Content-Type: application/json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Step 2 — Verify Wix Bookings is installed
|
|
29
|
+
|
|
30
|
+
Query staff members as an installation check. A successful response (even an empty list) confirms Bookings is installed:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
curl -sS -X POST \
|
|
34
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
35
|
+
-H "wix-site-id: <siteId>" \
|
|
36
|
+
-H "Content-Type: application/json" \
|
|
37
|
+
-d '{"query":{}}' \
|
|
38
|
+
"https://www.wixapis.com/bookings/v1/staff-members/query"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- **200 OK** → proceed.
|
|
42
|
+
- **403 / "Business schedule not found"** → Bookings app is not installed. Return `status: "error"` with the response verbatim. Do not attempt to install it — the orchestrator's Setup phase handles app installation.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Step 3 — Create staff members (when `intent.bookings.hasStaff` is `true`)
|
|
47
|
+
|
|
48
|
+
Execute this step BEFORE Step 4 when staff are required. APPOINTMENT services need at least one resource to exist before they can be created.
|
|
49
|
+
|
|
50
|
+
For each staff member to create (derive names from the brand; default to 2 staff):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Build the payload — only include phone/email when non-empty.
|
|
54
|
+
# The V1 staff-members API rejects "" as an invalid email/phone.
|
|
55
|
+
curl -sS -X POST \
|
|
56
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
57
|
+
-H "wix-site-id: <siteId>" \
|
|
58
|
+
-H "Content-Type: application/json" \
|
|
59
|
+
-d '{
|
|
60
|
+
"staffMember": {
|
|
61
|
+
"name": "<First Last>",
|
|
62
|
+
"description": "<professional bio — 1–2 sentences, brand-appropriate>"
|
|
63
|
+
}
|
|
64
|
+
}' \
|
|
65
|
+
"https://www.wixapis.com/bookings/v1/staff-members"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
> **Do NOT send `"phone": ""` or `"email": ""`** — V1 validates them as format-checked fields and rejects empty strings with `is not a valid email/phone`. Omit the keys entirely when you don't have a real value. If the merchant later wants to populate phone/email, they can do it from the dashboard or via a PATCH.
|
|
69
|
+
|
|
70
|
+
**Important:** Staff inherit business working hours at creation time. For a basic seed, this is fine — the merchant can configure custom hours from the dashboard later.
|
|
71
|
+
|
|
72
|
+
Save `staffMember.id` and `staffMember.resourceId` from each response. **You will pass the `resourceId` values into the `staffMemberIds` field on each service in Step 4.**
|
|
73
|
+
|
|
74
|
+
> **Note:** `staffMemberIds` on services expects `resourceId` values (from `staffMember.resourceId`), NOT `staffMember.id`. Using `staffMember.id` here is the most common cause of "service has no provider" runtime errors.
|
|
75
|
+
|
|
76
|
+
> **Do NOT attempt custom working hours setup during seed.** The two-step custom-hours workflow (assignWorkingHoursSchedule + WORKING_HOURS events) is complex and fragile. Default inherited hours are sufficient for an initial build. If you encounter "Business schedule not found", skip staff creation and return a warning in `notes`.
|
|
77
|
+
|
|
78
|
+
### When `hasStaff` is `false`
|
|
79
|
+
|
|
80
|
+
For `serviceType: "CLASS"`, no staff/resource is needed at service-creation time — skip this step.
|
|
81
|
+
|
|
82
|
+
For `serviceType: "APPOINTMENT"` with `hasStaff: false`, you **still must pass a resource** in `staffMemberIds` — an empty `[]` is **rejected** with `MISSING_APPOINTMENT_RESOURCES` (*"service of type appointment requires at least one staff member or service resource"*). The Wix Bookings app provisions a default **Business Owner** resource at install; query it and pass **its** `resourceId`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Get the default Business-Owner resourceId (no staff were created).
|
|
86
|
+
curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "wix-site-id: <siteId>" -H "Content-Type: application/json" \
|
|
87
|
+
-d '{"query":{}}' "https://www.wixapis.com/bookings/v1/staff-members/query"
|
|
88
|
+
# → take the staffMember named "Business Owner" and use its `resourceId` as the single
|
|
89
|
+
# entry in each APPOINTMENT service's `staffMemberIds`.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
(Do **not** rely on `[]` — the default resource does not auto-satisfy the requirement at create time. CLASS services don't need this.)
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Step 4 — Create services (Services V2 API)
|
|
97
|
+
|
|
98
|
+
**Endpoint:** `POST https://www.wixapis.com/_api/bookings/v2/services`
|
|
99
|
+
|
|
100
|
+
> **V2 payload is flat — NOT nested under `info`.** Fields are `name`, `description`, `tagLine` at the top level of the `service` object. The V1 shape (`info.name`, `info.description`) is rejected by V2. Price uses `value`, not `amount`.
|
|
101
|
+
|
|
102
|
+
For each service to create (derive names, descriptions, and prices from `brand` + `intent.bookings`):
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
curl -sS -X POST \
|
|
106
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
107
|
+
-H "wix-site-id: <siteId>" \
|
|
108
|
+
-H "Content-Type: application/json" \
|
|
109
|
+
-d '{
|
|
110
|
+
"service": {
|
|
111
|
+
"type": "<APPOINTMENT|CLASS>",
|
|
112
|
+
"name": "<service name>",
|
|
113
|
+
"description": "<brand-appropriate description>",
|
|
114
|
+
"tagLine": "<short tagline>",
|
|
115
|
+
"defaultCapacity": 1,
|
|
116
|
+
"onlineBooking": {
|
|
117
|
+
"enabled": true,
|
|
118
|
+
"requireManualApproval": false,
|
|
119
|
+
"allowMultipleRequests": false
|
|
120
|
+
},
|
|
121
|
+
"schedule": {
|
|
122
|
+
"availabilityConstraints": {
|
|
123
|
+
"sessionDurations": [<duration as integer, e.g. 60>]
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"payment": {
|
|
127
|
+
"rateType": "FIXED",
|
|
128
|
+
"fixed": {
|
|
129
|
+
"price": {
|
|
130
|
+
"value": "<price as string, e.g. \"75.00\">",
|
|
131
|
+
"currency": "USD"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"options": {
|
|
135
|
+
"online": true,
|
|
136
|
+
"inPerson": false,
|
|
137
|
+
"deposit": false,
|
|
138
|
+
"pricingPlan": false
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"locations": [
|
|
142
|
+
{
|
|
143
|
+
"type": "BUSINESS"
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"staffMemberIds": [<resourceId values — staff from Step 3, OR the default Business-Owner resourceId when hasStaff is false. NEVER [] for APPOINTMENT — it 400s with MISSING_APPOINTMENT_RESOURCES>]
|
|
147
|
+
}
|
|
148
|
+
}' \
|
|
149
|
+
"https://www.wixapis.com/_api/bookings/v2/services"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
> **`locations.type` enum:** the valid values are `UNKNOWN_LOCATION_TYPE`, `CUSTOM`, `BUSINESS`, and `CUSTOMER`. Use `"BUSINESS"` for "at the business address" (the seed default). Do NOT use `"OWNER_BUSINESS"` here — that string IS valid for `createBooking.bookedEntity.slot.location.locationType` on the bookings endpoint, but the **services** endpoint rejects it. Same field name, different enum.
|
|
153
|
+
|
|
154
|
+
> **`staffMemberIds`:** pass `resourceId` values (not staffMember `id` values). For APPOINTMENT this array must be **non-empty** — when `hasStaff` is `false`, pass the default **Business Owner** `resourceId` (query it per § "When hasStaff is false"); `[]` is rejected with `MISSING_APPOINTMENT_RESOURCES` (verified live). CLASS services don't require it.
|
|
155
|
+
|
|
156
|
+
**Response shape:**
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"service": {
|
|
160
|
+
"id": "<uuid>",
|
|
161
|
+
"name": "<name>",
|
|
162
|
+
"description": "<description>",
|
|
163
|
+
"mainSlug": { "name": "<url-slug>", "custom": false },
|
|
164
|
+
"supportedSlugs": [{ "name": "<url-slug>", "custom": false }],
|
|
165
|
+
"type": "APPOINTMENT",
|
|
166
|
+
"payment": { ... },
|
|
167
|
+
"schedule": { ... }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
> **Slug:** Extract from `service.mainSlug.name`. If absent, derive from the service name: lowercase, replace spaces and non-alphanumeric chars with hyphens, deduplicate hyphens.
|
|
173
|
+
|
|
174
|
+
### Service creation guidelines
|
|
175
|
+
|
|
176
|
+
- **Count**: Create exactly `intent.bookings.serviceCount` services (default 3 when not specified).
|
|
177
|
+
- **Type**: Use `"APPOINTMENT"` for 1-on-1 services (the default); use `"CLASS"` when `intent.bookings.serviceType === "CLASS"`. For `CLASS`, set `defaultCapacity` to the max participants (e.g. `20`) instead of `1`.
|
|
178
|
+
|
|
179
|
+
> **⚠️ CLASS services need scheduled sessions before anyone can sign up.** Creating a CLASS service does **not** create any bookable sessions. The front-end lists bookable sessions via `eventTimeSlots.listEventTimeSlots()`, which returns scheduled **session events** — a freshly created CLASS service has none, so its calendar is permanently empty. **For CLASS services you MUST run Step 4b below** to schedule sessions; otherwise the class calendar is a dead end. (APPOINTMENT services don't need this — their bookable times come from staff working hours + `availabilityTimeSlots`.)
|
|
180
|
+
- **`sessionDurations`**: Required for APPOINTMENT. An array containing one integer (minutes). Do NOT specify for CLASS or COURSE services.
|
|
181
|
+
- **Names + descriptions**: Derive from `brand.description`. Examples: a yoga studio → "60-min Vinyasa Flow" (60 min), "30-min Morning Meditation" (30 min), "90-min Deep Restore" (90 min). A hair salon → "Women's Cut & Style" (60 min, $85), "Men's Haircut" (30 min, $45), "Balayage Color" (120 min, $150). Make them brand-appropriate — not generic.
|
|
182
|
+
- **Duration** (integers in minutes): Brand-appropriate. Consultation → 30; standard service → 60; premium/complex → 90–120.
|
|
183
|
+
- **Price `value`**: A string, not a number. Brand-appropriate, non-trivial. A budget studio might charge `"25.00"`; a premium clinic `"250.00"`. Default to mid-range when unclear.
|
|
184
|
+
- **Currency**: Set `"USD"` unless the brand's locale implies otherwise — **but note the site's business currency wins**: Services V2 silently stores the site-locale currency (e.g. a EUR-locale site stores `EUR` even when you send `USD`). This is not an error; the page templates format from the service's returned `currency`, so display stays correct. Don't fight it.
|
|
185
|
+
- **Fire all service creates as a parallel batch** — they are independent calls.
|
|
186
|
+
|
|
187
|
+
### Required fields summary (V2)
|
|
188
|
+
|
|
189
|
+
| Field | Required | Notes |
|
|
190
|
+
|-------|----------|-------|
|
|
191
|
+
| `type` | Yes | `APPOINTMENT`, `CLASS`, or `COURSE` |
|
|
192
|
+
| `name` | Yes | Display name |
|
|
193
|
+
| `defaultCapacity` | Yes | `1` for APPOINTMENT; participant count for CLASS |
|
|
194
|
+
| `onlineBooking.enabled` | Yes | Set to `true` for online booking |
|
|
195
|
+
| `payment.rateType` | Yes | `FIXED`, `NO_FEE`, `VARIED`, or `CUSTOM` |
|
|
196
|
+
| `payment.options.online` or `payment.options.inPerson` | Yes | At least one must be `true` |
|
|
197
|
+
| `schedule.availabilityConstraints.sessionDurations` | APPOINTMENT only | Array with one integer (minutes) |
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Step 4b — Create class sessions (CLASS services only)
|
|
202
|
+
|
|
203
|
+
A CLASS service is bookable only once its **schedule** has scheduled session events. Sessions are **Calendar Events V3** (not part of `@wix/bookings`), created via REST. Run this for every service you created with `type: "CLASS"`; skip it entirely for APPOINTMENT.
|
|
204
|
+
|
|
205
|
+
**Endpoint:** `POST https://www.wixapis.com/calendar/v3/events`
|
|
206
|
+
|
|
207
|
+
You need two things from earlier steps:
|
|
208
|
+
- the service's **schedule id** — `service.schedule.id` from the Step 4 create response (every service has one);
|
|
209
|
+
- a **resource id** — a CLASS event requires **at least one resource** (`resources` non-empty) or it's rejected. Use a staff `resourceId` from Step 3; if no staff were created, use the default Business-Owner resource's `resourceId` (query `POST /bookings/v1/staff-members/query` with `{"query":{}}` and take the one named `Business Owner`).
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# One session. Repeat for several upcoming dates per class (e.g. 3 over the next
|
|
213
|
+
# ~2 weeks) so the calendar isn't empty. `localDate` is local time, no `Z`.
|
|
214
|
+
curl -sS -X POST \
|
|
215
|
+
-H "Authorization: Bearer $TOKEN" -H "wix-site-id: <siteId>" -H "Content-Type: application/json" \
|
|
216
|
+
-d '{
|
|
217
|
+
"event": {
|
|
218
|
+
"scheduleId": "<service.schedule.id>",
|
|
219
|
+
"type": "CLASS",
|
|
220
|
+
"start": { "localDate": "2026-06-05T09:00:00" },
|
|
221
|
+
"end": { "localDate": "2026-06-05T10:00:00" },
|
|
222
|
+
"resources": [ { "id": "<staff resourceId>", "permissionRole": "WRITER" } ],
|
|
223
|
+
"totalCapacity": 12
|
|
224
|
+
}
|
|
225
|
+
}' \
|
|
226
|
+
"https://www.wixapis.com/calendar/v3/events"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
> **Gotchas (verified against the live API):**
|
|
230
|
+
> - `resources[].permissionRole` **must** be `"WRITER"` (or `"COMMENTER"`). Omitting it defaults to `UNKNOWN_ROLE` → `400 "resources.permissionRole must not be UNKNOWN_ROLE"`.
|
|
231
|
+
> - `resources` must be **non-empty** for CLASS — a session with no resource is rejected.
|
|
232
|
+
> - `start`/`end` use `{ "localDate": "YYYY-MM-DDThh:mm:ss" }` (local, no `Z`); seconds are ignored.
|
|
233
|
+
> - For a recurring weekly schedule instead of one-off sessions, add `event.recurrenceRule` (`{ "frequency": "WEEKLY", "interval": 1, "days": ["MONDAY"], "until": { "localDate": "..." } }` — only `WEEKLY` and a **single** day are supported; this creates a `MASTER` event and Wix auto-generates the instances). One-off sessions are simpler and sufficient for a seed.
|
|
234
|
+
> - `totalCapacity` defaults to the schedule's `defaultCapacity` if omitted; set it explicitly to the class size.
|
|
235
|
+
|
|
236
|
+
Verify by fetching slots the way the front-end does (`eventTimeSlots.listEventTimeSlots({ serviceIds: [id], … })`) or `POST /calendar/v3/events/query` filtered by `scheduleId`. Record each session's event id in your return under `seeded.bookings.services[].sessionEventIds` if you want Phase 4 to deep-link.
|
|
237
|
+
|
|
238
|
+
> **Booking-policy toggles (optional, CLASS).** Both live on the default booking policy — get it once via `POST /bookings/v1/booking-policies/query` with body `{"query":{}}` (an empty `{}` body is rejected with `query must not be empty`), then `PATCH /bookings/v1/booking-policies/<id>` with the relevant block + `fieldMask`:
|
|
239
|
+
> - **Waitlist** (so a full session offers "join waitlist"): `"waitlistPolicy": { "enabled": true, "capacity": 10, "reservationTimeInMinutes": 10 }`, `fieldMask.paths: ["waitlistPolicy"]`.
|
|
240
|
+
> - **Multi-participant** (party size > 1): `"participantsPolicy": { "maxParticipantsPerBooking": <N> }`, `fieldMask.paths: ["participantsPolicy"]`. **Defaults to `1`** — without raising it, the front-end party-size selector lets the user pick >1 but `createBooking` rejects it. Raise it (e.g. `4`) to actually enable multi-participant class booking.
|
|
241
|
+
>
|
|
242
|
+
> Skip whichever the build doesn't surface.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Step 5 — Return contract
|
|
247
|
+
|
|
248
|
+
**Return this JSON inline as your agent return** (per `references/shared/RETURN_CONTRACT.md`) — do **NOT** write a `.wix/seed-returns/` file; the orchestrator aggregates seeder returns and is the sole writer of `.wix/seeded.json`:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"phase": "seed-bookings",
|
|
253
|
+
"status": "ok",
|
|
254
|
+
"seeded": {
|
|
255
|
+
"services": [
|
|
256
|
+
{
|
|
257
|
+
"id": "<uuid>",
|
|
258
|
+
"slug": "<mainSlug.name>",
|
|
259
|
+
"name": "<name>",
|
|
260
|
+
"type": "APPOINTMENT",
|
|
261
|
+
"durationMinutes": 60,
|
|
262
|
+
"price": "75.00",
|
|
263
|
+
"currency": "USD"
|
|
264
|
+
}
|
|
265
|
+
],
|
|
266
|
+
"staff": [
|
|
267
|
+
{ "id": "<uuid>", "resourceId": "<uuid>", "name": "<name>" }
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
"recipeCalls": [
|
|
271
|
+
{ "url": "https://www.wixapis.com/_api/bookings/v2/services", "status": 200 }
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
- `staff` is an empty array `[]` when `intent.bookings.hasStaff` is false or Step 3 was skipped.
|
|
277
|
+
- **For `CLASS` services, schedule sessions in Step 4b** and report them (e.g. `seeded.bookings.services[].sessionEventIds`). Only if Step 4b is skipped or fails, add a `notes` entry so the orchestrator surfaces it: `"notes": ["CLASS sessions not scheduled — add session times in the Bookings dashboard before sign-up works."]`
|
|
278
|
+
- On any REST error: set `status: "error"`, include the failing call's response verbatim under `"error"`.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Common failure modes
|
|
283
|
+
|
|
284
|
+
| Failure | Recovery |
|
|
285
|
+
|---------|----------|
|
|
286
|
+
| 403 on service create | Re-mint token and retry once. If still 403, Bookings app was not installed — return `status: "error"`. |
|
|
287
|
+
| 400 `"defaultCapacity is required"` | Add `"defaultCapacity": 1` to the payload (required in V2, not obvious from V1 docs). |
|
|
288
|
+
| 400 `"onlineBooking is required"` | Add `"onlineBooking": { "enabled": true }` — required in V2. |
|
|
289
|
+
| 400 on `payment.options` | At least one of `online` or `inPerson` must be `true`. Set `"inPerson": true` as fallback. |
|
|
290
|
+
| 400 `"sessionDurations is required"` | Add `"schedule": { "availabilityConstraints": { "sessionDurations": [60] } }` for APPOINTMENT types. |
|
|
291
|
+
| 400 `MISSING_APPOINTMENT_RESOURCES` on service create | An APPOINTMENT service's `staffMemberIds` must be **non-empty**. Pass the created staff `resourceId`s (Step 3), or — when `hasStaff` is false — the default **Business Owner** `resourceId` (query `/bookings/v1/staff-members/query` with `{"query":{}}`). An empty `[]` always 400s here. |
|
|
292
|
+
| 400 enum error on `locations.type` | Use `"BUSINESS"`, not `"OWNER_BUSINESS"`. The services endpoint accepts `UNKNOWN_LOCATION_TYPE`, `CUSTOM`, `BUSINESS`, `CUSTOMER`. `OWNER_BUSINESS` is valid on `createBooking.bookedEntity.slot.location.locationType` only. |
|
|
293
|
+
| 400 `is not a valid email/phone` on staff create | Don't send `"phone": ""` or `"email": ""`. Omit the keys entirely when you don't have a real value. |
|
|
294
|
+
| `mainSlug` absent in response | Derive slug from `service.name`: lowercase, replace spaces with hyphens, strip non-alphanumeric. |
|
|
295
|
+
| Staff create returns "Business schedule not found" | Skip staff creation. Return partial seeded data with `notes: ["Staff creation skipped — business schedule not found; configure via Bookings dashboard"]`. |
|
|
@@ -50,7 +50,7 @@ body: {
|
|
|
50
50
|
}
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The response returns the created item's `_id` — collect these for the
|
|
53
|
+
The response returns the created item's `_id` — collect these for the seed return contract.
|
|
54
54
|
|
|
55
55
|
**Every text field must be populated in the `data` object.** Do not create items with empty `data: {}` and expect Phase 2 or the image agent to fill in text fields — they only handle their own scope (pages and images respectively).
|
|
56
56
|
|
|
@@ -70,6 +70,8 @@ body: {
|
|
|
70
70
|
}
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
> **Bulk insert (N ≥ 2 items):** use the `wix-manage` skill's recipe — `wix-manage/references/cms/cms-data-items-crud.md` § "Bulk Insert Items" (`POST /wix-data/v2/bulk/items/insert`, up to 1000/call, one `dataCollectionId` per call → fire one bulk call per collection as a parallel batch). It also documents bulk update/patch/delete. Do not hand-roll N single inserts for a multi-item seed.
|
|
74
|
+
|
|
73
75
|
### Verify inserts with a live query (mandatory)
|
|
74
76
|
|
|
75
77
|
After inserting all items, **query each collection once** and confirm every field you sent is present in the stored `data`. A POST without errors does NOT prove the content persisted — the API has accepted insert bodies with missing fields before, and the failure is invisible until a human opens the page.
|
|
@@ -140,7 +142,7 @@ Emit a structured JSON block at the end of your completion message per `../share
|
|
|
140
142
|
{
|
|
141
143
|
"status": "complete",
|
|
142
144
|
"phase": "cms-seed",
|
|
143
|
-
"scope": "seed
|
|
145
|
+
"scope": "seed",
|
|
144
146
|
"summary": "Created {N} collections; seeded {M} items; verified every field persisted",
|
|
145
147
|
"data": {
|
|
146
148
|
"collections": [
|
|
@@ -5,7 +5,7 @@ description: "Implements structured content pages (About, FAQ, portfolios, team
|
|
|
5
5
|
|
|
6
6
|
# CMS Implementer
|
|
7
7
|
|
|
8
|
-
Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, REST auth + doc lookups, prompt-inlined inputs (
|
|
8
|
+
Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, REST auth + doc lookups, prompt-inlined inputs (read only your `.wix/seeded.json` slice), return contract, style conventions, and common failure modes.
|
|
9
9
|
|
|
10
10
|
## Scope routing
|
|
11
11
|
|
|
@@ -29,6 +29,32 @@ Pick based on the business type (the orchestrator names one in your prompt):
|
|
|
29
29
|
|
|
30
30
|
See `<SKILL_ROOT>/references/verticals/cms.md` frontmatter.
|
|
31
31
|
|
|
32
|
+
## Seed return
|
|
33
|
+
|
|
34
|
+
The `seed` scope emits this `data` shape in its return JSON (envelope rules in `../shared/RETURN_CONTRACT.md`). Phase 4 CMS page agents reference these collection names; the image agent attaches entity images to CMS items by ID.
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"status": "complete",
|
|
39
|
+
"phase": "cms-seed",
|
|
40
|
+
"scope": "seed",
|
|
41
|
+
"data": {
|
|
42
|
+
"collections": [
|
|
43
|
+
{
|
|
44
|
+
"name": "about-content",
|
|
45
|
+
"itemIds": ["665f3363-..."],
|
|
46
|
+
"fields": ["heading", "body", "image"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "faq",
|
|
50
|
+
"itemIds": ["abc", "def", "ghi", "jkl", "mno", "pqr"],
|
|
51
|
+
"fields": ["question", "answer", "sortOrder"]
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
32
58
|
## Page width (FAQ, About, long-form CMS)
|
|
33
59
|
|
|
34
60
|
Read `references/shared/STYLING.md` § "Prose / reading width". **Do not** wrap FAQ/About body copy in `max-w-3xl` unless `--container-3xl` exists in `src/styles/global.css` `@theme`. Prefer `container-reading`, `max-w-6xl` (when `--container-6xl` is defined), or `max-w-[48rem]`. A bare `max-w-3xl` with only a spacing scale ships a ~80px column.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"wix-stores": "215238eb-22a5-4c36-9e7b-e7c08025e04e",
|
|
3
3
|
"wix-forms": "225dd912-7dea-4738-8688-4b8c6955ffc2",
|
|
4
|
-
"wix-blog": "14bcded7-0066-7c35-14d7-466cb3f09103"
|
|
4
|
+
"wix-blog": "14bcded7-0066-7c35-14d7-466cb3f09103",
|
|
5
|
+
"wix-gift-cards": "d80111c5-a0f4-47a8-b63a-65b54d774a27"
|
|
5
6
|
}
|