@wix/agent-skills 1.19.1 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +1 -1
- package/skills/wix-app/references/SERVICE_PLUGIN.md +14 -1
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +7 -2
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +53 -28
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +17 -4
- package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +71 -31
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +3 -4
- package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +4 -0
- package/skills/wix-app/references/service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.md +118 -0
- package/skills/wix-app/references/service-plugin/BOOKING-POLICY.md +65 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-PRICING.md +71 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +11 -3
- package/skills/wix-app/references/service-plugin/BOOKINGS-VALIDATION.md +137 -0
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +23 -16
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +4 -2
- package/skills/wix-app/references/service-plugin/REALTIME-PERMISSIONS.md +45 -0
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +15 -0
- package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +4 -0
- package/skills/wix-app/references/service-plugin/VALIDATIONS.md +4 -0
- package/skills/wix-base44-connector/SKILL.md +46 -24
- package/skills/wix-headless/SKILL.md +1 -0
- package/skills/wix-headless/references/CAPABILITIES.md +7 -2
- package/skills/wix-headless/references/SDK_HANDOFF.md +2 -1
- package/skills/wix-headless/references/inline-recipes/experience-store.md +49 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +431 -115
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +1 -1
- package/skills/wix-headless/references/managed/CREATE.md +5 -0
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +8 -8
- package/skills/wix-headless-fast/install/fast-path.mjs +3 -1
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +13 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +227 -56
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +37 -3
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +266 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +165 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +48 -15
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +129 -11
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +36 -6
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +190 -24
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +62 -4
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +21 -2
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +5 -1
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +59 -6
- package/skills/wix-manage/SKILL.md +8 -5
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +4 -1
- package/skills/wix-manage/references/bookings/create-appointment-service.md +3 -1
- package/skills/wix-manage/references/bookings/create-class-service.md +3 -1
- package/skills/wix-manage/references/bookings/create-course-service.md +4 -2
- package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +68 -3
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-seasonal-promotion.md +1 -1
- package/skills/wix-manage/references/events/create-wix-event.md +46 -0
- package/skills/wix-manage/references/events/manage-wix-events.md +51 -1
- package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +6 -6
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +108 -14
- package/skills/wix-manage/references/seo/manage-content-plan.md +1 -1
- package/skills/wix-manage/references/seo/manage-seo-suggestions.md +351 -0
- package/skills/wix-manage/references/sites/upload-static-site.md +264 -34
- package/skills/wix-vibe-headless/install/deploy.cjs +33 -2
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +1 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +13 -4
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
- package/skills/wix-vibe-headless/platforms/base44.md +19 -4
- package/skills/wix-vibe-headless/platforms/generic.md +6 -0
- package/skills/wix-vibe-headless/references/_shared/nav/nav.tanstack.js +30 -0
- package/skills/wix-vibe-headless/references/_shared/routing.md +86 -0
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +44 -0
- package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +36 -0
- package/skills/wix-vibe-headless/references/shared/app/lib/nav.js +9 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +110 -27
- package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +32 -14
- package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +14 -3
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +47 -35
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +77 -30
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +31 -2
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +31 -6
|
@@ -10,14 +10,24 @@ export interface ProductSummary {
|
|
|
10
10
|
id: string;
|
|
11
11
|
slug: string;
|
|
12
12
|
name: string;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The price the buyer pays for the cheapest variant, formatted (an automatic discount already
|
|
15
|
+
* applied). When `price !== maxPrice` the product is a RANGE — render "price – maxPrice".
|
|
16
|
+
*/
|
|
14
17
|
price: string;
|
|
15
|
-
/** Highest price, formatted — differs from `price` when variants are priced differently. */
|
|
18
|
+
/** Highest variant price, formatted — differs from `price` when variants are priced differently. */
|
|
16
19
|
maxPrice: string;
|
|
17
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Struck "was" price, formatted; null when not on sale — and ALWAYS null for a range (a lone
|
|
22
|
+
* struck minimum beside a range claims a saving that may not apply to the variant picked).
|
|
23
|
+
*/
|
|
18
24
|
compareAtPrice: string | null;
|
|
19
|
-
/**
|
|
25
|
+
/** The primary merchant ribbon ("New", "Best Seller"); null when none. Same as ribbons[0]. */
|
|
20
26
|
ribbon: string | null;
|
|
27
|
+
/** EVERY merchant ribbon, primary first ("New", "Sale") — render all of them, one shared style. */
|
|
28
|
+
ribbons: string[];
|
|
29
|
+
/** The cheapest variant's id — what a direct add sends for a product with no options; null when unknown. */
|
|
30
|
+
minPriceVariantId: string | null;
|
|
21
31
|
availability: Availability;
|
|
22
32
|
/** OUT_OF_STOCK but pre-orderable — label "Pre-order", not "Sold out". */
|
|
23
33
|
preorder: boolean;
|
|
@@ -27,6 +37,11 @@ export interface ProductSummary {
|
|
|
27
37
|
hoverImageUrl: string;
|
|
28
38
|
/** e.g. "2 colors · 3 sizes"; "" for a single-variant product. */
|
|
29
39
|
optionsSummary: string;
|
|
40
|
+
/**
|
|
41
|
+
* Hex colors of a color option's visible choices, catalog order — render as small dots on the
|
|
42
|
+
* tile (a preview, not a picker: selection happens in QuickAdd or on the PDP). [] when none.
|
|
43
|
+
*/
|
|
44
|
+
swatches: string[];
|
|
30
45
|
/** True when the product can be added to the cart with no choices (single variant, in stock). */
|
|
31
46
|
quickAddable: boolean;
|
|
32
47
|
}
|
|
@@ -60,15 +75,21 @@ export interface ProductVariant {
|
|
|
60
75
|
variantId: string;
|
|
61
76
|
/** The option selections this variant answers to: optionName -> choiceName. */
|
|
62
77
|
choices: Record<string, string>;
|
|
78
|
+
/** The price the buyer pays, formatted — an automatic discount beats the regular price. */
|
|
63
79
|
price: string;
|
|
80
|
+
/** Struck "was" price, formatted; null unless it is real and higher than `price`. */
|
|
64
81
|
compareAtPrice: string | null;
|
|
65
82
|
inStock: boolean;
|
|
83
|
+
/** Out of stock but pre-orderable — still buyable (the add carries preOrderRequested). */
|
|
84
|
+
preorderEnabled: boolean;
|
|
66
85
|
}
|
|
67
86
|
|
|
68
87
|
/** A product as the detail page needs it. */
|
|
69
88
|
export interface ProductDetail extends ProductSummary {
|
|
70
89
|
/** Product description as an HTML string — render with innerHTML, not as text. */
|
|
71
90
|
descriptionHtml: string;
|
|
91
|
+
/** Merchant info sections (materials, shipping, care…) — title + HTML; render as sections or accordions. */
|
|
92
|
+
infoSections: { title: string; html: string }[];
|
|
72
93
|
/** Every gallery image as a resolved https URL, main image first, de-duplicated. */
|
|
73
94
|
gallery: string[];
|
|
74
95
|
options: ProductOption[];
|
|
@@ -80,6 +101,36 @@ export interface Category {
|
|
|
80
101
|
id: string;
|
|
81
102
|
slug: string;
|
|
82
103
|
name: string;
|
|
104
|
+
/** Plain-text description when the merchant wrote one; "" otherwise. */
|
|
105
|
+
description: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface FacetChoice {
|
|
109
|
+
/** The option choice id — what searchCatalog({ choiceIds }) filters on. */
|
|
110
|
+
id: string;
|
|
111
|
+
name: string;
|
|
112
|
+
/** Hex color for a swatch facet; null for a text facet. */
|
|
113
|
+
colorCode: string | null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** One filterable option across the catalog (or a category): "Color" with its choices. */
|
|
117
|
+
export interface Facet {
|
|
118
|
+
name: string;
|
|
119
|
+
isColor: boolean;
|
|
120
|
+
choices: FacetChoice[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The catalog's (or category's) lowest and highest product price, as numbers in site currency — the slider's bounds. */
|
|
124
|
+
export interface PriceRange {
|
|
125
|
+
min: number;
|
|
126
|
+
max: number;
|
|
127
|
+
currency: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** What the filter panel needs beyond the product page: the facets and the price bounds of the scope. */
|
|
131
|
+
export interface FacetData {
|
|
132
|
+
facets: Facet[];
|
|
133
|
+
priceRange: PriceRange | null;
|
|
83
134
|
}
|
|
84
135
|
|
|
85
136
|
export interface CartLine {
|
|
@@ -97,6 +148,11 @@ export interface CartLine {
|
|
|
97
148
|
descriptionLines: string[];
|
|
98
149
|
/** Not IN_STOCK → the line can't be checked out as-is. */
|
|
99
150
|
status: string;
|
|
151
|
+
/**
|
|
152
|
+
* The recurring plan's terms for a subscription line — "Monthly plan · every month · 12 payments";
|
|
153
|
+
* "" for a one-time purchase. A subscription line must read as one in the cart.
|
|
154
|
+
*/
|
|
155
|
+
subscription: string;
|
|
100
156
|
}
|
|
101
157
|
|
|
102
158
|
export interface Cart {
|
|
@@ -105,5 +161,7 @@ export interface Cart {
|
|
|
105
161
|
itemCount: number;
|
|
106
162
|
/** Formatted subtotal (after discounts) — from the cart estimate, not hand-summed. */
|
|
107
163
|
subtotal: string;
|
|
164
|
+
/** Formatted CART-level discount (a coupon or cart rule) — "" when none; item discounts are already in subtotal. */
|
|
165
|
+
discount: string;
|
|
108
166
|
currency: string;
|
|
109
167
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
// The site chrome for every storefront page — THIS file (header/footer/nav) plus the @theme
|
|
3
3
|
// token block in src/styles/global.css are what you brand. Keep:
|
|
4
|
-
// - the <slot name="seo-tags" /> in <head> (the product page's owner-editable SEO lands there
|
|
4
|
+
// - the <slot name="seo-tags" /> in <head> (the product page's owner-editable SEO lands there,
|
|
5
|
+
// including the <title> — which is why ours is printed only when the slot is empty)
|
|
5
6
|
// - the global.css import (Tailwind + the design tokens every shipped component reads)
|
|
6
7
|
// - one <CartButton> in the header and one <CartDrawer> per page (both client:only —
|
|
7
8
|
// they read browser cart state and must not server-render)
|
|
9
|
+
// - category links in the nav (or your own menu/footer) — every category page reachable by
|
|
10
|
+
// a real link (`/category/<slug>`), from the live tree, so a category the owner adds later
|
|
11
|
+
// shows up with no code change
|
|
8
12
|
import CartButton from "../components/storefront/CartButton";
|
|
9
13
|
import CartDrawer from "../components/storefront/CartDrawer";
|
|
14
|
+
import { fetchCategories } from "../wix/storefront/catalog";
|
|
15
|
+
import type { Category } from "../wix/storefront/types";
|
|
10
16
|
import "../styles/global.css";
|
|
11
17
|
|
|
12
18
|
interface Props {
|
|
@@ -14,6 +20,15 @@ interface Props {
|
|
|
14
20
|
description?: string;
|
|
15
21
|
}
|
|
16
22
|
const { title, description } = Astro.props;
|
|
23
|
+
|
|
24
|
+
// Non-fatal: the chrome renders without the links if the read fails. Six in the header keeps a
|
|
25
|
+
// large tree from crowding it — put the rest in a menu or the footer, but keep them linked.
|
|
26
|
+
let navCategories: Category[] = [];
|
|
27
|
+
try {
|
|
28
|
+
navCategories = (await fetchCategories()).slice(0, 6);
|
|
29
|
+
} catch {
|
|
30
|
+
/* the shop link still reaches everything */
|
|
31
|
+
}
|
|
17
32
|
---
|
|
18
33
|
|
|
19
34
|
<!doctype html>
|
|
@@ -21,7 +36,8 @@ const { title, description } = Astro.props;
|
|
|
21
36
|
<head>
|
|
22
37
|
<meta charset="UTF-8" />
|
|
23
38
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
24
|
-
<
|
|
39
|
+
{/* The product page's <SEO.Tags> renders the <title>; print ours only when no page filled the slot — two titles otherwise. */}
|
|
40
|
+
{!Astro.slots.has("seo-tags") && <title>{title}</title>}
|
|
25
41
|
{description && <meta name="description" content={description} />}
|
|
26
42
|
<slot name="seo-tags" />
|
|
27
43
|
</head>
|
|
@@ -31,6 +47,9 @@ const { title, description } = Astro.props;
|
|
|
31
47
|
<a href="/" class="text-base font-bold tracking-tight text-foreground no-underline">Brand Name</a>
|
|
32
48
|
<nav class="flex items-center gap-6 text-sm">
|
|
33
49
|
<a href="/shop" class="text-foreground no-underline transition-colors hover:text-muted-foreground">Shop</a>
|
|
50
|
+
{navCategories.map((c) => (
|
|
51
|
+
<a href={`/category/${c.slug}`} class="hidden text-foreground no-underline transition-colors hover:text-muted-foreground md:inline">{c.name}</a>
|
|
52
|
+
))}
|
|
34
53
|
<CartButton client:only="react" />
|
|
35
54
|
</nav>
|
|
36
55
|
</div>
|
|
@@ -75,7 +75,11 @@ the shipped UI**: give at least one product a color option and put one product o
|
|
|
75
75
|
truthfully to the business (a ceramics studio has glaze colors; a bakery doesn't).
|
|
76
76
|
|
|
77
77
|
**Seeding is additive — never delete or overwrite existing content.** No cleanup, no removing
|
|
78
|
-
"sample" data, no resets. If a cleanup genuinely seems needed, ask the user first.
|
|
78
|
+
"sample" data, no resets. If a cleanup genuinely seems needed, ask the user first. The one
|
|
79
|
+
exception is built in: the fast path passes `--fresh-site` because it created the site seconds
|
|
80
|
+
earlier, and the seed then deletes the Stores install's sample products before creating yours
|
|
81
|
+
(`samplesDeleted` in the result). Never pass that flag by hand against a site that existed before.
|
|
82
|
+
Categories are idempotent by name too — a re-run reuses "Donuts" instead of creating a second one.
|
|
79
83
|
|
|
80
84
|
**A bulk create can partially succeed.** The result carries `failures: [{ name, error }]` next
|
|
81
85
|
to `products` — read it. A non-empty `failures` means those products are genuinely absent, not
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Storefront seed — a BUILD-TIME script, never shipped in the app. Run it from the project
|
|
2
2
|
// root (where wix.config.json lives) with a plan file:
|
|
3
3
|
//
|
|
4
|
-
// node <SKILL_ROOT>/references/storefront/seed/seed-store.mjs plan.json
|
|
4
|
+
// node <SKILL_ROOT>/references/storefront/seed/seed-store.mjs plan.json [--fresh-site]
|
|
5
5
|
//
|
|
6
6
|
// It mints its own site token via the Wix CLI (the token never leaves this process), installs
|
|
7
7
|
// the Wix Stores app if needed, waits for the V3 catalog, bulk-creates products (variants
|
|
@@ -331,11 +331,34 @@ async function stockOptionlessProducts(ctx, created) {
|
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
// Existing categories by name (for idempotent reruns) — a re-run of the seed must reuse
|
|
335
|
+
// "Donuts", not create a second one. Empty map on any failure (falls back to create).
|
|
336
|
+
export async function queryCategoriesByNames(ctx, names) {
|
|
337
|
+
const out = new Map();
|
|
338
|
+
if (!names.length) return out;
|
|
339
|
+
try {
|
|
340
|
+
const r = await req(ctx, "/categories/v1/categories/query", {
|
|
341
|
+
body: { treeReference: { appNamespace: "@wix/stores", treeKey: null }, query: { cursorPaging: { limit: 100 } } },
|
|
342
|
+
});
|
|
343
|
+
const wanted = new Set(names);
|
|
344
|
+
for (const c of r.categories ?? []) if (wanted.has(c.name) && !out.has(c.name)) out.set(c.name, c.id);
|
|
345
|
+
} catch (e) {
|
|
346
|
+
console.error(`category name pre-check failed (creating everything): ${String(e.message).slice(0, 120)}`);
|
|
347
|
+
}
|
|
348
|
+
return out;
|
|
349
|
+
}
|
|
350
|
+
|
|
334
351
|
// Categories share the @wix/stores tree revision — concurrent creates 409, so: sequential.
|
|
352
|
+
// Idempotent by name: a name that already exists is reused, never duplicated.
|
|
335
353
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/create-category.md
|
|
336
354
|
export async function createCategories(ctx, names) {
|
|
355
|
+
const existing = await queryCategoriesByNames(ctx, names);
|
|
337
356
|
const out = [];
|
|
338
357
|
for (const name of names) {
|
|
358
|
+
if (existing.has(name)) {
|
|
359
|
+
out.push({ id: existing.get(name), name });
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
339
362
|
const r = await req(ctx, "/categories/v1/categories", {
|
|
340
363
|
body: { category: { name, visible: true }, treeReference: { appNamespace: "@wix/stores", treeKey: null } },
|
|
341
364
|
});
|
|
@@ -344,6 +367,24 @@ export async function createCategories(ctx, names) {
|
|
|
344
367
|
return out;
|
|
345
368
|
}
|
|
346
369
|
|
|
370
|
+
// The Stores install drops sample products ("I'm a product") into every new catalog. On a site the
|
|
371
|
+
// fast path JUST created nothing else can exist, so they are deleted before seeding — otherwise the
|
|
372
|
+
// storefront ships with the samples between the real products. Only called with --fresh-site; a
|
|
373
|
+
// seed against an existing site never deletes anything (SEED.md: seeding is additive).
|
|
374
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-delete-products.md
|
|
375
|
+
export async function deleteAllProducts(ctx) {
|
|
376
|
+
let deleted = 0;
|
|
377
|
+
for (let pass = 0; pass < 10; pass++) {
|
|
378
|
+
const r = await req(ctx, "/stores/v3/products/query", { body: { query: { cursorPaging: { limit: 100 } } } });
|
|
379
|
+
const ids = (r.products ?? []).map((p) => p.id).filter(Boolean);
|
|
380
|
+
if (!ids.length) break;
|
|
381
|
+
await req(ctx, "/stores/v3/bulk/products/delete", { body: { productIds: ids } });
|
|
382
|
+
deleted += ids.length;
|
|
383
|
+
if (ids.length < 100) break;
|
|
384
|
+
}
|
|
385
|
+
return deleted;
|
|
386
|
+
}
|
|
387
|
+
|
|
347
388
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/bulk-add-items-to-category.md
|
|
348
389
|
export async function addProductsToCategories(ctx, mapping) {
|
|
349
390
|
for (const [categoryId, productIds] of Object.entries(mapping)) {
|
|
@@ -421,12 +462,22 @@ async function setSiteCurrency(ctx, currency) {
|
|
|
421
462
|
* threaded in memory. This is the default path — call it once instead of the individual
|
|
422
463
|
* functions.
|
|
423
464
|
*/
|
|
424
|
-
export async function setupStore(ctx, { products = [], categories = {}, currency } = {}) {
|
|
465
|
+
export async function setupStore(ctx, { products = [], categories = {}, currency } = {}, { freshSite = false } = {}) {
|
|
425
466
|
validateProducts(products);
|
|
426
467
|
await installStoresApp(ctx);
|
|
427
468
|
// Before any product exists: a product's price is stored in the site currency at create time,
|
|
428
469
|
// so switching afterwards leaves the catalog priced in the old one.
|
|
429
470
|
if (currency) await setSiteCurrency(ctx, currency);
|
|
471
|
+
// A just-created site holds only the install's sample products — clear them (never on an
|
|
472
|
+
// existing site: the flag is the fast path's, set only when it created the site itself).
|
|
473
|
+
let samplesDeleted = 0;
|
|
474
|
+
if (freshSite) {
|
|
475
|
+
try {
|
|
476
|
+
samplesDeleted = await deleteAllProducts(ctx);
|
|
477
|
+
} catch (e) {
|
|
478
|
+
console.error(`sample cleanup failed (seeding on top): ${String(e.message).slice(0, 120)}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
430
481
|
|
|
431
482
|
// Idempotent by name: an errored bulk create (429/5xx) may still have applied server-side,
|
|
432
483
|
// and SKILL.md tells the agent to re-run a failed seed — creating only the names that don't
|
|
@@ -478,21 +529,23 @@ export async function setupStore(ctx, { products = [], categories = {}, currency
|
|
|
478
529
|
// failures is part of the result, not an exception: a partial seed still leaves a usable
|
|
479
530
|
// store, and the agent needs the names to report rather than silently shipping a short
|
|
480
531
|
// catalog. Re-run the seed to retry them — existing names are skipped, not duplicated.
|
|
481
|
-
return { products: withNames, categories: cats, imagesAttached, failures };
|
|
532
|
+
return { products: withNames, categories: cats, imagesAttached, samplesDeleted, failures };
|
|
482
533
|
}
|
|
483
534
|
|
|
484
535
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
485
536
|
|
|
486
537
|
const invokedDirectly = process.argv[1] && import.meta.url.endsWith(process.argv[1].split("/").pop());
|
|
487
538
|
if (invokedDirectly) {
|
|
488
|
-
const
|
|
539
|
+
const args = process.argv.slice(2);
|
|
540
|
+
const freshSite = args.includes("--fresh-site");
|
|
541
|
+
const planPath = args.find((a) => !a.startsWith("--"));
|
|
489
542
|
if (!planPath) {
|
|
490
|
-
console.error("usage: node seed-store.mjs <plan.json> (run from the project root)");
|
|
543
|
+
console.error("usage: node seed-store.mjs <plan.json> [--fresh-site] (run from the project root)");
|
|
491
544
|
process.exit(1);
|
|
492
545
|
}
|
|
493
546
|
const plan = JSON.parse(readFileSync(planPath, "utf8"));
|
|
494
547
|
const ctx = makeCtx();
|
|
495
|
-
setupStore(ctx, plan)
|
|
548
|
+
setupStore(ctx, plan, { freshSite })
|
|
496
549
|
.then((result) => console.log(JSON.stringify(result, null, 2)))
|
|
497
550
|
.catch((e) => {
|
|
498
551
|
console.error(e.message);
|
|
@@ -48,6 +48,9 @@ Generate an SEO content plan and read its blog post topics, or troubleshoot an e
|
|
|
48
48
|
### [Manage Google Search Console for a Wix Site](references/seo/manage-google-search-console.md)
|
|
49
49
|
"Connect a Wix site to Google Search Console and drive its setup through the public GSC Connection and Site Readiness APIs: check connection and readiness, start the Google authorization, verify ownership, add the property, submit the sitemap, request indexing, read search performance, run URL inspection, recover a stale connection, or disconnect. The site owner authorizes in their own browser via a single-use connect URL."
|
|
50
50
|
|
|
51
|
+
### [Generate AI SEO Suggestions for a Wix Site](references/seo/manage-seo-suggestions.md)
|
|
52
|
+
"Generate AI-written SEO text for a Wix site through the public Tag Suggestions and Page Optimization APIs: title tag and meta description options for a page, alt text for the images of a page, store product, or blog post (one image or up to 20 per call), corrected heading levels for a page's headings, and a whole-page rewrite of title, description, headings, and body text aimed at the page's focus keyword, returned as before/after pairs. Use it when the user wants to improve, optimize, audit, or write the SEO text of a page or the homepage. Suggestions are returned for review; applying one is a separate SEO tags write."
|
|
53
|
+
|
|
51
54
|
---
|
|
52
55
|
|
|
53
56
|
## Accessibility
|
|
@@ -107,7 +110,7 @@ Full CRUD operations for Wix Bookings services using Services API. Covers servic
|
|
|
107
110
|
"Answers whether an appointment-based Wix Bookings service currently has bookable availability — the primary question — and diagnoses the cause only when there's no availability or the owner asks why. To diagnose, first rules out service-level blockers the availability endpoint can't see (service hidden, online booking off), then runs DiagnoseAvailability for ordered, machine-readable staff/setup reasons, with a manual fallback for booking-policy and capacity causes. Use when someone asks whether a service has availability, or why a service shows no times / customers can't book it."
|
|
108
111
|
|
|
109
112
|
### [End-to-End Booking Flow](references/bookings/end-to-end-booking-flow.md)
|
|
110
|
-
|
|
113
|
+
Books and settles appointments, classes and courses with the site owner's credentials — an operator managing bookings, or server-side code booking as the owner. Covers service discovery, availability with Time Slots V2, creating the booking, and settling it by direct confirmation or eCommerce checkout. A visitor booking for themselves needs a visitor token instead; this recipe links that path.
|
|
111
114
|
|
|
112
115
|
### [External Calendar Integration](references/bookings/external-calendar-integration.md)
|
|
113
116
|
OAuth-based integration with Google Calendar, Microsoft Outlook, and Apple Calendar. Covers authentication flows, sync configuration, and bidirectional event management.
|
|
@@ -251,7 +254,7 @@ Buy a domain through Wix or connect one the user already owns — intent, availa
|
|
|
251
254
|
"Creates an event with the Wix Events V3 API — the required request body, ISO-8601 date and time settings, venue/online/TBD location, RSVP vs ticketed registration, guest capacity, short vs rich-text descriptions, ticket tiers and pricing, and recurring series. Covers the exact field shapes and the API's misleading validation messages. Use when the user wants to create an event, set its date, location, description, guest limit or ticket prices, or set up a repeating event."
|
|
252
255
|
|
|
253
256
|
### [Manage Wix Events — Publishing, Cancelling, Cloning and Counting](references/events/manage-wix-events.md)
|
|
254
|
-
"Operates on events that already exist with the Wix Events V3 API — publishing a draft, cancelling, deleting, cloning, updating an event's date or details, and counting events. Use when the user wants to publish or cancel an event, duplicate one, move an event's date, or count their events. Creating an event, its tickets or a recurring series is a separate recipe."
|
|
257
|
+
"Operates on events that already exist with the Wix Events V3 API — finding an event by title, publishing a draft, cancelling, deleting (one or by filter), cloning, updating an event's date or details, and counting events. Use when the user wants to find, publish or cancel an event, duplicate one, move an event's date, delete events in bulk, or count their events. Creating an event, its tickets or a recurring series is a separate recipe."
|
|
255
258
|
|
|
256
259
|
---
|
|
257
260
|
|
|
@@ -289,7 +292,7 @@ Creates payment links for unpaid bookings using Payment Links API. Links booking
|
|
|
289
292
|
**Routing — Google paid-advertising campaigns for a site (Smart & Performance Max).** All flows require a Google Ads account, created once via the setup recipe. Budgets are in micros (1,000,000 = 1 currency unit). REST base: `https://www.wixapis.com/_serverless/pa-google/v1`.
|
|
290
293
|
- **First-time setup / "connect Google Ads" / `ACCOUNT_NOT_FOUND`** → [Install and Create an Account](references/google-ads/install-and-create-account.md) (do this before anything else).
|
|
291
294
|
- **Suggested keywords / geo / budget / ad copy / images** → [Get AI Campaign Suggestions](references/google-ads/get-campaign-suggestions.md).
|
|
292
|
-
- **
|
|
295
|
+
- **Improve an existing campaign / see what to fix next / Campaign Success Guide / mark or reopen a recommendation** → [Manage a Campaign Success Guide](references/google-ads/manage-campaign-success-guide.md).
|
|
293
296
|
- **Create a multi-channel / lead-gen / Shopping campaign** → [Create a Performance Max Campaign](references/google-ads/create-performance-max-campaign.md).
|
|
294
297
|
- **Pause / resume / launch / update budget / delete / history** → [Manage Campaign Lifecycle](references/google-ads/manage-campaign-lifecycle.md).
|
|
295
298
|
- **Performance, conversions, search terms, per-product / per-asset metrics** → [Query Campaign Performance Analytics](references/google-ads/query-campaign-analytics.md).
|
|
@@ -308,7 +311,7 @@ Creates payment links for unpaid bookings using Payment Links API. Links booking
|
|
|
308
311
|
"Manages existing Google Ads campaigns on a Wix site: list/get, launch (first activation) vs resume (reactivate after a pause), pause a running campaign — optionally with a scheduled auto-resume date — update name/budget/targeting, change the daily budget, delete permanently, and read status history / change log. Use when the user wants to 'pause my Google ad', 'resume my campaign', 'stop the campaign', 'change my daily budget', 'rename the campaign', 'delete this campaign', 'list my Google Ads campaigns', or 'why did my campaign status change'. Requires an existing Google Ads account and campaign. REST base https://www.wixapis.com/_serverless/pa-google/v1."
|
|
309
312
|
|
|
310
313
|
### [Manage a Campaign Success Guide](references/google-ads/manage-campaign-success-guide.md)
|
|
311
|
-
"
|
|
314
|
+
"Campaign Success Guide for existing Wix Google Ads Performance Max Leads campaigns. Use after creating a supported campaign, even while it is learning or has no metrics, and whenever users ask how to improve a campaign, what to fix next, to view the guide, or to mark or reopen a recommendation. Covers campaign and site selection; prioritized actionable recommendations; deduplicated, destination-specific Editor and Google Ads navigation; offers to perform supported work after approval; Merchant Center and Business Profile connection follow-ups; and suggestion-status tracking."
|
|
312
315
|
|
|
313
316
|
### [Query Campaign Performance Analytics](references/google-ads/query-campaign-analytics.md)
|
|
314
317
|
"Reads performance analytics for a Google Ads campaign on a Wix site: daily performance metrics (impressions, clicks, CTR, cost, leads, phone calls) with optional previous-period comparison and trends; conversion metrics from Wix Analytics (orders, revenue, leads, CPL, ROAS); the search terms that triggered a campaign's ads; per-product shopping performance for retail campaigns; and per-asset performance (headlines, descriptions, images) for PMAX Leads. Explains when to use campaignResourceName vs the Wix campaignId, the dateRange shape, field enums, sorting, and paging. Use when the user asks 'how is my campaign doing', 'show ad performance', 'what search terms triggered my ads', 'which products/assets perform best', 'campaign ROI/ROAS', or 'conversions from my Google ads'. REST base https://www.wixapis.com/_serverless/pa-google/v1."
|
|
@@ -425,7 +428,7 @@ Drive the Wix Site Import agent to migrate an existing store or site from anothe
|
|
|
425
428
|
"Builds direct links to the account-level sites pages on manage.wix.com — the My Sites list (all sites in the account) and each site's own dashboard. Pairs the site list with the Query Sites read API. Use when the user asks where something is in the Wix dashboard, wants a direct link to a dashboard page, or you need a dashboard URL to include with the result of an API operation."
|
|
426
429
|
|
|
427
430
|
### [Upload a Website or HTML Files](references/sites/upload-static-site.md)
|
|
428
|
-
|
|
431
|
+
Publish a user's ready-made website — an index.html, a static build, or a zip exported from an AI builder or any other tool — as a new live Wix site. Covers both ways to get there — handing the user the Wix Headless drop page, and calling the Wix Headless instant-site REST API yourself when you can reach the files and make outbound HTTPS requests. When you also hold the user's identity, covers putting the published site straight into their Wix account and reading back its final live URL. Use whenever the user wants to upload, publish, deploy, or host their own HTML/CSS/JS as a NEW site, including files generated for them earlier in the conversation, or to update a site already published this way (iterate on the same site instead of creating another). Not for migrating a live store/site from another platform by URL or from CSV exports (use Site Import), not for adding HTML or custom code into an existing Wix site, and not for uploading images or documents to a site's media files.
|
|
429
432
|
|
|
430
433
|
---
|
|
431
434
|
|
|
@@ -201,6 +201,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
201
201
|
-H 'Authorization: <AUTH>' \
|
|
202
202
|
-H 'Content-Type: application/json' \
|
|
203
203
|
-d '{
|
|
204
|
+
"returnEntity": true,
|
|
204
205
|
"services": [{
|
|
205
206
|
"name": "Consultation",
|
|
206
207
|
"type": "APPOINTMENT",
|
|
@@ -234,6 +235,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
234
235
|
-H 'Authorization: <AUTH>' \
|
|
235
236
|
-H 'Content-Type: application/json' \
|
|
236
237
|
-d '{
|
|
238
|
+
"returnEntity": true,
|
|
237
239
|
"services": [{
|
|
238
240
|
"name": "Free Consultation",
|
|
239
241
|
"type": "APPOINTMENT",
|
|
@@ -264,6 +266,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
264
266
|
-H 'Authorization: <AUTH>' \
|
|
265
267
|
-H 'Content-Type: application/json' \
|
|
266
268
|
-d '{
|
|
269
|
+
"returnEntity": true,
|
|
267
270
|
"services": [{
|
|
268
271
|
"name": "Yoga Class",
|
|
269
272
|
"type": "CLASS",
|
|
@@ -283,7 +286,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
283
286
|
}'
|
|
284
287
|
```
|
|
285
288
|
|
|
286
|
-
After creation, use
|
|
289
|
+
After creation, use `results[0].item.schedule.id` from the response to create class events with `bulkCreateEvents` (see Step 3). This requires `returnEntity: true` on the request — without it the response carries only `results[0].itemMetadata.id`, which has no `schedule.id`; and the created service is directly under `item` (there is no `item.service`).
|
|
287
290
|
|
|
288
291
|
**Required Fields:**
|
|
289
292
|
|
|
@@ -109,6 +109,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
109
109
|
-H 'Authorization: <AUTH>' \
|
|
110
110
|
-H 'Content-Type: application/json' \
|
|
111
111
|
-d '{
|
|
112
|
+
"returnEntity": true,
|
|
112
113
|
"services": [{
|
|
113
114
|
"name": "<SERVICE_NAME>",
|
|
114
115
|
"description": "<GENERATED_DESCRIPTION>",
|
|
@@ -142,6 +143,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
142
143
|
-H 'Authorization: <AUTH>' \
|
|
143
144
|
-H 'Content-Type: application/json' \
|
|
144
145
|
-d '{
|
|
146
|
+
"returnEntity": true,
|
|
145
147
|
"services": [{
|
|
146
148
|
"name": "<SERVICE_NAME>",
|
|
147
149
|
"description": "<GENERATED_DESCRIPTION>",
|
|
@@ -171,7 +173,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
171
173
|
- `schedule.availabilityConstraints.sessionDurations` sets the appointment length
|
|
172
174
|
- Availability is based on the assigned staff member's working hours schedule
|
|
173
175
|
|
|
174
|
-
Save the `serviceId` from the response: `results[0].item.service.id
|
|
176
|
+
Save the `serviceId` from the response: `results[0].item.id` (the created service is directly under `item` — there is no `item.service`). This requires `returnEntity: true` on the request; without it the response carries only `results[0].itemMetadata.id`.
|
|
175
177
|
|
|
176
178
|
---
|
|
177
179
|
|
|
@@ -96,6 +96,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
96
96
|
-H 'Authorization: <AUTH>' \
|
|
97
97
|
-H 'Content-Type: application/json' \
|
|
98
98
|
-d '{
|
|
99
|
+
"returnEntity": true,
|
|
99
100
|
"services": [{
|
|
100
101
|
"name": "<SERVICE_NAME>",
|
|
101
102
|
"description": "<GENERATED_DESCRIPTION>",
|
|
@@ -124,6 +125,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
124
125
|
-H 'Authorization: <AUTH>' \
|
|
125
126
|
-H 'Content-Type: application/json' \
|
|
126
127
|
-d '{
|
|
128
|
+
"returnEntity": true,
|
|
127
129
|
"services": [{
|
|
128
130
|
"name": "<SERVICE_NAME>",
|
|
129
131
|
"description": "<GENERATED_DESCRIPTION>",
|
|
@@ -149,7 +151,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
149
151
|
- `defaultCapacity` is **required** — sets max participants per session
|
|
150
152
|
- After creation, class sessions must be scheduled separately via `bulkCreateEvents` using the returned `service.schedule.id` (see [Create and Update Booking Services](./create-and-update-booking-services.md))
|
|
151
153
|
|
|
152
|
-
**Response:** Save the service ID from `results[0].itemMetadata.id`
|
|
154
|
+
**Response:** Save the service ID from `results[0].item.id` and the schedule ID for `bulkCreateEvents` from `results[0].item.schedule.id` (the created service is directly under `item`). This requires `returnEntity: true` on the request — without it the response carries only `results[0].itemMetadata.id`, which has no `schedule.id`.
|
|
153
155
|
|
|
154
156
|
---
|
|
155
157
|
|
|
@@ -101,6 +101,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
101
101
|
-H 'Authorization: <AUTH>' \
|
|
102
102
|
-H 'Content-Type: application/json' \
|
|
103
103
|
-d '{
|
|
104
|
+
"returnEntity": true,
|
|
104
105
|
"services": [{
|
|
105
106
|
"name": "<SERVICE_NAME>",
|
|
106
107
|
"description": "<GENERATED_DESCRIPTION>",
|
|
@@ -129,6 +130,7 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
129
130
|
-H 'Authorization: <AUTH>' \
|
|
130
131
|
-H 'Content-Type: application/json' \
|
|
131
132
|
-d '{
|
|
133
|
+
"returnEntity": true,
|
|
132
134
|
"services": [{
|
|
133
135
|
"name": "<SERVICE_NAME>",
|
|
134
136
|
"description": "<GENERATED_DESCRIPTION>",
|
|
@@ -157,8 +159,8 @@ curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
|
157
159
|
- After creation, course sessions must be scheduled separately via `bulkCreateEvents` using the returned `service.schedule.id` (see [Create and Update Booking Services](./create-and-update-booking-services.md))
|
|
158
160
|
- If the user asked you to create the course and gave the session count/times, the job is not complete after `bulkCreateServices`; continue by creating the Calendar events, then verify the service has future events before telling the user it is ready
|
|
159
161
|
|
|
160
|
-
Save the `serviceId` from the response: `results[0].item.service.id
|
|
161
|
-
Save the `service.schedule.id` from the response for the follow-up `bulkCreateEvents` request.
|
|
162
|
+
Save the `serviceId` from the response: `results[0].item.id` (the created service is directly under `item` — there is no `item.service`). This requires `returnEntity: true` on the request; without it the response carries only `results[0].itemMetadata.id`.
|
|
163
|
+
Save the `service.schedule.id` from the response (`results[0].item.schedule.id`) for the follow-up `bulkCreateEvents` request.
|
|
162
164
|
|
|
163
165
|
---
|
|
164
166
|
|
|
@@ -1,11 +1,68 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "End-to-End Booking Flow"
|
|
3
|
-
description:
|
|
3
|
+
description: Books and settles appointments, classes and courses with the site owner's credentials — an operator managing bookings, or server-side code booking as the owner. Covers service discovery, availability with Time Slots V2, creating the booking, and settling it by direct confirmation or eCommerce checkout. A visitor booking for themselves needs a visitor token instead; this recipe links that path.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# End-to-End Booking Flow (REST)
|
|
7
7
|
|
|
8
|
-
Step-by-step flow for
|
|
8
|
+
Step-by-step flow for creating and settling bookings **with the site owner's credentials** —
|
|
9
|
+
see who that covers, and where visitor-facing code goes instead, immediately below.
|
|
10
|
+
|
|
11
|
+
> ## ⚠️ These are owner-side calls, not visitor-facing code
|
|
12
|
+
>
|
|
13
|
+
> Every call below runs with the **site owner's credentials** — an API key or the site's admin
|
|
14
|
+
> token. That covers two situations, and both are this recipe:
|
|
15
|
+
>
|
|
16
|
+
> - an **operator managing bookings**, calling these APIs from a script, a management tool, or a
|
|
17
|
+
> back-office screen
|
|
18
|
+
> - **server-side code in a site or app acting as the owner** — a backend function booking a
|
|
19
|
+
> client in, a scheduled job, a webhook handler
|
|
20
|
+
>
|
|
21
|
+
> Either way the booking belongs to the site, not to the person being booked.
|
|
22
|
+
>
|
|
23
|
+
> **Building a site or app where a visitor books for themselves? These payloads are right, the
|
|
24
|
+
> identity is not.** A visitor's booking and checkout run on an **anonymous visitor token** minted
|
|
25
|
+
> from the site's OAuth app client id. Using the owner's credentials for a visitor's purchase
|
|
26
|
+
> produces a checkout that belongs to the site: no cart persistence, no abandoned-checkout
|
|
27
|
+
> recovery, and no attribution to the customer. Read these first:
|
|
28
|
+
>
|
|
29
|
+
> - [Book an Appointment](https://dev.wix.com/docs/api-reference/business-solutions/bookings/skills/book-an-appointment)
|
|
30
|
+
> — the same flow from the visitor's side: visitor token, slot re-validation, booking form
|
|
31
|
+
> fields, checkout hand-off, and eight pitfalls from real conversations
|
|
32
|
+
> - [Retrieve Tokens](https://dev.wix.com/docs/api-reference/business-management/headless/authentication/retrieve-tokens)
|
|
33
|
+
> — minting and refreshing the anonymous visitor token
|
|
34
|
+
> - [Allow Redirect URIs and Domains](https://dev.wix.com/docs/go-headless/authentication/setup/allow-redirect-uris-and-domains)
|
|
35
|
+
> — registering your URLs so the visitor returns to your app after the Wix-hosted checkout
|
|
36
|
+
> - [Manage OAuth Apps](../sites/manage-oauth-apps.md) — creating the OAuth app, whose id is the
|
|
37
|
+
> `client_id` the frontend mints visitor tokens from
|
|
38
|
+
> - [Bookings Quick Start](https://dev.wix.com/docs/go-headless/self-managed-headless/tutorials/java-script-sdk-tutorials/bookings-quick-start)
|
|
39
|
+
> — the same flow through the JavaScript SDK on a headless site
|
|
40
|
+
>
|
|
41
|
+
> Mixed apps are normal: the owner's credentials manage the catalog, staff, policies and coupons
|
|
42
|
+
> (the recipes here), while the visitor's own token books and pays.
|
|
43
|
+
|
|
44
|
+
## Contents
|
|
45
|
+
|
|
46
|
+
1. [Query Available Services](#step-1-query-available-services) — `POST /bookings/v2/services/query`.
|
|
47
|
+
The three service types and how each one changes the rest of the flow, plus the four fields to
|
|
48
|
+
carry forward: `id`, `schedule.id`, `type`, `staffMemberIds`.
|
|
49
|
+
2. [Check Availability](#step-2-check-availability) — `POST /_api/service-availability/v2/time-slots`.
|
|
50
|
+
Time Slots V2 replaces the deprecated Availability Calendar. Dates must be full local
|
|
51
|
+
datetimes or the call 400s; `availableResources` stays empty unless you ask for it by resource
|
|
52
|
+
type; the `location.locationType` a slot returns is **not** the value Create Booking accepts.
|
|
53
|
+
Classes come from a different endpoint and carry an `eventId`.
|
|
54
|
+
3. [Create the Booking](#step-3-create-the-booking) — `POST /_api/bookings-service/v2/bookings`.
|
|
55
|
+
One payload shape per service type: appointments need every slot field spelled out, classes
|
|
56
|
+
need only the service and event ids and derive the rest, courses book a whole schedule.
|
|
57
|
+
Participants are `totalParticipants` **or** `participantsChoices`, never both. The booking
|
|
58
|
+
lands as `CREATED` and is not on the calendar until step 4.
|
|
59
|
+
4. [Confirm or Process Payment](#step-4-confirm-or-process-payment) — two branches. Free and
|
|
60
|
+
pay-at-location bookings are confirmed directly with a payment status. Paid bookings go into a
|
|
61
|
+
cart that references the booking id, and whoever is paying is sent to its checkout URL; a
|
|
62
|
+
server-to-server place-order path skips the payment page entirely, which only fits an operator
|
|
63
|
+
or backend job booking someone in.
|
|
64
|
+
5. [Service Type Summary](#service-type-summary) — appointment, class and course side by side:
|
|
65
|
+
which `bookedEntity` each uses, which availability API feeds it, and what makes it different.
|
|
9
66
|
|
|
10
67
|
## Prerequisites
|
|
11
68
|
|
|
@@ -256,7 +313,15 @@ Use the booking ID as `catalogItemId` with the Wix Bookings app ID. Save `cart.i
|
|
|
256
313
|
|
|
257
314
|
Redirect the user to the returned `checkoutUrl`. After payment, the booking is automatically confirmed.
|
|
258
315
|
|
|
259
|
-
|
|
316
|
+
> When the person paying is a visitor, create the cart and this URL **with their visitor token**,
|
|
317
|
+
> not the owner's credentials — see the note at the top. The call succeeds either way, which is
|
|
318
|
+
> what makes it easy to get wrong.
|
|
319
|
+
|
|
320
|
+
**4c. Place Order — no payment page (alternative, server-to-server)**
|
|
321
|
+
|
|
322
|
+
> This one only makes sense on the owner's side: it creates the order outright, with nobody
|
|
323
|
+
> visiting a payment page. Right for an operator or backend job booking someone in; not a way to
|
|
324
|
+
> charge a visitor, who still has to be sent to checkout.
|
|
260
325
|
|
|
261
326
|
First calculate the cart to get a price-verification token:
|
|
262
327
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Flow: Seasonal Promotion"
|
|
3
|
-
description: SEASONAL sub-flow — load [Goal: Seasonal Revenue] FIRST (it owns classification and routing); this is a sub-step, NOT a direct entry from README.
|
|
3
|
+
description: "SEASONAL sub-flow — load [Goal: Seasonal Revenue] FIRST (it owns classification and routing); this is a sub-step, NOT a direct entry from README."
|
|
4
4
|
---
|
|
5
5
|
# Flow: Seasonal Promotion
|
|
6
6
|
|