@wix/agent-skills 1.18.2 → 1.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +42 -40
  3. package/skills/wix-app/references/BACKEND_API.md +151 -152
  4. package/skills/wix-app/references/DASHBOARD_PAGE.md +6 -10
  5. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +16 -7
  6. package/skills/wix-app/references/EXTENSION_REGISTRATION.md +12 -8
  7. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +53 -131
  8. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +1 -2
  9. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +83 -73
  10. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +3 -2
  11. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +9 -0
  12. package/skills/wix-app/references/editor-react-component/PARTS.md +5 -2
  13. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +1 -1
  14. package/skills/wix-app/scripts/pkg-root.cjs +73 -0
  15. package/skills/wix-app/scripts/scan-a11y-code.cjs +310 -40
  16. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +118 -24
  17. package/skills/wix-app/scripts/scan-a11y-render.cjs +551 -0
  18. package/skills/wix-app/scripts/scan-a11y-review.cjs +373 -0
  19. package/skills/wix-base44-connector/SKILL.md +24 -22
  20. package/skills/wix-base44-connector/scripts/utils.cjs +39 -3
  21. package/skills/wix-docs/SKILL.md +47 -20
  22. package/skills/wix-headless/entry/bootstrap.mjs +106 -50
  23. package/skills/wix-headless/entry/skill.md +5 -3
  24. package/skills/wix-headless/references/managed/AUTHENTICATION.md +25 -5
  25. package/skills/wix-headless-fast/SKILL.md +25 -7
  26. package/skills/wix-headless-fast/entry/skill.md +23 -14
  27. package/skills/wix-headless-fast/install/fast-path.mjs +48 -5
  28. package/skills/wix-headless-fast/install/pin-agents-md.mjs +57 -0
  29. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +3 -3
  30. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +204 -35
  31. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +146 -36
  32. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +8 -2
  33. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +95 -20
  34. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +21 -2
  35. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +104 -18
  36. package/skills/wix-manage/SKILL.md +14 -8
  37. package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +59 -33
  38. package/skills/wix-manage/references/bookings/bookings-staff-setup.md +45 -22
  39. package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +70 -12
  40. package/skills/wix-manage/references/calendar/configure-default-business-hours.md +53 -17
  41. package/skills/wix-manage/references/google-ads/billing-and-payment.md +3 -3
  42. package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +8 -8
  43. package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +4 -4
  44. package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +1 -1
  45. package/skills/wix-manage/references/google-ads/install-and-create-account.md +9 -9
  46. package/skills/wix-manage/references/google-ads/query-campaign-analytics.md +7 -7
  47. package/skills/wix-manage/references/seo/manage-content-plan.md +181 -135
  48. package/skills/wix-manage/references/seo/manage-google-search-console.md +272 -0
  49. package/skills/wix-manage/references/sites/upload-static-site.md +53 -0
  50. package/skills/wix-manage/references/stores/change-store-currency.md +1 -1
  51. package/skills/wix-manage/references/stores/create-product-catalog-v3.md +17 -1
  52. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +12 -9
  53. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +12 -9
  54. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
  55. package/skills/wix-vibe-headless/platforms/base44.md +11 -3
  56. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +5 -1
  57. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +5 -4
  58. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +33 -5
  59. package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +0 -80
  60. package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +0 -140
  61. package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +0 -112
  62. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +0 -100
  63. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +0 -130
  64. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +0 -48
  65. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +0 -66
  66. package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +0 -103
  67. package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +0 -56
  68. package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +0 -23
