@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.
Files changed (195) hide show
  1. package/README.md +5 -9
  2. package/package.json +1 -1
  3. package/skills/wix-app/SKILL.md +51 -42
  4. package/skills/wix-app/references/BACKEND_EVENT.md +10 -65
  5. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +101 -406
  6. package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +14 -111
  7. package/skills/wix-app/references/DASHBOARD_MODAL.md +56 -205
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +19 -98
  9. package/skills/wix-app/references/DASHBOARD_PLUGIN.md +6 -108
  10. package/skills/wix-app/references/DATA_COLLECTION.md +113 -198
  11. package/skills/wix-app/references/EMBEDDED_SCRIPT.md +41 -268
  12. package/skills/wix-app/references/EXTENSION_REGISTRATION.md +27 -76
  13. package/skills/wix-app/references/SERVICE_PLUGIN.md +35 -188
  14. package/skills/wix-app/references/SITE_PLUGIN.md +8 -92
  15. package/skills/wix-app/references/STORES_VERSIONING.md +1 -0
  16. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +0 -44
  17. package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +19 -330
  18. package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +0 -12
  19. package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +7 -0
  20. package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +0 -12
  21. package/skills/wix-app/references/dashboard-plugin/SLOTS.md +3 -15
  22. package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +2 -18
  23. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +2 -42
  24. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +1 -11
  25. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +1 -11
  26. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +2 -40
  27. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +2 -19
  28. package/skills/wix-app/references/service-plugin/VALIDATIONS.md +2 -18
  29. package/skills/wix-app/references/site-plugin/SLOTS.md +17 -68
  30. package/skills/wix-design-system/SKILL.md +4 -0
  31. package/skills/wix-headless/SKILL.md +131 -386
  32. package/skills/wix-headless/references/BUILD-astro.md +246 -0
  33. package/skills/wix-headless/references/BUILD-own-build.md +167 -0
  34. package/skills/wix-headless/references/BUILD.md +105 -0
  35. package/skills/wix-headless/references/DESIGN_SYSTEM.md +175 -0
  36. package/skills/wix-headless/references/DISCOVERY-connect.md +55 -0
  37. package/skills/wix-headless/references/DISCOVERY-create.md +238 -0
  38. package/skills/wix-headless/references/DISCOVERY.md +50 -306
  39. package/skills/wix-headless/references/PLAN-connect.md +19 -0
  40. package/skills/wix-headless/references/PLAN-create.md +17 -0
  41. package/skills/wix-headless/references/PLAN.md +92 -0
  42. package/skills/wix-headless/references/SEED.md +214 -0
  43. package/skills/wix-headless/references/SETUP.md +73 -248
  44. package/skills/wix-headless/references/{blog → astro/blog}/BLOG_PAGES.md +6 -5
  45. package/skills/wix-headless/references/{blog → astro/blog}/BLOG_SETUP.md +2 -2
  46. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +337 -0
  47. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +428 -0
  48. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +214 -0
  49. package/skills/wix-headless/references/{cms → astro/cms}/FAQ_KNOWLEDGE_BASE.md +14 -12
  50. package/skills/wix-headless/references/{cms → astro/cms}/PORTFOLIO.md +15 -16
  51. package/skills/wix-headless/references/{cms → astro/cms}/RESOURCE_LIBRARY.md +13 -15
  52. package/skills/wix-headless/references/{cms → astro/cms}/TEAM_DIRECTORY.md +11 -11
  53. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +354 -0
  54. package/skills/wix-headless/references/{ecom → astro/ecom}/CART_PAGES.md +8 -9
  55. package/skills/wix-headless/references/{ecom → astro/ecom}/CART_WIRING.md +6 -6
  56. package/skills/wix-headless/references/{ecom → astro/ecom}/COMPONENTS_CSS.md +6 -6
  57. package/skills/wix-headless/references/{forms → astro/forms}/CONTACT_FORM.md +18 -32
  58. package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/COMPONENTS.md +3 -3
  59. package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/PAGES.md +2 -2
  60. package/skills/wix-headless/references/{stores → astro/stores}/BACK_IN_STOCK.md +7 -7
  61. package/skills/wix-headless/references/{stores → astro/stores}/CATEGORY_PAGES.md +12 -13
  62. package/skills/wix-headless/references/{stores → astro/stores}/COMPONENTS_CSS.md +7 -7
  63. package/skills/wix-headless/references/{stores → astro/stores}/HOME_AND_NAV.md +11 -9
  64. package/skills/wix-headless/references/{stores → astro/stores}/PRODUCT_PAGES.md +11 -9
  65. package/skills/wix-headless/references/{stores → astro/stores}/SHARED_WIRING.md +8 -7
  66. package/skills/wix-headless/references/astro/templates/Footer.astro +27 -0
  67. package/skills/wix-headless/references/astro/templates/Layout.astro +132 -0
  68. package/skills/wix-headless/references/astro/templates/Navigation.astro +38 -0
  69. package/skills/wix-headless/references/astro/templates/astro.config.mjs +41 -0
  70. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +218 -0
  71. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +194 -0
  72. package/skills/wix-headless/references/astro/templates/bookings/ManageBooking.tsx +84 -0
  73. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +25 -0
  74. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +50 -0
  75. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +45 -0
  76. package/skills/wix-headless/references/astro/templates/bookings/api/confirm-booking.ts +34 -0
  77. package/skills/wix-headless/references/astro/templates/bookings/api/waitlist.ts +74 -0
  78. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +111 -0
  79. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +413 -0
  80. package/skills/wix-headless/references/astro/templates/bookings/manage-booking.astro +76 -0
  81. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +81 -0
  82. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +65 -0
  83. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +302 -0
  84. package/skills/wix-headless/references/astro/templates/global.css +286 -0
  85. package/skills/wix-headless/references/astro/templates/index.astro +56 -0
  86. package/skills/wix-headless/{templates → references/astro/templates}/stores/back-in-stock.ts +1 -1
  87. package/skills/wix-headless/{templates → references/astro/templates}/stores/components-stores.css +138 -33
  88. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +28 -19
  89. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +6 -6
  90. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +106 -0
  91. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +295 -0
  92. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +35 -232
  93. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +39 -8
  94. package/skills/wix-headless/references/commands/known-apps.json +2 -1
  95. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +142 -0
  96. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +88 -0
  97. package/skills/wix-headless/references/custom/blog/WIRING.md +52 -0
  98. package/skills/wix-headless/references/custom/bookings/WIRING.md +88 -0
  99. package/skills/wix-headless/references/custom/cms/WIRING.md +97 -0
  100. package/skills/wix-headless/references/custom/ecom/WIRING.md +67 -0
  101. package/skills/wix-headless/references/custom/forms/WIRING.md +101 -0
  102. package/skills/wix-headless/references/custom/stores/WIRING.md +80 -0
  103. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +12 -12
  104. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +6 -6
  105. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +13 -13
  106. package/skills/wix-headless/references/images/INSTRUCTIONS.md +58 -60
  107. package/skills/wix-headless/references/seed-recipes.md +49 -0
  108. package/skills/wix-headless/references/shared/AGENTS_MD.md +47 -0
  109. package/skills/wix-headless/references/shared/AUTHENTICATION.md +108 -0
  110. package/skills/wix-headless/references/shared/BUILD_NOISE.md +2 -2
  111. package/skills/wix-headless/references/shared/DESIGN_MD.md +85 -0
  112. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +41 -0
  113. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +40 -39
  114. package/skills/wix-headless/references/shared/IMPLEMENTER.md +92 -41
  115. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +3 -3
  116. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +32 -312
  117. package/skills/wix-headless/references/shared/STYLING.md +48 -21
  118. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +59 -29
  119. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +31 -28
  120. package/skills/wix-headless/references/verticals/_schema.json +31 -117
  121. package/skills/wix-headless/references/verticals/_schema.md +60 -150
  122. package/skills/wix-headless/references/verticals/blog.md +5 -99
  123. package/skills/wix-headless/references/verticals/bookings.md +41 -0
  124. package/skills/wix-headless/references/verticals/cms.md +9 -100
  125. package/skills/wix-headless/references/verticals/ecom.md +6 -77
  126. package/skills/wix-headless/references/verticals/forms.md +5 -85
  127. package/skills/wix-headless/references/verticals/gift-cards.md +6 -103
  128. package/skills/wix-headless/references/verticals/stores.md +10 -144
  129. package/skills/wix-headless/scripts/check-manifest.mjs +148 -11
  130. package/skills/wix-headless/scripts/compose.mjs +540 -0
  131. package/skills/wix-headless/scripts/emit-design-tokens.mjs +136 -53
  132. package/skills/wix-headless/scripts/flatten.sh +61 -0
  133. package/skills/wix-headless/scripts/generate-images.mjs +242 -0
  134. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +67 -43
  135. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +3 -3
  136. package/skills/wix-headless/scripts/scaffold.sh +113 -27
  137. package/skills/wix-headless/scripts/seed-utilities.sh +50 -12
  138. package/skills/wix-headless/scripts/write-decorative-json.mjs +62 -0
  139. package/skills/wix-manage/SKILL.md +4 -8
  140. package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +2 -0
  141. package/skills/wix-manage/references/cms/cms-data-items-crud.md +4 -0
  142. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +2 -0
  143. package/skills/wix-manage/references/cms/cms-schema-management.md +4 -0
  144. package/skills/wix-manage/references/domains/domain-search-and-purchase.md +10 -7
  145. package/skills/wix-manage/references/ecommerce/api-shipping.md +443 -0
  146. package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +5 -6
  147. package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +4 -5
  148. package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +4 -5
  149. package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +1 -1
  150. package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +1 -2
  151. package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +2 -2
  152. package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +4 -5
  153. package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +7 -7
  154. package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +209 -72
  155. package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +9 -10
  156. package/skills/wix-manage/references/ecommerce/skill-graph.md +4 -4
  157. package/skills/wix-manage/references/forms/create-form.md +50 -0
  158. package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +2 -0
  159. package/skills/wix-manage/references/stores/create-product-from-image.md +91 -38
  160. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +2 -0
  161. package/skills/wix-app/references/custom-element-widget/default-thumbnail.png +0 -0
  162. package/skills/wix-app/references/data-collection/extension-template.ts +0 -107
  163. package/skills/wix-headless/references/ORCHESTRATION.md +0 -269
  164. package/skills/wix-headless/references/commands/install-app.md +0 -42
  165. package/skills/wix-headless/references/commands/mcp-bootstrap.md +0 -28
  166. package/skills/wix-headless/references/designer/INSTRUCTIONS.md +0 -828
  167. package/skills/wix-headless/references/shared/MCP_PREFIX.md +0 -105
  168. package/skills/wix-headless/scripts/copy-utility-templates.mjs +0 -153
  169. package/skills/wix-headless/scripts/finalize-run-json.mjs +0 -141
  170. package/skills/wix-headless/scripts/init-site-json.mjs +0 -63
  171. package/skills/wix-headless/scripts/preview.sh +0 -45
  172. package/skills/wix-headless/scripts/release.sh +0 -73
  173. package/skills/wix-headless/templates/ecom/components-ecom.css +0 -215
  174. package/skills/wix-manage/references/events/list-events.md +0 -203
  175. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartBadge.tsx +0 -0
  176. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartView.tsx +0 -0
  177. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/cart.astro +0 -0
  178. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/discounts.ts +0 -0
  179. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/thank-you.astro +0 -0
  180. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/GiftCardPurchase.tsx +0 -0
  181. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_home-teaser-snippet.astro +0 -0
  182. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_nav-snippet.astro +0 -0
  183. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/components-gift-cards.css +0 -0
  184. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.astro +0 -0
  185. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.ts +0 -0
  186. /package/skills/wix-headless/{templates → references/astro/templates}/stores/AddToCartButton.tsx +0 -0
  187. /package/skills/wix-headless/{templates → references/astro/templates}/stores/BackInStockForm.tsx +0 -0
  188. /package/skills/wix-headless/{templates → references/astro/templates}/stores/CategoryRail.astro +0 -0
  189. /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductCard.astro +0 -0
  190. /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductPurchase.tsx +0 -0
  191. /package/skills/wix-headless/{templates → references/astro/templates}/stores/SeoTags.astro +0 -0
  192. /package/skills/wix-headless/{templates → references/astro/templates}/stores/categories.ts +0 -0
  193. /package/skills/wix-headless/{templates → references/astro/templates}/stores/category/[slug].astro +0 -0
  194. /package/skills/wix-headless/{templates → references/astro/templates}/stores/products/[slug].astro +0 -0
  195. /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 MCP
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`, MCP tools 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.
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 `mcp__wix-mcp-remote__CallWixSiteAPI` is genuinely unavailable. In that case, the user can create posts manually in the Wix dashboard.
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** — `CallWixSiteAPI: POST /blog/v3/posts/query` with body `{ "query": { "paging": { "limit": 1 } } }`
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
- CallWixSiteAPI: POST https://www.wixapis.com/apps-installer-service/v1/app-instance/install
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 `CallWixSiteAPI` tool-call shape — include `siteId`, `reason`, `sourceDocUrl` wrapper fields and pass `body` as a real object (NOT a stringified JSON). See `../../shared/MCP_PREFIX.md` § "CallWixSiteAPI call conventions".
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
- CallWixSiteAPI: GET /members/v1/members
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
- CallWixSiteAPI: POST /blog/v3/draft-posts
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 `CallWixSiteAPI` call per post)
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., adding a cover image via PATCH after creation), it becomes `hasUnpublishedChanges: true`. You must call `POST /blog/v3/draft-posts/{draftPostId}/publish` to re-publish. To avoid this, include media in the initial creation call.
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
- CallWixSiteAPI: POST /blog/v3/posts/query
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
- ### Step 5: Log Results
141
+ ## Step 5: Return Results
143
142
 
144
- Write a sidecar file at `.wix/logs/blog-data.md` (see `../../shared/LIFECYCLE_LOG.md` for the sidecar contract). Do **not** append to `.wix/lifecycle.log.md` directly — the orchestrator concatenates all sidecars at the end. Use a `####` heading so the entry nests under `### features-orchestrator` in the assembled log:
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
- ```markdown
147
- ## blog
148
- - Status: complete
149
- - Content: {n} posts published ({post titles})
150
- - Images: {generated (n/n attached) | skipped (user declined) | not attempted}
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, MCP prefix, site.json read pattern, return contract, style conventions, and common failure modes.
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 MCP with Ricos content) | `./BLOG_CONTENT.md` |
15
- | `components` | Components (blog service module, RicosViewer, consts, astro.config sitemap) | `./BLOG_SETUP.md` |
16
- | `pages` | Pages (listing, detail, RSS, BlogPost layout, home contribution) | `./BLOG_PAGES.md` |
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
- See `<SKILL_ROOT>/references/verticals/blog.md` frontmatter.
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"]`. |