Generated banner for Rp Target Wix
Generated logo for Rp Target Wix

Rp Target Wix

WixOfficialPublished by Wix

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Medium risk
  • Has scripts

From the author

Wix target adapter with verified write primitives, shared execution runtimes, and contract tests. Use when vendoring Wix writers, validating API shapes, or Wix provisioning mechanics.
— the description by Wix

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The rp-target-wix skill serves as the authoritative Wix-side write surface for migration projects. It centralizes verified request builders, shared execution logic, and contract testing to ensure consistency across different source platforms. By decoupling the Wix API plumbing from project-specific migration logic, it prevents redundant implementation and reduces the risk of breaking changes. The skill manages critical runtime behaviors such as retries, throttling, checkpointing, and audit logging, while providing a structured approach to domain entity suitability and dependency management.

What it does

  • Provides verified request builders and execution primitives for Wix API interactions.
  • Implements shared runtime logic for retries, throttling, and checkpointing.
  • Manages crosswalk helpers and URL preservation for data integrity.
  • Defines domain entity suitability and dependency wiring via structured domain files.
  • Includes contract tests to ensure stable request and runtime behavior.
  • Supports bulk operations and normalization for efficient data ingestion.

When to use it

  • When building or maintaining migration pipelines that target Wix as the destination platform.
  • When you need to perform verified writes to Wix entities like products, orders, or contacts.
  • When implementing shared migration infrastructure that requires consistent throttling and retry policies.
  • When validating API shapes or provisioning mechanics against live Wix environments.

Advantages

  • Reduces code duplication by centralizing Wix API plumbing and runtime logic.
  • Ensures high reliability through verified request builders and contract tests.
  • Simplifies the addition of new source platforms by maintaining a platform-agnostic write surface.
  • Provides robust error handling and rate-limiting strategies out of the box.
  • Facilitates auditability through standardized execution reports and logging.

Usage examples

“How do I ensure my migration handles Wix Stores product bulk creation correctly?”
The skill provides a bulk creation primitive that handles normalization and correlation. You should use the exported BULK_PRODUCT_LIMITS and storesProductBulkCost to batch records via the ndjson stream, ensuring you correlate results by itemMetadata.originalIndex rather than HTTP status.
“How should I handle Wix site notifications during a large import?”
Use the site notifications mute primitives to mute all site notifications during the import window. Ensure you pass a project-identifying reason string to the mute call for auditability and use the idempotent re-mute pattern during preflight checks.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/wix/rp-target-wix-nymy/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip rp-target-wix.zip -d .agents/skills

Documentation by Wix

rp-target-wix

Wix target adapter. Owns the Wix-side write surface and shared execution runtime that every migration shares — verified once here so the platform-agnostic stages and per-project codegen never re-derive (and re-break) it. This is the symmetric counterpart to rp-source-wordpress: that adapter owns reading a source platform; this one owns writing to Wix.

When this skill is used

Not a stage in the flow — a reference + shared library consulted by:

  • rp-import-codegen vendors lib/wix-writers.js into the project (like the wp-http transport) plus the shared write/setup runtime modules, and generates thin project-specific write specs/transforms that call these primitives/runtime APIs. Codegen does not re-emit Wix API plumbing.

  • rp-setup-discovery / rp-execute-setup consult the "Verified endpoints" and "Provisioning" notes for app-install / Wix-Data / collection mechanics.

Because the Wix surface is identical across source platforms, adding a new source (rp-source-shopify, …) requires no change here.

Shared runtime boundary

This adapter owns the shared Wix layer written once for all migrations.

It should own:

  • verified request builders and endpoint primitives

  • shared Wix client/auth handling

  • shared retry and throttling policy

  • generic write execution runtime

  • generic setup execution/runtime behavior

  • checkpoint helpers

  • local crosswalk helpers (state/crosswalk/crosswalk.ndjson authority, append-only attempt journal, optional CMS mirror seed/upload adapters)

  • URL preservation helpers (state/url-preservation/ base paths, ledger, planned redirects, and unresolved route rows)

  • NDJSON record-stream I/O (lib/ndjson.js): streaming read/write, batching by count and by cost, cursoring, and append-only producers. Migration data files are record streams, so the scan/batch/cursor primitives live here once instead of being re-derived by codegen.

  • audit log emission

  • compact execution report generation

  • contract tests for stable request/runtime behavior

Per-migration code generation should own only:

  • field maps

  • transforms

  • write specs

  • setup-plan renderings

  • entity ordering

  • dependency wiring — populate WriteSpec.dependencyRefs from each in-scope entity's domain-knowledge dependsOn field (spec 0041), not re-derived per project

Write contract (the verified Wix surface)

lib/wix-writers.js exposes pure request builders (build*Request) + executors. Shapes marked VERIFIED were validated by a real call against a live site, not just read from docs. Shapes marked UNVERIFIED are docs-schema/MCP-derived bootstrap primitives that must be surfaced in execution plans until a live contract call promotes them. It also exports sendDirectRest and notifyMissingWriter for generated native REST paths when Wix has a native entity but this adapter does not yet ship a dedicated primitive.

This resource should also expose shared execution-runtime modules above those primitives, so generated migrations use one common path for retries, throttling, checkpointing, reporting, and audit logging.

Read/return contract for query* executors. Every query* executor (queryStoresProducts, queryStoresCategories, queryContacts, queryCoupons, queryOrders) unwraps the response to the entity array and returns one page, discarding pagingMetadata. Two failure modes follow, both of which have shipped:

  • The return value IS the array. Reading .products / .categories off it a second time gives undefined → []. A dedupe index or existing-entity safety net then comes back empty without erroring, which is indistinguishable from a fresh site — so the import duplicates what is already there, and a setup verification reports 0 categories on a site with 25.

  • These executors cannot be cursor-paged, because the cursor is in the metadata they discarded. Use a queryAll* primitive where one exists — queryAllStoresCategories, queryAllStoresProducts, queryAllDataItems — otherwise send wix.send(build<X>Request(body)) and read pagingMetadata.cursors.next off the raw response.

Every sweep used for dedupe must throw on a partial result rather than return it. "Empty net" and "empty store" look identical to the caller, and the caller assumes the second.

Domain entity suitability knowledge

Target entity suitability lives in domains/, not in a large prose table in this skill. Use the deterministic reader as the normal access path:

node scripts/domain-knowledge.js list-domains
node scripts/domain-knowledge.js list-entities --domain stores
node scripts/domain-knowledge.js read-entity --ref stores/product
node scripts/domain-knowledge.js resolve-source --source-system woocommerce --source-entity product
node scripts/domain-knowledge.js resolve-source --route /wc/v3/products
node scripts/domain-knowledge.js list-flagged --flag IMPORT_UNRELIABLE
node scripts/domain-knowledge.js summarize-entities --refs stores/product,ecom/order

domains/index.json is generated from domain/entity files and checked in. After editing domain knowledge, run:

node scripts/domain-knowledge-validate.js --write-index

Authoring or editing any entity file also requires the spec 0041 dependency check, both directions: (1) what does this entity need to already exist and be crosswalked — record it in dependsOn: string[] (a domain/entity ref array; empty is a real, reviewed answer, not the same as the field being absent); (2) does this entity change what an already-written entity's dependsOn should say (e.g. authoring gift-cards/gift-card should have prompted revisiting ecom/order.json — it hadn't, until spec 0042 caught it by hand). domain-knowledge-validate.js checks dependsOn refs resolve and the whole graph stays acyclic whenever the field is present, but it is not yet required on every entity — node scripts/domain-knowledge-validate.js --list-missing-deps prints the live backlog of entities that have never had it authored at all; that command is the source of truth, not a hand-maintained list.

writerId values in entity files must match exported functions from lib/wix-writers.js; use null for direct REST plans, setup/manual work, or unsupported native gaps.

Entity files may also expose fieldContracts[] for verified nested write paths. These are machine-readable contracts for codegen and dry-run validators, not replacements for the human notes below. For Stores products, domains/stores/entities/product.json records the verified Catalog V3 create contract for product.subscriptionDetails, including required nested paths, allowed recurrence values, read-back behavior, and the live-validated subscriptions[].description <= 60 constraint. Generated transforms should consume that metadata through the vendored wix-writers.js exports instead of copying limits into project-local code.

Live verification helpers must not edit domains/ directly. When a probe changes a target write assumption, emit a proposal artifact and let the orchestrator promote it deliberately:

node skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js stores subscription-create \
  --artifact migrations/<project>/setup/stores-subscription-verification.json \
  --proposal-artifact migrations/<project>/setup/contract-ledger-proposal.json

Use lib/contract-ledger.js to validate the handoff: a passed verification without a proposal is incomplete, and a proposal that is neither promoted into fieldContracts[] nor explicitly deferred is stale product knowledge. Codegen should consume fieldContracts[] through scripts/domain-knowledge.js summarize-entities, which includes promoted contracts.

