56 "summary": "sendSmsReminder has 'Default: true' on both Create Booking and Bulk Create Booking (read 2026-08-16), the inverse of events/rsvp's disableNotifications and this API's own participantNotification.notifyParticipants. Sending sendSmsReminder:false is harmless; it is not required. Covered by site mute: when getSiteMuteState reads muted:true, this is not a reason to block, skip, delay or warn about the entity."
64 "summary": "Create Add On declares durationInMinutes and maxQuantity as a required ONE-OF: every add-on is EITHER duration-based (durationInMinutes 1..1440, i.e. max 24 hours) OR quantity-based (maxQuantity 1..9999), never both and never neither. A source extra that is conceptually both ('add 30 minutes, up to 3 times') cannot be expressed as one add-on and must be split or ledgered. The two kinds also behave differently at booking time: for duration-based add-ons quantity is not applicable - passing quantity 1 is accepted but silently cleared, and any other value returns INVALID_ARGUMENT with the message 'Invalid AddOn details: either duration or quantity must be set correctly'. LIVE 2026-10-27 (throwaway site, muted): a create carrying BOTH durationInMinutes and maxQuantity is ACCEPTED (200) and silently keeps only durationInMinutes. importBookingsAddOn therefore refuses a both-kinds body before sending; the split is a mapping decision."
72 "summary": "Wix Bookings is not present on a fresh headless site, and neither `/bookings/v2/services` nor `/bookings/v2/resources/query` reveal that by erroring — both answer with ordinary 200s/validation errors regardless. Create Service instead fails with a plain 'form Form with id 00000000-0000-0000-0000-000000000000 doesn't exist', which reads like a form problem but is actually the app-install gap. Install with appDefId 13d21c63-b5ec-5912-8397-c3a5ddb27a97 (see rp-execute-setup SKILL.md) before the first Create Service call; this also auto-provisions the default booking form and one default 'Business Owner' staff resource."
118 "summary": "VERIFIED LIVE 2026-10-27 : update-tags stores a tag id that does not exist (e.g. 'zzzzz'). Tag ids come only from read-back crm/label records via the readiness grant."
119 },
120 "contact": {
121 "path": "domains/crm/entities/contact.json",
122 "displayName": "CRM Contact",
123 "classification": "native-plus-cms",
124 "reliabilityStatus": "partially-reliable",
125 "reliabilityFlags": [],
126 "summary": "Original contact created dates are not writable as native createdDate."
134 "summary": "Only set subscribed states when the migration has consent evidence. A real per-order checkout-consent-checkbox flag (see the woocommerce order.meta_data sourceAlias above) is sufficient evidence; do not infer consent from mere presence as a customer or from having placed an order with no consent flag."
135 },
136 "label": {
137 "path": "domains/crm/entities/label.json",
138 "displayName": "CRM Label",
139 "classification": "native",
140 "reliabilityStatus": "partially-reliable",
141 "reliabilityFlags": [],
142 "summary": "Do not collapse dynamic segments or marketing automation state into static labels without review."
143 }
144 }
145 },
146 "ecom": {
147 "path": "domains/ecom/domain.json",
148 "displayName": "Wix eCommerce",
149 "ownerHint": "eCommerce Orders, refunds, checkout, and order-history domain owners",
157 "summary": "WooCommerce shipping zones (location rules + per-zone methods) and Wix delivery profiles/regions do not map one-to-one; carrier-calculated Woo methods (e.g. a real UPS/FedEx/USPS integration plugin) have no data equivalent and become reconfigure-in-Wix decisions — shipping-build.js's classifyMethod flags these as a gap rather than guessing a rate."
161 "displayName": "eCommerce Discount Rule (\"Automatic Discounts\" in the dashboard)",
162 "classification": "native",
163 "reliabilityStatus": "partially-reliable",
164 "reliabilityFlags": [],
165 "summary": "`scopes[].id` accepts ANY non-empty string server-side (create succeeds with an arbitrary value like `\"stores-catalog\"`), but the merchant dashboard's own Automatic Discounts editor only recognizes the id patterns Wix's own tooling generates: `all_<catalogAppId>` (storewide), `specific_<catalogAppId>` (named products, id + catalogItemIds), `collections_<catalogAppId>` (CUSTOM_FILTER, a collection). VERIFIED LIVE 2026-08-29 in anonymized live verification: a rule created with `scopes[].id: \"stores-catalog\"` shows in the dashboard as created by a third-party integration with NO edit option at all -- confirmed by direct comparison via Get Discount Rule against a dashboard-created rule in the same anonymized verification environment, whose only structural difference was this id (`all_215238eb-22a5-4c36-9e7b-e7c08025e04e`) plus a populated `settings` object (see the settings-object pitfall below). This defeats any placeholder-rule strategy that relies on the merchant reconfiguring the rule afterward in the dashboard -- always use the real `all_`/`specific_`/`collections_` prefix, never an arbitrary caller-assigned id, even though the API accepts one."
169 "displayName": "eCommerce Order Applied Discount",
170 "classification": "native",
171 "reliabilityStatus": "reliable",
172 "reliabilityFlags": [],
173 "summary": "Applied discounts import as historical fact via the merchantDiscount variant — Wix never re-evaluates them as live discount rules, and Import Order stores all money values as-is without recalculating priceSummary. The mapper must therefore keep the source order's totals internally consistent itself; nothing on the Wix side reconciles appliedDiscounts against priceSummary.discount. VERIFIED LIVE 2026-08-16: an order carrying appliedDiscounts totalling 8.00 with priceSummary.total 22.00 on a 30.00 subtotal was stored exactly as sent — and a control order with the same appliedDiscounts but NO priceSummary.discount kept total == subtotal, proving Wix applies nothing."
177 "displayName": "eCommerce Order Invoice Document (preserved reference)",
178 "classification": "native",
179 "reliabilityStatus": "reliable",
180 "reliabilityFlags": [],
181 "summary": "Do NOT create, publish or re-issue a Wix invoice for a historical sale. Re-issuing mints a SECOND legal document for the same sale under a different number, and the original -- issued and numbered by whoever issued it -- remains the authoritative body. Separately, whether publishing an invoice emails the customer is undocumented in either direction, and on a multi-year store a wrong assumption emails thousands of customers an invoice for a sale they made years ago. The contract is NON-USE, provable by a route-level test rather than by a runtime probe: tests/target-wix/no-invoices-api-contract-test.js."
185 "displayName": "eCommerce Order Line Item Options (Description Lines)",
186 "classification": "native-plus-cms",
187 "reliabilityStatus": "reliable",
188 "reliabilityFlags": [],
189 "summary": "lineItems[].modifierGroups is READ-ONLY on import: it is absent from the Import Order writable request body and marked read-only (maxItems 10) on the returned Order object — addon values can never be recreated as structured modifiers on historical orders. descriptionLines[] is the import-time carrier. VERIFIED LIVE 2026-08-16: an imported line item carrying descriptionLines and catalogReference.options read back with modifierGroups: [] — the field stays empty even when the referenced catalog product genuinely has modifiers attached."
201 "displayName": "eCommerce Order Merchant Note (Activity)",
202 "classification": "native",
203 "reliabilityStatus": "reliable",
204 "reliabilityFlags": [],
205 "summary": "Order.activities[] is read-only on Create/Import Order — a merchant note can NEVER ride the order import payload. It must be a separate Add Activities call AFTER Import Order creates the order, using the crosswalked Wix order id."
213 "summary": "An order total exists on orders that were never paid. VERIFIED LIVE on a production WooCommerce store: `_order_total` is present on cancelled and failed orders, which almost never carry a paid date. Creating an APPROVED payment wherever a total exists would mint approved payments for unpaid orders, overstating revenue in a more authoritative-looking place than the bug it replaced. Require a paid date."
221 "summary": "Verify whether the selected order write shape preserves refund amounts, reasons, dates, and line references."
222 },
223 "order": {
224 "path": "domains/ecom/entities/order.json",
225 "displayName": "eCommerce Order",
226 "classification": "native-plus-cms",
227 "reliabilityStatus": "partially-reliable",
228 "reliabilityFlags": [],
229 "summary": "Never route historical orders through createOrder — it decrements inventory, emails the buyer, and auto-creates contacts; importOrder is the only import-safe writer."
237 "summary": "A source local_pickup method must NEVER be written as a ShippingOption. A ShippingOption has no app/carrier/role field, so a 0-amount collection option renders at checkout as a free DELIVERY sitting next to the real paid delivery -- buyers pick it expecting their order shipped for nothing. Observed live: collection rendered as a 0-priced shipping choice beside a paid one. The classification is terminal: kind:'pickup' produces a PickupLocation and no shipping option."
245 "summary": "DeliveryCarrier.backupRate does not create a ShippingOption and does not clear Wix's missing-rates dashboard warning. Live verification showed that a carrier-only region can write and read back successfully while remaining non-functional at checkout. Always create a matching ShippingOption for every imported DeliveryRegion from the same classified method used for DeliveryCarrier.backupRate; see shipping-build.js's buildShippingOptionInput."
255 "summary": "Verified against the authoritative internal proto: the fqdn is wix.platform.v1.wishlist (service com.wixpress.wishlist.Wishlist). Its three mutating RPCs (GetWishlist/AddToWishlist/RemoveFromWishlist) are ALPHA maturity, exposure: INTERNAL, and scoped to 'the default wishlist by context's member' -- the caller's own session, not an arbitrary member named by a backend script. The only RPCs open to a site-level manage permission (GetWishlistById, GetWishlists) are read-only. There is no proto-level path for a migration script to write wishlist items on behalf of pre-existing customers who have never authenticated to the new site."
256 }
257 }
258 },
259 "events": {
260 "path": "domains/events/domain.json",
261 "displayName": "Wix Events",
262 "ownerHint": "Events, registration, RSVP, and ticketing domain owners",
270 "summary": "Wix Events categories are flat; a hierarchical source event taxonomy (tribe_events_cat is hierarchical) flattens on import — faithfulness-ledger entry required."
271 },
272 "event": {
273 "path": "domains/events/entities/event.json",
274 "displayName": "Event",
275 "classification": "native",
276 "reliabilityStatus": "partially-reliable",
277 "reliabilityFlags": [],
278 "summary": "VERIFIED LIVE 2026-10-27: a past startDate is accepted on create, as a draft and as a published event (status ENDED, registration CLOSED_AUTOMATICALLY). Historical events import natively."
286 "summary": "The form is per-event configuration (an ordered list of controls), not standalone records: there is no create — controls are added/updated on the event's existing form. Historical submissions do not live here; they arrive as RSVP/order data."
287 },
288 "rsvp": {
289 "path": "domains/events/entities/rsvp.json",
290 "displayName": "Event RSVP",
291 "classification": "native",
292 "reliabilityStatus": "partially-reliable",
293 "reliabilityFlags": [],
294 "summary": "RSVP creation emails the guest a confirmation unless disableNotifications is set (the domain's registration-notifications-default-on pitfall). Setting disableNotifications:true is harmless; it is not required. Covered by site mute: when getSiteMuteState reads muted:true, this is not a reason to block, skip, delay or warn about the entity."
302 "summary": "A ticket definition is the purchasable ticket type (name, price, limits) — importing it does not carry sold tickets. Past ticket sales remain not importable (domain pitfall ticket-order-history-not-importable)."
303 }
304 }
305 },
306 "forms": {
307 "path": "domains/forms/domain.json",
308 "displayName": "Wix Forms",
309 "ownerHint": "Wix Forms (form schemas, form submissions) domain owners — the Forms APIs sit under the CRM docs tree, not Business Solutions",
317 "summary": "VERIFIED LIVE 2026-10-26: Create Submission has no notification control (only submission, additionalMetadata, captchaToken) and fires Submission Created, the trigger of the form's automations. With the site muted, an API-created entry STILL emailed the site owner: the per-form 'New submission received' automation is a transactional email that mute does not cover. With that automation and the preinstalled site-wide one set INACTIVE, no email was sent. Contact-facing notifications remain covered by site mute. The entries import pauses the form's automations first (pauseFormAutomations), best-effort: a failed pause never holds or delays the entries."
318 },
319 "form": {
320 "path": "domains/forms/entities/form.json",
321 "displayName": "Wix Form (form schema)",
322 "classification": "native",
323 "reliabilityStatus": "partially-reliable",
324 "reliabilityFlags": [],
325 "summary": "LIVE-VERIFIED 2026-09-16: the severe reading below is CONFIRMED, and it is worse than 'the field doesn't appear in the dashboard' — a formField built WITHOUT a top-level `identifier` creates successfully (200; the field shows up fine in the read-side `fields[]` projection) but Create Submission then rejects EVERY key in `submissions` as UNKNOWN_VALUE_ERROR ('must NOT have additional properties'), even against a form left untouched for 20+ minutes (ruling out eventual consistency as the cause — verified by retrying the identical call every 15s for 5 minutes with no change). The giveaway: the SAME create request, submitted with `identifier` set, is accepted by Create Submission within 1-2 seconds. Diffing a working pre-existing form's `formFields[]` (via Query Forms) against a broken one built without `identifier` is what surfaced this — `formFields` in the READ response is EMPTY on both a working and a broken form (a legacy/unpopulated projection on this API version; do not use its length as a success signal), so the only way to catch this pre-emptively is to always set `identifier` explicitly, never rely on it being optional. A generic field with no better semantic match still needs `identifier: 'TEXT_INPUT'` (matching its own componentType) — never omit it."
326 }
327 }
328 },
329 "gift-cards": {
330 "path": "domains/gift-cards/domain.json",
331 "displayName": "Wix Gift Cards",
332 "ownerHint": "Gift cards, stored value, and store credit domain owners",
340 "summary": "A site supports a MAXIMUM OF ONE gift card product (documented as a temporary limitation; second create → 409 GIFT_CARD_PRODUCT_ALREADY_EXISTS). A source shop with several gift-card products (different designs, different denomination sets, seasonal variants) CANNOT be migrated one-to-one: collapse them into a single product whose presetVariants union the denominations, and ledger the lost designs/descriptions as fidelity_loss. Check for an existing product before creating — on a non-empty destination the create will fail rather than merge."
348 "summary": "Omit `giftCard.notificationInfo` entirely on import. Including it emails the recipient — immediately when `notificationDate` is empty — so a historical import would re-mail every card holder. Covered by site mute: when getSiteMuteState reads muted:true, this is not a reason to block, skip, delay or warn about the entity. Omitting notificationInfo is still the simplest payload."
394 "summary": "This surface defines the profile field (name, fieldType, defaultPrivacy); per-member values are written on the member profile. Import definitions before members or the value writes have no field to land in."
412 "summary": "VERIFIED LIVE 2026-08-16: POST /members/v1/members rejects a `@wix.com` loginEmail with 400 INVALID_EMAIL_DOMAIN, although Contacts V5 accepts that domain. Never mint a synthetic member email on @wix.com. The reserved `.invalid` TLD was accepted by other surfaces in the same probe session but is NOT live-verified for Create Member: the first synthetic author or fallback member a run creates must record whether Members accepted it."
427 "summary": "Create Offline Order documents no member-notification behavior and has no suppress flag (request: planId, memberId, paid, startDate, couponCode, submissionId); the sibling Mark As Paid documents that offline payments 'trigger events and emails'. A 2026-08-16 probe on a muted site observed no email. Covered by site mute: when getSiteMuteState reads muted:true, this is not a reason to block, skip, delay or warn about the entity."
435 "summary": "plan.status is REQUIRED on create and is NOT listed among the docs' required parameters. VERIFIED LIVE 2026-08-16: omitting it 400s with REQUIRED_FIELD on plan.status; 'ACTIVE' is accepted. Any generated create payload built strictly from the documented required-field list will fail on the first call."
436 }
437 }
438 },
439 "sales-channels": {
440 "path": "domains/sales-channels/domain.json",
441 "displayName": "Wix Sales Channels",
442 "ownerHint": "Stores multichannel/marketplace integrations — the \"Sales Channels\" section of the Wix Stores dashboard (Catalog > Sales Channels)",
452 "summary": "Public support docs describe the original \"Amazon by Wix\" app as deprecated, with Wix recommending a third-party replacement. A newer internal service, wix.stores_channels.amazon.v1.amazon_passive_feed, implements an automatic AMAZON_XML feed push to an Amazon-owned S3 bucket. The current merchant onboarding path is not independently verified; re-verify whether the dashboard's Amazon entry uses this service or a third-party app before relying on the steps below."
463 "summary": "No FQDN matching \"ebay\" was found in the internal Business Schema registry, unlike the other channels in this domain. The mechanism below is sourced only from public Wix support documentation; treat it as lower confidence until an internal service is located or the current dashboard behavior is independently verified."
471 "summary": "Dependencies: stores/product must be migrated before the merchant connects Google & YouTube, so continuous catalog sync has products to publish."
479 "summary": "The connect flow below reproduces the sync OUTCOME, not the plugin's per-product configuration. Meta for WooCommerce's fb_visibility, _wc_facebook_product_image_source, _wc_facebook_google_product_category, _wc_facebook_gender, and _wc_facebook_color_attribute have no receiving field: wix.stores_channels.marketplace_overrides.v1.product_override (the live, GA per-product channel-override mechanism) implements only googleProductOverrides/youtubeProductOverrides in its oneof — no facebookProductOverrides variant exists (verified via docs-schema MCP fqdn_schema, 2026-08-16). Report these as accepted, reported loss."
487 "summary": "Unlike Meta/TikTok, Pinterest requires a site-ownership verification step (a Pinterest-issued HTML tag pasted into Wix Custom Code) BEFORE the catalog-link step. A runbook that skips straight to \"Get Catalog Link\" will fail — the merchant must verify the site first."
510 "summary": "An item is addressed by `{itemType}:{itemId}` where itemId is the WIX GUID of an already-created item (a page, a Blog post, a Stores product). SEO tags therefore import in a SECOND pass, after the owning entity has been written and its id recorded in the crosswalk. There is no create-by-source-id path here, which is why crosswalkRequired is true."
518 "summary": "POST /promote/seo/v1/seo-patterns/{pageType} fails with 409 PATTERN_ALREADY_EXISTS when the site already has its own pattern for that page type, and PATCH on the same path fails with 404 PATTERN_NOT_FOUND when it does not. `source` on the object says which is the case (the site's own pattern versus the Wix default). Read first, then branch; do not retry the other verb blindly."
526 "summary": "A `robots` tag with `noindex` or `none` at site level stops search engines indexing the WHOLE site. A WordPress source in staging almost always carries site-wide noindex (blog_public = 0; verified live on an anonymized verification environment 2026-08-16, where every yoast_head_json.robots reported index: noindex). Copying the source's site-level robots value across would silently de-index the new Wix site. Never carry noindex; treat indexability as a launch decision, not migrated data. setSiteSeoTags never writes robots (exclusion site-noindex-refused), its builder refuses a new robots tag, and a robots tag the site already has is sent back verbatim - leaving it out of the full-replacement PATCH would make the site indexable, which is equally a launch decision. Every write also emits the operator action site-indexing-launch-decision."
541 "summary": "`fields.paths[]` is destructive in one direction: a property NAMED in the mask but ABSENT from `businessContact` is CLEARED. Migrating only an address therefore means sending paths ['address'] and nothing more -- adding 'email','phone','fax' to be thorough wipes contact details the merchant may already have entered by hand. Build the mask from the payload's own keys. Wix's own documented example is itself unsafe to copy: it lists 'businessSchedule' in paths while sending none."
542 },
543 "locale": {
544 "path": "domains/site/entities/locale.json",
545 "displayName": "Site Locale (payment currency, time zone, language, locale)",
546 "classification": "setup-config",
547 "reliabilityStatus": "reliable",
548 "reliabilityFlags": [],
549 "summary": "Update Business Region stored fabricated currency and time-zone strings exactly as sent, and a three-letter language was also stored. Validate currency against ISO 4217, require an IANA-shaped time-zone name of at most 30 characters that Intl accepts, require a two-letter lowercase language and a language-region locale, then read every value back."
560 "displayName": "Back In Stock Notification Request",
561 "classification": "native",
562 "reliabilityStatus": "partially-reliable",
563 "reliabilityFlags": [],
564 "summary": "VERIFIED LIVE 2026-10-27: create answers 428 REQUEST_COLLECTION_DISABLED until Start Collecting Requests (POST .../settings/start-collecting { appId: Wix Stores }) has run; it is the setup step start-back-in-stock-collection, verified by Get Settings (a PUT with an empty body) showing collectingRequests true for the Stores app id. createBackInStockRequest rethrows the 428 with code BACK_IN_STOCK_COLLECTION_DISABLED: a setup failure, not a per-row retry. Collecting also shows the \"Notify When Available\" button on out-of-stock products."
565 },
566 "brand": {
567 "path": "domains/stores/entities/brand.json",
568 "displayName": "Stores Brand",
569 "classification": "native",
570 "reliabilityStatus": "reliable",
571 "reliabilityFlags": [],
572 "summary": "The Wix Brand object is {id, name, revision, dates} only; source brand logos and descriptions have no native home and are dropped unless preserved via the CMS fallback. Record the loss in the faithfulness ledger."
608 "summary": "Wix Stores has no bundle product type: Catalog V3 productType is PHYSICAL | DIGITAL only, with no composed-product entity, child-product reference or component-group field (product-object docs checked 2026-08-16). The bundle imports as a normal product with its own price and stock; the links to its parts are set up in a bundle app by the merchant."
616 "summary": "Attaching an existing customization to a product by id ALONE fails. VERIFIED LIVE 2026-08-16: product.modifiers[] = [{ id: <customization GUID>, mandatory: false }] → 400 REQUIRED_ONE_OF_FIELD 'freeTextSettings or choicesSettings must not be empty', and adding freeTextSettings without a title → 400 MIN_LENGTH on modifiers[0].modifierSettings.freeTextSettings.title. A ConnectedModifier must restate name, modifierRenderType, and the full settings block (title included) next to the id. Note the field renames between the two surfaces: the standalone customization uses freeTextInput/customizationRenderType, the product-side modifier uses freeTextSettings/modifierRenderType — a transform that reuses the create payload verbatim will fail."
632 "summary": "A Catalog V3 variant is created inside its product's write (product.variantsInfo.variants[]); there is no standalone variant import. Never map a source variation to its own import task: map it into the stores/product row, and let the product task produce the variant identities (createStoresProduct operationContracts.childOutputs)."
648 "summary": "CORRECTED 2026-08-16 (this entity previously claimed the opposite). A Wix Stores promotion is 'a visual banner displayed on store category pages, including their product gallery sections' — it is a MERCHANDISING IMAGE with a name, a required media asset, an optional destinationUrl, and category placements. It carries NO discount, NO percentage, NO scope, NO date window, and NO price effect of any kind; the create schema has no such fields and a body containing them is rejected. Discount-rule plugin campaigns therefore DO NOT map here — route them to stores/coupon, to native discount rules, or to per-product pricing on stores/product. Mapping a source discount campaign to this entity would produce a decorative banner and silently lose the discount."
656 "summary": "Every review needs a contact: Bulk Create requires reviews.author.contactId, and the Create Review introduction states the single-create method requires a contact ID too. Importing reviews therefore CREATES OR RESOLVES A CONTACT PER DISTINCT REVIEWER — a CRM side effect well beyond the catalog, and reviewers are frequently not customers. Resolve each reviewer by email through Query Contacts first, create only on miss, and disclose the contact count at the execution gate."
664 "summary": "Subscription expiry/length, initial signup price, and free trial have NO product.subscriptionDetails field. these are reported loss — ledger them as fidelity_loss entries in the migration report; they are NOT CMS-carried. Do not invent an extended-field or CMS home for them."
665 }
666 }
667 },
668 "tax": {
669 "path": "domains/tax/domain.json",
670 "displayName": "Wix Tax",
671 "ownerHint": "eCommerce tax domain owners (Tax Groups / Tax Regions / Manual Tax Mappings)",
687 "summary": "Create/Query/Delete Tax Group are all `/billing/v1/tax-groups...`, not `/ecom/v1/...` (unlike sibling Discount Rules) or `/stores/v2/...` (unlike Coupons)."
703 "summary": "There is exactly one TaxSettings object per site (Get returns defaults if never set; Upsert creates-or-updates the same singleton). Treat this as ordinary setup/config work, never a per-record write with a crosswalk."