@@ -0,0 +1,272 @@
1
+ ---
2
+ name: "Manage Google Search Console for a Wix Site"
3
+ description: "Connect a Wix site to Google Search Console and drive its setup through the public GSC Connection and Site Readiness APIs: check connection and readiness, start the Google authorization, verify ownership, add the property, submit the sitemap, request indexing, read search performance, run URL inspection, recover a stale connection, or disconnect. The site owner authorizes in their own browser via a single-use connect URL."
4
+ ---
5
+
6
+ # Manage Google Search Console for a Wix Site
7
+
8
+ The **GSC Connection API** links a Google account to the site. The **GSC Site
9
+ Readiness API** acts through that account: verifies ownership, adds the Search
10
+ Console property, submits the sitemap, requests indexing, reads search performance
11
+ and URL inspection. Both select the site from the caller's authorization context
12
+ and take no site ID. Use the site already selected in the environment. If none is
13
+ selected, list the available sites. Select the only site automatically; if several
14
+ are available, ask the user to choose by name before making site-specific calls.
15
+ Never invent a site ID or ask the user to type one.
16
+
17
+ All endpoints are under `https://www.wixapis.com`: Connection calls under
18
+ `/gsc/connection/v1`, everything else under `/gsc/v1`.
19
+
20
+ ## Start every task with two reads
21
+
22
+ Both are side-effect free. Do them before describing the site's state.
23
+
24
+ ```
25
+ GET https://www.wixapis.com/gsc/connection/v1/connection
26
+ ```
27
+
28
+ ```json
29
+ { "connection": { "status": "VALID", "connectedEmailMasking": "m*******@gmail.com", "ownedByCaller": true } }
30
+ ```
31
+
32
+ ```
33
+ GET https://www.wixapis.com/gsc/v1/site-readiness
34
+ ```
35
+
36
+ ```json
37
+ { "siteReadiness": { "status": "NOT_READY", "blockingReason": "SITE_OWNER_NOT_VERIFIED" } }
38
+ ```
39
+
40
+ Read them together:
41
+
42
+ | `connection.status` | `siteReadiness` | Meaning | Next |
43
+ |---|---|---|---|
44
+ | `NOT_CONNECTED` | `NOT_READY` / `SITE_OWNER_NOT_VERIFIED` | No Google account linked | Connect (below) |
45
+ | `PENDING` | any | An authorization attempt is open | Ask the user to finish it in their browser, do not start another |
46
+ | `VALID` | `READY` | Connected and verified | Property steps if the user wants them |
47
+ | `VALID` | `NOT_READY` / `SITE_OWNER_NOT_VERIFIED` | Connected, not verified | Verify Site |
48
+ | `INVALID` | `NOT_READY` / `TOKEN_INVALID` | Credentials rejected by Google | Reconnect |
49
+ | `VALID` | `NOT_READY` / `DOMAIN_MISSING` | No connected domain | User connects a domain first |
50
+
51
+ `blockingReason` is `UNKNOWN_BLOCKING_REASON` when the status is `READY`; not an
52
+ error. Treat an unrecognized reason as "not ready, no known next action".
53
+
54
+ `READY` covers connection and verification only. Whether the property was added,
55
+ the sitemap submitted, or indexing requested comes from **List Events**.
56
+
57
+ ## `VALID` can be stale, and `MISSING_TOKEN` means reconnect
58
+
59
+ `INVALID` is only written when a real call to Google is rejected, so a site nobody
60
+ has queried since its credentials broke keeps reporting `VALID`. The tell arrives
61
+ on the first Google-facing call:
62
+
63
+ ```json
64
+ { "message": "FAILED_PRECONDITION: Missing Token", "details": { "applicationError": { "code": "MISSING_TOKEN" } } }
65
+ ```
66
+
67
+ The reference describes `MISSING_TOKEN` as "no Google account is connected"; it
68
+ also fires when the stored credentials are gone while Get Connection still said
69
+ `VALID`. Either way: the Google account must be reconnected. Do not retry, and do
70
+ not tell the user the site is connected.
71
+
72
+ ## Connect a Google account
73
+
74
+ Get Connect URL is `GET`, but it **writes**: every call starts a new single-use
75
+ authorization attempt (two-hour expiry) and Get Connection reports `PENDING` while
76
+ it is open. Use Get Connection to check connection status. Call Get Connect URL
77
+ when starting a connection or reconnection attempt; while the user completes that
78
+ attempt, use Get Connection for status checks. Request a new URL when a new attempt
79
+ is needed, such as after expiry. On an already-connected site it is allowed: the
80
+ same Google account refreshes the credentials, a different one replaces the
81
+ connection. Say which before you do it.
82
+
83
+ 1. Read Get Connection. If `VALID`, stop unless the user wants to reconnect.
84
+ 2. Call [Get Connect URL](https://dev.wix.com/docs/api-reference/business-management/seo/google-search-console/connection-v1/get-connect-url). The request takes no parameters or body.
85
+
86
+ ```
87
+ GET https://www.wixapis.com/gsc/connection/v1/connect-url
88
+ ```
89
+
90
+ ```json
91
+ { "connectUrl": "https://accounts.google.com/o/oauth2/v2/auth?client_id=407408718192.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fwww.wixapis.com%2Fgsc%2Fconnection%2Fv1%2Fconnect-callback&response_type=code&scope=openid%20email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fwebmasters&state=EXAMPLE_STATE" }
92
+ ```
93
+
94
+ The response above is illustrative. Wix generates the authorization URL,
95
+ including its OAuth query parameters. Give the user the exact returned
96
+ `connectUrl`; do not construct, modify, or reuse the example URL.
97
+
98
+ 3. Give the URL to the user and ask them to tell you when they have finished. Wix
99
+ completes the exchange server-side; there is no code to send back.
100
+ 4. When the user says they are done, read Get Connection once and confirm `VALID`.
101
+ Do not poll while waiting. If it is still `PENDING`, the user has not finished;
102
+ if the attempt expired (two hours), request a fresh URL only after they agree.
103
+
104
+ ## Writes need a published, indexable site with a connected domain
105
+
106
+ Get Connect URL, Verify Site, Add Site, and Submit Sitemap fail with
107
+ `FAILED_PRECONDITION` until the site is published, has a connected (premium)
108
+ domain, and allows indexing. The code names the first failing check:
109
+
110
+ ```json
111
+ { "message": "FAILED_PRECONDITION: The site has no connected domain. Connect a domain before connecting the site to Google Search Console",
112
+ "details": { "applicationError": { "code": "DOMAIN_NOT_CONNECTED" } } }
113
+ ```
114
+
115
+ Codes: `SITE_NOT_PUBLISHED`, `DOMAIN_NOT_CONNECTED`, `SITE_NOT_INDEXABLE`. A free
116
+ site on a `wixsite.com` address cannot complete these writes. Report the missing
117
+ prerequisite and stop. When listing sites to find one that qualifies, filter for
118
+ premium sites with a connected domain.
119
+
120
+ ## Set the site up in Search Console
121
+
122
+ Run the steps the user asked for, in this order, confirming before each write.
123
+ Bodies are empty unless shown.
124
+
125
+ | Step | Call | Notes |
126
+ |---|---|---|
127
+ | Verify ownership | `POST /gsc/v1/verify-site` with `{ "method": "META" }` | Synchronous against Google, allow up to 60 s. Idempotent. Wix already places the meta tag. |
128
+ | Add the property | `POST /gsc/v1/add-site` | Requires a verified site. |
129
+ | Submit the sitemap | `POST /gsc/v1/submit-sitemap` | Wix hosts and generates the sitemap. Google processes it asynchronously. |
130
+ | Request indexing | `POST /gsc/v1/request-site-indexing` | Success means Google accepted the request, not that the site is indexed. |
131
+ | Request one page | `POST /gsc/v1/request-page-indexing` with `{ "url": "https://www.example.com/about" }` | Full URL of a page on this site. |
132
+
133
+ All succeed with `{}`. Re-read Get Site Readiness after each step and act on the
134
+ current `blockingReason`. Verify Site on an already-verified site returns `{}` and
135
+ re-stamps the `SITE_VERIFIED` event date: safe, but not a change to report.
136
+
137
+ Two Google-side failures are retryable. `RESOURCE_EXHAUSTED` (`GOOGLE_QUOTA_EXCEEDED`):
138
+ the quota is daily, tell the user to retry tomorrow. `UNAVAILABLE`
139
+ (`GOOGLE_UNAVAILABLE`): retry once after a few seconds. On any other error your next
140
+ message is the final response: name the step that failed and the error code, and
141
+ make no further Search Console calls.
142
+
143
+ ## Check what was done: List Events and List Sitemaps
144
+
145
+ ```
146
+ GET https://www.wixapis.com/gsc/v1/events
147
+ ```
148
+
149
+ ```json
150
+ { "events": [
151
+ { "eventType": "ACCOUNT_CONNECTED", "occurredDate": "2025-10-29T11:10:15.869Z" },
152
+ { "eventType": "SITE_VERIFIED", "occurredDate": "2026-09-07T11:39:31.992Z" },
153
+ { "eventType": "SITE_ADDED", "occurredDate": "2025-10-29T11:07:39.050Z" } ] }
154
+ ```
155
+
156
+ One entry per event type with the date it **last** occurred, **not sorted by
157
+ date**: never read the last entry as the latest event. Types: `ACCOUNT_CONNECTED`,
158
+ `SITE_VERIFIED`, `SITE_ADDED`, `SITEMAP_SUBMITTED`, `SITE_INDEXING_REQUESTED`,
159
+ `PAGE_INDEXING_REQUESTED`. A missing type means that step never happened.
160
+
161
+ ```
162
+ GET https://www.wixapis.com/gsc/v1/sitemaps
163
+ ```
164
+
165
+ ```json
166
+ { "sitemaps": [ { "url": "https://www.example.com/blog-posts-sitemap.xml", "type": "sitemap", "pending": false,
167
+ "lastDownloadedDate": "2026-09-05T15:14:47.210Z", "errorCount": "0", "warningCount": "0",
168
+ "contents": [ { "type": "web", "submittedCount": "1", "indexedCount": "1" } ] } ] }
169
+ ```
170
+
171
+ Wix submits several sitemaps, typically one per content type. `pending: true` with
172
+ an empty `type` means Google has not processed it yet. Counts are strings.
173
+
174
+ ## Read search performance
175
+
176
+ ```
177
+ GET https://www.wixapis.com/gsc/v1/search-analytics?startDate=2026-08-01&endDate=2026-08-31&dimensions=query&rowLimit=5
178
+ ```
179
+
180
+ ```json
181
+ { "results": [ { "keys": ["best of santorini"], "clicks": 0, "impressions": 3, "ctr": 0, "position": 36 } ] }
182
+ ```
183
+
184
+ - `startDate` and `endDate` are required, `YYYY-MM-DD`, end inclusive.
185
+ - `dimensions` repeats: `date`, `query`, `page`, `country`, `device`,
186
+ `searchAppearance`; omit for one aggregate row. `keys` follow your order.
187
+ - `type`: `web` (default), `image`, `video`, `news`. Page with `rowLimit`/`startRow`.
188
+ - It is Google's own report: the latest days are usually missing, and there is no
189
+ history from before the property was added. Empty `results` is a real answer.
190
+
191
+ ## Inspect pages
192
+
193
+ Page IDs are Wix static-page IDs, not URLs. Discover them with
194
+ [List Item SEO Tags](https://dev.wix.com/docs/api-reference/business-management/seo/item-seo-tags-v1/list-item-seo-tags)
195
+ for `STATIC_PAGE`. The request has no body:
196
+
197
+ ```
198
+ GET https://www.wixapis.com/promote/seo/v1/item-seo-tags/STATIC_PAGE
199
+ ```
200
+
201
+ Relevant response fields (unrelated SEO fields omitted; IDs are illustrative):
202
+
203
+ ```json
204
+ { "itemSeoTags": [ { "id": "STATIC_PAGE:c1dmp", "itemType": "STATIC_PAGE", "itemId": "c1dmp" } ],
205
+ "pagingMetadata": { "count": 1, "hasNext": false, "cursors": {} } }
206
+ ```
207
+
208
+ If `pagingMetadata.hasNext` is `true`, pass `pagingMetadata.cursors.next` back as
209
+ `paging.cursor` in the next request's query string, URL-encoded. Continue until
210
+ `hasNext` is `false`. The optional `paging.limit` controls the page size; omit the
211
+ cursor on the first request. Treat cursors as opaque.
212
+
213
+ Use each selected entry's `itemId` as a `pageIds` entry for Inspect URLs. Do not
214
+ use the composite `id` (`STATIC_PAGE:c1dmp`) or a page URL, and never invent an ID.
215
+ The example below assumes discovery returned `c1dmp` for the selected page.
216
+
217
+ ```
218
+ POST https://www.wixapis.com/gsc/v1/inspect-urls
219
+ ```
220
+
221
+ ```json
222
+ { "pageIds": ["c1dmp"], "updateResults": true }
223
+ ```
224
+
225
+ ```json
226
+ { "results": [ { "page": { "pageId": "c1dmp", "name": "Home", "path": "/", "itemType": "STATIC_PAGE" },
227
+ "status": "OK", "createdDate": "2026-09-07T12:09:55.301Z",
228
+ "data": { "inspectionResultLink": "https://search.google.com/search-console/inspect?resource_id=https%3A%2F%2Fwww.example.com%2F&id=EXAMPLE_INSPECTION_ID",
229
+ "indexStatusResult": { "verdict": "PASS", "coverageState": "Submitted and indexed",
230
+ "indexingState": "INDEXING_ALLOWED", "lastCrawlTime": "2026-08-16T11:39:45Z",
231
+ "googleCanonical": "https://www.example.com/", "userCanonical": "https://www.example.com/" } } } ],
232
+ "status": "COMPLETE", "remainingPageCount": 0 }
233
+ ```
234
+
235
+ - `data.inspectionResultLink` is returned by Google. The complete URL above is
236
+ illustrative; use the actual returned link unchanged, including its query
237
+ parameters. Do not construct a link from the example values.
238
+ - Each page spends one unit of the account's Google quota (2,000 per property per
239
+ day): confirm the page list first. Sites over 2,000 pages fail with
240
+ `SITE_EXCEEDS_INSPECTION_LIMIT`.
241
+ - The site stores **one** inspection; without `updateResults: true` a new call
242
+ replaces it. Prefer `true` unless the user wants a clean run.
243
+ - `ONGOING`: poll `GET https://www.wixapis.com/gsc/v1/inspection` every few
244
+ seconds until `COMPLETE` or `ERROR` (same shape plus `updatedDate`).
245
+ - `remainingPageCount > 0` on `COMPLETE` means quota ran out. Name the missing
246
+ pages; do not call it done.
247
+ - `data` is Google's raw URL Inspection payload: read `indexStatusResult.verdict`
248
+ (`PASS`, `PARTIAL`, `FAIL`, `NEUTRAL`) and `coverageState`; text is English only.
249
+ `UNKNOWN_INSPECTION_STATUS` with empty `results` means never inspected.
250
+
251
+ ## Disconnect and clear
252
+
253
+ Two different removals; confirm which one the user wants.
254
+
255
+ | Call | Removes | Keeps |
256
+ |---|---|---|
257
+ | `POST /gsc/connection/v1/disconnect` | Stored Google credentials | Verification tag, Search Console property |
258
+ | `POST /gsc/v1/clear-verification` | Verification tag, verification state, **and** credentials | Search Console property |
259
+
260
+ Neither removes the site from Search Console or revokes Wix's access on Google's
261
+ side. Disconnect with no stored credentials returns `NOT_FOUND`
262
+ (`CONNECTION_NOT_FOUND`).
263
+
264
+ ## When a call fails
265
+
266
+ - `403` or `PERMISSION_DENIED`: stop after the first and report the missing SEO
267
+ permission (reads need view access to SEO settings, writes need manage access).
268
+ Do not try another site, path, or method.
269
+ - `FAILED_PRECONDITION`: act on `details.applicationError.code` as above. Do not
270
+ retry the same call.
271
+ - Get Connect URL is a recovery step only for `MISSING_TOKEN`, `INVALID`, or
272
+ `TOKEN_INVALID`, and only after the user agrees to reconnect.
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: "Upload a Website or HTML Files"
3
+ description: Get a user's ready-made website live on Wix when they already have the site files — an index.html, a zip, or a static build exported from an AI builder or any other tool — by sending them to the Wix Headless drop page, where they upload the files and instantly get a live hosted site they can claim into their account. Use whenever the user wants to upload, publish, or host their own HTML/CSS/JS files as a NEW site. Not for migrating a live store/site from another platform by URL or from CSV exports (use Site Import), not for adding HTML or custom code into an existing Wix site, and not for uploading images or documents to a site's media files.
4
+ ---
5
+
6
+ # Upload a Website or HTML Files
7
+
8
+ The user already has a finished website as files — hand-written HTML, a zip
9
+ export, or the output of an AI site builder — and wants it live on Wix as a
10
+ new site. There is no API call to make. Send them to the upload page and
11
+ explain the flow:
12
+
13
+ ```
14
+ https://www.wix.com/headless/drop?utm_campaign=mcp
15
+ ```
16
+
17
+ **Always give the URL exactly as written above, including the
18
+ `utm_campaign=mcp` query parameter.** It attributes the visit to an
19
+ assistant/MCP referral; dropping or rewriting it breaks that attribution.
20
+
21
+ ## What happens on that page
22
+
23
+ 1. The user drags in their files (or a zip) — no login needed.
24
+ 2. Wix hosts them immediately on a live URL they can open and share.
25
+
26
+ ## Tell the user before they upload
27
+
28
+ Share the requirements that match their situation — these are the common
29
+ reasons an upload fails:
30
+
31
+ - The upload must contain an **HTML file at the top level of the site's
32
+ files**. It does not need to be named `index.html` — a single HTML file of
33
+ any name is served as the homepage — and a zip or folder that wraps
34
+ everything in one top-level folder (e.g. `my-site/index.html`) is unwrapped
35
+ automatically.
36
+ - Size limits: **3 MB per file, 20 MB total.**
37
+ - **Static files only** — HTML, CSS, JavaScript, and assets. If the user has
38
+ framework source code that needs a build step (a `package.json`, React/Vue
39
+ source, etc.), they must run their build first and upload the build output
40
+ folder's contents, not the source.
41
+
42
+ ## Route the request correctly
43
+
44
+ | The user has… | Do this |
45
+ |---|---|
46
+ | Site files on their machine (HTML/zip/AI-builder export), wants a new site | This recipe — send them to the drop URL above |
47
+ | A live site or store on another platform (a URL), or CSV/TSV exports, and wants content/products migrated | [Site Import](site-import.md) |
48
+ | An existing Wix site they want to add HTML, an embed, or custom code into | Not this recipe — that's embedding custom code in the site editor |
49
+ | Images, videos, or documents to add to a site | [Upload Media to Wix](../media/upload-media-to-wix.md) — media goes to the site's media files, not a new site |
50
+
51
+ Do not try to perform the upload for the user through an API, and do not
52
+ create a site for them with other site-creation tools instead — the drop page
53
+ is the flow.
@@ -5,6 +5,6 @@ description: "Changes the store's payment currency through Site Properties and e
5
5
 
6
6
  # Change Store Currency
7
7
 
8
- Use the [Site Properties currency recipe](https://dev.wix.com/docs/api-reference/business-management/site-properties/skills/change-payment-currency-site-properties.md) to set the requested payment currency and read it back. Confirm the change with the user before updating it.
8
+ Use the [Change a Site's Regional Properties (Currency, Time Zone, Language) via Site Properties API](../site-properties/change-payment-currency-site-properties.md) recipe to set the requested payment currency and read it back. Confirm the change with the user before updating it.
9
9
 
10
10
  Catalog product responses may temporarily show the previous currency after carts and checkout use the new one. Allow time for propagation, then recheck.
@@ -83,9 +83,22 @@ If any product is missing a price, **do not call the API**. Ask for the missing
83
83
 
84
84
  ## Build each product
85
85
 
86
+ > [!WARNING]
87
+ > **Hard requirement before every create call:** If `productType` is `"PHYSICAL"`, the request MUST include `physicalProperties` on the product and on every variant:
88
+ >
89
+ > ```json
90
+ > {
91
+ > "productType": "PHYSICAL",
92
+ > "physicalProperties": {},
93
+ > "variantsInfo": {"variants": [{"physicalProperties": {}}]}
94
+ > }
95
+ > ```
96
+ >
97
+ > Put supplied physical fields such as weight or dimensions inside those objects; use `{}` when none were supplied. Never invent them, and never execute or retry a physical-product request when either object is absent.
98
+
86
99
  | Concern | Rule |
87
100
  |---|---|
88
- | Type | `PHYSICAL` uses product- and variant-level `physicalProperties`; `DIGITAL` omits them and carries `inventoryItem.inStock` plus `digitalProperties.digitalFile` on each variant. |
101
+ | Type | `PHYSICAL` requires both `productType: "PHYSICAL"` and product-level `physicalProperties`, plus both `productType: "PHYSICAL"` and variant-level `physicalProperties` on every variant. `DIGITAL` omits physical properties and carries `inventoryItem.inStock` plus `digitalProperties.digitalFile` on each variant. |
89
102
  | Variants | Include at least one. Price, SKU, barcode, inventory, and digital file are variant-level fields. |
90
103
  | Price | Use a string in `price.actualPrice.amount`; add `compareAtPrice` only when supplied. |
91
104
  | SKU | Preserve supplied strings exactly, including `#`, punctuation, and leading zeroes. Keep variant SKUs unique unless explicitly requested otherwise. |
@@ -221,6 +234,8 @@ The first is for single endpoints; the second is for bulk endpoints. If the user
221
234
 
222
235
  For one product when the user supplied no quantity, stock state, or inventory-tracking request, the complete direct path is `POST /stores/v3/products` with `{"product": PRODUCT}`. Do not infer `inStock: true`, add `inventoryItem`, or choose `/products-with-inventory`; physical product type does not imply inventory. If it has ready images, add `"fields": ["MEDIA_ITEMS_INFO"]` to the same no-inventory request.
223
236
 
237
+ For a physical product on either create endpoint, never send `productType: "PHYSICAL"` by itself. Include `physicalProperties: {}` on the product and `physicalProperties: {}` on every physical variant (or populate those objects with the supplied physical dimensions/weight). The API validates these as aligned one-of pairs and returns 400 if either corresponding field is missing.
238
+
224
239
  For one physical product with inventory, the complete direct path is one `POST /stores/v3/products-with-inventory` call with `{"product": PRODUCT}`. Put `inventoryItem` on its priced variant. If it has ready images, add `"fields": ["MEDIA_ITEMS_INFO"]` to that same request. The product shape, envelope, endpoint, and validation paths are complete here: do not search another recipe or schema, probe an alternate endpoint, or make a post-create read unless the projected create response does not prove requested media.
225
240
 
226
241
  ### Request projected media when validating images
@@ -300,4 +315,5 @@ Retry only failed transient items, not successful products. If a requested field
300
315
  - `variantsInfo must not be empty`: include at least one priced variant per product.
301
316
  - Option/variant mismatch: create every requested combination and reference every option once per variant.
302
317
  - Media not visible: use the uploaded `wixstatic.com` URL in `media.itemsInfo.items`; do not set `media.main`.
318
+ - `productType` alignment 400 (`product is invalid: productType and the corresponding physical_properties field must be passed together`): pair `productType: "PHYSICAL"` with `physicalProperties` on the product and on every physical variant. The API validates these as aligned one-of pairs, so do not retry with only one level populated. Pair `productType: "DIGITAL"` with the digital variant fields instead.
303
319
  - Digital product rejected at add-to-cart: `ITEM_NOT_FOUND_IN_CATALOG` means the variant has no `digitalProperties.digitalFile`; `exceeds available inventory` means it has no `inventoryItem.inStock`. Both read back as a healthy product.
@@ -11,7 +11,8 @@ or configuration changes are needed.
11
11
  **Banner disabled for this entry flow:** do not add `<WixManageBanner/>`; skip the shared
12
12
  storefront instructions' optional banner integration.
13
13
 
14
- Follow STEPs 1–4 below exactly (run STEP 3 in parallel with STEP 2).
14
+ Follow STEPs 1–4 below exactly. Fire product image generations first, then build the client
15
+ (STEP 2), and seed (STEP 3) **after** the client is built — generation finishes while you build.
15
16
 
16
17
  ## STEP 1 — Install the Wix skills and deploy the storefront vertical to the app
17
18
 
@@ -89,17 +90,19 @@ Inline via exec_tool, `base44` is already declared — use it directly; do **not
89
90
  `@base44/sdk`, re-declare it, or call `createClient()` (that's for standalone `.js` files only;
90
91
  inline it throws *"Identifier 'base44' has already been declared."*).
91
92
 
92
- **Product images.** Generate with **Base44's built-in image generation**, then attach via the
93
- storefront seed module's image-attach step.
94
-
95
- **Seed images with the FINAL url, in one call.** Use the real `https://media.base44.com/...` url
96
- from the **completed** `generate_image` result and pass it straight into your single `setupStore`
97
- call (images included). `generate_image` runs in the background while you build the client, so the
98
- urls are ready by the time you seed.
93
+ **Product images.** Fire every `generate_image` **before you build the client**, and seed **after**
94
+ it — generation finishes while you build, so by the time you write the seed call your images are
95
+ ready: each finished result now reads `status: "completed"` with its final
96
+ `https://media.base44.com/...` url. Read every `imageUrl` from the results **as they read at that
97
+ moment** — not from what they said when you called the tool — and pass them in the single
98
+ `setupStore` call. A result without a final url by then (still running, or failed) → seed that
99
+ product **without** `imageUrl` and attach it afterwards with `attachProductImages`.
99
100
 
100
101
  ## STEP 4 — Wrap up
101
102
 
102
- ### Preview
103
+ ### Verify
104
+
105
+ Verify **once, after the seed** — home, shop, and a product page against the populated catalog.
103
106
 
104
107
  Images that are still generating may show `/__generating__/…` placeholders;
105
108
  the platform replaces these automatically at turn end, with a stock fallback if generation
@@ -24,7 +24,8 @@ No installation, deployment, or AGENTS.md update is needed.
24
24
  The deployed implementation and configuration files do not need to be read; use the build and seed
25
25
  guides below for their contracts and usage. Continue with STEP 1.
26
26
 
27
- Follow STEPs 1–3 below exactly (run STEP 2 in parallel with STEP 1).
27
+ Follow STEPs 1–3 below exactly. Fire product image generations first, then build the client
28
+ (STEP 1), and seed (STEP 2) **after** the client is built — generation finishes while you build.
28
29
 
29
30
  ## STEP 1 — Build the client
30
31
 
@@ -74,17 +75,19 @@ Inline via exec_tool, `base44` is already declared — use it directly; do **not
74
75
  `@base44/sdk`, re-declare it, or call `createClient()` (that's for standalone `.js` files only;
75
76
  inline it throws *"Identifier 'base44' has already been declared."*).
76
77
 
77
- **Product images.** Generate with **Base44's built-in image generation**, then attach via the
78
- storefront seed module's image-attach step.
79
-
80
- **Seed images with the FINAL url, in one call.** Use the real `https://media.base44.com/...` url
81
- from the **completed** `generate_image` result and pass it straight into your single `setupStore`
82
- call (images included). `generate_image` runs in the background while you build the client, so the
83
- urls are ready by the time you seed.
78
+ **Product images.** Fire every `generate_image` **before you build the client**, and seed **after**
79
+ it — generation finishes while you build, so by the time you write the seed call your images are
80
+ ready: each finished result now reads `status: "completed"` with its final
81
+ `https://media.base44.com/...` url. Read every `imageUrl` from the results **as they read at that
82
+ moment** — not from what they said when you called the tool — and pass them in the single
83
+ `setupStore` call. A result without a final url by then (still running, or failed) → seed that
84
+ product **without** `imageUrl` and attach it afterwards with `attachProductImages`.
84
85
 
85
86
  ## STEP 3 — Wrap up
86
87
 
87
- ### Preview
88
+ ### Verify
89
+
90
+ Verify **once, after the seed** — home, shop, and a product page against the populated catalog.
88
91
 
89
92
  Images that are still generating may show `/__generating__/…` placeholders;
90
93
  the platform replaces these automatically at turn end, with a stock fallback if generation
@@ -62,7 +62,7 @@ need to diagnose and fix it.
62
62
 
63
63
  **No seeding in this flow** — the client is the only deliverable. Do not seed, populate, or write data to Wix.
64
64
 
65
- ### Preview
65
+ ### Verify
66
66
 
67
67
  Images that are still generating may show `/__generating__/…` placeholders;
68
68
  the platform replaces these automatically at turn end, with a stock fallback if generation
@@ -11,7 +11,8 @@ only), safe in the frontend; use it directly for the Wix client setup.
11
11
  **Banner enabled for this entry flow:** mount `<WixManageBanner/>` once in the Layout, above
12
12
  the header in the fixed top region, per the vertical instructions. It is preview-only.
13
13
 
14
- Follow STEPs 1–5 below exactly (run STEP 4 in parallel with STEP 3 — **except `forms`**, see STEP 3).
14
+ Follow STEPs 1–5 below exactly (run STEP 4 in parallel with STEP 3 — **except `forms`**, see
15
+ STEP 3, and **except a seed that attaches entity images**, which runs after the build; see STEP 4).
15
16
 
16
17
  ## STEP 1 — Install the Wix skills locally
17
18
 
@@ -98,8 +99,15 @@ rejects the write. Wire routes/imports in with `find_replace`, leave the rest as
98
99
 
99
100
  Seed by calling your vertical's ready-made seed module — read
100
101
  `.agents/skills/wix-vibe-headless/references/<vertical>/seed/SEED.md` and follow it (the loader
101
- snippet, admin connector token, entity images with the final `media.base44.com` url, and every field
102
- shape are there). Gaps or an unexpected shape → the documentation skill available in your environment.
102
+ snippet, admin connector token, and every field shape are there). Gaps or an unexpected shape → the
103
+ documentation skill available in your environment.
104
+
105
+ **Entity images:** fire every image generation **before you build the client**, and run an
106
+ image-carrying seed **after** the build — generation finishes while you build, and each finished
107
+ result then reads `status: "completed"` with its final `https://media.base44.com/...` url. Read
108
+ every image url from the results **as they read at that moment**, not from what they said when you
109
+ called the tool. No final url by then (still running, or failed) → seed without that image and
110
+ attach it afterwards per the vertical's `SEED.md`.
103
111
  Seeding is **admin-only** — not part of the client, which is built solely per the `wix-vibe-headless` skill.
104
112
 
105
113
  - **Additive only:** never delete or overwrite the user's content, even apparent sample data; ask first if a cleanup truly seems needed.
@@ -9,7 +9,7 @@ or configuration changes are needed to build the UI. Never mock products or hand
9
9
  `/checkout` URL; the shipped cart uses the eCom redirect session.
10
10
 
11
11
  ## Prerequisites
12
- - The site's **Wix Stores** catalog is the read/cart target. It's installed and seeded separately, in parallel with this build — so it may be empty at build time; render the empty state until products land.
12
+ - The site's **Wix Stores** catalog is the read/cart target. It may be empty while the client is being built — render the empty state cleanly. Flows that seed do so after the client is built; verify the storefront once the catalog is populated, not against the empty state.
13
13
 
14
14
  ## Already installed in `src/`
15
15
  Successful deployment verified these files are in place; use this map without needing to read their source (`@/` → `src/`).
@@ -336,6 +336,10 @@ wire these exactly as shown below.
336
336
  When adding storefront routes and providers to `src/App.jsx`, preserve the existing platform
337
337
  authentication setup, including `AuthProvider`, `useAuth`, and their `@/lib/AuthContext` imports.
338
338
  Do not remove or replace that authentication logic.
339
+ The shipped commerce flow needs no login: shoppers browse, cart, and check out on the Wix visitor
340
+ session, which is separate from platform user auth. Whether the storefront is public or members-only
341
+ is the brief's call — default to public, and gate routes behind platform auth only when the brief
342
+ asks for it. Neither choice affects the commerce flow.
339
343
  - Wrap the routed tree in `<CartProvider>` (from `@/context/CartContext`).
340
344
  - Put your **header + footer in a `Layout`** that renders `<Outlet/>` between them, and nest every
341
345
  route under one pathless `<Route element={<Layout/>}>`. Your brand chrome then wraps **every** page
@@ -19,9 +19,7 @@ const ctx = { token: accessToken };
19
19
  // ONE call: install (+ wait for V3) → create products → categories → attach images, ids kept
20
20
  // in memory (no hand-threading). Categories map name -> product NAMES. Pass an imageUrl per product
21
21
  // to attach its image; omit it to skip images.
22
- // imageUrl must be the FINAL https://media.base44.com/... url from the COMPLETED generate_image
23
- // result — not a still-generating /__generating__/<id>.png placeholder (Wix can't fetch that).
24
- // generate_image runs in the background while you build, so the urls are ready by seed time.
22
+ // imageUrl: a public, fetchable https:// url — Wix copies the image bytes at attach time.
25
23
  const result = await seed.setupStore(ctx, {
26
24
  currency: "EUR", // Pass only if the user asked for a currency or it's obvious for the store; else omit this line.
27
25
  products: [
@@ -38,6 +36,8 @@ const result = await seed.setupStore(ctx, {
38
36
  categories: { "Legends": ["The Glam Rocker"], "Rising Stars": [] }, // omit if the brief names none
39
37
  });
40
38
  // result: { products:[{id,slug,revision,name}], categories:[{id,name}], imagesAttached,
39
+ // imagesSkipped (product names whose imageUrl was not an absolute https:// url — attach those afterwards),
40
+ // productsWithoutImages (product names seeded with no imageUrl — attach afterwards once urls exist),
41
41
  // currency: { requested, actual, status, warnings } }
42
42
  ```
43
43
 
@@ -112,6 +112,8 @@ enrolls in — is **Pricing Plans**, not a Stores product, so it isn't seeded he
112
112
  download — uploaded and created with both the file and stock, which is what the cart requires
113
113
  (`quantity` is ignored). It's also the only way in: a file-less digital product is created
114
114
  successfully, reads back healthy, and is then rejected at add-to-cart as `ITEM_NOT_FOUND_IN_CATALOG`.
115
+ No real, fetchable file in hand → seed the product as **physical** with `inStock: true` and tell the
116
+ user; swap it to a digital download once a real file exists.
115
117
 
116
118
  Two things this module does **not** seed, so don't try:
117
119
 
@@ -138,7 +140,6 @@ const products = await seed.bulkCreateProducts(ctx, [ // → [{i
138
140
  ]);
139
141
  const cats = await seed.createCategories(ctx, ["Legends"]); // sequential → [{id,name}]
140
142
  await seed.addProductsToCategories(ctx, { [cats[0].id]: [products[0].id] });
141
- // images: use the FINAL https://media.base44.com/... url only (never a /__generating__/ placeholder)
142
143
  await seed.attachProductImages(ctx, products.map((p, i) => ({ id: p.id, url: imageUrls[i], altText: p.slug })));
143
144
  ```
144
145
 
@@ -257,7 +257,8 @@ async function uploadDigitalFile(ctx, url, fileName) {
257
257
  if (!mimeType) throw new Error(`digitalFileName needs one of these extensions (${Object.keys(FILE_MIME).join(", ")}): ${fileName}`);
258
258
  const { uploadUrl } = await req(ctx, "/site-media/v1/files/generate-upload-url", { body: { mimeType, fileName } });
259
259
  const src = await fetch(url);
260
- if (!src.ok) throw new Error(`digitalFileUrl ${url} -> ${src.status}`);
260
+ if (!src.ok) throw new Error(`digitalFileUrl ${url} -> ${src.status}. A digital product needs a real, ` +
261
+ `fetchable file — with none at hand, seed this product as physical with inStock: true and tell the user.`);
261
262
  const res = await fetch(uploadUrl, {
262
263
  method: "PUT", headers: { "Content-Type": mimeType }, body: Buffer.from(await src.arrayBuffer()),
263
264
  });
@@ -416,9 +417,17 @@ async function addProductsToCategories(ctx, mapping) {
416
417
  // caller never manages a revision token — attach any number of times, in any pass. Wix re-hosts the
417
418
  // image from the url server-side; the re-hosted media can take a little while to appear on read-back
418
419
  // (propagation) — that's normal, not a failure, so we don't block on it.
420
+ // Wix imports the image bytes server-side, so an attach needs an absolute, publicly fetchable url.
421
+ const isFetchableImageUrl = (url) => typeof url === "string" && /^https:\/\//.test(url);
422
+
419
423
  async function attachProductImages(ctx, items) {
420
424
  if (!items?.length) return;
421
425
  if (items.some((it) => !it.id)) throw new Error("Image attachment requires a product ID for every item; no image request was sent.");
426
+ const unfetchable = items.filter((it) => !isFetchableImageUrl(it.url));
427
+ if (unfetchable.length) throw new Error(
428
+ `Image url(s) for [${unfetchable.map((it) => it.altText || it.id).join(", ")}] are not absolute ` +
429
+ `https:// urls, and Wix copies the image bytes at attach time. Re-call attachProductImages once each ` +
430
+ `image has its final url. No image request was sent; products are unaffected.`);
422
431
  const ids = items.map((it) => it.id);
423
432
  const q = await req(ctx, "/stores/v3/products/query", { body: { query: { filter: { id: { $in: ids } }, paging: { limit: ids.length } } } });
424
433
  const revById = new Map((q.products ?? []).map((p) => [p.id, p.revision]));
@@ -475,7 +484,8 @@ async function configureCurrency(ctx, requested) {
475
484
  * digitalFileUrl?, digitalFileName? }],
476
485
  * categories?: { [categoryName]: string[] }, // map of category name -> product NAMES in it
477
486
  * }}
478
- * @returns { products: [{id,slug,revision,name}], categories: [{id,name}], imagesAttached: number, currency: {requested,actual,status,warnings} }
487
+ * @returns { products: [{id,slug,revision,name}], categories: [{id,name}], imagesAttached: number,
488
+ * imagesSkipped?: string[], note?: string, currency: {requested,actual,status,warnings} }
479
489
  */
480
490
  async function setupStore(ctx, { products = [], categories = {}, currency } = {}) {
481
491
  validateProducts(products);
@@ -498,11 +508,29 @@ async function setupStore(ctx, { products = [], categories = {}, currency } = {}
498
508
  }
499
509
 
500
510
  const imageItems = withNames
501
- .map((p, i) => ({ id: p.id, url: products[i]?.imageUrl, altText: products[i]?.altText ?? p.slug }))
511
+ .map((p, i) => ({ id: p.id, url: products[i]?.imageUrl, altText: products[i]?.altText ?? p.slug, name: p.name }))
502
512
  .filter((it) => it.url);
503
- if (imageItems.length) await attachProductImages(ctx, imageItems);
513
+ // A url Wix cannot fetch would fail the attach: seed the product imageless and report it,
514
+ // so the caller attaches once the final url exists.
515
+ const readyItems = imageItems.filter((it) => isFetchableImageUrl(it.url));
516
+ const skipped = imageItems.filter((it) => !isFetchableImageUrl(it.url)).map((it) => it.name);
517
+ if (readyItems.length) await attachProductImages(ctx, readyItems);
504
518
 
505
- return { products: withNames, categories: cats, imagesAttached: imageItems.length, currency: currencyResult };
519
+ const withoutImages = withNames.filter((p, i) => !products[i]?.imageUrl).map((p) => p.name);
520
+ return {
521
+ products: withNames, categories: cats, imagesAttached: readyItems.length,
522
+ ...(skipped.length && {
523
+ imagesSkipped: skipped,
524
+ note: "these products' image urls were not absolute https:// urls — attach them with " +
525
+ "attachProductImages once each image has its final url",
526
+ }),
527
+ ...(withoutImages.length && !skipped.length && {
528
+ productsWithoutImages: withoutImages,
529
+ note: "these products were seeded without an image — once their images have final urls, " +
530
+ "attach them with attachProductImages",
531
+ }),
532
+ currency: currencyResult,
533
+ };
506
534
  }
507
535
 
508
536
  module.exports = {