CapabilityEndpointNotes / traps
HTML → rich contentPOST /ricos/v1/ricos-document/convert/to-ricosVERIFIED. options.plugins enum is UPPERCASE — the docs example shows lowercase and 400s.
Import media from URLPOST /site-media/v1/files/importVERIFIED. Async: response is PENDING; poll GET /site-media/v1/files/{id} for READY before referencing.
Blog categoryPOST /blog/v3/categoriesVERIFIED. body { category: { label, slug, description } }.
Blog tagPOST /blog/v3/tagsVERIFIED. Body is top-level { label, language } — NOT { tag: { label, slug } }; slug is derived.
Blog postPOST /blog/v3/draft-posts → …/{id}/publishVERIFIED. memberId required (3rd-party). VERIFIED (2026-08-02): the site owner's auto-created user-member satisfies it — it was already present on our API-provisioned test site with zero Members-area interaction (single-site observation), so attribute-to-owner needs no member provisioning. Resolve it via listMembers + loginEmail match — never derive it from the account/user GUID (the observed id equality is n=1 on a solo account, undocumented). Also VERIFIED (2026-08-02): author is re-assignable after publish — PATCH /blog/v3/draft-posts/{id} { draftPost: { memberId } } then republish updates the published post's author (post id == draft id; republish fires non-suppressed events — do author-upgrade passes inside the notification-mute window). Visible custom cover media requires both heroImage.id and media: { displayed:true, custom:true, wixMedia:{ image:{ id }}}. heroImage.id alone leaves the cover hidden.
Blog posts (bulk)POST /blog/v3/bulk/draft-posts/createUNVERIFIED bootstrap (public docs + wix/skills wix-manage recipe, 2026-07-21). Max 20 posts/call; bulkCreateDraftPosts chunks larger inputs. Prefer over per-post creates for N ≥ 2 (single create runs ~25–30s/call per the recipe). Publish is still per-post — a bulk publish flag is unverified. Surface in the execution plan until the live contract call promotes it.
CMS itemPOST /wix-data/v2/itemsVERIFIED. { dataCollectionId, dataItem: { data } }. Requires Wix Data enabled (else WDE0110).
MembersGET/POST /members/v1/membersVERIFIED. dedup by loginEmail (gated PII; use a fallback member when absent). VERIFIED (2026-08-02, single-site observation): the target-site member list can already contain auto-created user-members for the owner/contributing Wix users (status: APPROVED; resolve by loginEmail, never by deriving from the account GUID — the observed id equality is undocumented) — never dedupe or count these against source-site members; DELETE /members/v1/members/{id} works for cleaning up importer-created members. Throughput: no bulk create; documented ≥1s spacing (10k ≈ 3h serial floor — plan + resume via crosswalk). Create sends no email and does not fire the signup automations trigger (source-confirmed) — silent by default. Activation policy (decided 2026-08-03): passwords are never imported, and passwordless members complete the standard forgot-password flow (confirmed). Delivery = post-import label-wave automation: owner creates one dashboard automation (trigger label added, e.g. migrated-2026 → owner-branded email: "Log in → Forgot password"); importer applies the label via API in batches for rate control/re-targeting; enable only after the import window (AUTO-02 pause). Never mass-send send-set-password-email (3h single-use link), and never fetch/store per-member reset links — they are credential-equivalent secrets and the no-link flow needs none.
Stores product (BULK — the scale path)POST /stores/v3/bulk/products-with-inventory/createUNVERIFIED bootstrap. Use this, not per-product creates, for any catalog above a handful — 1000 products is 11 calls instead of 1000. Creates up to 100 products with inline options, inline brand/ribbon/infoSections, all variants AND per-variant inventory items in one request. Five caps apply simultaneously and exceeding ANY ONE rejects the whole request: products ≤100, variantsInfo.variants ≤1000 total, options ≤100 total, modifiers ≤100 total, infoSections ≤100 total. With 2 options per product the options cap binds at 50 products, not 100 — so batch with lib/ndjson.js → readBatchesByLimits using the exported BULK_PRODUCT_LIMITS + storesProductBulkCost, never on record count alone. TRAP: bulk is NOT atomic — a 200 can carry per-item failures in results[].itemMetadata.success; never infer success from the HTTP status. TRAP: correlate results by itemMetadata.originalIndex, not response position. TRAP: bulkActionMetadata.undetailedFailures counts failures whose detail the server dropped; ignoring it silently loses records. returnEntity:false (default) still returns itemMetadata.id, which is all a crosswalk needs — pass true only for a contract probe that must inspect the created entity. bulkCreateStoresProductsWithInventory handles normalization, correlation and the unaccounted-item check, returning { results, succeeded, failed, undetailedFailures, unaccounted }. Normalization is purely local — HTML descriptions travel as plainDescription for Wix to convert server-side — so a batch is one HTTP call. It used to convert one description per product first, which made a 100-product batch 101 calls and reliably tripped the Ricos endpoint's 403 throttle.
Stores productPOST /stores/v3/products, POST /stores/v3/products/query, PATCH /stores/v3/products/{id}, GET /stores/v3/products/{id}, GET /stores/v3/products/slug/{slug}VERIFIED (2026-07-05; media patch verified in migration-20260715-01). buildCreateStoresProductRequest runs normalizeStoresProductV3, which bakes in the create traps: PHYSICAL requires physicalProperties:{}; simple/V1-ish top-level price, sku, and physical properties are moved into a single V3 variant; option/variant choice names clamped to 50 (identical truncation both places); variant optionChoiceNames.renderType defaulted to TEXT_CHOICES; compareAtPrice dropped unless strictly > actualPrice; variant price must be a money object ({ actualPrice: { amount: "14.95" } }) — a bare string/number 400s "Expected an object" (verified 2026-07-19), so the normalizer coerces scalar prices. Descriptions: a string description is HTML and is moved to plainDescription by normalizeStoresProductV3; Wix converts that to rich content server-side, so the product path never calls the Ricos endpoint (description proper is a Ricos document object — HTML there is a type error). TRAP: plainDescription is silently ignored when description is also set — a 200 with an empty description — so the normalizer throws when both are present. TRAP: hard cap of 16,000 characters (the old Ricos path chunked at 28k and merged, so it was effectively unbounded); over-long bodies throw rather than truncate, and need an info section or an explicit mapping-gaps.json entry. For product media, prefer media.itemsInfo.items[] with external url values when the source URLs are publicly reachable: the Stores product API ingests them in the background, avoiding the slower, heavily-throttled Media Manager pre-import path. Use pre-imported Wix media ids only when the target flow requires them. Upsert lookups: getStoresProduct(wix, id) and getStoresProductBySlug(wix, slug) return the product or throw 404 — callers should catch and treat as null for "not found". Catalog sweeps: queryStoresProducts returns one page unwrapped to the array with the cursor discarded, so a crosswalk-recovery or name-match safety net must use queryAllStoresProducts(wix), which follows the cursor and throws rather than returning a partial index. Slugs: sanitizing is owned by lib/wix-build.js (toWixSlug, applied automatically via the coerce: 'slug' rule on product.slug), not by this module and deliberately not by normalizeStoresProductV3 — the writer passes the caller's slug through unchanged so URL preservation can record the original next to the derived plannedTargetSlug. It matters because Wix rejects anything outside [a-z0-9-] and slug validation precedes application, so one bad slug (Shopify mints ph-5_5 from a "pH 5.5" title) 400s the entire bulk batch rather than just that product. General patch: patchStoresProduct(wix, { productId, revision, ...fields }) applies normalizeStoresProductV3, so a string description moves to plainDescription here too; revision from the existing product is required. UNVERIFIED for get-by-id, get-by-slug, and general-field patch (endpoint VERIFIED; arbitrary-field patching and slug lookup not yet live-tested). Variable products / variant matrices: Wix Stores V3 requires server-assigned optionChoiceId GUIDs before a variant matrix can be built — those IDs do not exist until after the initial createStoresProduct call returns. A single-pass import therefore cannot create a full variant matrix in one call. The two-pass approach: (1) create the product with options declared (but no variants), (2) create each option choice via POST /stores/v3/products/{id}/options/{optionId}/choices to obtain server-assigned choice IDs, (3) create variants via POST /stores/v3/products/{id}/variants. When the two-pass flow is out of scope (e.g. public-data single-pass run), import as a single-variant product at the base price, record the fidelity loss in mapping-gaps.json, surface it in the execution-plan report, and ensure the generated product detail page renders productOptions when non-empty so the storefront is ready once options are added.
Stores categoryPOST /categories/v1/categories, POST /categories/v1/categories/queryVERIFIED (2026-07-05) for create. Body { category, treeReference } — treeReference is top-level (sibling of category, {appNamespace:"@wix/stores"}); nesting it 400s "treeReference must not be empty". Empty optional strings such as description: "" are omitted. Query: same top-level treeReference trap applies. queryStoresCategories(wix) returns one page (100 max) unwrapped to the array — it does not return all categories, and the unwrap discards the cursor so it cannot be paged. Any dedupe/upsert index must use queryAllStoresCategories(wix), which follows pagingMetadata.cursors.next and throws rather than returning a partial index. Observed 2026-07-29: a site with 119 categories read back as 100 through the single-page call, which would have duplicated the missing 19. UNVERIFIED for query (not yet live-tested).
Wix Tag (global)POST /tags/v1/tagsUNVERIFIED bootstrap. Body { tag: { name, fqdn } }. FQDN for Stores V3 products: "wix.stores.catalog.v3.product". Max 100 tags per FQDN. Create tags before products; crosswalk tag IDs into product tags.publicTags.tagIds[] at create time. Tags are a native Wix entity — do not route product tags to CMS.
Stores inventory itemPOST /stores/v3/inventory-itemsUNVERIFIED bootstrap. { inventoryItem: { variantId, productId, inStock: true } } marks a variant as in-stock at the default location (omit locationId). Inventory items are not auto-created on product create — call this for each variant after createStoresProduct. For quantity-tracked stock use { quantity, trackQuantity: true } instead of inStock. setProductVariantsInStock(wix, { productId, variantIds }) is a convenience wrapper that calls createInventoryItem for each variant. Requires permission scope SCOPE.STORES.INVENTORY_ITEM_WRITE.
Stores category membershipPOST /categories/v1/bulk/categories/add-itemVERIFIED (2026-07-05). { item:{ catalogItemId, appId }, categoryIds[], treeReference }; appId = Wix Stores 215238eb-…, catalogItemId = Wix product id.
ContactsPOST /contacts/v5/contacts, POST /contacts/v5/bulk/contacts/upsert, POST /contacts/v5/contacts/query, GET /contacts/v5/contacts/{id}, PATCH /contacts/v5/contacts/{id}GA (docs verified 2026-08-04 — was Developer Preview when selected on 2026-07-26). Do not fall back to V4 unless a migration explicitly chooses a legacy compatibility path. The GA contact shape is FLAT — no info wrapper, no emails.items/phones.items list wrappers: one main email/phone (contact matching + subscription status live on these) plus additionalEmails/additionalPhones arrays; addresses[] keeps postal fields nested under address; company is { name, jobTitle }. Create and update both take { contact, allowDuplicates }; update requires the current revision and has no fieldMask. Phone tags: OTHER/MAIN/HOME/MOBILE/WORK/FAX (no UNTAGGED). buildCreateContactRequest/buildUpdateContactRequest accept the flat GA contact (legacy info payloads convert strictly — non-mechanical keys like extendedFields/labelKeys throw). Live create/query/update verification is still pending a token with Contacts permissions (2026-07-26 probe got 403) — keep V5 writers surfaced as UNVERIFIED in execution reports until a live contract test promotes them. Custom fields (GA): the V5 contact carries extendedFields.namespaces.<ns>; per the V5 contact-object docs, definitions go through the Data Extension Schema API with FQDN wix.contacts.*.contact (user-defined values under _user_fields). The V4 Contacts Extended Fields API (POST /contacts/v4/extended-fields, values under info.extendedFields) pairs with the V4 surface only — do not mix. CAVEAT: the Data Extension Schema intro's supported-objects table does not list contacts yet (docs inconsistency at GA cutover); verify the DES path live during setup before relying on it. Labels → tags: V5 exposes tags.privateTags.tagIds[] managed via the Tags API (same FQDN); V4 label APIs and the labels_added automation trigger are a V4-surface concept — resolve the tag/label story during setup before planning post-import label waves through V5 writers.
CouponsPOST /stores/v2/coupons, POST /stores/v2/coupons/queryUNVERIFIED bootstrap. Prefer native Wix coupons because Wix has a native coupon entity; do not special-case coupons into CMS merely for caution or because scoping must be mapped. Fallback is only for source semantics with no native representation.
eCom orderPOST /ecom/v1/orders/import (historical orders), POST /ecom/v1/orders/query, POST /ecom/v1/orders/{orderId}/activities/addVERIFIED LIVE (2026-08-12 reference-store run; re-verified 2026-08-16 with applied discounts, line-item description lines and merchant notes). Import Order is the only import-safe path — see ecom/order.json. POST /ecom/v1/orders (createOrder) remains BLOCKED for migration: it decrements inventory, emails the buyer and auto-creates contacts. TRAP: order.number must be numeric — a prefixed source number fails the whole call with a bare 400 {"message":"Not a numeric value"} and no field path.
Site notifications mutePOST /notification-preferences/v1/site-mute/mute, POST /notification-preferences/v1/site-mute/unmute, GET /notification-preferences/v1/site-muteVERIFIED (2026-08-04, full cycle live on a test target: mute → state read → idempotent re-mute → unmute → state restored, all 200). Spec 0012. Mutes all notifications of the site in context — all recipients, all channels; sendability denied regardless of recipient-level preferences. All three calls return { siteMuteState: { muted, reason?, mutedBy: { wixUserId } } }; executors unwrap to siteMuteState. Mute body { reason?: string } (≤500, clamped by the builder) — always pass a project-identifying reason (RePlatform migration — <project>) so the mute is auditable. TRAP: re-mute overwrites reason (last caller wins) — the import preflight's idempotent re-call must pass the same reason as setup. AUTH TRAP: user tokens only — the CLI-minted OauthNG site token (WIX_AUTH_TOKEN from config/wix.env) works; an account API key gets a uniform empty-body 403 on all three endpoints. Hard invariant: when mute is in effect (always for WIX_SITE_STRATEGY=new; explicit opt-in for existing sites), a failed mute halts the run before any import write — no degraded mode. unmuteSiteNotifications is never called by the flow itself — explicit owner request only; after an on-request unmute, confirm muted: false via getSiteMuteState.
Direct native RESTany Wix REST path derived by codegenUNVERIFIED generated path for native Wix entities missing a dedicated adapter writer. Must log, call notifyMissingWriter, and be shown in the execution plan.

