45 "when": "Attendee/registrant PII import: Bookings has no bulk 'import a past booking' path, mirroring the Events domain's ticket-order-history-not-importable gap. Only the service+session definitions are importable, never historical bookings."
56 "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."
61 "summary": "For appointment-based services at least one staffMemberIds entry is REQUIRED at create, and the docs are explicit that the values are RESOURCE GUIDs (staffMember.resourceId), not staff-member GUIDs - 'Query staff members with the RESOURCE_DETAILS field set to retrieve resourceId'. A WooCommerce booking product has no provider concept at all, so the import must provision (or reuse) a staff member during setup and record the substitution in the faithfulness ledger; the app install already provisions one default 'Business Owner' resource that can be reused. For classes and courses staffMemberIds is read-only and derived from the recurring sessions, so it must NOT be sent."
62 },
63 {
64 "code": "five-required-create-fields",
65 "severity": "blocker",
66 "summary": "Create Service requires type (APPOINTMENT | CLASS | COURSE), name, onlineBooking, payment, and defaultCapacity - all five, on every call. defaultCapacity must be exactly 1 for APPOINTMENT services; a source per-slot capacity greater than 1 therefore cannot be expressed as an appointment and must either become a CLASS or be ledgered as loss. payment.options must have at least one of inPerson or online true even when rateType is NO_FEE."
71 "summary": "There is no '/bookings/v2/...' endpoint for a service's date/time occurrence. The one-time or recurring session is an ordinary Calendar V3 event (POST /calendar/v3/events) on `service.schedule.id` (present on the Create Service response). `event.type` must equal the service's `type` or the create 400s 'type must match the service type'; a CLASS/COURSE event additionally requires `resources: [{ id, permissionRole: 'WRITER' }]` naming a real Bookings resource (POST /bookings/v2/resources/query) or it 400s 'resources must have at least 1 resource for class events'. `start.localDate`/`end.localDate` are LOCAL date-time strings with no offset/zone suffix, paired with a separate `timeZone` — the same trap documented for Wix Events."
72 },
73 {
74 "code": "payment-options-has-no-default",
75 "severity": "warning",
76 "summary": "Omitting `payment.options` 400s 'It is mandatory to specify either payment.options.online or payment.options.inPerson as true', even for a NO_FEE service with no money changing hands. Always set at least one of `online`/`inPerson`. (Live-verified error string 2026-08-12; the docs state the same requirement.)"
77 },
78 {
79 "code": "default-capacity-required-by-type",
80 "severity": "warning",
81 "summary": "`defaultCapacity` must be exactly 1 for `type: APPOINTMENT` and greater than 1 for `CLASS`/`COURSE`. The source rarely carries a capacity figure (The Events Calendar has none) — pick and document a deterministic default rather than guessing per record."
82 },
83 {
84 "code": "no-category-means-invisible",
85 "severity": "warning",
86 "summary": "Create Service: 'Without an assigned category, the service isn't visible on the live site.' Services are not auto-categorized, so the import must create/crosswalk a Categories V2 category and set category.id, or every imported service silently fails to appear. Separately, service.hidden controls whether the service shows in Wix Bookings pages and widgets - set it deliberately rather than letting the default decide."
87 },
88 {
89 "code": "no-organizer-or-category-field",
90 "severity": "warning",
91 "summary": "Bookings Services have no organizer/contact field and use their own Categories V2 catalog, disconnected from any other domain's categories (e.g. Wix Events categories). A source record's organizer name/email and taxonomy have nowhere native to land and are dropped unless explicitly folded into `description` or a real Bookings category is created and linked."
96 "summary": "schedule.availabilityConstraints.sessionDurations is required for appointment-based services and must NOT be specified for classes or courses. For appointment services with varied pricing by session length the field is still mandatory but its values are IGNORED - real durations come from Service Options and Variants. Duration values are minutes, min 1 and max 44639 (30 days minus a minute), maxItems 50."
101 "summary": "payment.options.pricingPlan cannot be combined with onlineBooking.requireManualApproval, and online payment requires rateType FIXED or VARIED. A source service that is both 'admin must confirm' and 'payable by membership' cannot be recreated as-is; pick one and ledger the other."
102 },
103 {
104 "code": "location-country-is-iso-alpha-2",
105 "severity": "warning",
106 "summary": "`locations[].custom.address.country` validates as an ISO-3166-1 alpha-2 code ('IL'), not a free-text country name ('Israel'). `formattedAddress` and `city` are free text and accept the source value directly; convert or omit `country`."
107 },
108 {
109 "code": "locations-immutable-on-bulk-create",
110 "severity": "warning",
111 "summary": "The Bulk Create Services parameter table marks locations as immutable (maxItems 500). Set Service Locations exists as a dedicated method, so plan location assignment as part of the create payload or through that method - not as a later Update Service field patch."
116 "summary": "Create Service explicitly says not to specify existing add-on groups; use Create Add On Group / Set Add Ons For Group after the service exists. Add-on import is therefore a second pass (bookings/service-add-on), ordered after the service."
121 "summary": "`hidden: true` only affects the public storefront/booking page — a migrated service still shows normally under the dashboard's Bookings > Services list, live-confirmed by the site owner. Do not treat 'I don't see my migrated classes' as evidence `hidden` needs to change; ask which dashboard view is being checked first. A genuinely separate dashboard view lists actual customer bookings/reservations (appointments against a session), not services or sessions themselves — that view is correctly empty when the source recorded zero registrants (e.g. The Events Calendar exposes no RSVP/attendee data on its public REST routes), since this migration only imports service+session definitions, never historical bookings (see the unsupported-native-gap fallback above)."
122 }
123 ],
124 "mappingGuidance": [
125 "One Wix service per source bookable record. For the WooCommerce mwb_booking product type the natural target is type APPOINTMENT (the plugin books a person-less time window against one product); a source product whose per-slot capacity is greater than 1 (mwb_mbfw_max_bookings / mwb_mbfw_maximum_booking_per_unit) is a CLASS instead, because APPOINTMENT forces defaultCapacity = 1.",
126 "Small-group workshops/classes with per-attendee capacity and 'booking required' language are a Bookings fit (type CLASS); large public ticketed happenings stay Wix Events — see rp-source-wordpress's the-events-calendar profile for the classification heuristic.",
127 "Create with `hidden: true` and `onlineBooking.enabled: false` for an initial migration pass, mirroring Wix Events' `draft: true` — this avoids turning on real online booking/payment collection before a human reviews pricing and policy.",
128 "name and description come from the product title and description; media.mainMedia takes the product image after the media import has uploaded it (Media Manager GUIDs, not source URLs).",
129 "payment.rateType FIXED with payment.fixed.price = the product price (the plugin writes its unit cost to both mwb_mbfw_booking_unit_cost and _price). payment.options.inPerson = true is the safe default, because the source checkout (WooCommerce gateways) does not migrate.",
130 "A blank/empty source cost defaults to NO_FEE (free) — record this as an explicit mapping note per affected record, never invent a price.",
131 "onlineBooking.enabled = true, and onlineBooking.requireManualApproval = (source mwb_mbfw_admin_confirmation === 'yes') - that flag is the plugin's 'Booking confirmation required by admin' checkbox.",
132 "schedule.availabilityConstraints.sessionDurations: derive minutes from the source cadence pair mwb_mbfw_booking_unit x mwb_mbfw_booking_count. A day-unit source booking has no minute duration of its own; use the product's daily window (mwb_mbfw_daily_calendar_start_time / mwb_mbfw_daily_calendar_end_time) where present and ledger the assumption otherwise.",
133 "locations: the plugin stores a free-text venue in mwb_mbfw_booking_location, which maps to a CUSTOM location (the documented default type). Fold a per-event source venue into `locations[0]` as a CUSTOM address (formattedAddress + city + ISO alpha-2 country code) rather than BUSINESS. Business locations must exist first if the source venue should become a real location rather than free text.",
134 "category.id: crosswalk the source product category (WooCommerce product_cat) to a Categories V2 category. Without it the service is invisible on the live site. [NEEDS-VERIFICATION] the mwb-bookings-for-woocommerce profile records that the plugin assigns a 'booking' product_cat term only when the PRO edition is active with its category setting enabled, and that was never observed live - so do not assume a discriminating source category exists. Until bookings/service-category is authored (the categories-v2 gap in the docs survey), setup creates a single catch-all category and the source's real category structure is flattened into it; ledger that loss.",
135 "Import services BEFORE bookings and before add-ons: a booking needs bookedEntity plus a resolvable service, and add-on groups can only be attached to a service that already exists.",
136 "Bulk Create Services accepts up to 100 services per call and is the documented path for large catalogs, but the verified-live writer (`createBookingsService`) creates one at a time - persist the returned service.id (and schedule.id, which is the handle into Business Management > Calendar) against the source record id in the crosswalk either way."
148 "note": "object page: type enum APPOINTMENT|CLASS|COURSE, hidden, category (services aren't auto-assigned; without one the service is invisible), defaultCapacity, form, onlineBooking, locations, policy, schedule.id (links into Business Management > Calendar), staffMemberIds are resource IDs and are read-only for classes/courses (read 2026-08-16)"
153 "note": "POST https://www.wixapis.com/_api/bookings/v2/services; required type + name + onlineBooking + payment + defaultCapacity; defaultCapacity must be 1 for appointments; rateType FIXED|VARIED|CUSTOM|NO_FEE and at least one of payment.options.inPerson/online; pricingPlan option incompatible with requireManualApproval; sessionDurations required for appointments and forbidden for classes/courses; staffMemberIds are resource GUIDs; don't specify existing add-on groups (read 2026-08-16)"
171 "note": "source shape: mwb_booking product type + unregistered mwb_mbfw_*/wps_mbfw_* product meta (cadence, capacity, venue, admin confirmation, daily window)"
172 },
173 {
174 "type": "live-verification",
175 "path": "migrations/reference-run (2026-08-12): real POST /bookings/v2/services, POST /calendar/v3/events, POST /apps-installer-service/v1/app-instance/install, POST /form-schema-service/v4/forms/{id}/clone against the live reference store; 8/8 services + sessions created and independently re-queried."
176 }
177 ],
178 "notes": "Two independent authorings merged 2026-08-16. The Events Calendar path (WordPress events -> CLASS services + Calendar V3 sessions) was exercised live on the reference store 2026-08-12, 8/8 - that is where reliability:reliable and the verified error strings come from. The WooCommerce mwb-bookings-for-woocommerce path (WPS Bookings for WooCommerce 3.12.0, authored 2026-08-16) is docs-verified only and has never been exercised live; treat its cadence/capacity/venue meta mapping as unproven. Service creation itself has no documented customer-facing notification (the only 'notif' hits on the Create Service and Service object pages are waitlist reservationTimeInMinutes prose), which is why importSafe is true here while bookings/booking is false."