@wix/agent-skills 1.6.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 +5 -9
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +51 -42
- package/skills/wix-app/references/BACKEND_EVENT.md +10 -65
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +101 -406
- package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +14 -111
- package/skills/wix-app/references/DASHBOARD_MODAL.md +56 -205
- package/skills/wix-app/references/DASHBOARD_PAGE.md +19 -98
- package/skills/wix-app/references/DASHBOARD_PLUGIN.md +6 -108
- package/skills/wix-app/references/DATA_COLLECTION.md +113 -198
- package/skills/wix-app/references/EMBEDDED_SCRIPT.md +41 -268
- package/skills/wix-app/references/EXTENSION_REGISTRATION.md +27 -76
- package/skills/wix-app/references/SERVICE_PLUGIN.md +35 -188
- package/skills/wix-app/references/SITE_PLUGIN.md +8 -92
- package/skills/wix-app/references/STORES_VERSIONING.md +1 -0
- package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +0 -44
- package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +19 -330
- package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +0 -12
- package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +7 -0
- package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +0 -12
- package/skills/wix-app/references/dashboard-plugin/SLOTS.md +3 -15
- package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +2 -18
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +2 -42
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +1 -11
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +1 -11
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +2 -40
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +2 -19
- package/skills/wix-app/references/service-plugin/VALIDATIONS.md +2 -18
- package/skills/wix-app/references/site-plugin/SLOTS.md +17 -68
- package/skills/wix-design-system/SKILL.md +4 -0
- package/skills/wix-headless/SKILL.md +131 -386
- 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 +105 -0
- package/skills/wix-headless/references/DESIGN_SYSTEM.md +175 -0
- 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 +50 -306
- 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 +92 -0
- package/skills/wix-headless/references/SEED.md +214 -0
- package/skills/wix-headless/references/SETUP.md +73 -248
- package/skills/wix-headless/references/{blog → astro/blog}/BLOG_PAGES.md +6 -5
- package/skills/wix-headless/references/{blog → astro/blog}/BLOG_SETUP.md +2 -2
- 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 +214 -0
- package/skills/wix-headless/references/{cms → astro/cms}/FAQ_KNOWLEDGE_BASE.md +14 -12
- package/skills/wix-headless/references/{cms → astro/cms}/PORTFOLIO.md +15 -16
- package/skills/wix-headless/references/{cms → astro/cms}/RESOURCE_LIBRARY.md +13 -15
- package/skills/wix-headless/references/{cms → astro/cms}/TEAM_DIRECTORY.md +11 -11
- package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +354 -0
- package/skills/wix-headless/references/{ecom → astro/ecom}/CART_PAGES.md +8 -9
- package/skills/wix-headless/references/{ecom → astro/ecom}/CART_WIRING.md +6 -6
- package/skills/wix-headless/references/{ecom → astro/ecom}/COMPONENTS_CSS.md +6 -6
- package/skills/wix-headless/references/{forms → astro/forms}/CONTACT_FORM.md +18 -32
- package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/COMPONENTS.md +3 -3
- package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/PAGES.md +2 -2
- package/skills/wix-headless/references/{stores → astro/stores}/BACK_IN_STOCK.md +7 -7
- package/skills/wix-headless/references/{stores → astro/stores}/CATEGORY_PAGES.md +12 -13
- package/skills/wix-headless/references/{stores → astro/stores}/COMPONENTS_CSS.md +7 -7
- package/skills/wix-headless/references/{stores → astro/stores}/HOME_AND_NAV.md +11 -9
- package/skills/wix-headless/references/{stores → astro/stores}/PRODUCT_PAGES.md +11 -9
- package/skills/wix-headless/references/{stores → astro/stores}/SHARED_WIRING.md +8 -7
- package/skills/wix-headless/references/astro/templates/Footer.astro +27 -0
- package/skills/wix-headless/references/astro/templates/Layout.astro +132 -0
- package/skills/wix-headless/references/astro/templates/Navigation.astro +38 -0
- package/skills/wix-headless/references/astro/templates/astro.config.mjs +41 -0
- 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/ecom/components-ecom.css +302 -0
- package/skills/wix-headless/references/astro/templates/global.css +286 -0
- package/skills/wix-headless/references/astro/templates/index.astro +56 -0
- package/skills/wix-headless/{templates → references/astro/templates}/stores/back-in-stock.ts +1 -1
- package/skills/wix-headless/{templates → references/astro/templates}/stores/components-stores.css +138 -33
- package/skills/wix-headless/references/blog/BLOG_CONTENT.md +28 -19
- package/skills/wix-headless/references/blog/INSTRUCTIONS.md +6 -6
- 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 +35 -232
- package/skills/wix-headless/references/cms/INSTRUCTIONS.md +39 -8
- 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 +88 -0
- 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 +12 -12
- package/skills/wix-headless/references/forms/INSTRUCTIONS.md +6 -6
- package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +13 -13
- package/skills/wix-headless/references/images/INSTRUCTIONS.md +58 -60
- package/skills/wix-headless/references/seed-recipes.md +49 -0
- package/skills/wix-headless/references/shared/AGENTS_MD.md +47 -0
- package/skills/wix-headless/references/shared/AUTHENTICATION.md +108 -0
- package/skills/wix-headless/references/shared/BUILD_NOISE.md +2 -2
- package/skills/wix-headless/references/shared/DESIGN_MD.md +85 -0
- package/skills/wix-headless/references/shared/DOCS_SEARCH.md +41 -0
- package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +40 -39
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +92 -41
- package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +3 -3
- package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +32 -312
- package/skills/wix-headless/references/shared/STYLING.md +48 -21
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +59 -29
- package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +31 -28
- package/skills/wix-headless/references/verticals/_schema.json +31 -117
- package/skills/wix-headless/references/verticals/_schema.md +60 -150
- package/skills/wix-headless/references/verticals/blog.md +5 -99
- package/skills/wix-headless/references/verticals/bookings.md +41 -0
- package/skills/wix-headless/references/verticals/cms.md +9 -100
- package/skills/wix-headless/references/verticals/ecom.md +6 -77
- package/skills/wix-headless/references/verticals/forms.md +5 -85
- package/skills/wix-headless/references/verticals/gift-cards.md +6 -103
- package/skills/wix-headless/references/verticals/stores.md +10 -144
- package/skills/wix-headless/scripts/check-manifest.mjs +148 -11
- package/skills/wix-headless/scripts/compose.mjs +540 -0
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +136 -53
- package/skills/wix-headless/scripts/flatten.sh +61 -0
- package/skills/wix-headless/scripts/generate-images.mjs +242 -0
- package/skills/wix-headless/scripts/patch-decorative-slots.mjs +67 -43
- package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +3 -3
- package/skills/wix-headless/scripts/scaffold.sh +113 -27
- package/skills/wix-headless/scripts/seed-utilities.sh +50 -12
- package/skills/wix-headless/scripts/write-decorative-json.mjs +62 -0
- package/skills/wix-manage/SKILL.md +4 -8
- package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +2 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +4 -0
- package/skills/wix-manage/references/cms/cms-references-and-relationships.md +2 -0
- package/skills/wix-manage/references/cms/cms-schema-management.md +4 -0
- package/skills/wix-manage/references/domains/domain-search-and-purchase.md +10 -7
- package/skills/wix-manage/references/ecommerce/api-shipping.md +443 -0
- package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +5 -6
- package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +4 -5
- package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +4 -5
- package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +1 -1
- package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +1 -2
- package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +2 -2
- package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +4 -5
- package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +7 -7
- package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +209 -72
- package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +9 -10
- package/skills/wix-manage/references/ecommerce/skill-graph.md +4 -4
- package/skills/wix-manage/references/forms/create-form.md +50 -0
- package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +2 -0
- package/skills/wix-manage/references/stores/create-product-from-image.md +91 -38
- package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +2 -0
- package/skills/wix-app/references/custom-element-widget/default-thumbnail.png +0 -0
- package/skills/wix-app/references/data-collection/extension-template.ts +0 -107
- package/skills/wix-headless/references/ORCHESTRATION.md +0 -269
- package/skills/wix-headless/references/commands/install-app.md +0 -42
- package/skills/wix-headless/references/commands/mcp-bootstrap.md +0 -28
- package/skills/wix-headless/references/designer/INSTRUCTIONS.md +0 -828
- package/skills/wix-headless/references/shared/MCP_PREFIX.md +0 -105
- package/skills/wix-headless/scripts/copy-utility-templates.mjs +0 -153
- package/skills/wix-headless/scripts/finalize-run-json.mjs +0 -141
- package/skills/wix-headless/scripts/init-site-json.mjs +0 -63
- package/skills/wix-headless/scripts/preview.sh +0 -45
- package/skills/wix-headless/scripts/release.sh +0 -73
- package/skills/wix-headless/templates/ecom/components-ecom.css +0 -215
- package/skills/wix-manage/references/events/list-events.md +0 -203
- /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartBadge.tsx +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartView.tsx +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/ecom/cart.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/ecom/discounts.ts +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/ecom/thank-you.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/GiftCardPurchase.tsx +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_home-teaser-snippet.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_nav-snippet.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/components-gift-cards.css +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.ts +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/AddToCartButton.tsx +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/BackInStockForm.tsx +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/CategoryRail.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductCard.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductPurchase.tsx +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/SeoTags.astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/categories.ts +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/category/[slug].astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/products/[slug].astro +0 -0
- /package/skills/wix-headless/{templates → references/astro/templates}/stores/products/index.astro +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Recipe: Seed Initial Blog Posts via
|
|
1
|
+
# Recipe: Seed Initial Blog Posts via REST
|
|
2
2
|
|
|
3
3
|
Create 3 on-brand blog posts so the blog has real content on first preview.
|
|
4
4
|
|
|
@@ -8,24 +8,24 @@ Create 3 on-brand blog posts so the blog has real content on first preview.
|
|
|
8
8
|
> 3. **Use `@wix/blog`, not `@wix/data`** — blog posts are NOT CMS collections.
|
|
9
9
|
> 4. **`RICH_CONTENT` fieldset required** — without it, `post.richContent` is undefined and rendering fails.
|
|
10
10
|
|
|
11
|
-
> **Required in the Seed phase.** When dispatched with `Scope: seed`,
|
|
11
|
+
> **Required in the Seed phase.** When dispatched with `Scope: seed`, the CLI-minted REST token and discovery context are guaranteed available. Always execute this section — do not skip. A blog with zero posts on first preview is a build failure.
|
|
12
12
|
>
|
|
13
|
-
> **Skip only for standalone invocations** where the Blog skill is invoked directly AND
|
|
13
|
+
> **Skip only for standalone invocations** where the Blog skill is invoked directly AND CLI token mint fails after recovery. In that case, the user can create posts manually in the Wix dashboard.
|
|
14
14
|
|
|
15
15
|
## Step 0: Ensure Blog App Is Installed
|
|
16
16
|
|
|
17
17
|
Before querying blog data, verify the Blog app is installed:
|
|
18
18
|
|
|
19
|
-
1. **Probe** — `
|
|
19
|
+
1. **Probe** — `REST: POST https://www.wixapis.com/blog/v3/posts/query`
|
|
20
20
|
2. **If the API returns a "REQUIRED_APP_NOT_INSTALLED" error** → install the Wix Blog app:
|
|
21
21
|
```
|
|
22
|
-
|
|
22
|
+
REST: POST https://www.wixapis.com/apps-installer-service/v1/app-instance/install
|
|
23
23
|
body: {
|
|
24
24
|
"tenant": { "tenantType": "SITE", "id": "<siteId>" },
|
|
25
25
|
"appInstance": { "appDefId": "14bcded7-0066-7c35-14d7-466cb3f09103", "enabled": true }
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
|
-
> Translate this prose-HTTP form into the full `
|
|
28
|
+
> Translate this prose-HTTP form into the full `curl` tool-call shape — pass `body` as JSON in `-d` (NOT a stringified JSON). See `../shared/AUTHENTICATION.md` for the standard REST headers.
|
|
29
29
|
|
|
30
30
|
Then retry the probe query to confirm installation succeeded.
|
|
31
31
|
3. **If the probe succeeds** → proceed to Step 1.
|
|
@@ -37,7 +37,7 @@ Before querying blog data, verify the Blog app is installed:
|
|
|
37
37
|
Blog posts require a `memberId` (the post author). Fetch the first site member:
|
|
38
38
|
|
|
39
39
|
```
|
|
40
|
-
|
|
40
|
+
REST: GET https://www.wixapis.com/members/v1/members
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
Extract the first member's `_id` from the response. This will be used as the author for all posts.
|
|
@@ -70,7 +70,7 @@ For each post, call `POST /blog/v3/draft-posts` with `publish: true` to create a
|
|
|
70
70
|
> Do not include the `media` field — cover images are handled separately by the image agent after post creation.
|
|
71
71
|
|
|
72
72
|
```
|
|
73
|
-
|
|
73
|
+
REST: POST https://www.wixapis.com/blog/v3/draft-posts
|
|
74
74
|
body: {
|
|
75
75
|
"draftPost": {
|
|
76
76
|
"title": "<Post Title>",
|
|
@@ -119,17 +119,16 @@ Use these node types to build rich, varied content:
|
|
|
119
119
|
- TEXT nodes are always leaf nodes inside PARAGRAPH, HEADING, CODE_BLOCK, etc.
|
|
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
|
-
- Create all 3 posts in sequence (one `
|
|
123
|
-
- **Best practice:** include all fields (title, content, media) in the initial creation call to avoid needing a re-publish
|
|
122
|
+
- Create all 3 posts in sequence (one `curl` call per post)
|
|
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
|
|
|
129
128
|
Query published posts to confirm all 3 exist:
|
|
130
129
|
|
|
131
130
|
```
|
|
132
|
-
|
|
131
|
+
REST: POST https://www.wixapis.com/blog/v3/posts/query
|
|
133
132
|
body: {
|
|
134
133
|
"query": {
|
|
135
134
|
"paging": { "limit": 10 }
|
|
@@ -139,14 +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
|
```
|
|
152
160
|
|
|
161
|
+
The JSON block MUST be the last content in your message.
|
|
@@ -5,21 +5,21 @@ 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,
|
|
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
|
|
|
12
12
|
| Scope | Phase | Reference |
|
|
13
13
|
|-------|-------|-----------|
|
|
14
|
-
| `seed` | Seed (post seeding via
|
|
15
|
-
| `components` | Components (blog service module, RicosViewer, consts, astro.config sitemap) |
|
|
16
|
-
| `pages` | Pages (listing, detail, RSS, BlogPost layout,
|
|
14
|
+
| `seed` | Seed (post seeding via REST with Ricos content) | `./BLOG_CONTENT.md` |
|
|
15
|
+
| `components` | Components (blog service module, RicosViewer, consts, astro.config sitemap) | `../astro/blog/BLOG_SETUP.md` |
|
|
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
|
|
|
@@ -29,4 +29,4 @@ See `<SKILL_ROOT>/references/verticals/blog.md` frontmatter.
|
|
|
29
29
|
| `<article set:html={post.content} />` | `<RicosViewer client:only="react" content={post.richContent} />` — content is Ricos format |
|
|
30
30
|
| Skip `wixMetadata` on `[...slug].astro` | Required for Wix page routing + SEO |
|
|
31
31
|
| Query posts without `RICH_CONTENT` fieldset | Always request `RICH_CONTENT` or `post.richContent` is undefined |
|
|
32
|
-
| Scoped `<style>` for RicosViewer overrides | `<style is:global>` — React islands don't inherit scoped Astro styles |
|
|
32
|
+
| Scoped `<style>` for RicosViewer overrides | `<style is:global>` — React islands don't inherit scoped Astro styles |
|
|
@@ -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"]`. |
|