Safe-mode contact replacement

lib/wix-writers.js exports the shared safe-mode runtime:

createSafeModeConfig(env)
mockEmailForEntity(entityType, entityId)
sanitizeContactFieldsForSafeMode(value, options)
sanitizeWixRequestBody(body, options)

Generated imports must pass safeModeOptions to writer builders/executors when SAFE_MODE is enabled. Request builders sanitize a copied request body before returning it and throw SafeModeBlockedError before any send when suspicious non-replaced email values remain.

Safe-mode replacement paths use deterministic request-body paths with object fields, array wildcards, and Wix wrapper arrays, for example:

contact.email.email
contact.additionalEmails[].email
contact.phone.phone
order.billingInfo.email
dataItem.data.submissions[].email

Replacement only touches primitive leaf values — a generic path that lands on an object (such as the GA Contacts V5 email object) is skipped rather than clobbered, and any raw email left inside a skipped object still trips the suspicious-value block.

Target domain entity files may define safeModeContactFields[] with { kind, targetPath, source, notes } entries. Mapper/codegen must merge those target hints with source-side contact evidence and user mappings, then pass resolved request-body paths to the shared writers.

Stores subscription validation

lib/wix-writers.js exports the Stores subscription contract and deterministic helpers:

  • STORES_SUBSCRIPTION_CONTRACT

  • normalizeStoresProductSubscriptions

  • validateStoresProductSubscriptionDetails

Generated import code should call the normalizer from product transforms and the validator during dry-run and immediately before live create. Validation failures are record-level preflight failures/deferred outcomes; do not send known-invalid subscription payloads to Wix to discover field errors live.

Catalog V3 subscriptions are a native Stores target, not an automatic CMS fallback or semantic loss. Product create with subscriptionDetails.allowOneTimePurchases and subscriptions[] entries containing title, description, frequency, interval, and autoRenewal was verified live on 2026-07-26 in the nopong migration; Wix returned server-assigned subscription option ids. Mapping/codegen should emit native recurring product creates when the source cadence is known. General subscription patching remains unverified.

Stores live verification helpers

Use the shared Stores verification CLI for live target checks that would otherwise become migration-local snippets:

node skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js stores subscription-create \
  --artifact migrations/<project>/setup/stores-subscription-verification.json \
  --proposal-artifact migrations/<project>/setup/contract-ledger-proposal.json
node skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js stores product-count \
  --artifact migrations/<project>/execution/stores-product-count-verification.json
node skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js stores product-by-source-marker \
  --marker-path customFields.sourceId --marker-value <source-id> \
  --artifact migrations/<project>/execution/stores-product-marker-verification.json
node skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js stores delete-probe \
  --product-id <probe-product-id> \
  --artifact migrations/<project>/setup/stores-probe-cleanup.json

The CLI reads WIX_AUTH_TOKEN and WIX_SITE_ID from the environment or config/wix.env under the current project root, creates the shared Wix client, and sends requests through wix.send(...). It writes machine-readable artifacts with the target site id, endpoint, method, verified nested paths, discovered constraints, probe id, cleanup status, warnings, recovery instructions, and timestamp. Failed probe cleanup is not hidden: the artifact must remain with an explicit warning and a delete-probe recovery command.

Routed URL preservation behavior

URL preservation is a mapping and execution-state concern. This adapter owns only the Wix-side facts that generated code may rely on when deciding whether a final target URL is known.

Current import-only phase rules:

  • Do not configure Wix redirects or site routing.

  • Do not guess a destination relative URL from a slug when Wix route behavior has not been verified for that entity and delivery mode.

  • When the destination base path or final URL is unknown, write URL ledger rows with urlStatus: "pending_target_route" or target_url_missing and record unresolved rows for the future website-builder phase.

  • When a Wix create/update response or a safe declared lookup returns a final slug or relative path, generated code may record it as actualTargetSlug or actualTargetRelativeUrl and write a redirect plan if it differs from the source URL.

Known routed entity status:

EntityURL behavior contract
Blog postsCreate/publish is verified for content writes. Final public route exposure after publish is not verified here; generated code must treat the target route as pending_target_route unless a safe lookup in the generated write spec proves the final relative URL.
Blog categoriesCategory create is verified. Final public category route exposure is not verified here; default to pending_target_route.
Stores productsProduct create and slug-bearing product operations are verified, but arbitrary route/base-path configuration is not part of this phase. Generated code may record actual target slugs only when returned or safely looked up; target relative URLs remain pending_target_route unless the route pattern is explicitly verified in the project.
Stores categories/collectionsCategory create is verified. Final collection/category route exposure is not verified here; default to pending_target_route.
CMS items with dynamic pagesCMS item writes are verified. Dynamic page route patterns are site-builder configuration, not native item import output; default to pending_target_route unless website-builder artifacts define the route.

Shared execution runtime contract

In addition to endpoint primitives, this adapter should define the reusable runtime API that generated migrations call.

Conceptual contracts:

executeWriteSpec({
  spec,
  items,
  client,
  dryRun,
  runContext,
}): Promise<WriteExecutionReport>

executeSetupPlan({
  plan,
  client,
  dryRun,
  runContext,
}): Promise<SetupExecutionReport>

The exact filenames are implementation details, but the shared runtime should be the single place that owns generic Wix execution mechanics.

WriteSpec contract

Generated migrations should pass a declarative WriteSpec into the shared runtime.

Each spec should define, as applicable:

  • entity

  • mode: create | update | upsert | bulk_create | bulk_upsert

  • create

  • update

  • lookup

  • bulk

  • batchSize

  • concurrency

  • retryPolicy

  • throttlePolicy

  • auditKeys

  • crosswalkAuthority: "local"

  • cmsMirror

  • crosswalkStrategy

  • reconciliationStrategy

  • dependencyRefs

  • verificationLevel: verified | unverified

This resource should document how each field is interpreted by the runtime.

WriteExecutionReport contract

The shared runtime should return a compact, token-aware report suitable for the agent.

It should include:

  • run ID

  • entity

  • counts: input, attempted, created, updated, skipped, failed, retried, throttled

  • grouped error buckets

  • references to fuller on-disk logs

The runtime should prefer grouped summaries over raw per-record detail.

These per-entity or per-phase reports are intermediate runtime outputs. They are inputs to the final completion artifact, not replacements for it.

Completion artifact contract

The shared runtime should support deterministic emission of a final machine-readable completion artifact for the whole run.

Preferred output:

  • execution/completion-report.json

This artifact should be the authoritative source for:

  • final run status

  • final imported/updated/skipped/failed counts

  • separate dry-run counts when dryRun: true: requests built, Wix calls skipped, would-create/update/delete, would require live lookup, and local validation failures

  • grouped skipped/deferred outcomes

  • grouped failure outcomes

  • dashboard/editor/preview destinations

  • artifact references for audit/error/execution logs

