@wix/agent-skills 1.14.0 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/package.json +1 -1
- package/skills/README.md +23 -0
- package/skills/wix-app/SKILL.md +10 -2
- package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
- package/skills/wix-app/references/CODE_QUALITY.md +1 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
- package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
- package/skills/wix-app/references/DATA_COLLECTION.md +5 -3
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +25 -9
- package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
- package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
- package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
- package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
- package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
- package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
- package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
- package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
- package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
- package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
- package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
- package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
- package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +57 -0
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +23 -105
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
- package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +18 -16
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
- package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
- package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
- package/skills/wix-app/references/stores/INVENTORY.md +37 -0
- package/skills/wix-app/references/stores/QUERY.md +87 -0
- package/skills/wix-app/references/stores/WRITE.md +55 -0
- package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
- package/skills/wix-auth/SKILL.md +12 -0
- package/skills/wix-auth/references/device-flow.md +140 -0
- package/skills/wix-docs/SKILL.md +188 -0
- package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
- package/skills/wix-docs/references/EXTRACTING.md +120 -0
- package/skills/wix-headless/SKILL.md +89 -130
- package/skills/wix-headless/entry/bootstrap.mjs +93 -0
- package/skills/wix-headless/entry/skill.md +76 -0
- package/skills/wix-headless/references/AI_FEATURES.md +178 -0
- package/skills/wix-headless/references/CAPABILITIES.md +86 -0
- package/skills/wix-headless/references/DISCOVERY.md +34 -56
- package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
- package/skills/wix-headless/references/FEEDBACK.md +129 -0
- package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +99 -0
- package/skills/wix-headless/references/SEED.md +45 -187
- package/skills/wix-headless/references/SETUP.md +32 -103
- package/skills/wix-headless/references/astro.md +98 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +213 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
- package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
- package/skills/wix-headless/references/inline-recipes/setup-bookings.md +232 -0
- package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
- package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +192 -0
- package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +155 -0
- package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +197 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +136 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
- package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
- package/skills/wix-headless/references/managed/CONNECT.md +74 -0
- package/skills/wix-headless/references/managed/CREATE.md +130 -0
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +79 -0
- package/skills/wix-headless/references/non-astro.md +59 -0
- package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
- package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +15 -0
- package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
- package/skills/wix-headless/references/stripe/DEPLOYMENT.md +71 -0
- package/skills/wix-manage/SKILL.md +29 -3
- package/skills/wix-manage/references/analytics/query-site-analytics.md +298 -0
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
- package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
- package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
- package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +299 -0
- package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
- package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +517 -0
- package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +218 -0
- package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
- package/skills/wix-manage/references/sites/site-import.md +270 -0
- package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +57 -0
- package/skills/wix-vibe-headless/SKILL.md +142 -0
- package/skills/wix-vibe-headless/platforms/README.md +22 -0
- package/skills/wix-vibe-headless/platforms/base44.md +142 -0
- package/skills/wix-vibe-headless/platforms/generic.md +87 -0
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
- package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +157 -0
- package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
- package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +217 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
- package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
- package/skills/wix-vibe-headless/references/events/wix-events-browse.js +136 -0
- package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
- package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
- package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +223 -0
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
- package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
- package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +164 -0
- package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
- package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +146 -0
- package/skills/wix-headless/references/BUILD-astro.md +0 -253
- package/skills/wix-headless/references/BUILD-own-build.md +0 -167
- package/skills/wix-headless/references/BUILD.md +0 -105
- package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
- package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
- package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
- package/skills/wix-headless/references/PLAN-connect.md +0 -19
- package/skills/wix-headless/references/PLAN-create.md +0 -17
- package/skills/wix-headless/references/PLAN.md +0 -92
- package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
- package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
- package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
- package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
- package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
- package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
- package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
- package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
- package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
- package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
- package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
- package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
- package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
- package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
- package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
- package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
- package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
- package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
- package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
- package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
- package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
- package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
- package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
- package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
- package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
- package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
- package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
- package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
- package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
- package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
- package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
- package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
- package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
- package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
- package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
- package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
- package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
- package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
- package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
- package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
- package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
- package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
- package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
- package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
- package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
- package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
- package/skills/wix-headless/references/astro/templates/global.css +0 -288
- package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
- package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
- package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
- package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
- package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
- package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
- package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
- package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
- package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
- package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
- package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
- package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
- package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
- package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
- package/skills/wix-headless/references/bookings/FLOW.md +0 -246
- package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
- package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
- package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
- package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
- package/skills/wix-headless/references/commands/known-apps.json +0 -6
- package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
- package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
- package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
- package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
- package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
- package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
- package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
- package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
- package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
- package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
- package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
- package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
- package/skills/wix-headless/references/seed-recipes.md +0 -49
- package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
- package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
- package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
- package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
- package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
- package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
- package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
- package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
- package/skills/wix-headless/references/shared/STYLING.md +0 -140
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
- package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
- package/skills/wix-headless/references/verticals/_schema.json +0 -74
- package/skills/wix-headless/references/verticals/_schema.md +0 -95
- package/skills/wix-headless/references/verticals/blog.md +0 -31
- package/skills/wix-headless/references/verticals/bookings.md +0 -41
- package/skills/wix-headless/references/verticals/cms.md +0 -30
- package/skills/wix-headless/references/verticals/ecom.md +0 -25
- package/skills/wix-headless/references/verticals/forms.md +0 -26
- package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
- package/skills/wix-headless/references/verticals/stores.md +0 -33
- package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
- package/skills/wix-headless/scripts/compose.mjs +0 -520
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
- package/skills/wix-headless/scripts/flatten.sh +0 -61
- package/skills/wix-headless/scripts/generate-images.mjs +0 -242
- package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
- package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
- package/skills/wix-headless/scripts/scaffold.sh +0 -139
- package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
- package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
- package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
- package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
- package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Setup Events"
|
|
3
|
+
description: Initializes a Wix Events (Events V3) backend — creates one or more events (each TICKETING or RSVP) as drafts with future dates, adds ticket definitions for ticketed events, then publishes. Specifies the *how* (calls + format); counts, which events are ticketed vs RSVP, dates, and ticket tiers come from the request.
|
|
4
|
+
---
|
|
5
|
+
**RECIPE**: Business Recipe – Initial Setup for Wix Events (Events V3)
|
|
6
|
+
|
|
7
|
+
> **Standard call shape (every curl below).** The `<AUTH>` placeholder is shorthand for `Authorization: Bearer <TOKEN>` only. Body-bearing requests also need `Content-Type: application/json`.
|
|
8
|
+
|
|
9
|
+
A concise checklist for turning a freshly provisioned Wix site with the **Wix Events** app installed into a populated set of published, registerable events.
|
|
10
|
+
**Notice** that this recipe is **NOT** meant for coding purposes and is **ONLY** meant for initial Events backend setup. (The frontend read/registration contract is the sibling recipe `how-to-code-events.md`.)
|
|
11
|
+
|
|
12
|
+
> **This recipe is the *how*, not the *what*.** What to seed — how many events, which are ticketed vs free (RSVP), their dates/locations, and which ticket tiers and prices a ticketed event has — is determined by the request you're fulfilling. This recipe only specifies the calls and the request format; it does not decide quantities, types, or which events to create.
|
|
13
|
+
|
|
14
|
+
> **API surfaces:** events, ticket definitions, and publish all use **Events V3** on the **public** host `https://www.wixapis.com/events/v3/...`. The Wix Events **app id** (needed only by the frontend, kept here for reference) is `140603ad-af8d-84a5-2c80-a0f60cb47351`. The app is **pre-installed** by setup — do **not** reinstall it; if a create call returns `403`/app-not-installed, **fail loudly** with the response verbatim rather than trying to install it.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Article: Steps for Setting Up Wix Events
|
|
19
|
+
**YOU MUST** complete all the following steps **in the given order** (1-3) without skipping any and **without requiring additional user input**. The **Attach images** step runs last, only when `imagery` is on.
|
|
20
|
+
|
|
21
|
+
**⚠️ CRITICAL ORDER REQUIREMENT: create each event as a DRAFT (STEP 1) → add its ticket definitions (STEP 2, ticketed only) → PUBLISH (STEP 3).** Two one-way constraints force this order:
|
|
22
|
+
- **`registration.initialType` is immutable after create** — a `TICKETING` event can never become `RSVP` (or vice-versa). Decide the type at create time from the request; never plan to convert.
|
|
23
|
+
- **Publishing is one-way** — once published, an event can't return to draft. So attach the ticket definitions to the *draft* first; publishing a ticketed event before its tickets exist ships a ticketed event with nothing to buy.
|
|
24
|
+
|
|
25
|
+
There is **no clean-up step** — a fresh Wix Events install ships **no** sample events, so there is nothing to delete first.
|
|
26
|
+
|
|
27
|
+
### STEP 1: Create the event(s) as a draft
|
|
28
|
+
|
|
29
|
+
Create one event per the request's event count (default 1). Each event is **either** `TICKETING` (paid tickets) **or** `RSVP` (free registration) — read which from the request. Create with `"draft": true` so STEP 2 can attach ticket definitions before the event goes live.
|
|
30
|
+
|
|
31
|
+
**⚠️ CRITICAL: dates MUST be in the future.** A past event is neither purchasable nor registerable and won't show in the live listing (the frontend filters to upcoming). Convert any human date from the request to a **future** ISO-8601 UTC instant; if none is given, default to a plausible near-future date (~60–90 days out) and note it in the kept output so the user can adjust.
|
|
32
|
+
|
|
33
|
+
**Ticketed event** (`TICKETING`) — `POST https://www.wixapis.com/events/v3/events`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -X POST 'https://www.wixapis.com/events/v3/events' \
|
|
37
|
+
-H 'Authorization: <AUTH>' \
|
|
38
|
+
-H 'Content-Type: application/json' \
|
|
39
|
+
-d '{
|
|
40
|
+
"draft": true,
|
|
41
|
+
"event": {
|
|
42
|
+
"title": "Summer Synth Festival",
|
|
43
|
+
"shortDescription": "One night of analog sound under the stars.",
|
|
44
|
+
"location": {
|
|
45
|
+
"name": "The Echo Lot",
|
|
46
|
+
"type": "VENUE",
|
|
47
|
+
"address": { "addressLine": "120 Harbor St", "city": "Seattle", "subdivision": "US-WA", "postalCode": "98101", "country": "US" }
|
|
48
|
+
},
|
|
49
|
+
"dateAndTimeSettings": {
|
|
50
|
+
"startDate": "<FUTURE_DATE>T03:30:00.000Z",
|
|
51
|
+
"endDate": "<FUTURE_DATE>T07:00:00.000Z",
|
|
52
|
+
"timeZoneId": "America/Los_Angeles",
|
|
53
|
+
"showTimeZone": true
|
|
54
|
+
},
|
|
55
|
+
"registration": {
|
|
56
|
+
"initialType": "TICKETING",
|
|
57
|
+
"tickets": { "ticketLimitPerOrder": 8, "currency": "USD", "reservationDurationInMinutes": 20 }
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"fields": ["DETAILS", "TEXTS", "REGISTRATION", "URLS"]
|
|
61
|
+
}'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Free / RSVP event** (`RSVP`) — same call; only the `registration` block changes (no `tickets`):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
"registration": {
|
|
68
|
+
"initialType": "RSVP",
|
|
69
|
+
"rsvp": { "responseType": "YES_ONLY" }
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**⚠️ CRITICAL FORMAT REQUIREMENTS:**
|
|
74
|
+
- **`registration.initialType` is `"TICKETING"` or `"RSVP"` and is immutable** — set it correctly at create time.
|
|
75
|
+
- **RSVP events seed NO form fields.** The registration form is **built-in** (first name + last name + email, required, can't be removed). Use `"responseType": "YES_ONLY"`, or `"YES_AND_NO"` to let guests decline. Do not seed custom fields.
|
|
76
|
+
- **`location`** — for a real venue use `"type": "VENUE"` with an `address` (`subdivision` is an ISO-3166-2 code like `US-WA`; `country` is ISO alpha-2). For an online event use `"type": "ONLINE"` with just a `name`. For an undecided venue use `"location": { "locationTbd": true, "name": "<placeholder>" }` instead of an address.
|
|
77
|
+
- **Dates** — `startDate`/`endDate` are ISO-8601 UTC (`...Z`), future, `endDate` after `startDate`; `timeZoneId` is an IANA tz.
|
|
78
|
+
|
|
79
|
+
**⚠️ Reading the response — the created event is under `event`, with `event.id` and `event.slug`.** A successful create returns `200` with this shape (REST view → `id`/`slug`):
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{ "event": {
|
|
83
|
+
"id": "<eventId>",
|
|
84
|
+
"slug": "summer-synth-festival",
|
|
85
|
+
"title": "Summer Synth Festival",
|
|
86
|
+
"status": "DRAFT",
|
|
87
|
+
"registration": { "initialType": "TICKETING", "status": "CLOSED_MANUALLY" },
|
|
88
|
+
"dateAndTimeSettings": { … },
|
|
89
|
+
"location": { … }
|
|
90
|
+
} }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Keep each event's **`event.id`** (the GUID — needed for STEP 2 and STEP 3) and **`event.slug`** (defaults to the kebab-cased title — the frontend routes and the checkout redirect bind to it). `slug` is the URL identifier; do **not** confuse it with `id`.
|
|
94
|
+
|
|
95
|
+
### STEP 2: Create ticket definitions (TICKETING events only — skip for RSVP)
|
|
96
|
+
|
|
97
|
+
A ticketed event needs at least one **ticket definition** (a purchasable tier) or there's nothing to buy. Create **one tier per ticket tier in the request** (default a single `"General Admission"` tier if none named) against `POST https://www.wixapis.com/events/v3/ticket-definitions`. The tier-creates for one event are independent — they may be fired as one parallel batch.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
curl -X POST 'https://www.wixapis.com/events/v3/ticket-definitions' \
|
|
101
|
+
-H 'Authorization: <AUTH>' \
|
|
102
|
+
-H 'Content-Type: application/json' \
|
|
103
|
+
-d '{
|
|
104
|
+
"ticketDefinition": {
|
|
105
|
+
"eventId": "<eventId FROM STEP 1>",
|
|
106
|
+
"name": "General Admission",
|
|
107
|
+
"description": "Standing-room access to the full lineup.",
|
|
108
|
+
"initialLimit": 200,
|
|
109
|
+
"pricingMethod": { "fixedPrice": { "value": "65.00", "currency": "USD" } },
|
|
110
|
+
"feeType": "FEE_INCLUDED"
|
|
111
|
+
},
|
|
112
|
+
"fields": ["SALES_DETAILS"]
|
|
113
|
+
}'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**⚠️ CRITICAL FORMAT REQUIREMENTS:**
|
|
117
|
+
- **`pricingMethod.fixedPrice.value` is a decimal STRING** (`"65.00"`), not a number (`65`) — a number fails validation.
|
|
118
|
+
- **`name` is capped at 30 characters** — keep tier names short (`"Premium Floor"`, not `"Premium Floor Standing Pit Access"`).
|
|
119
|
+
- **`feeType`** — `"FEE_INCLUDED"` (guest pays exactly the listed price; the Wix fee is deducted from your payout) or `"FEE_ADDED_AT_CHECKOUT"` (fee shown on top). Pick one and be consistent. `"NO_FEE"` is valid **only** for free tickets (a `fixedPrice.value` of `"0"` — rare; prefer an RSVP event for free admission).
|
|
120
|
+
- **`initialLimit`** is the integer inventory cap for the tier; **omit it for unlimited** tickets.
|
|
121
|
+
- The event currency is set on the event (`registration.tickets.currency`, STEP 1); keep the tier currency consistent with it.
|
|
122
|
+
|
|
123
|
+
**⚠️ Reading the response — the created tier is under `ticketDefinition`, id at `ticketDefinition.id`:**
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{ "ticketDefinition": {
|
|
127
|
+
"id": "<ticketDefinitionId>",
|
|
128
|
+
"eventId": "<eventId>",
|
|
129
|
+
"name": "General Admission",
|
|
130
|
+
"initialLimit": 200,
|
|
131
|
+
"pricingMethod": { "fixedPrice": { "value": "65.00", "currency": "USD" } },
|
|
132
|
+
"feeType": "FEE_INCLUDED"
|
|
133
|
+
} }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Keep each **`ticketDefinition.id`** (the frontend lists tiers and reserves by it). On a partial failure, retry only the **failed** tier-creates **once** with the same format; do not loop.
|
|
137
|
+
|
|
138
|
+
### STEP 3: Publish the event
|
|
139
|
+
|
|
140
|
+
Once the event (and, for ticketed events, its ticket definitions) exists, publish it to go live: `POST https://www.wixapis.com/events/v3/events/{eventId}/publish` with body `{}`.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
curl -X POST 'https://www.wixapis.com/events/v3/events/<eventId>/publish' \
|
|
144
|
+
-H 'Authorization: <AUTH>' \
|
|
145
|
+
-H 'Content-Type: application/json' \
|
|
146
|
+
-d '{}'
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
A `200` with `status: "UPCOMING"` (plus `OPEN_TICKETS` on the registration for a ticketed event) means it's live. **Publishing is one-way** — there's no un-publish — so confirm the tickets are created (STEP 2) before publishing a ticketed event.
|
|
150
|
+
|
|
151
|
+
### STEP 4 (optional): Group events by a format / track — Event Categories
|
|
152
|
+
|
|
153
|
+
**Only when the request wants events grouped or filtered by a format/track** (e.g. talk / workshop / social). Wix Events has a first-class **Categories** API for this — use it; do **not** invent an endpoint. **⚠️ It is `v1`, NOT `v3`**, and the assign path is specific:
|
|
154
|
+
|
|
155
|
+
1. **Create one category per group** — `POST https://www.wixapis.com/events/v1/categories` with `{ "category": { "name": "Talks" } }` → keep `category.id`. One call each.
|
|
156
|
+
2. **Assign events to a category** — `POST https://www.wixapis.com/events/v1/categories/{categoryId}/events` with `{ "eventId": ["<eventId>", …] }`. **⚠️ The path is `/{categoryId}/events`, NOT `/assign`** (and `v1`, not `v3/categories`) — the wrong forms `404`.
|
|
157
|
+
3. **Verify via the EVENT read, not the category list.** Assignment can lag a few seconds — `listEventsByCategory` may briefly return `[]`, so don't gate on it. Confirm with `queryEvents` (or `getEventBySlug`) requesting **`fields: ["CATEGORIES"]`** — each event then carries `categories.categories[]` with the assigned `{ id, name }` (REST view — the id key is `id`, not `_id`).
|
|
158
|
+
|
|
159
|
+
Nothing else in the seed depends on categories, and the frontend filters **client-side** off the category name (`how-to-code-events.md`) — skip this step entirely if the request has no grouping.
|
|
160
|
+
|
|
161
|
+
### Attach images (imagery ON only — skip otherwise)
|
|
162
|
+
|
|
163
|
+
**Only when `imagery` is on** (`SEED.md` § "Entity images"). Events were created text-only; this pass-2 step writes a generated hero image onto each event's **`mainImage`**. Generate + import per `references/IMAGE_GENERATION.md` → keep `file.url` and its `file.id`, then update the event. This works **before or after publish** — updating an event (unlike publishing) is not one-way — so run it here regardless of an event's status.
|
|
164
|
+
|
|
165
|
+
**`mainImage` is an `Image` OBJECT `{ id, url, height, width, altText }`.** The binding field is the image **`id`** (the WixMedia image id); `url`/`altText` are descriptive. **⚠️ `height` and `width` are REQUIRED for the image to render** — the schema states the image only appears when both are defined, so always send them (use the generated dimensions, e.g. `1024`×`1024`). Events V3 uses **no `revision`** — this is a partial update keyed by `event.id`; pass only the field you're setting.
|
|
166
|
+
|
|
167
|
+
`PATCH https://www.wixapis.com/events/v3/events/{eventId}`:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
curl -X PATCH 'https://www.wixapis.com/events/v3/events/<eventId>' \
|
|
171
|
+
-H 'Authorization: <AUTH>' \
|
|
172
|
+
-H 'Content-Type: application/json' \
|
|
173
|
+
-d '{ "event": { "id": "<eventId>", "mainImage": { "id": "<file.id>", "url": "<file.url>", "height": 1024, "width": 1024, "altText": "<alt>" } }, "fields": ["DETAILS"] }'
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- **`mainImage` reads back only under the `DETAILS` fieldset** — pass `"fields": ["DETAILS"]` (as above, and on any confirming `getEvent`/`queryEvents`) or the response omits it and a confirm check looks empty.
|
|
177
|
+
- **Never block on image failure** (`SEED.md` § "Entity images" / IMAGE_GENERATION "Credits, cost & the not-generating fallback") — on failure, skip and leave the event text-only.
|
|
178
|
+
|
|
179
|
+
### Paid-ticket precondition — record it, do NOT block
|
|
180
|
+
|
|
181
|
+
Seeding **succeeds** and the event goes live regardless of payment setup. But **completing a paid purchase** later requires, in the site dashboard, both:
|
|
182
|
+
- a **premium plan**, and
|
|
183
|
+
- at least one **configured payment method** (Wix Payments / Stripe / PayPal).
|
|
184
|
+
|
|
185
|
+
Free / RSVP events need neither. This is **not** a seeding failure and **not** something to fix here — record it in the kept `notes` so it's surfaced plainly (*"Paid tickets require a premium plan + a configured payment method in the dashboard to complete a purchase."*). Never imply tickets are payable when no payment method is configured, and never fail the seed over it.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Conclusion
|
|
190
|
+
Following these steps **in order** sets up a published Events V3 site:
|
|
191
|
+
- Every event is created with its **immutable `registration.initialType`** (`TICKETING` or `RSVP`) chosen up front, with **future** dates so it's purchasable/registerable and appears in the live listing.
|
|
192
|
+
- Every **ticketed** event has at least one **ticket definition** (price as a decimal string, name ≤ 30 chars, a valid `feeType`) created **before** publish; **RSVP** events seed no tickets and no form fields (the name + email form is built-in).
|
|
193
|
+
- Every event is **published** (one-way) so it's live, after its tickets exist.
|
|
194
|
+
- IDs kept for the coding handoff: `eventIds[]`, event `slug`s, and per ticketed event its `ticketDefinitionIds[]` (`[]` for RSVP).
|
|
195
|
+
- The paid-ticket precondition (premium plan + payment method) is **noted**, not treated as a failure.
|
|
196
|
+
- Events are seeded **text-only**; when `imagery` is on, the **Attach images** step writes a `mainImage` **object** onto each (`{ id, url, height, width }` — `height`/`width` REQUIRED or it won't render; no revision; PATCH `/events/v3/events/{eventId}`).
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Setup Forms"
|
|
3
|
+
description: Initializes a Wix Forms backend — deletes the install's default sample form, then creates one lead-capture form schema per requested form (fields + human-readable `target` keys + each INPUT's `CONTACTS_*` system `identifier` + a `steps` layout, so the form and its submissions render in the dashboard, `namespace` wix.form_app.form) and verifies each persisted via the form summary. Specifies the *how* (calls + format); which forms, their fields, and counts come from the request.
|
|
4
|
+
---
|
|
5
|
+
**RECIPE**: Business Recipe – Initial Setup for Wix Forms (Form Schemas v4)
|
|
6
|
+
|
|
7
|
+
> **Standard call shape (every curl below).** The `<AUTH>` placeholder is shorthand for `Authorization: Bearer <TOKEN>` only. Body-bearing requests also need `Content-Type: application/json`. Send `wix-site-id: <SITE_ID>` on every call.
|
|
8
|
+
|
|
9
|
+
A concise checklist for preparing any new Wix site that uses the **Wix Forms** app to capture leads (contact / inquiry / signup / RSVP-style forms).
|
|
10
|
+
**Notice** this recipe is for **initial backend setup ONLY**, not for coding the frontend.
|
|
11
|
+
|
|
12
|
+
> **This recipe is the *how*, not the *what*.** How many forms, each form's fields, and their labels come from the request you're fulfilling. This recipe only specifies the calls and the request format; it does not decide which forms or fields to create.
|
|
13
|
+
|
|
14
|
+
> **API surfaces:** Wix Forms is a **standalone CRM API** — a form **schema** (the field definitions) is created on the **Form Schemas v4** service at `https://www.wixapis.com/form-schema-service/v4/forms`. In the docs portal this lives under **CRM ▸ Forms ▸ Form Schemas**, *not* under Business Solutions. This is **NOT** the events/bookings per-event registration form (a different thing). The Forms app's `appDefId` is `225dd912-7dea-4738-8688-4b8c6955ffc2`; an `UNSUPPORTED_FORM_NAMESPACE` error means the app isn't installed. Call the **public** host shown above (no `/_api/` prefix). There is no bulk endpoint — one POST per form.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Article: Steps for Setting Up Wix Forms
|
|
19
|
+
**YOU MUST** complete the steps in order, without requiring additional user input: clean the default form (STEP 1) **before** creating yours (STEP 2), then verify (STEP 3).
|
|
20
|
+
|
|
21
|
+
**⚠️ CRITICAL ORDER REQUIREMENT: clean the install's default sample form FIRST (STEP 1), before creating any form.** Listing-then-deleting before you create guarantees every id you delete is a pre-existing form, never one you just created — and it prevents a name collision (see STEP 1). **But only delete forms that are obviously the install's own default sample form:** the site may already hold the owner's **real forms** (a connect/iterate run, or an owner-populated site). If what's there isn't obviously install sample data, or you're unsure, **do not delete it — ask the user first** (`SEED.md`: seeding is additive; deleting real content needs the owner's approval).
|
|
22
|
+
|
|
23
|
+
### STEP 1: Clean — remove any pre-existing (install-default) forms
|
|
24
|
+
|
|
25
|
+
A freshly installed Wix Forms app **may ship a default "Get in touch" form** (a contact form with `first_name` / `email` / `message` fields). Its presence is **not deterministic** — some fresh installs ship it and others don't, so it appears provisioning/timing-dependent. Rather than assume, **list what's actually there and delete whatever comes back** — this is a safe no-op when the list is empty.
|
|
26
|
+
|
|
27
|
+
1. **List the existing forms** — `GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form`. `namespace` is a **required** query param. Collect every `form.id` from the response (`forms[].id`). On a fresh install this is **either empty or the single default form** — both are fine.
|
|
28
|
+
2. **Delete each** — `DELETE https://www.wixapis.com/form-schema-service/v4/forms/{formId}` (one call per id; returns `200 {}`). Because the list ran **before** any create, every id returned is a pre-existing form — safe to delete. If the list was empty, issue no DELETE (a correct no-op).
|
|
29
|
+
|
|
30
|
+
**⚠️ Why clean even though it's often a no-op: when the default "Get in touch" form IS present, form names collide and the service silently auto-suffixes.** Form names must be unique; if the request's form is also called "Get in touch" and a default exists that you didn't clean, the create succeeds but your form is renamed **"Get in touch 1"**, quietly diverging from the name the frontend expects. Cleaning first removes that hazard whenever the default happens to be present. (With the default present, a second "Get in touch" create comes back renamed "Get in touch 1".)
|
|
31
|
+
|
|
32
|
+
### STEP 2: Create each form schema (fields + targets)
|
|
33
|
+
|
|
34
|
+
Create the forms the request names — **one POST per form** to `POST https://www.wixapis.com/form-schema-service/v4/forms`. **How many forms, and each form's fields/labels, come from the request you're fulfilling — this step only gives the call and the required format.** (There is no bulk-create; issue one call per form. Forms are independent — no shared revision — so concurrent creates are safe, but typically it's just one form.)
|
|
35
|
+
|
|
36
|
+
**⚠️ CRITICAL: every field needs a client-generated LOWERCASE GUID `id` — GENERATE IT IN THE SHELL, never type one from memory.** Each `formFields[].id` must be a distinct valid GUID (two the same → `400 DUPLICATED_FIELD_IDS`). Generate one per INPUT field **plus** one for the submit button and one for the layout step. **Generate them LOWERCASE** — each id is referenced twice (once as `formFields[].id`, once as the layout's `steps[].layout.large.items[].fieldId`) and they must match, but the server **lowercases** stored field ids; an uppercase `uuidgen` id in the layout would no longer match the stored field id and the dashboard layout would break. So lowercase at generation:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# lowercase GUIDs — uuidgen (lowercased), with python3 / node fallbacks (both already lowercase)
|
|
40
|
+
lc() { uuidgen 2>/dev/null | tr 'A-Z' 'a-z' || python3 -c 'import uuid;print(uuid.uuid4())' || node -e 'console.log(crypto.randomUUID())'; }
|
|
41
|
+
F1=$(lc); F2=$(lc); F3=$(lc) # one per INPUT field
|
|
42
|
+
SUBMIT=$(lc) # the submit-button (DISPLAY) field
|
|
43
|
+
STEP=$(lc) # the layout step (page)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Request body shape** (a 3-field contact form — repeat/adjust the INPUT `formFields[]` and the matching layout `items[]` per the request):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"form": {
|
|
51
|
+
"name": "Get in touch",
|
|
52
|
+
"namespace": "wix.form_app.form",
|
|
53
|
+
"formFields": [
|
|
54
|
+
{ "id": "$SUBMIT", "hidden": false, "identifier": "SUBMIT_BUTTON", "fieldType": "DISPLAY",
|
|
55
|
+
"displayOptions": { "displayFieldType": "PAGE_NAVIGATION", "pageNavigationOptions": { "nextPageText": "Next", "previousPageText": "Back", "submitText": "Submit" } } },
|
|
56
|
+
{ "id": "$F1", "hidden": false, "identifier": "CONTACTS_FIRST_NAME", "fieldType": "INPUT", "inputOptions": {
|
|
57
|
+
"target": "first_name", "pii": true, "required": true, "inputType": "STRING", "readOnly": false,
|
|
58
|
+
"stringOptions": { "validation": { "format": "UNKNOWN_FORMAT", "enum": [] }, "componentType": "TEXT_INPUT", "textInputOptions": { "label": "First name", "showLabel": true } } } },
|
|
59
|
+
{ "id": "$F2", "hidden": false, "identifier": "CONTACTS_EMAIL", "fieldType": "INPUT", "inputOptions": {
|
|
60
|
+
"target": "email", "pii": true, "required": true, "inputType": "STRING", "readOnly": false,
|
|
61
|
+
"stringOptions": { "validation": { "format": "EMAIL", "enum": [] }, "componentType": "TEXT_INPUT", "textInputOptions": { "label": "Email", "showLabel": true } } } },
|
|
62
|
+
{ "id": "$F3", "hidden": false, "identifier": "CONTACTS_PHONE", "fieldType": "INPUT", "inputOptions": {
|
|
63
|
+
"target": "phone", "pii": true, "required": false, "inputType": "STRING", "readOnly": false,
|
|
64
|
+
"stringOptions": { "validation": { "format": "PHONE", "enum": [] }, "componentType": "TEXT_INPUT", "textInputOptions": { "label": "Phone", "showLabel": true } } } }
|
|
65
|
+
],
|
|
66
|
+
"steps": [
|
|
67
|
+
{ "id": "$STEP", "name": "Page 1", "layout": { "large": { "items": [
|
|
68
|
+
{ "fieldId": "$F1", "row": 0, "column": 0, "width": 12, "height": 1 },
|
|
69
|
+
{ "fieldId": "$F2", "row": 1, "column": 0, "width": 12, "height": 1 },
|
|
70
|
+
{ "fieldId": "$F3", "row": 2, "column": 0, "width": 12, "height": 1 },
|
|
71
|
+
{ "fieldId": "$SUBMIT", "row": 3, "column": 0, "width": 12, "height": 1 }
|
|
72
|
+
], "sections": [] } } }
|
|
73
|
+
],
|
|
74
|
+
"enabled": true
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**⚠️ CRITICAL FORMAT REQUIREMENTS:**
|
|
80
|
+
- **`namespace` MUST be `"wix.form_app.form"`** (the Wix Forms namespace) — any other value fails with `400 UNSUPPORTED_FORM_NAMESPACE`. It is immutable after create.
|
|
81
|
+
- **Every INPUT field MUST carry a non-empty `target`** — the human-readable key the frontend binds to (input `name` = `target`). An empty/missing target fails with `400 UNSUPPORTED_FIELD_TARGETS_NAME` (`MISSING_FIELD_TARGETS`). Targets are **immutable** (set once).
|
|
82
|
+
- **Targets MUST be unique within a form** — two fields sharing a target → `400 DUPLICATED_FIELD_TARGETS`. Use lowercase snake_case keys the frontend can reuse verbatim (`first_name`, `email`, `phone`, `message`).
|
|
83
|
+
- **Field envelope:** each field is `{ id, hidden: false, identifier: "<CONTACTS_*>", fieldType: "INPUT", inputOptions: { target, readOnly: false, inputType: "STRING", stringOptions: { validation: { format, enum: [] }, componentType: "TEXT_INPUT", textInputOptions: { label, showLabel } } } }`. The `identifier` is MANDATORY for dashboard rendering — see the CRITICAL identifier block below. Use `inputType: "STRING"` + `componentType: "TEXT_INPUT"` for text fields. Set `required: true` on the fields the form must collect (defaults to `false`); mark `pii: true` on personal fields (name/email/phone).
|
|
84
|
+
|
|
85
|
+
**Dropdown (enum) fields — concrete envelope.** A dropdown is still `inputType: "STRING"`; only the `componentType` and its options block change. Set `componentType: "DROPDOWN"` and put the choices under `stringOptions.dropdownOptions.options` as `{ id, label, value }` — the `value` is what a submission stores (keep it a stable machine key), and **every option needs its own lowercase GUID `id`** (see the CRITICAL below). Keep the `validation` block as with any field (a plain dropdown reads back `format: UNDEFINED`, so write `UNKNOWN_FORMAT`):
|
|
86
|
+
|
|
87
|
+
```jsonc
|
|
88
|
+
// generate an option GUID per choice in the shell, same lc() helper as the field ids:
|
|
89
|
+
// O1=$(lc); O2=$(lc); O3=$(lc)
|
|
90
|
+
{ "id": "$F4", "hidden": false, "identifier": "budget", "fieldType": "INPUT", "inputOptions": {
|
|
91
|
+
"target": "budget", "required": false, "inputType": "STRING", "readOnly": false,
|
|
92
|
+
"stringOptions": {
|
|
93
|
+
"validation": { "format": "UNKNOWN_FORMAT", "enum": [] },
|
|
94
|
+
"componentType": "DROPDOWN",
|
|
95
|
+
"dropdownOptions": {
|
|
96
|
+
"label": "Budget", "showLabel": true, "placeholder": "Select a range",
|
|
97
|
+
"options": [
|
|
98
|
+
{ "id": "$O1", "label": "Under $1k", "value": "under_1k" },
|
|
99
|
+
{ "id": "$O2", "label": "$1k–$5k", "value": "1k_5k" },
|
|
100
|
+
{ "id": "$O3", "label": "$5k+", "value": "5k_plus" }
|
|
101
|
+
]
|
|
102
|
+
} } } }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Add a matching `steps[].layout.large.items[]` entry for its `fieldId` like any other field.
|
|
106
|
+
|
|
107
|
+
**⚠️ CRITICAL: every dropdown `options[]` entry MUST carry its own lowercase GUID `id` — a create without it 400s `options[N].id is not a valid GUID / must not be empty`.** The API spec marks the option `id` optional (it's for Multilingual translation), but the create validator **rejects an option with no `id`**. Generate one GUID per option in the shell (the same `lc()` helper used for field ids), exactly as you do for field/step ids — never type one from memory.
|
|
108
|
+
|
|
109
|
+
**⚠️ A custom dropdown's options round-trip in `fields[].view.options`, NOT `formFields[]` — and the frontend reads them from there.** Verified live (2026-07-12): a dropdown whose `identifier` is a plain custom key (like `"budget"` — dropdowns rarely map to a `CONTACTS_*` system field) is **dropped from `formFields[]` by the identifier→dashboard rule below**, so it does **not** show in the Wix dashboard summary — *but its data is fully captured and its options survive under `form.fields[].view.options` as `[{ id, label, value }]`* (alongside `view.label` / `view.placeholder`). That's exactly where the schema-driven frontend reads dropdown choices from (`how-to-code-forms.md` projects `fields[]`, not `formFields[]`), so **a custom dropdown renders and submits correctly on the headless site even though the dashboard doesn't list it.** So: seed the dropdown as the request needs; verify persistence on the GET round-trip (STEP 3) by asserting the field appears in `fields[]` with a non-empty `view.options`; and — as with any custom (non-`CONTACTS_*`) field — do **not** claim the dashboard fully renders it (platform limitation, same as the CRITICAL identifier block). If a dropdown *does* map cleanly to a contact field, give it the matching `CONTACTS_*` identifier and it will also show in the dashboard.
|
|
110
|
+
|
|
111
|
+
Other richer types (number, checkbox group, date) follow the same envelope with a different `inputType`/`stringOptions`/`arrayOptions` — but plain STRING text + dropdowns cover the lead-capture case.
|
|
112
|
+
|
|
113
|
+
**⚠️ CRITICAL: every field MUST carry a `stringOptions.validation` block, or the field is NOT submittable — a visitor submission of it fails with `400 "must NOT have additional properties"`.** The submission validator builds its allowed-keys schema **only from fields that have a `validation` block**. A field created without one exists on the form and even renders, but any submission that includes its `target` is rejected as an unknown property (and `required` is silently dropped too) — the form looks fine but silently rejects real submissions. So give **every** field a `validation` block:
|
|
114
|
+
- Plain text / name / message → `"validation": { "format": "UNKNOWN_FORMAT", "enum": [] }`.
|
|
115
|
+
- Email → `"validation": { "format": "EMAIL", "enum": [] }`; phone → `"PHONE"`; URL → `"URL"`. (Full `format` enum: `UNKNOWN_FORMAT`, `DATE`, `TIME`, `DATE_TIME`, `EMAIL`, `URL`, `UUID`, `PHONE`, `URI`, `HOSTNAME`, `COLOR_HEX`, `CURRENCY`, `LANGUAGE`, `DATE_OPTIONAL_TIME`.)
|
|
116
|
+
- **Use `UNKNOWN_FORMAT`, NOT `UNDEFINED`, on write.** The create/GET response echoes an unconstrained format back as `"UNDEFINED"` (and the docs' create example shows `"format": "UNDEFINED"`), but sending `"UNDEFINED"` on **create** fails with `400 "format enum must be in […]"` — `UNDEFINED` is the read-back value, not a writable one. Write `UNKNOWN_FORMAT`; it stores and reads back as `UNDEFINED`.
|
|
117
|
+
- **Length & pattern constraints go in the SAME `validation` block — seed them when the request asks for field rules.** `stringOptions.validation` also accepts `minLength` (int), `maxLength` (int), and `pattern` (a regex string) alongside `format`/`enum`. A request like "first name 2–40 chars", "message at least 20 chars", or "note capped at 500" maps directly to these. Verified live (2026-07-12): `"validation": { "format": "UNKNOWN_FORMAT", "enum": [], "minLength": 2, "maxLength": 12, "pattern": "^[A-Za-z ]+$" }` persists and reads back under `validation.string.{minLength,maxLength,pattern}`. **These constraints are enforced server-side by `createSubmission`** — a violating submit is rejected with a field violation (`errorPath` = the field's `target`, e.g. message `"must have between 2 and 12 characters"`), which the frontend surfaces inline (see `how-to-code-forms.md`). The frontend also reads them from the schema to pre-validate client-side, so seeding them is what makes owner-set field rules flow through with no code change. `pattern` is a standard JS-style regex; omit it unless the request calls for a specific character rule.
|
|
118
|
+
- `required` (at `inputOptions` level) lands in `validation.required` — but **only takes effect if the field has a `validation` block** (no block ⇒ `required` is dropped along with the field's submittability). This is another reason every field needs the block.
|
|
119
|
+
- If a create fails transiently on a fresh site (`5xx`, or an identity/propagation error right after install — the install returns `appInstance.status: "UNKNOWN"` until it propagates), retry the same call **once**; do not loop. (Creates typically succeed immediately, but keep the retry-once safety net.)
|
|
120
|
+
|
|
121
|
+
**⚠️ CRITICAL: include a `steps` layout + a `SUBMIT_BUTTON` DISPLAY field, or the Wix dashboard shows every submission EMPTY.** The form is technically submittable with INPUT fields alone — the submission *data* stores fine either way, and the headless frontend renders its own UI — BUT the Wix **Forms dashboard** (`Customers & Leads ▸ Forms & Submissions`) renders each submission's values against the form's **layout** (`formFields` + `steps`). A form seeded **without** a layout stores complete submissions that the dashboard displays as **blank** ("—" summary, empty details panel), so the site owner can't read their leads in the UI — it looks broken even though the data is intact. So always seed the layout:
|
|
122
|
+
- Add a **`SUBMIT_BUTTON`** DISPLAY field (`fieldType: "DISPLAY"`, `displayOptions.displayFieldType: "PAGE_NAVIGATION"`) to `formFields`.
|
|
123
|
+
- Add a **single `steps` entry** whose `layout.large.items[]` places **every** field (each INPUT + the submit button) by `fieldId`, one per row.
|
|
124
|
+
- **Each `items[].fieldId` MUST equal the corresponding `formFields[].id`** — this is why the ids are generated **lowercase** up front (the server lowercases stored field ids; an uppercased layout `fieldId` would no longer match and the dashboard layout silently breaks).
|
|
125
|
+
- A **single create call with the layout persists it** — no follow-up PATCH needed. (Omitting the layout is the cause of blank dashboard submissions; adding it via PATCH to an already-created form also fixes them retroactively.)
|
|
126
|
+
|
|
127
|
+
**⚠️⚠️ CRITICAL: every INPUT field MUST carry a system `identifier`, or it is DROPPED FROM `formFields[]` and NEVER shows in the dashboard.** This is the #1 dashboard-blank cause and it is SEPARATE from (and stronger than) the layout requirement above — a form can have a perfect `steps` layout and still be blank. On create, the server keeps an INPUT field's component in `formFields[]` **only if its `identifier` is a recognized system value.** Without a recognized identifier (no identifier, a custom string, an extended-field key like `custom.x`, or a GUID — all fail) the field is normalized into the legacy `fields[]` array **only**, `formFields[]` comes back holding just the `SUBMIT_BUTTON`, and `GET .../{formId}/summary` returns **zero fields** → the Wix dashboard renders the form and every submission **blank**. (The public headless site still submits fine — the submission service matches by `target` — so this defect is invisible from the frontend; you must verify it server-side in STEP 3.)
|
|
128
|
+
- **Map each field to its contact identifier.** Set `identifier` on every INPUT field to the `CONTACTS_*` value for what it collects. Verified working: `CONTACTS_FIRST_NAME`, `CONTACTS_LAST_NAME`, `CONTACTS_EMAIL`, `CONTACTS_PHONE`. (The contacts schema has more system fields — address, company, birthdate, etc.; use the matching `CONTACTS_*` identifier. `SUBMIT_BUTTON` is the identifier for the DISPLAY submit field.)
|
|
129
|
+
- **⚠️ Custom (non-contact) fields do NOT render in the dashboard via this API.** A field with no matching `CONTACTS_*` identifier (e.g. "medical conditions", "fitness goal", a waiver checkbox) cannot be made to appear in the Wix Forms dashboard through `form-schema-service/v4/forms` — registering a Contacts extended field and referencing its `custom.<key>` does **not** work. Such fields still store submission data correctly (frontend + `fields[]` + submission records) but the owner won't see them in the dashboard UI. **So: for lead-capture forms the request should lean on contact-mappable fields (name / email / phone / address / company / birthdate). If the request genuinely needs custom fields, seed them anyway (data is captured) but do NOT report the dashboard as fully rendering — only the contact-mapped fields will show.** This is a platform limitation, not a seed bug; surface it rather than silently shipping a half-blank dashboard.
|
|
130
|
+
- Also set `hidden: false` on each field and `readOnly: false` in `inputOptions` (matches the shape shown in the docs).
|
|
131
|
+
|
|
132
|
+
**⚠️ Do NOT rely on `postSubmissionTriggers.upsertContact` (contact mapping) through this endpoint — it is SILENTLY DROPPED.** The docs' create example includes a `postSubmissionTriggers.upsertContact.fieldsMapping` block to auto-create a CRM contact from a submission. Live, the create returns `200` **but the trigger is not persisted** (absent from a follow-up GET — the same silent-drop pattern as CMS multi-refs at insert). Don't depend on it in the seed. You don't need it: **form submissions are recorded against the schema regardless**, so leads are captured by the seeded form's `formId` on their own. The seed's job ends at the schema + targets.
|
|
133
|
+
|
|
134
|
+
**⚠️ Reading the response — read the field `target`s from `form.fields[]`; the layout persists under `form.formFields[]` + `form.steps[]`.** A successful create returns `200` with:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{ "form": {
|
|
138
|
+
"id": "<formId>",
|
|
139
|
+
"fields": [
|
|
140
|
+
{ "id": "<field-id>", "target": "first_name", "view": { "label": "First name" }, "pii": true },
|
|
141
|
+
{ "id": "<field-id>", "target": "email", "validation": { "string": { "format": "EMAIL" }, "required": true } }
|
|
142
|
+
],
|
|
143
|
+
"formFields": [ /* the field components incl. the submit button */ ],
|
|
144
|
+
"steps": [ /* the layout you sent */ ],
|
|
145
|
+
"namespace": "wix.form_app.form", "name": "Get in touch", "enabled": true
|
|
146
|
+
} }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Read the **`form.id`** (→ the `formId` to keep and hand to the frontend) and each **`form.fields[].target`** (→ the input `name`s the frontend renders). **All** field definitions are normalized into **`fields[]`** (read `target`s here) — but **`formFields[]` only echoes back the components the server materialized: the `SUBMIT_BUTTON` plus each INPUT that carried a recognized `CONTACTS_*` identifier** (see the CRITICAL identifier block above). `steps` echoes the layout you sent. So `fields[]` having all N fields does NOT mean the dashboard will render them — `formFields[]` / summary is the dashboard-truth source. The field `id`s are echoed **lowercased** — which is exactly why you generated them lowercase, so the `steps[].…fieldId`s still match. Downstream, the frontend binds by **`target`**, never by field id.
|
|
150
|
+
|
|
151
|
+
### STEP 3: Verify each form persisted (mandatory)
|
|
152
|
+
|
|
153
|
+
A `200` on create is not proof the form is queryable. After creating, **list once** and confirm every seeded form is present with its targets:
|
|
154
|
+
|
|
155
|
+
`GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form` — for each form you created, confirm its `id` appears, its `fields[].target` set matches what you sent, and its **`steps`** is non-empty (a `steps: []` means the layout didn't persist).
|
|
156
|
+
|
|
157
|
+
**⚠️ Then verify the dashboard will actually render — call `GET https://www.wixapis.com/form-schema-service/v4/forms/{formId}/summary` and assert `formSummary.fields` is NON-EMPTY and its count equals the number of contact-mapped INPUT fields you seeded.** This is the reliable dashboard-truth check: the summary returns exactly the fields the Wix dashboard shows (i.e. the `formFields[]` components, minus the submit button). A `summary.fields: []` (or a count short of your contact-mapped inputs) means the identifier mapping failed and the dashboard is blank for those fields — **do not report success; fix the `identifier`s (STEP 2 CRITICAL block) and re-create.** Expect the summary count to equal your contact-mappable fields only; if the form has custom (non-`CONTACTS_*`) fields, they will be legitimately absent from the summary (platform limitation) — call that out in the handoff rather than treating it as a pass or a hard failure.
|
|
158
|
+
|
|
159
|
+
If a form is missing, its layout didn't persist, or its summary is unexpectedly empty, re-create it once and re-verify; if it still fails, surface the response verbatim rather than reporting success.
|
|
160
|
+
|
|
161
|
+
**Keep** per form: the **`formId`** and the ordered list of field **`target`** keys (with each field's label and `required`) — that map is the producer for the coding handoff (the frontend sets each input's `name` = `target` and submits against the `formId`).
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Conclusion
|
|
166
|
+
Following these steps **in order** sets up a Wix Forms backend:
|
|
167
|
+
- Starts from a **clean form list** — any pre-existing form (the install's occasional default "Get in touch") is listed-then-deleted first (a safe no-op when none exists), avoiding the silent name-collision auto-suffix when a default is present.
|
|
168
|
+
- Contains exactly the forms the request calls for, each created in the **`wix.form_app.form`** namespace with unique, non-empty, immutable **`target`** keys per field.
|
|
169
|
+
- **Every field carries a `stringOptions.validation` block** (`UNKNOWN_FORMAT` for plain text, `EMAIL`/`PHONE`/… otherwise) — without it the field is created but **not submittable** (visitor submissions 400 as "additional properties"). This is what makes the seeded form actually accept the frontend's submissions.
|
|
170
|
+
- Each form is created **with a `steps` layout + a `SUBMIT_BUTTON`**, AND **every INPUT field carries its `CONTACTS_*` system `identifier`** — both are required for the site owner's **Wix dashboard to render** the form and its submissions. A field with no recognized identifier is dropped from `formFields[]`/summary and shows blank (the data is still stored). Custom (non-contact) fields cannot render in the dashboard via this API (platform limitation) — captured as data only.
|
|
171
|
+
- Dashboard rendering is **verified via `GET .../forms/{formId}/summary`** (non-empty, count == contact-mapped inputs), not merely by `steps` being present.
|
|
172
|
+
- Contact-mapping (`postSubmissionTriggers`) is **not** relied upon (silently dropped here); submissions are captured against the schema regardless.
|
|
173
|
+
- Every form is **verified present** via a namespace list before completion.
|
|
174
|
+
- **Keep** per form the `formId` and its field `target` keys — the producer for the coding handoff.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Setup Online Store"
|
|
3
|
+
description: Initializes a Stores catalog with Catalog V3 — cleans default sample products, then bulk-creates products with options/variants/inventory (visible), and creates + assigns categories. Specifies the *how* (calls + format); counts and the specific products/categories come from the request being fulfilled.
|
|
4
|
+
---
|
|
5
|
+
**RECIPE**: Business Recipe – Initial Setup for a Wix Online Store (Catalog V3)
|
|
6
|
+
|
|
7
|
+
> **Standard call shape (every curl below).** The `<AUTH>` placeholder is shorthand for `Authorization: Bearer <TOKEN>` only. Body-bearing requests also need `Content-Type: application/json`.
|
|
8
|
+
|
|
9
|
+
A concise checklist for preparing any new Wix site that uses the Online Stores app with Catalog V3.
|
|
10
|
+
**Notice** that this recipe is **NOT** meant for coding purposes and is **ONLY** meant for initial catalog setup.
|
|
11
|
+
|
|
12
|
+
> **This recipe is the *how*, not the *what*.** What to seed — how many products, which are in/out of stock, which categories — is determined by the request you're fulfilling. This recipe only specifies the calls and the request format; it does not decide quantities or which items to create.
|
|
13
|
+
|
|
14
|
+
> **API surfaces:** products use Catalog V3 (`https://www.wixapis.com/stores/v3/...`). The Categories API is the exception — it lives on a `v1` endpoint (`https://www.wixapis.com/categories/v1/categories`). Don't mix in any pre-V3 product endpoints.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Article: Steps for Setting Up a Wix Online Store
|
|
19
|
+
**YOU MUST** complete the following steps **in the given order** (1-4) and **without requiring additional user input**. One conditional: **the category steps (3-4) run only when the request names categories** — if `intent.stores.categoriesNamed` is empty, **create none** (skill policy, per `SEED.md` § "What to seed per capability") and skip the category steps. Products (steps 1-2) always run; the **Attach images** step runs last, only when `imagery` is on.
|
|
20
|
+
|
|
21
|
+
**⚠️ CRITICAL ORDER REQUIREMENT: Do the product operations FIRST (clean + create, Steps 1-2), then categories (Steps 3-4). Categories API might take some time to be fully available after Stores installation, so always finish products before attempting category operations.**
|
|
22
|
+
|
|
23
|
+
### STEP 1: Clean the store — remove the default sample products
|
|
24
|
+
|
|
25
|
+
A freshly provisioned Wix Stores app comes pre-seeded with demo/sample products. **Only remove products that are obviously the install's own demo/sample data on a fresh install.** Do **not** assume the existing products are samples: the site may already hold the owner's **real catalog** (a connect/iterate run, or an owner-populated store). If what's there isn't obviously install demo data, or you're unsure, **do not delete it — ask the user first** (`SEED.md`: seeding is additive; deleting real content needs the owner's approval). When they clearly are the install's samples, remove them **before** creating yours so the storefront shows only the intended catalog.
|
|
26
|
+
|
|
27
|
+
1. **List the existing products** — `POST https://www.wixapis.com/stores/v3/products/query` with body `{"query": {"paging": {"limit": 50}}}`. Collect every `product.id` from the response.
|
|
28
|
+
2. **Bulk-delete them in one call** — `POST https://www.wixapis.com/stores/v3/bulk/products/delete` with body `{"productIds": ["<id1>", "<id2>", …]}` (the ids from step 1; up to 100 per call). On a fresh install the query returns only the install's sample products — delete those. **If it returns anything that could be the owner's real catalog, stop and ask first** (above).
|
|
29
|
+
|
|
30
|
+
### STEP 2: Bulk-create the products (with options)
|
|
31
|
+
|
|
32
|
+
Create the products in a **single bulk request** to `POST https://www.wixapis.com/stores/v3/bulk/products-with-inventory/create`. **How many products, and which are in or out of stock, are set by the request you're fulfilling — this step only gives the call and the required format.** Each product needs a real web image URL relevant to it, and `price` via `actualPrice` (+ optional `compareAtPrice`).
|
|
33
|
+
|
|
34
|
+
**⚠️ CRITICAL: PRODUCT & VARIANT VISIBILITY — set `"visible": true` explicitly.**
|
|
35
|
+
Storefront product queries (`searchProducts` / `queryProducts`) return **only visible products** to site visitors. A product created without `"visible": true` is created successfully but will **NOT appear** on the live site — the catalog looks empty. So:
|
|
36
|
+
- Set `"visible": true` on **every product** (product level).
|
|
37
|
+
- Set `"visible": true` on **every variant**. A variant with `inventoryItem.quantity > 0` is buyable; a variant that should be out-of-stock stays `"visible": true` with `"quantity": 0`.
|
|
38
|
+
- Never rely on a default — always include `visible`.
|
|
39
|
+
|
|
40
|
+
**Request body shape** (one representative product shown — repeat product objects inside the `products` array):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"products": [
|
|
45
|
+
{
|
|
46
|
+
"name": "Pro Basketball Sneaker",
|
|
47
|
+
"productType": "PHYSICAL",
|
|
48
|
+
"physicalProperties": {},
|
|
49
|
+
"visible": true,
|
|
50
|
+
"visibleInPos": true,
|
|
51
|
+
"description": {
|
|
52
|
+
"nodes": [
|
|
53
|
+
{
|
|
54
|
+
"type": "PARAGRAPH",
|
|
55
|
+
"id": "desc1",
|
|
56
|
+
"nodes": [{ "type": "TEXT", "textData": { "text": "High-performance basketball sneaker with superior ankle support." } }],
|
|
57
|
+
"paragraphData": { "textStyle": { "textAlignment": "AUTO" } }
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"metadata": { "version": 1, "id": "basketball-desc-001" }
|
|
61
|
+
},
|
|
62
|
+
"options": [
|
|
63
|
+
{
|
|
64
|
+
"name": "Size",
|
|
65
|
+
"optionRenderType": "TEXT_CHOICES",
|
|
66
|
+
"choicesSettings": { "choices": [ { "choiceType": "CHOICE_TEXT", "name": "8" }, { "choiceType": "CHOICE_TEXT", "name": "9" } ] }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "Color",
|
|
70
|
+
"optionRenderType": "SWATCH_CHOICES",
|
|
71
|
+
"choicesSettings": { "choices": [ { "choiceType": "ONE_COLOR", "name": "Red", "colorCode": "#FF0000" }, { "choiceType": "ONE_COLOR", "name": "Blue", "colorCode": "#0000FF" } ] }
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"variantsInfo": {
|
|
75
|
+
"variants": [
|
|
76
|
+
{
|
|
77
|
+
"choices": [
|
|
78
|
+
{ "optionChoiceNames": { "optionName": "Size", "choiceName": "8", "renderType": "TEXT_CHOICES" } },
|
|
79
|
+
{ "optionChoiceNames": { "optionName": "Color", "choiceName": "Red", "renderType": "SWATCH_CHOICES" } }
|
|
80
|
+
],
|
|
81
|
+
"price": { "actualPrice": { "amount": "159.99" }, "compareAtPrice": { "amount": "200.00" } },
|
|
82
|
+
"visible": true,
|
|
83
|
+
"inventoryItem": { "quantity": 25, "preorderInfo": { "enabled": false } },
|
|
84
|
+
"physicalProperties": {}
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"returnEntity": true
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**⚠️ CRITICAL FORMAT REQUIREMENTS:**
|
|
95
|
+
- **Description MUST be rich-text nodes**, not a plain string — a plain string causes an `"Expected an object"` error. Use the `{ "nodes": [...], "metadata": {...} }` shape shown.
|
|
96
|
+
- **Media — gated by the `imagery` policy (`SEED.md` § "Entity images"), no exception for stores.** **Always create products text-only here** — omit `media`. When `imagery` is **on**, the **Attach images** step below writes generated brand images in a second pass; it does **not** happen at create time.
|
|
97
|
+
- **Physical products MUST set `"productType": "PHYSICAL"` and an empty `"physicalProperties": {}`** (on the product and on each variant).
|
|
98
|
+
- **Options:** text options use `"optionRenderType": "TEXT_CHOICES"` + `"choiceType": "CHOICE_TEXT"`; color options use `"optionRenderType": "SWATCH_CHOICES"` + `"choiceType": "ONE_COLOR"` + a `colorCode`.
|
|
99
|
+
- **Variants = the full Cartesian product** of all option choices; each variant references **all** options via `optionChoiceNames`, sets `price.actualPrice.amount` (+ optional `compareAtPrice.amount`) as **strings**, and `inventoryItem.quantity`.
|
|
100
|
+
- If part of the bulk request fails, retry the failed products **once** with the exact same format; do not loop.
|
|
101
|
+
|
|
102
|
+
**⚠️ CRITICAL: options/variants are for things the buyer *selects and buys* — not for attributes you only filter or display by.** Make something an `option` (and thus a variant) **only if the buyer picks it to purchase a distinct SKU** (Size, Color, Format). An attribute you merely **filter, badge, or display by** — roast level, material, genre, "new arrival" — is **not** a variant: encode it in the **product name**, its **category**, or `description`, and never as an option. Modeling a display-only attribute as an option multiplies the variant Cartesian product for nothing (slower seeding, larger payload) and produces a buyer-facing selector that shouldn't exist. (A single-variant product is fine — give it one variant with no options.) **Default cardinality — keep it small:** unless the request names specific options, give each product **at most one option with ≤3 choices** (≤3 variants); many products legitimately have **none** (a single variant). This is a floor, not a cap — honor a larger/explicit option set when intent names one. The two-option example above shows the multi-option *format*; it is not the default shape to reach for.
|
|
103
|
+
|
|
104
|
+
**⚠️ Reading the response — created products are under `productResults.results[]`, NOT a top-level `results`.** A successful bulk create returns `200` with this shape:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{ "productResults": { "results": [
|
|
108
|
+
{ "itemMetadata": { "id": "<productId>", "originalIndex": 0, "success": true },
|
|
109
|
+
"item": { "id": "<productId>", "slug": "<slug>", "name": "…", "visible": true, … } }
|
|
110
|
+
] } }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Read each product's **`id`** (→ the `catalogItemId` you'll assign to categories in STEP 4 and the frontend will use) and **`slug`** from `productResults.results[].item`. There is **no** top-level `results` key — reading `response.results` finds nothing and makes a successful create look like it returned zero products. Check `productResults.results` first; **do not re-create on an empty top-level `results`**.
|
|
114
|
+
|
|
115
|
+
### STEP 3: Create the store's categories
|
|
116
|
+
|
|
117
|
+
Create the categories for the store — **the categories that fit the user's request.** Which categories (and how many) are set by the request you're fulfilling; this step only gives the call and format.
|
|
118
|
+
|
|
119
|
+
Use the **Create Category** endpoint: `POST https://www.wixapis.com/categories/v1/categories`. **There is no bulk-create endpoint for `/categories/v1/`** (the `events/v1/bulk/categories/create` URL is for the Events product, not Stores).
|
|
120
|
+
|
|
121
|
+
**⚠️ CRITICAL: create the categories SEQUENTIALLY — one call at a time, never concurrently.** Every category lives in the single shared `@wix/stores` category tree, which carries **one revision**. Firing the creates concurrently makes them race on that revision: all but one fail with `409 INVALID_REVISION` ("Outdated revision for entity id @wix/stores"), and the failed-then-retried calls can leave **duplicate categories** behind. So issue each create, wait for its response, then issue the next. (There are only a handful of categories — the serial cost is negligible.)
|
|
122
|
+
|
|
123
|
+
The request body must include a top-level `treeReference` field — it must **not** be nested inside the `category` object:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"category": {
|
|
128
|
+
"name": "Drinkware",
|
|
129
|
+
"description": "The Drinkware category includes a wide range of containers designed for holding beverages",
|
|
130
|
+
"visible": true
|
|
131
|
+
},
|
|
132
|
+
"treeReference": {
|
|
133
|
+
"appNamespace": "@wix/stores",
|
|
134
|
+
"treeKey": null
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### STEP 4: Add Each Product to a Category
|
|
140
|
+
|
|
141
|
+
Add each product to the category it belongs to (per the request you're fulfilling). Acquire the product ids (from STEP 2's response) first. Use the **Bulk Add Items To Category** endpoint — products are the `items`: `POST https://www.wixapis.com/categories/v1/bulk/categories/{categoryId}/add-items` (one call per `categoryId` — the path parameter is single).
|
|
142
|
+
|
|
143
|
+
**Issue these add-items calls SEQUENTIALLY too** — one call at a time, waiting for each response before the next. They mutate the same shared `@wix/stores` tree as the creates above, so concurrent calls risk the same `409 INVALID_REVISION` race. Serial is the safe, deterministic choice (the category count is small).
|
|
144
|
+
|
|
145
|
+
The request body is `items` (each with the product's `catalogItemId` and the Stores `appId`) plus a `treeReference` at the **same level** as `items` (not nested):
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"items": [
|
|
150
|
+
{ "catalogItemId": "<productId>", "appId": "215238eb-22a5-4c36-9e7b-e7c08025e04e" }
|
|
151
|
+
],
|
|
152
|
+
"treeReference": {
|
|
153
|
+
"appNamespace": "@wix/stores",
|
|
154
|
+
"treeKey": null
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Attach images (imagery ON only — skip otherwise)
|
|
160
|
+
|
|
161
|
+
**Only when `imagery` is on** (`SEED.md` § "Entity images"). Products were created text-only above; this pass-2 step writes a generated brand image onto each. Generate + import per `references/IMAGE_GENERATION.md`, keep `file.url`, then PATCH the product.
|
|
162
|
+
|
|
163
|
+
**The exact working call — do this per product (getting it right first time avoids a multi-round debug loop):**
|
|
164
|
+
|
|
165
|
+
1. **`GET https://www.wixapis.com/stores/v3/products/{id}`** → read `revision`, `options`, and `variantsInfo` from **`response.product.*`**. The product is **nested under a `product` key** — the GET response is *not* flat.
|
|
166
|
+
2. **`PATCH https://www.wixapis.com/stores/v3/products/{id}`** with the body below. **⚠️ Everything nests under a `product` wrapper.** Putting `id` / `revision` / `media` at the **root** fails `400 "product is invalid: revision must not be empty"` — the #1 cause of the loop here. **Do not send a field mask** (the validator runs before masking → `428`):
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"product": {
|
|
171
|
+
"id": "<product id>",
|
|
172
|
+
"revision": "<revision from the GET — required; omitting it is the 400 above>",
|
|
173
|
+
"media": { "itemsInfo": { "items": [ { "url": "<static.wixstatic.com/…>", "altText": "…" } ] } },
|
|
174
|
+
"options": "<echo the GET's product.options, unchanged>",
|
|
175
|
+
"variantsInfo": "<echo the GET's product.variantsInfo, unchanged>"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
The image may be referenced by a `static.wixstatic.com` **`url`** or by its Wix Media **`id`** (`<hash>~mv2.png`) — either works.
|
|
180
|
+
|
|
181
|
+
- The field that persists is **`media.itemsInfo.items`**, NOT `media.main`. **⚠️ `media.main` set ALONE silently no-ops** — a `200` comes back but re-query shows no image; that is the trap. The server promotes the first `itemsInfo.items` entry to `media.main` for you, so **verify success by reading `media.main.image.url`** — it must resolve to a `static.wixstatic.com` URL. Check that nested string, **not** the presence of `media.main` (which can be a non-null object with no image and makes a failed attach look successful). Conversely `media.itemsInfo` is **write-only** — it comes back `null` on read, so never assert on it.
|
|
182
|
+
- Send **one image per product** (primary); a larger gallery is out of scope for the seed.
|
|
183
|
+
- **Never block on image failure** — skip and leave the product text-only.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Conclusion
|
|
188
|
+
Following these steps **in order** sets up a new V3 Wix Online Store site:
|
|
189
|
+
- Starts from a **clean catalog** — the install's default sample products are removed first.
|
|
190
|
+
- Contains the products and categories called for by the request, all created **`visible: true`** so they appear on the live site.
|
|
191
|
+
- Each product is connected to the category it belongs to.
|
|
192
|
+
- All products follow the correct Catalog V3 API format specified in STEP 2.
|