The shared runtime should also support deterministic rendering inputs for:

  • execution/review/completion-summary.md

The rendered summary should be derived from completion-report.json, not independently invented by the agent.

Audit log contract

The shared runtime should emit append-only machine-readable audit events.

Preferred format: NDJSON.

Each event should capture, at minimum:

  • timestamp

  • run ID

  • phase: setup | import

  • entity

  • operation

  • source ID when applicable

  • target ID when known

  • endpoint or primitive used

  • attempt number

  • result status

  • HTTP status or platform error code when applicable

  • latency

The run ID is required because audit files are append-only across dry-runs, retries, and recovery passes. Completion reports should be generated from runtime counters for the current run and may use audit aggregation only when filtered by run ID. The shared lib/audit-summary.js helper enforces this filter for fallback/verification summaries.

Dry-run runtime contract

The shared Wix runtime exports createDryRunConfig, normalizeDryRunValue, createWixClient, and createWixSetupExecutor.

Dry-run is enabled by DRY_RUN=true or by the generated entrypoint's --dry-run flag. It is disabled by default. true, 1, yes, and on enable it; false, 0, no, and off disable it.

When createWixClient({ dryRun: true }) is used:

  • authToken and siteId may be blank unless the generated project requires the site ID for a local artifact namespace;

  • send() must not call fetch, SDK, MCP, or CLI transports;

  • send() must capture the would-send request under state/attempts/wix-request-captures.ndjson when projectDir or requestCapturePath is supplied;

  • captured headers must omit Authorization and redact secret-like header/body keys;

  • captured bodies must be after SAFE_MODE contact-value sanitization;

  • response payloads must preserve the live shape expected by writer helpers, for example { contact }, { product }, { dataItem }, { file }, or an expected collection field such as { contacts: [] } for skipped queries.

Dry-run placeholder target IDs are not live authority. If downstream local execution needs them, write them only to state/crosswalk/dry-run-crosswalk.ndjson or keep them in memory. Never append simulated target IDs to state/crosswalk/crosswalk.ndjson and never upload them to a CMS crosswalk mirror.

When createWixSetupExecutor({ dryRun: true }) is used, each setup step must first be reduced to a structured intent, then captured as planned_dry_run without invoking MCP, CLI, SDK, or REST transports that access or mutate Wix account/site state.

Artifact authority at run completion

The shared runtime contract should preserve explicit artifact authority:

  1. execution-log.md for chronology and operator/debug context only

  2. completion-report.json for final outcome

  3. completion-summary.md for deterministic user-facing rendering

The runtime should not force downstream consumers to reconstruct final outcome by reading raw audit logs or chronological execution logs.

Retry and throttling policy

This adapter should define one shared retry/throttling contract rather than letting each migration invent its own.

It should classify, at minimum:

  • retryable transport/network failures

  • retryable 429 responses

  • retryable 5xx responses

  • non-retryable 4xx validation failures unless explicitly listed otherwise

When the server provides backoff hints such as Retry-After, the shared runtime should honor them.

Bulk and fallback policy

The shared runtime should know:

  • when a native bulk primitive exists and is preferred

  • when to fall back from bulk to per-record writes

  • when an upsert requires lookup + create/update orchestration

  • when an unverified native path must be surfaced before execution rather than used silently

Media policy by target

Do not treat all media the same. The target domain decides whether media must exist in Wix first or can be ingested through the entity API itself.

  • Wix Stores products: prefer external URLs on media.itemsInfo.items[] and let the product API ingest them in the background. This is the default path when the URLs are publicly reachable.

  • Wix Blog cover images and other surfaces that require a Wix media id at create/update time: import media first, then pass the Wix media id into the entity payload.

  • Generic Media Manager import is a fallback/shared primitive, not the default for every entity carrying media.

Media import source URL reachability

The media primitive imports by URL: Wix servers fetch the provided sourceUrl. Public HTTPS URLs are expected; localhost, 127.0.0.1, Docker-only hosts, and other private-only URLs are not reachable by Wix during a live import. This is optional source preparation and, as far as we know today, affects media import only.

If the source system is local, the migration should either:

  • expose the source through a public HTTPS tunnel, then pass/rewire media URLs to that public base URL; or

  • skip/defer media import and clearly state which media-dependent references will be missing until media is imported.

Ngrok quick setup for macOS:

brew install ngrok
ngrok config add-authtoken "<YOUR_AUTHTOKEN>"
ngrok http 8090
export WP_BASE_URL=https://<id>.ngrok-free.app

Validate by real call — do not trust doc examples

Codegen-time MCP doc checks confirm an endpoint exists; they do not confirm the request shape works. The live wporg-news import proved doc examples can be wrong (lowercase Ricos plugins → 400) or incomplete (featured image field, tag body). The rule for this adapter:

  • Treat a shape as verified only after a real call succeeds — encode the working shape here with a // VERIFIED: (or // VERIFIED-TRAP:) note and a date.

  • A // UNVERIFIED: primitive is allowed as a bootstrap point for generated code, but it is not a silent live-write permission. The execution plan must call it out, and setup verification must either promote it with a sandbox/live validation or route to fallback.

  • Keep tests/target-wix/contract-test.js current: it issues one real call per primitive against a sandbox site and is the single place schema drift surfaces. Tests live in the repo's tests/ tree rather than in this bundle, so the published product carries no test harnesses. Run from the repo root:

    bash tests/run-all.sh target-wix
    WIX_AUTH_TOKEN=... WIX_SITE_ID=... bash tests/run-all.sh target-wix

    Without credentials the shape checks still run and the live calls are skipped. tests/target-wix/ndjson-contract-test.js covers lib/ndjson.js and never needs credentials.

    For live verification runs, pass --progress-log <path> and poll it per CONVENTIONS.md#progress-log-polling.

    Run on a cadence and after any Wix API change. A failing contract test — not a stranger's broken import — is how we learn the surface moved.

Stores catalog is Catalog V3 only — Catalog V1 is not supported

A migration only ever writes to a V3_CATALOG site. Catalog V1 is not a supported destination: there is no V1 write primitive here, no V1 fallback, and none should be added (a former V1 fallback was removed — it only masked real V3 errors as spurious 428s). A V1 site is a blocker the run halts on, not a variant the adapter, the mapper, or codegen accommodates. The V3 create traps are handled in lib/wix-writers.js (normalizeStoresProductV3 + the top-level treeReference builder), so callers never re-hit them.

On a site this run created, a Stores install is guaranteed Catalog V3. This section used to require verifying that live, after a 2026-07-30 incident where a site scaffolded from the blank headless template, with Stores installed afterwards through the App Installation API, came up V1_CATALOG — Catalog version is fixed at provisioning, so every V3 write on that site failed and the site had to be abandoned. The Wix platform has since changed: Catalog V3 is now guaranteed at provisioning regardless of scaffold template, so that failure mode can no longer occur for a newly-created site, and neither the --site-template commerce requirement nor the pre-write version gate apply anymore — see 0079-catalog-v3-guaranteed-retire-v1-gate.md.

This does not extend to an existing site this run did not create — rp-execute-setup ("A V1_CATALOG verdict is terminal here") still treats that case as a live risk and halts on it, since a pre-existing site's catalog version predates this guarantee.

What stays in codegen (not here)

Per-project field maps and ordering (which source field → which data key, the media/author/taxonomy ref maps, upsert-by-key) live in the generated transforms/write specs. This adapter holds the invariant Wix request shapes, transport, and shared execution mechanics. Collection names and schemas (PodcastEpisodes, …) are project-specific and come from the mapping plan.

Provisioning pointers (see rp-execute-setup)

  • Apps (Blog, Members) install via the App Installation API; ground appDefId from the official "Apps Created by Wix" table.

  • Wix Data enablement (WDE0110): install the Wix Data app appDefId e593b0bd-b783-45b8-97c2-873d42aacaf4 via the App Installation API; afterward POST /wix-data/v2/collections creates NATIVE collections with no WDE0110 (verified live). Fallback: a custom app with a data-collections extension (declares collections at install time, but can't express REFERENCE fields).

Scope & coverage

Wix has many apps/entities (Stores, Bookings, Events, Restaurants, Pricing Plans, CRM, …). This adapter does not pre-build all of them. Coverage is demand-driven and grows through reviewed releases. A migration may still target a native Wix entity before a dedicated primitive exists; in that case codegen emits a native REST path using sendDirectRest, logs the missing primitive, and calls notifyMissingWriter so the RePlatform team can add the writer later.

Native target ladder when no dedicated writer exists:

  1. Use the dedicated rp-target-wix primitive when one exists.

  2. If Wix has a native entity but no dedicated primitive, generate a native REST call from Wix MCP/docs-schema, mark it UNVERIFIED, log it, call notifyMissingWriter, and surface it in the execution plan before any write. This is not a silent live write.

  3. Use CMS only when there is no suitable native Wix entity, or when the native entity is explicitly rejected for fidelity/side-effect reasons. CMS is not a fallback for a missing adapter writer.

  4. Halt if neither a native path nor an acceptable CMS/custom target exists.

The invariant: anything not backed by a verified primitive is surfaced to the user for consent before execution — never written silently.

Setup runtime expectations

This adapter should also define the shared setup-side runtime used by setup execution.

It should cover reusable mechanics for:

  • app installation and verification

  • Wix Data enablement verification

  • CMS collection and field provisioning when supported

  • setup step ordering/checkpointing

  • setup audit logging

  • setup execution reporting

rp-execute-setup should consume setup artifacts through this runtime rather than re-implementing setup mechanics in skill-local prose or one-off scripts.

Contract-test expectations

Contract tests should validate not only request builders, but also the most important shared runtime invariants.

Examples:

  • verified request shapes still serialize correctly

  • retry policy classifies common Wix failures correctly

  • throttle/backoff handling honors server hints

  • compact reports preserve stable top-level fields

  • completion reports preserve stable top-level fields

  • audit events preserve the agreed machine-readable shape

Files

domains/index.json

{
  "schemaVersion": 1,
  "domains": {
    "blog": {
      "path": "domains/blog/domain.json",
      "displayName": "Wix Blog",
      "ownerHint": "Blog posts, categories, tags, authorship, and comments domain owners",
      "entities": {
        "category": {
          "path": "domains/blog/entities/category.json",
          "displayName": "Blog Category",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Wix Blog categories are flat; WordPress hierarchy needs CMS shadowing if it matters."
        },
        "comment": {
          "path": "domains/blog/entities/comment.json",
          "displayName": "Blog Comment",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Verify the target relation and notification behavior before native comment writes."
        },
        "post": {
          "path": "domains/blog/entities/post.json",
          "displayName": "Blog Post",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [
            "IMPORT_UNRELIABLE"
          ],
          "summary": "Exact original URL or slug preservation is not reliable through public Blog Draft Posts."
        },
        "tag": {
          "path": "domains/blog/entities/tag.json",
          "displayName": "Blog Tag",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Blog tag slug is derived; dedupe by label/language."
        }
      }
    },
    "bookings": {
      "path": "domains/bookings/domain.json",
      "displayName": "Wix Bookings",
      "ownerHint": "Bookings (services, bookings, sessions/schedules, availability, staff, resources, policies) domain owners",
      "entities": {
        "booking": {
          "path": "domains/bookings/entities/booking.json",
          "displayName": "Booking",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "summary": "sendSmsReminder has 'Default: true' on both Create Booking and Bulk Create Booking (read 2026-08-16). This is the inverse of every other notification control the KB has met - events/rsvp's disableNotifications and this API's own participantNotification.notifyParticipants both default to OFF, so a generated payload built from the required-field list alone is SAFE there and UNSAFE here. Omitting the flag opts every migrated customer into an SMS to their real phone number. Every create call in the generated script must set sendSmsReminder:false explicitly, and the script's own test must assert its presence on every element of a bulk request, not just the first."
        },
        "service-add-on": {
          "path": "domains/bookings/entities/service-add-on.json",
          "displayName": "Bookings Service Add-On",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "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'."
        },
        "service": {
          "path": "domains/bookings/entities/service.json",
          "displayName": "Bookings Service",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        }
      }
    },
    "cms": {
      "path": "domains/cms/domain.json",
      "displayName": "Wix CMS",
      "ownerHint": "CMS/Data collections, items, references, permissions, and indexes domain owners",
      "entities": {
        "collection": {
          "path": "domains/cms/entities/collection.json",
          "displayName": "CMS Collection",
          "classification": "setup-config",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Collections, fields, permissions, and indexes must be represented in setup before item import."
        },
        "data-item": {
          "path": "domains/cms/entities/data-item.json",
          "displayName": "CMS Data Item",
          "classification": "cms",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "_createdDate and _updatedDate are read-only; preserve source dates in explicit fields."
        },
        "reference": {
          "path": "domains/cms/entities/reference.json",
          "displayName": "CMS Reference",
          "classification": "cms",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "References and multi-references often require a second pass after target IDs are known."
        }
      }
    },
    "crm": {
      "path": "domains/crm/domain.json",
      "displayName": "Wix CRM",
      "ownerHint": "Contacts, labels, extended fields, and subscription-consent domain owners",
      "entities": {
        "contact": {
          "path": "domains/crm/entities/contact.json",
          "displayName": "CRM Contact",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Original contact created dates are not writable as native createdDate."
        },
        "email-subscription": {
          "path": "domains/crm/entities/email-subscription.json",
          "displayName": "Email Subscription",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Only set subscribed states when the migration has consent evidence."
        },
        "label": {
          "path": "domains/crm/entities/label.json",
          "displayName": "CRM Label",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Do not collapse dynamic segments or marketing automation state into static labels without review."
        }
      }
    },
    "ecom": {
      "path": "domains/ecom/domain.json",
      "displayName": "Wix eCommerce",
      "ownerHint": "eCommerce Orders, refunds, checkout, and order-history domain owners",
      "entities": {
        "delivery-profile": {
          "path": "domains/ecom/entities/delivery-profile.json",
          "displayName": "Delivery Profile",
          "classification": "setup-config",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "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."
        },
        "discount-rule": {
          "path": "domains/ecom/entities/discount-rule.json",
          "displayName": "eCommerce Discount Rule (\"Automatic Discounts\" in the dashboard)",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [
            "not-yet-live-verified"
          ],
          "summary": "The Wix dashboard calls this feature 'Automatic Discounts'; the API/data model name is DiscountRule (`ecom/v1/discount-rules`). Distinct from the Coupons API (stores/coupon.json) — coupons need a customer-entered code, discount rules apply automatically when their trigger is met. Do not conflate the two when a source plugin (e.g. WooCommerce Discount Rules) models automatic, no-code discounting."
        },
        "order-applied-discount": {
          "path": "domains/ecom/entities/order-applied-discount.json",
          "displayName": "eCommerce Order Applied Discount",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "order-line-item-options": {
          "path": "domains/ecom/entities/order-line-item-options.json",
          "displayName": "eCommerce Order Line Item Options (Description Lines)",
          "classification": "native-plus-cms",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "order-line-item": {
          "path": "domains/ecom/entities/order-line-item.json",
          "displayName": "eCommerce Order Line Item",
          "classification": "native-plus-cms",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Line items are written through their parent order; do not create standalone duplicates."
        },
        "order-merchant-note": {
          "path": "domains/ecom/entities/order-merchant-note.json",
          "displayName": "eCommerce Order Merchant Note (Activity)",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "order-refund": {
          "path": "domains/ecom/entities/order-refund.json",
          "displayName": "eCommerce Order Refund",
          "classification": "native-plus-cms",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Verify whether the selected order write shape preserves refund amounts, reasons, dates, and line references."
        },
        "order": {
          "path": "domains/ecom/entities/order.json",
          "displayName": "eCommerce Order",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Never route historical orders through createOrder — it decrements inventory, emails the buyer, and auto-creates contacts; importOrder is the only import-safe writer."
        },
        "shipping-option": {
          "path": "domains/ecom/entities/shipping-option.json",
          "displayName": "Wix Shipping Option",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "CRITICAL, LIVE-DISCOVERED 2026-08-15 (not documented on any Delivery Profile/Delivery Carrier doc page -- found only via dev.wix.com's 'Fix Shipping Coverage Gaps' skill article, a separate doc tree from shipping-delivery/delivery-profiles/*): DeliveryCarrier.backupRate does NOT create a ShippingOption, and does NOT clear Wix's own 'This region is missing rates. Add them so customers can complete checkout.' dashboard warning. VERIFIED on the reference store: 'Europe'/'Israel' (created by this pipeline via addDeliveryRegion + addDeliveryCarrier with a correctly-shaped, active backupRate) still showed the warning and had ZERO linked ShippingOptions, while 'Domestic'/'International' (Wix's own auto-created defaults, never touched by this pipeline) each already had a real ShippingOption. A region migration that only calls addDeliveryCarrier is INCOMPLETE -- it appears fully successful (the write returns 200, the read-back matches exactly what was sent) while silently leaving the region non-functional at checkout. Always create a matching ShippingOption (via createShippingOption) for every DeliveryRegion this pipeline creates, from the SAME classified method used for that region's DeliveryCarrier.backupRate -- see shipping-build.js's buildShippingOptionInput."
        }
      }
    },
    "events": {
      "path": "domains/events/domain.json",
      "displayName": "Wix Events",
      "ownerHint": "Events, registration, RSVP, and ticketing domain owners",
      "entities": {
        "category": {
          "path": "domains/events/entities/category.json",
          "displayName": "Events Category",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "summary": "Wix Events categories are flat; a hierarchical source event taxonomy (tribe_events_cat is hierarchical) flattens on import — faithfulness-ledger entry required."
        },
        "event": {
          "path": "domains/events/entities/event.json",
          "displayName": "Event",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Backdating is not documented either way. Historical events are the common migration case, so confirm a past startDate is accepted before promising event history; if rejected, past events fall back to CMS."
        },
        "registration-form": {
          "path": "domains/events/entities/registration-form.json",
          "displayName": "Event Registration Form",
          "classification": "setup-config",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "rsvp": {
          "path": "domains/events/entities/rsvp.json",
          "displayName": "Event RSVP",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "RSVP creation emails the guest a confirmation unless disableNotifications is set — the domain's registration-notifications-default-on pitfall. An import that forgets the flag mass-mails migrated guests. importSafe is false until the generated script provably sets it on every call AND the suppression itself is proven."
        },
        "ticket-definition": {
          "path": "domains/events/entities/ticket-definition.json",
          "displayName": "Event Ticket Definition",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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, owner tracker EVNT-01)."
        }
      }
    },
    "forms": {
      "path": "domains/forms/domain.json",
      "displayName": "Wix Forms",
      "ownerHint": "Wix Forms (form schemas, form submissions) domain owners — the Forms APIs sit under the CRM docs tree, not Business Solutions",
      "entities": {
        "form-submission": {
          "path": "domains/forms/entities/form-submission.json",
          "displayName": "Wix Form Submission",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "summary": "OBSERVED 2026-08-16, full method-parameter list of Create Submission read end to end: the call accepts exactly `submission`, `additionalMetadata` and `captchaToken`. There is NO disableNotifications, no suppressNotifications, no flowControlSettings, no participantNotification, and no `sendEmail`-style toggle anywhere on the Form Submissions surface; the words 'notification' and 'email' do not appear on the Create Submission, Submission Object, Confirm Submission or Form Submissions Introduction pages at all. What IS documented: creating a submission fires the `Submission Created` domain event, and the Form Schemas glossary defines 'post-submission trigger' as 'automated actions that are executed after a form submission is received' — i.e. the Wix Automations hook that the default 'new form submission' owner email rides on. Suppression therefore cannot be proven from the docs, so importSafe stays false, exactly as for events/rsvp. The only control we have is the site-wide MuteSite policy, which is not a per-call flag, is applied by default only to newly created target sites, and has not been verified to cover the Automations email path."
        },
        "form": {
          "path": "domains/forms/entities/form.json",
          "displayName": "Wix Form (form schema)",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "summary": "THE TWO DOCS PAGES CONTRADICT EACH OTHER on this field and both readings are recorded here, because either one is bad enough to matter. About Form Fields says the mild version: 'Wix Forms uses `identifier` to render submissions in the submission table, so a field with a missing or non-standard identifier may display incorrectly there, even though the form itself works and the API accepts the request.' The Form Object page says the severe version: 'An unrecognized value, such as a custom string or a GUID, is dropped rather than rejected: the request still succeeds, but the field doesn't appear in the response's formFields, and neither the field nor its submitted values appear in the Wix dashboard.' Both agree the request returns success, so neither failure mode surfaces as an error at import time. Behave as if the severe reading holds until a live probe settles it: map every source field type onto a known identifier from the Field types table, ledger anything forced to a generic TEXT_INPUT/TEXT_AREA as a fidelity loss, never invent an identifier or pass a source GUID through — and after creating a schema, DIFF the returned formFields against what was sent, which detects the drop directly. Note also that the identifier list is per-namespace: apps owning their own namespace may define additional identifiers, so the table applies to `wix.form_app.form` only."
        }
      }
    },
    "gift-cards": {
      "path": "domains/gift-cards/domain.json",
      "displayName": "Wix Gift Cards",
      "ownerHint": "Gift cards, stored value, and store credit domain owners",
      "entities": {
        "gift-card-product": {
          "path": "domains/gift-cards/entities/gift-card-product.json",
          "displayName": "Gift Card Product",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "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."
        },
        "gift-card": {
          "path": "domains/gift-cards/entities/gift-card.json",
          "displayName": "Gift Card",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "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."
        }
      }
    },
    "media": {
      "path": "domains/media/domain.json",
      "displayName": "Wix Media Manager",
      "ownerHint": "Media Manager import, upload, folders, and metadata domain owners",
      "entities": {
        "media-file": {
          "path": "domains/media/entities/media-file.json",
          "displayName": "Media File",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Media import is asynchronous; poll until READY before referencing the file."
        },
        "media-folder": {
          "path": "domains/media/entities/media-folder.json",
          "displayName": "Media Folder",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Do not require folder creation for media file preservation unless verified for the project."
        }
      }
    },
    "members": {
      "path": "domains/members/domain.json",
      "displayName": "Wix Members",
      "ownerHint": "Members identity, profile, roles, and badges domain owners",
      "entities": {
        "badge": {
          "path": "domains/members/entities/badge.json",
          "displayName": "Member Badge",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Verify badge creation and assignment surfaces before native execution."
        },
        "custom-field": {
          "path": "domains/members/entities/custom-field.json",
          "displayName": "Member Custom Field",
          "classification": "setup-config",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "member-about": {
          "path": "domains/members/entities/member-about.json",
          "displayName": "Member About",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "content is Ricos RichContent (node tree), not plain text; a WordPress user description must be wrapped as paragraph nodes. Malformed node trees fail the write. Verified-live minimal shape: { nodes: [ { type: 'PARAGRAPH', id: 'p1', paragraphData: {}, nodes: [ { type: 'TEXT', id: '', nodes: [], textData: { text, decorations: [] } } ] } ] }."
        },
        "member": {
          "path": "domains/members/entities/member.json",
          "displayName": "Site Member",
          "classification": "native-plus-cms",
          "reliabilityStatus": "unreliable",
          "reliabilityFlags": [
            "IMPORT_UNRELIABLE"
          ],
          "summary": "VERIFIED LIVE 2026-08-16: safe mode cannot import members at all today. `mockEmailForEntity` (rp-target-wix/lib/wix-writers.js) mints `replatform+<type>_<id>@wix.com`, and POST /members/v1/members rejects that domain with 400 INVALID_EMAIL_DOMAIN — so a safe-mode member import fails wholesale on the first record. The bug hid because Contacts V5 ACCEPTS @wix.com (existing test contacts use it), so only the Members surface trips it. A reserved-TLD address (RFC 2606 `.invalid`) was accepted by other surfaces in the same probe session and is the obvious candidate, but changing the safe-mode address format alters what gets written to a customer's site on every entity, not just members — it needs an explicit decision, not a silent lib patch. Until then: treat safe mode + member import as unsupported and say so in the plan rather than discovering it mid-run."
        }
      }
    },
    "pricing-plans": {
      "path": "domains/pricing-plans/domain.json",
      "displayName": "Wix Pricing Plans",
      "ownerHint": "Pricing Plans (paid plans, memberships, plan orders/subscriptions) domain owners",
      "entities": {
        "plan-order": {
          "path": "domains/pricing-plans/entities/plan-order.json",
          "displayName": "Pricing Plan Order",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "summary": "STILL UNPROVEN after a live probe (2026-08-16) — importSafe deliberately stays false. Create Offline Order documents no member-notification behavior and has no suppress/silent flag (the request is only planId, memberId, paid, startDate, couponCode, submissionId), while the sibling Mark As Paid explicitly documents that offline payments 'trigger events and emails'. The 2026-08-16 probe created a real offline order with paid:true and observed NOTHING that indicates an email either way — but the observation is worthless as proof of silence for two independent reasons: (a) the target site was under a site-wide notification mute for the whole probe, which by design suppresses all notifications to all recipients on all channels, so no email could have been sent regardless of the API's default behavior; and (b) the probe member's address was deliberately undeliverable (example.invalid), and there is no Wix read API that lists emails a site has sent, so even unmuted there would have been nothing to inspect. Absence of evidence here is an artifact of the method, not a finding. See notification-proof-requirements for what would actually settle it."
        },
        "plan": {
          "path": "domains/pricing-plans/entities/plan.json",
          "displayName": "Pricing Plan",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "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."
        }
      }
    },
    "sales-channels": {
      "path": "domains/sales-channels/domain.json",
      "displayName": "Wix Sales Channels",
      "ownerHint": "Stores multichannel/marketplace integrations — the \"Sales Channels\" section of the Wix Stores dashboard (Catalog > Sales Channels)",
      "entities": {
        "amazon-shop-connect": {
          "path": "domains/sales-channels/entities/amazon-shop-connect.json",
          "displayName": "Amazon",
          "classification": "manual-mapping",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [
            "not-independently-verified-live"
          ],
          "summary": "Public support docs describe the original \"Amazon by Wix\" app as deprecated since 2025-05-04, with Wix recommending a third-party app (Koongo) as the replacement. But a live internal service, wix.stores_channels.amazon.v1.amazon_passive_feed, was created 2026-06-17 and last updated 2026-07-20 — well after that deprecation — and implements an automatic feed-push mechanism (AMAZON_XML format, pushed to an Amazon-owned S3 bucket). This suggests Wix has since built a newer native Amazon integration distinct from the deprecated app, but the exact current merchant click-path was not independently confirmed against a live dashboard this session. Re-verify the steps below (and whether the current \"Amazon\" sidebar entry is this newer service or a third-party app) against a live site before relying on them for a real migration report."
        },
        "ebay-shop-connect": {
          "path": "domains/sales-channels/entities/ebay-shop-connect.json",
          "displayName": "eBay",
          "classification": "manual-mapping",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [
            "not-independently-verified-live",
            "no-internal-fqdn-match"
          ],
          "summary": "No FQDN matching \"ebay\" was found anywhere in the internal Business Schema registry this session (docs-schema MCP fqdn_lookup), unlike every other channel in this domain (Google, Facebook, TikTok, Pinterest, and Amazon all resolved to a real internal wix.stores_channels.* service). The mechanism below is sourced from support.wix.com only — treat it as lower confidence than the other entities in this domain until an internal service is located or a live site is checked."
        },
        "google-youtube-connect": {
          "path": "domains/sales-channels/entities/google-youtube-connect.json",
          "displayName": "Google & YouTube (Google Merchant Center, Shopping, YouTube Shopping)",
          "classification": "manual-mapping",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "This channel is the most automated of the group: after the one-time connect, sync is push-based and continuous — there is no feed link for the merchant to copy anywhere, unlike Meta/TikTok/Pinterest."
        },
        "meta-catalog-connect": {
          "path": "domains/sales-channels/entities/meta-catalog-connect.json",
          "displayName": "Meta Catalog (Facebook, Instagram, WhatsApp)",
          "classification": "manual-mapping",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "pinterest-catalog-connect": {
          "path": "domains/sales-channels/entities/pinterest-catalog-connect.json",
          "displayName": "Pinterest Catalogs",
          "classification": "manual-mapping",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "tiktok-catalog-connect": {
          "path": "domains/sales-channels/entities/tiktok-catalog-connect.json",
          "displayName": "TikTok Catalog",
          "classification": "manual-mapping",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": ""
        }
      }
    },
    "seo": {
      "path": "domains/seo/domain.json",
      "displayName": "Wix SEO",
      "ownerHint": "SEO (item SEO tags, site SEO tags, SEO patterns, robots.txt/ads.txt/llms.txt) domain owners",
      "entities": {
        "item-seo-tags": {
          "path": "domains/seo/entities/item-seo-tags.json",
          "displayName": "Item SEO Tags",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "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."
        },
        "seo-pattern": {
          "path": "domains/seo/entities/seo-pattern.json",
          "displayName": "SEO Pattern",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "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."
        },
        "site-seo-tags": {
          "path": "domains/seo/entities/site-seo-tags.json",
          "displayName": "Site SEO Tags",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "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 the reference store 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."
        }
      }
    },
    "stores": {
      "path": "domains/stores/domain.json",
      "displayName": "Wix Stores",
      "ownerHint": "Stores Catalog, Inventory, Coupons, Tags, and merchandising domain owners",
      "entities": {
        "back-in-stock-request": {
          "path": "domains/stores/entities/back-in-stock-request.json",
          "displayName": "Back In Stock Notification Request",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [
            "target-write-not-yet-live-verified"
          ],
          "summary": "Creating a request 428s with REQUEST_COLLECTION_DISABLED unless Start Collecting Requests has been called for the site first. This is a one-time setup step, not a per-record concern — emit it as a setup requirement, not a per-row retry."
        },
        "brand": {
          "path": "domains/stores/entities/brand.json",
          "displayName": "Stores Brand",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        },
        "category": {
          "path": "domains/stores/entities/category.json",
          "displayName": "Stores Category",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Pass Stores treeReference as a top-level sibling of category."
        },
        "coupon": {
          "path": "domains/stores/entities/coupon.json",
          "displayName": "Stores Coupon",
          "classification": "native-plus-cms",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Source coupon constraints may be narrower or differently modeled in Wix."
        },
        "inventory-item": {
          "path": "domains/stores/entities/inventory-item.json",
          "displayName": "Stores Inventory Item",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Inventory requires target product and variant IDs."
        },
        "product-customization": {
          "path": "domains/stores/entities/product-customization.json",
          "displayName": "Stores Product Customization",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "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."
        },
        "product-tag": {
          "path": "domains/stores/entities/product-tag.json",
          "displayName": "Stores Product Tag",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Use the verified product FQDN and resolve tag IDs before attaching them to products."
        },
        "product": {
          "path": "domains/stores/entities/product.json",
          "displayName": "Stores Product",
          "classification": "native-plus-cms",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Normalize V1/simple product fields into Catalog V3 product and variant shapes before create."
        },
        "promotion": {
          "path": "domains/stores/entities/promotion.json",
          "displayName": "Stores Promotion (Category Banner)",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "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."
        },
        "review": {
          "path": "domains/stores/entities/review.json",
          "displayName": "Product Review",
          "classification": "native",
          "reliabilityStatus": "unknown",
          "reliabilityFlags": [],
          "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."
        },
        "subscription-product": {
          "path": "domains/stores/entities/subscription-product.json",
          "displayName": "Stores Subscription Product Settings",
          "classification": "native",
          "reliabilityStatus": "partially-reliable",
          "reliabilityFlags": [],
          "summary": "Subscription expiry/length, initial signup price, and free trial have NO product.subscriptionDetails field. Decided by Spec Owner 2026-08-16: 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."
        }
      }
    },
    "tax": {
      "path": "domains/tax/domain.json",
      "displayName": "Wix Tax",
      "ownerHint": "eCommerce tax domain owners (Tax Groups / Tax Regions / Manual Tax Mappings)",
      "entities": {
        "manual-tax-mapping": {
          "path": "domains/tax/entities/manual-tax-mapping.json",
          "displayName": "Wix Manual Tax Mapping",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Create/Query/Delete Manual Tax Mapping are `/billing/v1/manual-tax-mappings...`, not `/ecom/v1/...`."
        },
        "tax-group": {
          "path": "domains/tax/entities/tax-group.json",
          "displayName": "Wix Tax Group",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Create/Query/Delete Tax Group are all `/billing/v1/tax-groups...`, not `/ecom/v1/...` (unlike sibling Discount Rules) or `/stores/v2/...` (unlike Coupons)."
        },
        "tax-region": {
          "path": "domains/tax/entities/tax-region.json",
          "displayName": "Wix Tax Region",
          "classification": "native",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "summary": "Create/Query/Delete Tax Region are `/billing/v1/tax-regions...`, not `/ecom/v1/...`."
        },
        "tax-settings": {
          "path": "domains/tax/entities/tax-settings.json",
          "displayName": "Wix Tax Settings",
          "classification": "setup-config",
          "reliabilityStatus": "reliable",
          "reliabilityFlags": [],
          "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."
        }
      }
    }
  },
  "sourceAliasIndex": {
    "wordpress:category": [
      "blog/category"
    ],
    "wix:blogCategory": [
      "blog/category"
    ],
    "wordpress:comment": [
      "blog/comment"
    ],
    "wix:comment": [
      "blog/comment"
    ],
    "wordpress:post": [
      "blog/post"
    ],
    "wix:blogPost": [
      "blog/post"
    ],
    "wordpress:tag": [
      "blog/tag"
    ],
    "wix:blogTag": [
      "blog/tag"
    ],
    "woocommerce:bookingOrderLineItem": [
      "bookings/booking"
    ],
    "woocommerce:bookingAdditionalServiceTerm": [
      "bookings/service-add-on"
    ],
    "woocommerce:bookingProduct": [
      "bookings/service"
    ],
    "wordpress:event": [
      "bookings/service",
      "events/event"
    ],
    "wordpress:custom_post_type": [
      "cms/collection"
    ],
    "wix:cmsCollection": [
      "cms/collection"
    ],
    "wordpress:custom_post_type_item": [
      "cms/data-item"
    ],
    "wix:cmsDataItem": [
      "cms/data-item"
    ],
    "wordpress:relationship": [
      "cms/reference"
    ],
    "wix:cmsReference": [
      "cms/reference"
    ],
    "woocommerce:customer": [
      "crm/contact",
      "members/member"
    ],
    "woocommerce:order": [
      "crm/contact",
      "ecom/order"
    ],
    "wordpress:user": [
      "crm/contact",
      "members/member"
    ],
    "wix:contact": [
      "crm/contact"
    ],
    "wordpress:newsletter_subscription": [
      "crm/email-subscription"
    ],
    "wix:emailSubscription": [
      "crm/email-subscription"
    ],
    "woocommerce:customer_tag": [
      "crm/label"
    ],
    "wix:contactLabel": [
      "crm/label"
    ],
    "woocommerce:shipping-zone": [
      "ecom/delivery-profile"
    ],
    "wix:deliveryProfile": [
      "ecom/delivery-profile"
    ],
    "woocommerce:discount-rule-definition": [
      "ecom/discount-rule"
    ],
    "woocommerce:applied_discount": [
      "ecom/order-applied-discount"
    ],
    "wix:ecomOrderAppliedDiscount": [
      "ecom/order-applied-discount"
    ],
    "woocommerce:order_item_addon_value": [
      "ecom/order-line-item-options"
    ],
    "wix:ecomOrderLineItemDescriptionLine": [
      "ecom/order-line-item-options"
    ],
    "woocommerce:order_line_item": [
      "ecom/order-line-item"
    ],
    "wix:ecomOrderLineItem": [
      "ecom/order-line-item"
    ],
    "woocommerce:order_note": [
      "ecom/order-merchant-note"
    ],
    "wix:ecomOrderActivity": [
      "ecom/order-merchant-note"
    ],
    "woocommerce:order_refund": [
      "ecom/order-refund"
    ],
    "wix:ecomOrderRefund": [
      "ecom/order-refund"
    ],
    "wix:ecomOrder": [
      "ecom/order"
    ],
    "woocommerce:shipping-zone.method": [
      "ecom/shipping-option"
    ],
    "wordpress:tribe_events_cat": [
      "events/category"
    ],
    "wix:event": [
      "events/event"
    ],
    "woocommerce:giftCard": [
      "gift-cards/gift-card"
    ],
    "wordpress:attachment": [
      "media/media-file"
    ],
    "wix:mediaFile": [
      "media/media-file"
    ],
    "wordpress:media_folder": [
      "media/media-folder"
    ],
    "wix:mediaFolder": [
      "media/media-folder"
    ],
    "wordpress:user_badge": [
      "members/badge"
    ],
    "wix:memberBadge": [
      "members/badge"
    ],
    "wix:member": [
      "members/member"
    ],
    "woocommerce:subscription": [
      "pricing-plans/plan-order"
    ],
    "woocommerce:subscriptionProduct": [
      "pricing-plans/plan"
    ],
    "woocommerce:amazon-for-woocommerce": [
      "sales-channels/amazon-shop-connect"
    ],
    "woocommerce:ebay-for-woocommerce": [
      "sales-channels/ebay-shop-connect"
    ],
    "woocommerce:google-listings-and-ads-for-woocommerce": [
      "sales-channels/google-youtube-connect"
    ],
    "woocommerce:facebook-for-woocommerce": [
      "sales-channels/meta-catalog-connect"
    ],
    "woocommerce:pinterest-for-woocommerce": [
      "sales-channels/pinterest-catalog-connect"
    ],
    "woocommerce:tiktok-for-woocommerce": [
      "sales-channels/tiktok-catalog-connect"
    ],
    "wordpress:yoastHeadJson": [
      "seo/item-seo-tags"
    ],
    "wordpress:rankMathPostMeta": [
      "seo/item-seo-tags"
    ],
    "woocommerce:back-in-stock-subscriber": [
      "stores/back-in-stock-request"
    ],
    "woocommerce:product_brand": [
      "stores/brand"
    ],
    "woocommerce:product_category": [
      "stores/category"
    ],
    "wix:storesCategory": [
      "stores/category"
    ],
    "woocommerce:coupon": [
      "stores/coupon"
    ],
    "wix:coupon": [
      "stores/coupon"
    ],
    "woocommerce:product_variation_inventory": [
      "stores/inventory-item"
    ],
    "wix:inventoryItem": [
      "stores/inventory-item"
    ],
    "woocommerce:addon-field-group": [
      "stores/product-customization"
    ],
    "woocommerce:product_tag": [
      "stores/product-tag"
    ],
    "wix:productTag": [
      "stores/product-tag"
    ],
    "woocommerce:product": [
      "stores/product"
    ],
    "wix:storesProduct": [
      "stores/product"
    ],
    "woocommerce:product_review": [
      "stores/review"
    ],
    "woocommerce:subscription-product-settings": [
      "stores/subscription-product"
    ],
    "woocommerce:tax-rate.rate": [
      "tax/manual-tax-mapping"
    ],
    "woocommerce:product.tax_class": [
      "tax/tax-group"
    ],
    "woocommerce:product.tax_status": [
      "tax/tax-group"
    ],
    "woocommerce:tax-rate": [
      "tax/tax-region"
    ],
    "woocommerce:settings.woocommerce_prices_include_tax": [
      "tax/tax-settings"
    ]
  },
  "routeAliasIndex": {
    "/wp/v2/categories": [
      "blog/category",
      "seo/item-seo-tags"
    ],
    "Blog Categories": [
      "blog/category"
    ],
    "/wp/v2/comments": [
      "blog/comment"
    ],
    "Comments API": [
      "blog/comment"
    ],
    "/wp/v2/posts": [
      "blog/post",
      "seo/item-seo-tags"
    ],
    "Blog Draft Posts": [
      "blog/post"
    ],
    "Blog Posts": [
      "blog/post"
    ],
    "/wp/v2/tags": [
      "blog/tag"
    ],
    "Blog Tags": [
      "blog/tag"
    ],
    "/wc/v3/orders": [
      "bookings/booking",
      "crm/contact",
      "ecom/order",
      "ecom/order-applied-discount",
      "ecom/order-line-item",
      "ecom/order-line-item-options",
      "gift-cards/gift-card"
    ],
    "/wp/v2/mwb_booking_service": [
      "bookings/service-add-on"
    ],
    "/wp/v2/mwb_booking_cost": [
      "bookings/service-add-on"
    ],
    "/wc/v3/products": [
      "bookings/service",
      "pricing-plans/plan",
      "seo/item-seo-tags",
      "stores/inventory-item",
      "stores/product",
      "stores/subscription-product",
      "tax/tax-group"
    ],
    "/tribe/events/v1/events": [
      "bookings/service",
      "events/event"
    ],
    "/wp/v2/{type}": [
      "cms/collection",
      "cms/data-item"
    ],
    "Wix Data Collections": [
      "cms/collection"
    ],
    "Wix Data Items": [
      "cms/data-item"
    ],
    "post meta": [
      "cms/reference"
    ],
    "term relationships": [
      "cms/reference"
    ],
    "Wix Data reference fields": [
      "cms/reference"
    ],
    "/wc/v3/customers": [
      "crm/contact",
      "members/member"
    ],
    "/wp/v2/users": [
      "crm/contact",
      "members/member"
    ],
    "CRM Contacts V4": [
      "crm/contact"
    ],
    "CRM Contacts V5": [
      "crm/contact"
    ],
    "plugin-specific subscription routes": [
      "crm/email-subscription"
    ],
    "Email Subscriptions API": [
      "crm/email-subscription"
    ],
    "customer metadata": [
      "crm/label"
    ],
    "segments": [
      "crm/label"
    ],
    "Contacts Labels": [
      "crm/label"
    ],
    "/wc/v3/shipping/zones": [
      "ecom/delivery-profile"
    ],
    "/wc/v3/shipping/zones/{id}/locations": [
      "ecom/delivery-profile"
    ],
    "/wc/v3/shipping/zones/{id}/methods": [
      "ecom/delivery-profile",
      "ecom/shipping-option"
    ],
    "eCommerce Delivery Profiles API": [
      "ecom/delivery-profile"
    ],
    "eCommerce Orders API": [
      "ecom/order",
      "ecom/order-applied-discount",
      "ecom/order-line-item",
      "ecom/order-line-item-options",
      "ecom/order-merchant-note",
      "ecom/order-refund"
    ],
    "/wc/v3/orders/{parentId}/notes": [
      "ecom/order-merchant-note"
    ],
    "/wc/v3/orders/{id}/refunds": [
      "ecom/order-refund"
    ],
    "/wp/v2/tribe_events_cat": [
      "events/category"
    ],
    "/wp/v2/tribe_events": [
      "events/event"
    ],
    "Events V3": [
      "events/event"
    ],
    "/wc/v3/gift-cards": [
      "gift-cards/gift-card"
    ],
    "/wp/v2/media": [
      "media/media-file"
    ],
    "Media Manager": [
      "media/media-file"
    ],
    "plugin-specific media folder routes": [
      "media/media-folder"
    ],
    "Media Manager folders": [
      "media/media-folder"
    ],
    "plugin-specific member badge routes": [
      "members/badge"
    ],
    "Members Badges APIs": [
      "members/badge"
    ],
    "Members APIs": [
      "members/member"
    ],
    "/wsp-route/v1/wsp-view-subscription": [
      "pricing-plans/plan-order"
    ],
    "/wc-instocknotifier/v3/list_subscriber": [
      "stores/back-in-stock-request"
    ],
    "/wc/v3/products/brands": [
      "stores/brand"
    ],
    "/wc/v3/products/categories": [
      "stores/category"
    ],
    "Catalog V3 Categories": [
      "stores/category"
    ],
    "V1 collections": [
      "stores/category"
    ],
    "/wc/v3/coupons": [
      "stores/coupon"
    ],
    "Coupons API": [
      "stores/coupon"
    ],
    "/wc/v3/products/{productId}/variations": [
      "stores/inventory-item"
    ],
    "Stores Inventory APIs": [
      "stores/inventory-item"
    ],
    "/ppom/v1/admin/field-groups/context": [
      "stores/product-customization"
    ],
    "/wc/v3/products/tags": [
      "stores/product-tag"
    ],
    "Tags API scoped to product FQDN": [
      "stores/product-tag"
    ],
    "Stores Catalog V1 Products": [
      "stores/product"
    ],
    "Stores Catalog V3 Products": [
      "stores/product"
    ],
    "/wc/v3/products/reviews": [
      "stores/review"
    ],
    "/wc/v3/taxes": [
      "tax/manual-tax-mapping",
      "tax/tax-region"
    ],
    "/wc/v3/taxes/classes": [
      "tax/tax-group"
    ],
    "/wc/v3/settings/tax": [
      "tax/tax-settings"
    ]
  },
  "capabilityIndex": {
    "content.blog-category": [
      "blog/category"
    ],
    "content.comments": [
      "blog/comment"
    ],
    "content.blog-post": [
      "blog/post"
    ],
    "content.blog-tag": [
      "blog/tag"
    ],
    "commerce.bookings": [
      "bookings/booking"
    ],
    "commerce.bookable-service-add-ons": [
      "bookings/service-add-on"
    ],
    "commerce.bookable-services": [
      "bookings/service"
    ],
    "cms.collection": [
      "cms/collection"
    ],
    "cms.data-item": [
      "cms/data-item"
    ],
    "cms.reference": [
      "cms/reference"
    ],
    "crm.contact": [
      "crm/contact"
    ],
    "crm.email-subscription": [
      "crm/email-subscription"
    ],
    "crm.label": [
      "crm/label"
    ],
    "commerce.delivery-profiles": [
      "ecom/delivery-profile"
    ],
    "commerce.discount-rules": [
      "ecom/discount-rule"
    ],
    "commerce.applied-discounts": [
      "ecom/order-applied-discount"
    ],
    "commerce.order-addon-values": [
      "ecom/order-line-item-options"
    ],
    "commerce.order-line-items": [
      "ecom/order-line-item"
    ],
    "commerce.payment-gateway-notes": [
      "ecom/order-merchant-note"
    ],
    "commerce.order-refunds": [
      "ecom/order-refund"
    ],
    "commerce.orders": [
      "ecom/order"
    ],
    "commerce.shipping-rate": [
      "ecom/shipping-option"
    ],
    "content.event-categories": [
      "events/category"
    ],
    "content.events": [
      "events/event"
    ],
    "content.event-registration-form": [
      "events/registration-form"
    ],
    "content.event-rsvps": [
      "events/rsvp"
    ],
    "content.event-ticket-definitions": [
      "events/ticket-definition"
    ],
    "content.form-submissions": [
      "forms/form-submission"
    ],
    "content.form-definitions": [
      "forms/form"
    ],
    "commerce.gift-card-products": [
      "gift-cards/gift-card-product"
    ],
    "commerce.gift-cards": [
      "gift-cards/gift-card"
    ],
    "media.file": [
      "media/media-file"
    ],
    "media.folder": [
      "media/media-folder"
    ],
    "identity.member-badge": [
      "members/badge"
    ],
    "identity.member-custom-fields": [
      "members/custom-field"
    ],
    "identity.member-about": [
      "members/member-about"
    ],
    "identity.member": [
      "members/member"
    ],
    "commerce.subscription-contracts": [
      "pricing-plans/plan-order"
    ],
    "commerce.subscription-plans": [
      "pricing-plans/plan"
    ],
    "commerce.amazon-catalog-sync": [
      "sales-channels/amazon-shop-connect"
    ],
    "commerce.ebay-catalog-sync": [
      "sales-channels/ebay-shop-connect"
    ],
    "commerce.google-shopping-catalog-sync": [
      "sales-channels/google-youtube-connect"
    ],
    "commerce.facebook-catalog-sync": [
      "sales-channels/meta-catalog-connect"
    ],
    "commerce.pinterest-catalog-sync": [
      "sales-channels/pinterest-catalog-connect"
    ],
    "commerce.tiktok-catalog-sync": [
      "sales-channels/tiktok-catalog-connect"
    ],
    "content.seo-meta": [
      "seo/item-seo-tags"
    ],
    "content.seo-patterns": [
      "seo/seo-pattern"
    ],
    "content.site-seo-tags": [
      "seo/site-seo-tags"
    ],
    "commerce.back-in-stock-notifications": [
      "stores/back-in-stock-request"
    ],
    "commerce.catalog-brand": [
      "stores/brand"
    ],
    "commerce.catalog-category": [
      "stores/category"
    ],
    "commerce.coupons": [
      "stores/coupon"
    ],
    "commerce.inventory": [
      "stores/inventory-item"
    ],
    "commerce.product-addon-fields": [
      "stores/product-customization"
    ],
    "commerce.catalog-product-tag": [
      "stores/product-tag"
    ],
    "commerce.catalog-product": [
      "stores/product"
    ],
    "marketing.storefront-banner": [
      "stores/promotion"
    ],
    "commerce.product-reviews": [
      "stores/review"
    ],
    "commerce.subscription-products": [
      "stores/subscription-product"
    ],
    "commerce.tax-rate-mapping": [
      "tax/manual-tax-mapping"
    ],
    "commerce.tax-treatment-group": [
      "tax/tax-group"
    ],
    "commerce.tax-region": [
      "tax/tax-region"
    ],
    "commerce.tax-settings": [
      "tax/tax-settings"
    ]
  },
  "flags": {
    "IMPORT_UNRELIABLE": [
      "blog/post",
      "members/member"
    ],
    "not-yet-live-verified": [
      "ecom/discount-rule"
    ],
    "not-independently-verified-live": [
      "sales-channels/amazon-shop-connect",
      "sales-channels/ebay-shop-connect"
    ],
    "no-internal-fqdn-match": [
      "sales-channels/ebay-shop-connect"
    ],
    "target-write-not-yet-live-verified": [
      "stores/back-in-stock-request"
    ]
  }
}

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an API key, Needs a command-line tool, Needs Node.js

Security

What our scanner noticed· Medium risk

  • Possible data-exfiltration endpoints
  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Wix
Publisher
Wix (Official)
Source
wix/skills · wix-replatform/resources/rp-target-wix · commit d168bc2
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Wix or Wix.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download rp-target-wix.zip