@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.
Files changed (99) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +1 -1
  3. package/skills/wix-app/references/SERVICE_PLUGIN.md +14 -1
  4. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +7 -2
  5. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +53 -28
  6. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +17 -4
  7. package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +71 -31
  8. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +3 -4
  9. package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +4 -0
  10. package/skills/wix-app/references/service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.md +118 -0
  11. package/skills/wix-app/references/service-plugin/BOOKING-POLICY.md +65 -0
  12. package/skills/wix-app/references/service-plugin/BOOKINGS-PRICING.md +71 -0
  13. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +11 -3
  14. package/skills/wix-app/references/service-plugin/BOOKINGS-VALIDATION.md +137 -0
  15. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +23 -16
  16. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
  17. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +4 -2
  18. package/skills/wix-app/references/service-plugin/REALTIME-PERMISSIONS.md +45 -0
  19. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +15 -0
  20. package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +4 -0
  21. package/skills/wix-app/references/service-plugin/VALIDATIONS.md +4 -0
  22. package/skills/wix-base44-connector/SKILL.md +46 -24
  23. package/skills/wix-headless/SKILL.md +1 -0
  24. package/skills/wix-headless/references/CAPABILITIES.md +7 -2
  25. package/skills/wix-headless/references/SDK_HANDOFF.md +2 -1
  26. package/skills/wix-headless/references/inline-recipes/experience-store.md +49 -0
  27. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +431 -115
  28. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +1 -1
  29. package/skills/wix-headless/references/managed/CREATE.md +5 -0
  30. package/skills/wix-headless/references/managed/DEPLOYMENT.md +8 -8
  31. package/skills/wix-headless-fast/install/fast-path.mjs +3 -1
  32. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +13 -0
  33. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +227 -56
  34. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +37 -3
  35. package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +266 -0
  36. package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +165 -0
  37. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +48 -15
  38. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +129 -11
  39. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +36 -6
  40. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +190 -24
  41. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +62 -4
  42. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +21 -2
  43. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +5 -1
  44. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +59 -6
  45. package/skills/wix-manage/SKILL.md +8 -5
  46. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +4 -1
  47. package/skills/wix-manage/references/bookings/create-appointment-service.md +3 -1
  48. package/skills/wix-manage/references/bookings/create-class-service.md +3 -1
  49. package/skills/wix-manage/references/bookings/create-course-service.md +4 -2
  50. package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +68 -3
  51. package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-seasonal-promotion.md +1 -1
  52. package/skills/wix-manage/references/events/create-wix-event.md +46 -0
  53. package/skills/wix-manage/references/events/manage-wix-events.md +51 -1
  54. package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +6 -6
  55. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +108 -14
  56. package/skills/wix-manage/references/seo/manage-content-plan.md +1 -1
  57. package/skills/wix-manage/references/seo/manage-seo-suggestions.md +351 -0
  58. package/skills/wix-manage/references/sites/upload-static-site.md +264 -34
  59. package/skills/wix-vibe-headless/install/deploy.cjs +33 -2
  60. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +1 -1
  61. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +13 -4
  62. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
  63. package/skills/wix-vibe-headless/platforms/base44.md +19 -4
  64. package/skills/wix-vibe-headless/platforms/generic.md +6 -0
  65. package/skills/wix-vibe-headless/references/_shared/nav/nav.tanstack.js +30 -0
  66. package/skills/wix-vibe-headless/references/_shared/routing.md +86 -0
  67. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +44 -0
  68. package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +1 -1
  69. package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +1 -1
  70. package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +1 -1
  71. package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +1 -1
  72. package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +1 -1
  73. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +42 -0
  74. package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +1 -1
  75. package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +1 -1
  76. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +42 -0
  77. package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +1 -1
  78. package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +1 -1
  79. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +42 -0
  80. package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +1 -1
  81. package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +1 -1
  82. package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +1 -1
  83. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +43 -0
  84. package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +1 -1
  85. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +1 -1
  86. package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +1 -1
  87. package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +1 -1
  88. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +43 -0
  89. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +1 -1
  90. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +1 -1
  91. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +36 -0
  92. package/skills/wix-vibe-headless/references/shared/app/lib/nav.js +9 -0
  93. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +110 -27
  94. package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +32 -14
  95. package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +14 -3
  96. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +47 -35
  97. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +77 -30
  98. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +31 -2
  99. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +31 -6
@@ -0,0 +1,65 @@
1
+ # Booking Policy Service Plugin Reference
2
+
3
+ ## Overview
4
+
5
+ The Booking Policy SPI lets you provide dynamic booking policies instead of a service's single static policy. Wix calls your `listBookingPolicies` handler whenever it needs to know how early/late a service can be booked or whether online booking is allowed — for example, when a customer loads the booking calendar or books a service. Your implementation can return a different policy for the same service based on your own criteria (e.g., customer type, time of year).
6
+
7
+ ## Request and Response Schema
8
+
9
+ Before implementing, call `ReadFullDocsMethodSchema` on the docs URL to get the full request/response types.
10
+
11
+ | Handler | Docs URL |
12
+ | --- | --- |
13
+ | `listBookingPolicies` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/policies/booking-policy-service-plugin/list-booking-policies?apiView=SDK |
14
+
15
+ The request's `serviceIds` array has 1–8 service GUIDs. Your response's `bookingPolicies` array must contain exactly one policy per requested service, **in the same order** — all fields are required unless the schema marks them optional.
16
+
17
+ ## Example: Different Early-Booking Windows by Service Category
18
+
19
+ ```typescript
20
+ import { bookingPolicy } from "@wix/bookings/service-plugins";
21
+ import { auth } from "@wix/essentials";
22
+ import { services } from "@wix/bookings";
23
+
24
+ bookingPolicy.provideHandlers({
25
+ listBookingPolicies: async (payload) => {
26
+ const { request } = payload;
27
+ const { serviceIds } = request;
28
+
29
+ // queryServices is a fluent query builder, not a request-object call — it returns { items }, not { services }.
30
+ const elevatedQueryServices = auth.elevate(services.queryServices);
31
+ const { items: matchedServices } = await elevatedQueryServices().in('_id', serviceIds).find();
32
+ const serviceById = new Map((matchedServices ?? []).map((s) => [s._id, s]));
33
+
34
+ const bookingPolicies = serviceIds.map((serviceId) => {
35
+ const isPremium = serviceById.get(serviceId)?.category?.name === "Premium";
36
+
37
+ return {
38
+ serviceId,
39
+ limitEarlyBooking: {
40
+ enabled: true,
41
+ earliestBookingInMinutes: isPremium ? 20160 : 10080, // 14 vs 7 days
42
+ },
43
+ limitLateBooking: {
44
+ enabled: true,
45
+ latestBookingInMinutes: 1440, // 1 day
46
+ },
47
+ onlineBooking: { enabled: true },
48
+ };
49
+ });
50
+
51
+ return { bookingPolicies };
52
+ },
53
+ });
54
+ ```
55
+
56
+ ## Manual Setup Required
57
+
58
+ None. Confirmed the overall availability/policy pipeline this plugin feeds into is live and enforcing real constraints (a dashboard booking attempt was correctly rejected for being outside staff working hours) — this SPI's own specific early/late-booking window differentiation wasn't isolated independently of that pipeline, but no dashboard configuration is needed beyond having the app installed and released.
59
+
60
+ ## Key Implementation Notes
61
+
62
+ 1. **One policy per service, same order** — the response array must line up positionally with the request's `serviceIds`; don't omit or reorder entries.
63
+ 2. **All fields required unless marked optional** — return `limitEarlyBooking`, `limitLateBooking`, and `onlineBooking` fully populated rather than partially.
64
+ 3. **Called on the hot path** — this handler runs whenever availability is queried or a booking is made, so keep it fast and avoid unnecessary external calls.
65
+ 4. **Elevate permissions** — use `auth.elevate` when querying Wix APIs (e.g., service details) from the handler.
@@ -0,0 +1,71 @@
1
+ # Bookings Pricing Provider Service Plugin Reference
2
+
3
+ ## ⚠️ Requires manual sign-up before it's live
4
+
5
+ Unlike every other SPI in this skill, this integration isn't self-serve. Before Wix will ever call your handler on a real site, email `bookings-integration@wix.com` with subject "Bookings Pricing Integration" and include: your business type, a few words about your business/market, the URL of the site hosting your app, and your app ID. Wix reviews the request and replies by email. **Implement and build the plugin, but tell the user this sign-up step is required and still pending** — don't present the feature as live just because the code compiles and builds.
6
+
7
+ ## Overview
8
+
9
+ The Bookings Pricing Provider lets your app calculate custom prices for bookings — regional tax, discounts for specific customer types, or any combination — instead of Wix's standard per-variant pricing. Once installed, your plugin becomes the **sole source of price** for the service: Wix does not hand you a pre-computed subtotal to adjust, so if you need a base price, derive or store it yourself (e.g. read it from the service via `@wix/bookings`, or from your own config).
10
+
11
+ Wix calls your handler only *after* a booking already exists (`booking._id` is set) — this SPI cannot be used in advance to preview a price list. There is only one handler; there's no separate "preview" handler.
12
+
13
+ **FQDN**: `wix.bookings.pricing.v1.pricing_provider`
14
+
15
+ ## Request and Response Schema
16
+
17
+ Before implementing, call `ReadFullDocsMethodSchema` on the docs URL to get the full request/response types. Note: the public docs page for this SPI still describes an older REST/webhook-only integration and does not yet reflect the `provideHandlers` SDK wrapper below — trust what `wix generate` actually scaffolds and the installed `@wix/auto_sdk_bookings_pricing-provider` types over the doc's prose/code samples, but its **field descriptions** (e.g. `booking.contactDetails.fullAddress.subdivision`) and the sign-up requirement above are still accurate.
18
+
19
+ | Handler | Docs URL |
20
+ | --- | --- |
21
+ | `calculatePrice` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/pricing/pricing-integration-service-plugin/calculate-price |
22
+
23
+ Your handler receives `{ request: { booking }, metadata }`, where `booking` is the full [booking object](https://dev.wix.com/docs/rest/business-solutions/bookings/bookings/bookings-writer-v2/booking-object) — pull whatever you need from it (e.g. `booking.contactDetails.fullAddress.subdivision` for regional tax, `booking.totalParticipants` / `booking.participantsChoices` for participant count). Return **either**:
24
+
25
+ - `{ calculatedPrice: <number> }` — a numeric price. The site owner's dashboard can perform further calculations on top of it, but calling the Preview Price API on a booking priced this way fails.
26
+ - `{ priceDescription: <string> }` — a textual price description — lets Preview Price succeed, but Wix cannot do further numeric calculations on it.
27
+
28
+ Decide which shape to return before you write the handler; you can't mix both per request.
29
+
30
+ ## Example: Regional Tax From Contact Address
31
+
32
+ ```typescript
33
+ import { pricingProvider } from "@wix/bookings/service-plugins";
34
+
35
+ const TAX_RATE_BY_SUBDIVISION: Record<string, number> = {
36
+ CA: 0.0725,
37
+ NY: 0.08,
38
+ };
39
+
40
+ const BASE_PRICE_PER_PARTICIPANT = 100; // wire this to your real per-service price
41
+
42
+ pricingProvider.provideHandlers({
43
+ calculatePrice: async (payload) => {
44
+ const { booking } = payload.request;
45
+ if (!booking) {
46
+ return { calculatedPrice: 0 };
47
+ }
48
+
49
+ const participants =
50
+ booking.totalParticipants ??
51
+ (booking.participantsChoices?.serviceChoices ?? []).reduce(
52
+ (sum, choice) => sum + (choice.numberOfParticipants ?? 0),
53
+ 0
54
+ ) ??
55
+ 1;
56
+
57
+ const subdivision = booking.contactDetails?.fullAddress?.subdivision;
58
+ const taxRate = subdivision ? TAX_RATE_BY_SUBDIVISION[subdivision] ?? 0 : 0;
59
+
60
+ return { calculatedPrice: BASE_PRICE_PER_PARTICIPANT * participants * (1 + taxRate) };
61
+ },
62
+ });
63
+ ```
64
+
65
+ ## Key Implementation Notes
66
+
67
+ 1. **Sign-up is mandatory and manual** — see the warning above; there is no self-serve activation flow.
68
+ 2. **You own pricing entirely** — the request doesn't carry a merchant-configured base price; compute or look one up yourself.
69
+ 3. **Only called after booking creation** — you cannot use this SPI to display prices in advance of a booking existing.
70
+ 4. **Pick one response shape** — a numeric `calculatedPrice` (lets the dashboard add further calculations, but breaks Preview Price) or a `priceDescription` (the reverse). Decide once, per your app's use case.
71
+ 5. **Confirm with the user before promising this feature** — since activation depends on an out-of-band email approval, tell the user this step is required rather than assuming the integration is live once code is deployed.
@@ -36,10 +36,12 @@ import { extendedBookings } from "@wix/bookings";
36
36
  staffSorting.provideHandlers({
37
37
  sortStaffMembers: async (payload) => {
38
38
  const { request } = payload;
39
- const { availableResourceIds, slot } = request;
39
+ // availableResourceIds is optional on the request type — default it, or
40
+ // spreading/iterating it below fails `tsc` with "possibly undefined."
41
+ const { availableResourceIds = [], slot } = request;
40
42
 
41
43
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
42
- const elevatedQuery = auth.elevate(extendedBookings.query);
44
+ const elevatedQuery = auth.elevate(extendedBookings.queryExtendedBookings);
43
45
  const result = await elevatedQuery({
44
46
  filter: {
45
47
  "bookedEntity.item.slot.resource.id": { "$in": availableResourceIds },
@@ -73,10 +75,16 @@ staffSorting.provideHandlers({
73
75
  });
74
76
  ```
75
77
 
78
+ ## Manual Setup Required
79
+
80
+ None. Confirmed live with a service that has 2 assigned staff — the dashboard's "Add booking" flow resolved a specific staff member from the plugin's sorted order, and the booking created successfully with no errors. No dashboard configuration is needed beyond having the app installed and released, and the service having 2+ staff assigned to genuinely exercise the sort.
81
+
76
82
  ## Key Implementation Notes
77
83
 
78
84
  1. **Return all IDs** - You must return every ID from `availableResourceIds`, just reordered
79
85
  2. **Performance matters** - Keep logic fast; the booking flow waits for your response
80
86
  3. **Elevate permissions** - Use `auth.elevate` when querying Wix APIs from the handler
81
87
  4. **Deterministic sorting** - Use a tiebreaker (e.g., resource ID) when priorities are equal
82
- 5. **Graceful degradation** - If your external data source is unavailable, return the original order rather than failing
88
+ 5. **Use `queryExtendedBookings`, not `query`** — `extendedBookings.query` is deprecated. `queryExtendedBookings` takes the same `filter`/`cursorPaging` shape, so it's a drop-in replacement; confirmed live after switching.
89
+ 6. **`availableResourceIds` is optional on the request type** — default it to `[]` when destructuring, or spreading/iterating it fails `tsc` with "possibly undefined."
90
+ 7. **Graceful degradation** - If your external data source is unavailable, return the original order rather than failing
@@ -0,0 +1,137 @@
1
+ # Bookings Validation Service Plugin Reference
2
+
3
+ ## Overview
4
+
5
+ The Bookings Validation SPI lets you implement custom validation logic for booking operations. When a customer creates, cancels, or reschedules a booking (single-service or multi-service), Wix calls the handler matching that operation before it executes. Return `valid: true` to allow the operation or `valid: false` to block it with a customer-facing message.
6
+
7
+ `bookingsValidation.provideHandlers` requires all six handlers in the object literal — TypeScript rejects a partial object even though you may only care about one or two targets. For the ones you don't have real logic for, provide a no-op that returns an empty `results`/`singleServiceBookingResults` array (an empty array is a no-op: "Omitting an item's result treats it as valid," and an empty results array omits every item's result). Confirmed live: `wix generate` itself scaffolds all six handlers by default — that's not incidental boilerplate you can trim.
8
+
9
+ ## Handlers
10
+
11
+ | Handler | Validation target | On error/timeout |
12
+ | --- | --- | --- |
13
+ | `validateBeforeCreate` | `CREATE` | Blocked (fail-closed) |
14
+ | `validateBeforeCancel` | `CANCEL` | Blocked (fail-closed) |
15
+ | `validateBeforeReschedule` | `RESCHEDULE` | Continues (fail-open) |
16
+ | `validateBeforeCreateMultiService` | `CREATE_MULTI_SERVICE` | Blocked (fail-closed) |
17
+ | `validateBeforeCancelMultiService` | `CANCEL_MULTI_SERVICE` | Blocked (fail-closed) |
18
+ | `validateBeforeRescheduleMultiService` | `RESCHEDULE_MULTI_SERVICE` | Continues (fail-open) |
19
+
20
+ ## Request and Response Schema
21
+
22
+ Before implementing, call `ReadFullDocsMethodSchema` on each docs URL to get the full request/response types.
23
+
24
+ | Handler | Docs URL |
25
+ | --- | --- |
26
+ | `validateBeforeCreate` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-validation-service-plugin/validate-before-create?apiView=SDK |
27
+ | `validateBeforeCancel` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-validation-service-plugin/validate-before-cancel?apiView=SDK |
28
+ | `validateBeforeReschedule` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-validation-service-plugin/validate-before-reschedule?apiView=SDK |
29
+ | `validateBeforeCreateMultiService` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-validation-service-plugin/validate-before-create-multi-service?apiView=SDK |
30
+ | `validateBeforeCancelMultiService` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-validation-service-plugin/validate-before-cancel-multi-service?apiView=SDK |
31
+ | `validateBeforeRescheduleMultiService` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-validation-service-plugin/validate-before-reschedule-multi-service?apiView=SDK |
32
+
33
+ **Correlation differs by handler — this is easy to get wrong, and doing so fails `tsc`, not just at runtime:**
34
+
35
+ | Handler(s) | Item shape | Max items | Result correlates by |
36
+ | --- | --- | --- | --- |
37
+ | `validateBeforeCreate`, `validateBeforeCreateMultiService` | `{ itemIndex, booking }` | 40 | `itemIndex` |
38
+ | `validateBeforeCancel`, `validateBeforeReschedule`, and their multi-service equivalents | `{ booking }` — **no `itemIndex` field at all** | 8 | `bookingId` (from `booking._id`) |
39
+
40
+ Only the create family carries `itemIndex`; cancel and reschedule items don't have one, so their results key on the booking's own ID instead. The single-service response field is `results`; the multi-service response field is `singleServiceBookingResults` — same per-item shape either way. Omitting an item's result treats it as valid. `contactDetails` and resource `name`/`email` fields are redacted before reaching your handler.
41
+
42
+ ## Example: Membership Limit and Cancellation Fee
43
+
44
+ This example enforces a pricing-plan booking limit on create, and blocks late cancellations.
45
+
46
+ ```typescript
47
+ import { bookingsValidation } from "@wix/bookings/service-plugins";
48
+ import { auth } from "@wix/essentials";
49
+ import { orders } from "@wix/pricing-plans";
50
+
51
+ bookingsValidation.provideHandlers({
52
+ validateBeforeCreate: async (payload) => {
53
+ const { request } = payload;
54
+
55
+ const results = await Promise.all(
56
+ (request.items ?? []).map(async (item) => {
57
+ const memberId = item.booking?.contactDetails?.contactId;
58
+ if (!memberId) {
59
+ return { itemIndex: item.itemIndex, result: { valid: true } };
60
+ }
61
+
62
+ // Not orders.listOrders — that method doesn't exist on @wix/pricing-plans.
63
+ // managementListOrders takes flat buyerIds/orderStatuses, not a nested filter.
64
+ const elevatedListOrders = auth.elevate(orders.managementListOrders);
65
+ const { orders: activeOrders } = await elevatedListOrders({
66
+ buyerIds: [memberId],
67
+ orderStatuses: ["ACTIVE"],
68
+ });
69
+
70
+ if ((activeOrders ?? []).length === 0) {
71
+ return {
72
+ itemIndex: item.itemIndex,
73
+ result: {
74
+ valid: false,
75
+ invalidReason: {
76
+ message: "An active membership is required to book this service.",
77
+ fieldViolations: [
78
+ { field: "booking.contactDetails", description: "No active membership found.", code: "PLAN_EXPIRED" },
79
+ ],
80
+ },
81
+ },
82
+ };
83
+ }
84
+
85
+ return { itemIndex: item.itemIndex, result: { valid: true } };
86
+ })
87
+ );
88
+
89
+ return { results };
90
+ },
91
+
92
+ validateBeforeCancel: async (payload) => {
93
+ const { request } = payload;
94
+
95
+ // Cancel items carry no itemIndex — correlate results by the booking's own _id.
96
+ const results = (request.items ?? []).map((item) => {
97
+ const bookingId = item.booking?._id ?? undefined;
98
+ const startDate = item.booking?.bookedEntity?.slot?.startDate;
99
+ const hoursUntilStart = startDate
100
+ ? (new Date(startDate).getTime() - Date.now()) / (1000 * 60 * 60)
101
+ : Infinity;
102
+
103
+ if (hoursUntilStart < 24) {
104
+ return {
105
+ bookingId,
106
+ result: {
107
+ valid: false,
108
+ invalidReason: { message: "Cancellations within 24 hours of the appointment aren't allowed." },
109
+ },
110
+ };
111
+ }
112
+
113
+ return { bookingId, result: { valid: true } };
114
+ });
115
+
116
+ return { results };
117
+ },
118
+
119
+ // Unimplemented targets still need a handler — see the note above the example.
120
+ validateBeforeReschedule: async () => ({ results: [] }),
121
+ validateBeforeCreateMultiService: async () => ({ singleServiceBookingResults: [] }),
122
+ validateBeforeCancelMultiService: async () => ({ singleServiceBookingResults: [] }),
123
+ validateBeforeRescheduleMultiService: async () => ({ singleServiceBookingResults: [] }),
124
+ });
125
+ ```
126
+
127
+ ## Manual Setup Required
128
+
129
+ None — confirmed live via the real Cancel Booking API: a cancellation under 24h was correctly blocked with a structured `428 VALIDATION_FAILED`, and one over 24h correctly succeeded. Wix calls this plugin automatically on the relevant booking operations once the app is installed and released.
130
+
131
+ ## Key Implementation Notes
132
+
133
+ 1. **Bulk requests, per-item results** — return a result for every item you received (keyed by `itemIndex` on create, by `bookingId` on cancel/reschedule — see the correlation table above); a missing entry is treated as valid, not rejected.
134
+ 2. **Respond fast** — `validateBeforeCreate`, `validateBeforeCancel`, and their multi-service equivalents fail-closed (block the operation) on error or timeout; `validateBeforeReschedule` and its multi-service equivalent fail-open (allow it).
135
+ 3. **Multiple providers run concurrently** — if other apps also register validators for the same target, any single rejection blocks the operation.
136
+ 4. **Write customer-facing messages** — `invalidReason.message` and `fieldViolations[].description` are shown to the customer; keep them clear and free of internal jargon. `fieldViolations[].code` is for your own programmatic handling, not display.
137
+ 5. **Elevate permissions** — use `auth.elevate` when querying Wix APIs (e.g., pricing plan orders) from the handler.
@@ -20,6 +20,10 @@ Before implementing, call `ReadFullDocsMethodSchema` on each docs URL to get the
20
20
  | `getEligibleTriggers` | https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/discounts/custom-discount-triggers-integration-service-plugin/get-eligible-triggers?apiView=SDK |
21
21
  | `listTriggers` | https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/extensions/discounts/custom-discount-triggers-integration-service-plugin/list-triggers?apiView=SDK |
22
22
 
23
+ ## ⚠️ `identifier` must be echoed from the request, never invented
24
+
25
+ `getEligibleTriggers` receives `request.triggers[]`, each with a `customTrigger._id` and an `identifier` Wix generated for that occurrence. Your response's `eligibleTriggers[i].identifier` **must be the exact same value** from the matching `request.triggers[i].identifier` — it's how Wix correlates your answer back to the discount rule that asked. A hardcoded or self-invented `identifier` never matches, so the discount silently never applies — confirmed live: the discount rule stays wired up and no error is raised, it just never fires.
26
+
23
27
  ## Example: Happy Hour and Digital Products Triggers
24
28
 
25
29
  This example defines two custom triggers: a time-based "Happy Hour" trigger and a product-type-based "Digital Sale" trigger.
@@ -30,22 +34,16 @@ import { customTriggers } from "@wix/ecom/service-plugins";
30
34
  customTriggers.provideHandlers({
31
35
  getEligibleTriggers: async (payload) => {
32
36
  const { request, metadata } = payload;
33
- // Use the `request` and `metadata` received from Wix and
34
- // apply custom logic.
35
- return {
36
- // Return your response exactly as documented to integrate with Wix.
37
- // Return value example:
38
- eligibleTriggers: [
39
- {
40
- customTriggerId: "my-happy-hour-trigger",
41
- identifier: "123",
42
- },
43
- {
44
- customTriggerId: "my-digital-sale-trigger",
45
- identifier: "234",
46
- },
47
- ],
48
- };
37
+ // request.triggers tells you which trigger occurrences Wix is asking about —
38
+ // filter to the ones you recognize and echo back their `identifier` exactly.
39
+ const eligibleTriggers = (request.triggers ?? [])
40
+ .filter((t) => t.customTrigger?._id === "my-happy-hour-trigger" /* + your eligibility check */)
41
+ .map((t) => ({
42
+ customTriggerId: "my-happy-hour-trigger",
43
+ identifier: t.identifier,
44
+ }));
45
+
46
+ return { eligibleTriggers };
49
47
  },
50
48
  listTriggers: async (payload) => {
51
49
  const { request, metadata } = payload;
@@ -79,3 +77,12 @@ customTriggers.provideHandlers({
79
77
  2. **Both handlers required** - You must implement both `getEligibleTriggers` and `listTriggers`
80
78
  3. **Dynamic eligibility** - `getEligibleTriggers` is called during checkout to determine which triggers are currently active
81
79
  4. **Static list** - `listTriggers` provides the master list of all possible triggers for configuration in the Wix dashboard
80
+ 5. **Manual step required, every time** — this plugin only supplies *eligibility*. It has no visible effect until the merchant creates an Automatic Discount that uses your trigger. Tell the user this step is required; don't imply the discount is live just because the plugin is installed. Confirmed live, click-by-click in the dashboard:
81
+ 1. Go to **Catalog** (left sidebar) → **Discounts** → **Automatic Discounts**.
82
+ 2. Click **Create Discount**.
83
+ 3. Pick a discount type (e.g. **Standard discount**) and click **Continue**.
84
+ 4. Under **Offer** → **"Are there any requirements?"**, check **Custom requirement**.
85
+ 5. A dropdown appears next to it — click it and select your trigger by the `name` your `listTriggers` handler returned for it (e.g. "Happy Hour 16:00-18:00"). It only shows up here once your app is installed and `listTriggers` includes it — there's no dashboard flow to type in an arbitrary trigger ID.
86
+ 6. Fill in the discount's offer/eligibility/dates as normal, then click **Save**.
87
+
88
+ Note: the dialog's own **"Create New"** button under that dropdown only offers Wix's *built-in* requirement templates (email domain, first purchase, order count, etc.) — it does not create a new app-linked custom trigger. Your trigger must already be coming back from `listTriggers` to appear in the dropdown at all.
@@ -67,6 +67,10 @@ giftVouchersProvider.provideHandlers({
67
67
  });
68
68
  ```
69
69
 
70
+ ## Manual Setup Required
71
+
72
+ No dashboard configuration beyond installing the app. But there's nothing to `redeem`/`getBalance` against until a real gift card exists — a customer (or you, via the Wix Gift Cards app's own purchase/issuance flow) must actually buy or be issued a gift card first. You can't shortcut this by calling `redeem` with a made-up code; the code has to correspond to a gift card your provider recognizes as real. Test by issuing a real gift card through the site's own gift-card purchase flow, then redeeming it at checkout.
73
+
70
74
  ## Singular Constraint
71
75
 
72
76
  `GIFT_CARDS_PROVIDER` is **singular** — only one component of this type is allowed per app. Do not scaffold or include two Gift Cards service plugins in the same app.
@@ -45,7 +45,6 @@ paymentSettings.provideHandlers({
45
45
  };
46
46
  }
47
47
  },
48
- getPaymentSettingsForCheckout: async () => ({ blockedPaymentOptions: [] }),
49
48
  });
50
49
  ```
51
50
 
@@ -84,10 +83,13 @@ paymentSettings.provideHandlers({
84
83
  };
85
84
  }
86
85
  },
87
- getPaymentSettingsForCheckout: async () => ({ blockedPaymentOptions: [] }),
88
86
  });
89
87
  ```
90
88
 
89
+ ## Manual Setup Required
90
+
91
+ No dashboard configuration beyond installing the app — Wix calls this SPI automatically once installed. The one real prerequisite: **a payment provider must already be connected** on the site (Wix Payments or another provider), or there's nothing for `requires3dSecure` to apply to. You can't exercise this handler with just a Cart `calculate`/`refresh` call — it only fires during actual payment submission (a customer entering card details), so verifying it end-to-end needs a real or sandbox payment attempt, not just an API-level cart inspection.
92
+
91
93
  ## Key Implementation Notes
92
94
 
93
95
  1. **Payment provider required** - Site must have a payment provider connected to collect payments
@@ -0,0 +1,45 @@
1
+ # Realtime Permissions Provider Service Plugin Reference
2
+
3
+ ## Overview
4
+
5
+ The Realtime Permissions Provider SPI controls who can receive messages on your app's [Wix Realtime](https://dev.wix.com/docs/sdk/core-modules/realtime/realtime/introduction) channels. When a subscriber from another app attempts a cross-app subscription to one of your channels, Wix calls your `checkSubscriberPermissions` handler and waits synchronously for the answer. Without a registered provider, Wix denies all cross-app subscription attempts by default.
6
+
7
+ This is the only SPI outside eCommerce and Bookings — it belongs to the Realtime module.
8
+
9
+ ## Request and Response Schema
10
+
11
+ Before implementing, call `ReadFullDocsMethodSchema` on the docs URL to get the full request/response types.
12
+
13
+ | Handler | Docs URL |
14
+ | --- | --- |
15
+ | `checkSubscriberPermissions` | https://dev.wix.com/docs/sdk/core-modules/realtime/extensions/realtime-permissions-provider/check-subscriber-permissions |
16
+
17
+ The request gives you the `channel` (`name` + optional `resourceId`), the `subscriber` (`type`: `ADMIN` / `MEMBER` / `VISITOR`, and `_id`), and `requestingAppId` (the app attempting the subscription, may be empty). Return `{ read: true }` to allow, `{ read: false }` to deny.
18
+
19
+ ## Example: Allow Members and Admins, Deny Visitors
20
+
21
+ ```typescript
22
+ import { realtimePermissionsProvider } from "@wix/realtime/service-plugins";
23
+
24
+ realtimePermissionsProvider.provideHandlers({
25
+ checkSubscriberPermissions: async (payload) => {
26
+ const { request } = payload;
27
+ const { subscriber, channel } = request;
28
+
29
+ if (channel?.name === "public-updates") {
30
+ return { read: true };
31
+ }
32
+
33
+ return { read: subscriber?.type !== "VISITOR" };
34
+ },
35
+ });
36
+ ```
37
+
38
+ ## Key Implementation Notes
39
+
40
+ 1. **The generated stub may include a `write` field — remove it.** `wix generate` for this `pluginType` has been observed scaffolding a response of `{ read: true, write: false }`, but `CheckSubscriberPermissionsResponse` only has `read?: boolean`. Confirm against the installed `@wix/auto_sdk_duplexer_realtime-permissions-provider` types if `tsc` doesn't catch it for you.
41
+ 2. **`channel` and `subscriber` are both optional on the request** — `channel?: Channel`, `subscriber?: Subscriber`. Accessing `.name`/`.type` without the `?.` above fails `tsc` with "possibly undefined."
42
+ 3. **Deny with data, not errors** — return `{ read: false }` for normal policy denials. Only throw `InvalidArgumentError` when the request itself is invalid or missing data; any other thrown error, timeout, or unreachable service makes Wix deny the subscription with a 503.
43
+ 4. **Respond quickly** — the call is synchronous and blocks the subscription attempt; a slow handler delays it, and a non-responsive one causes a 503 denial.
44
+ 5. **Be consistent** — the same channel, subscriber, and context should always yield the same decision.
45
+ 6. **No provider means no cross-app access** — until you implement this SPI, all cross-app subscription attempts to your channels are denied by default.
@@ -53,6 +53,21 @@ shippingRates.provideHandlers({
53
53
  });
54
54
  ```
55
55
 
56
+ ## ⚠️ Manual setup required — installing the app isn't enough
57
+
58
+ Like Additional Fees and Payment Settings, this plugin only supplies rates when Wix actually asks it to. Unlike those two, **it isn't asked automatically** — the merchant must explicitly turn your app on as a delivery carrier for a shipping region before Wix calls `getShippingRates` for it. Tell the merchant to do this in the dashboard (confirmed live, click-by-click):
59
+
60
+ 1. Go to **Settings** (left sidebar) → under **Business solutions**, click **Shipping, delivery & fulfillment**.
61
+ 2. Find the region to enable the carrier for (e.g. **Domestic** or **International**) and click **Manage Your Apps** in that region's header.
62
+ 3. In the **"Manage your installed apps"** dialog, check the box next to your app's name.
63
+ 4. A **backup rate** section appears (checked by default) with a **Shipping name** and **Rate at checkout** field — this is what Wix falls back to if your plugin errors or times out. Fill these in (or leave the defaults) — Wix requires a backup rate here, same as the API's `backupRate`.
64
+ 5. Optionally check **Add a handling fee to every order**.
65
+ 6. Click **Save**.
66
+
67
+ Confirmed live: enabling the carrier this way registers it with no errors. Whichever specific rate a given customer sees selected by default (this carrier's vs. another installed one's) is resolved at checkout-page render time, not by the Cart `calculate`/`refresh` endpoints — verify the actual customer-facing rate on the live checkout page, not from a raw API response alone.
68
+
69
+ (The same thing can be done via the [Add Delivery Carrier](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/shipping-delivery/delivery-profiles/add-delivery-carrier) API if you're scripting site setup yourself, but for a merchant using your app, send them to the dashboard steps above — not a raw API call.)
70
+
56
71
  ## Singular Constraint
57
72
 
58
73
  `ECOM_SHIPPING_RATES` is **singular** — only one component of this type is allowed per app. Do not scaffold or include two Shipping Rates service plugins in the same app.
@@ -45,6 +45,10 @@ toolsProvider.provideHandlers({
45
45
  });
46
46
  ```
47
47
 
48
+ ## Manual Setup Required
49
+
50
+ This handler is only invoked by the real Wix AI assistant deciding a tool is relevant — there's no direct API to force-invoke a specific tool for testing. Two things must both be true before it can fire at all: (1) a paired `APP_TOOLS` extension declares the tool with `activated: true` (see [APP_TOOLS.md](../APP_TOOLS.md)), and (2) a user actually asks the AI assistant something the tool's `description` matches. Verify by asking the assistant a matching question on the live site, not by calling this handler directly.
51
+
48
52
  ## Key Implementation Notes
49
53
 
50
54
  1. **Route on `methodName`** — use a `switch` or map to dispatch to the right handler logic
@@ -43,6 +43,10 @@ validations.provideHandlers({
43
43
  });
44
44
  ```
45
45
 
46
+ ## Manual Setup Required
47
+
48
+ None — confirmed live. Once the app is installed and released, Wix calls this plugin automatically on every cart/checkout calculation; no dashboard configuration or extra API call is needed to "turn it on."
49
+
46
50
  ## Key Implementation Notes
47
51
 
48
52
  1. **Return empty array when valid** - Return `{ violations: [] }` when no validation issues
@@ -23,8 +23,8 @@ they also surface from a search that began at the methods.
23
23
 
24
24
  In this skill:
25
25
 
26
- - **What are you building?** — route each feature to its identity: visitor token, admin token
27
- in a backend function, or admin ad hoc in exec_tool
26
+ - **What are you building?** — for visitors, for the owner, or management done for them —
27
+ decided per request from the user's intent, each on its own flow
28
28
  - **The helpers** — the `wx.*` loader every exec opens with
29
29
  - **Gather context** — `wx.context`, the report of what the site actually has
30
30
  - **Learn Wix** — find the APIs, learn their contracts
@@ -40,28 +40,41 @@ In this skill:
40
40
 
41
41
  ## What are you building?
42
42
 
43
- Choose the token by who the code acts for. A headless app can serve visitors, provide admin
44
- management tools, or do both.
45
-
46
- **A site for visitors** — use a visitor token for public reads and actions on behalf of the
47
- visitor, never the admin connector token. Call Wix directly from the browser through one shared
48
- visitor client (Write the code, below). Redirect sessions for Wix-hosted flows also require a
49
- visitor token minted for the headless OAuth app; see Visitor authentication and Wix-hosted flows below.
50
- **The OAuth app is a one-call prerequisite, not a dead end**: `wx.ensureOAuthApp` returns its
51
- `clientId`, creating the app when the site has none, so it is a visitor flow's first step — never
52
- a reason to move the flow onto the admin token or leave it for later.
53
- Anyone can mint an anonymous visitor token from the
54
- OAuth app's public `clientId`; no visitor login is required. APIs for the "current visitor"
55
- use that token to identify whose data and state to access. This applies both to a standalone
56
- headless frontend and to a frontend extending an existing Wix site.
57
-
58
- **An admin tool for the owner** — use the admin connector token in backend functions implementing
59
- admin logic; keep it secret and server-side. The frontend calls those functions, not Wix with
60
- visitor tokens. A custom headless management site extending the Wix back office follows this
61
- flow too. Ad hoc management calls in `exec_tool` also use the admin token. Backend functions
62
- also handle work requiring the owner's permissions, such as webhooks, scheduled jobs, and
63
- explicitly authorized elevated operations. For an app with both visitor and admin features,
64
- keep each feature on its corresponding flow.
43
+ Wix has two identities, visitor and admin, and each has its own flow. Decide which one every
44
+ request is for before acting on it — from what the user wants, not from the technology:
45
+
46
+ - **Visitor** — the people using the site: browsing, booking, buying, signing in as a member.
47
+ Visitor token, called from the browser through one shared visitor client.
48
+ - **Admin, built** — a management surface of the owner's own: a custom view, a bulk operation,
49
+ an internal tool. Admin connector token, server-side in backend functions the frontend calls.
50
+ - **Admin, done for them** — the user wants the business managed now, not an app: create the
51
+ products, change a setting, import the contacts. Admin token, ad hoc in exec_tool; nothing
52
+ ships in the app.
53
+
54
+ The owner already has a full Wix dashboard for running the business — orders, products,
55
+ bookings, settings, and more — so an admin build is a choice the user makes, not a gap to fill by
56
+ default. When a request has both visitor and admin parts, each keeps its own flow.
57
+
58
+ When the request doesn't say which, ask the user — in their terms (who the thing is for), not in
59
+ technical ones (identities, tokens, flows). When you decide without asking, tell them what you
60
+ chose the same way.
61
+
62
+ The rules that follow from the split:
63
+
64
+ - **Checkout runs on a visitor token, never the admin token** — including a checkout this app
65
+ creates for the visitor. The admin token *will* hand back a checkout URL, and that is the trap:
66
+ it belongs to the site rather than the buyer (no cart persistence, no abandoned-checkout
67
+ recovery, no attribution), and a redirect session refuses it outright (`403`). Returns reach
68
+ this app only from URLs in the OAuth app's redirect list (Visitor authentication and
69
+ Wix-hosted flows, below).
70
+ - **No OAuth app yet is not a reason to use the admin token.** `wx.ensureOAuthApp` returns the
71
+ `clientId`, creating the app when the site has none — a visitor flow's first step. Anyone can
72
+ mint an anonymous visitor token from that public `clientId`; no visitor login is required.
73
+ - **No login still means visitors.** `asServiceRole` governs this app's own data access and says
74
+ nothing about which identity Wix sees: a public page's Wix calls run on a visitor token. Reach
75
+ for the admin token because a feature is owner-side, never because nobody signs in.
76
+ - **Owner-side work beyond pages** — webhooks, scheduled jobs, explicitly authorized elevated
77
+ operations — is admin flow too, in backend functions.
65
78
 
66
79
  ```
67
80
  visitor pages ──(visitor token)────────────────────────► wixapis.com
@@ -69,6 +82,11 @@ admin pages ──► base44/functions/… ──(admin token)────►
69
82
  exec_tool ──(admin token, ad hoc management)────────► wixapis.com
70
83
  ```
71
84
 
85
+ **The connected site owns its payments.** Whatever the visitor buys is paid on the Wix-hosted page
86
+ this app hands them off to, with the provider the site already has. Base44's
87
+ `<available_payment_providers>` note is about checkout that Base44 hosts instead — it does not
88
+ apply to these flows, and asks nothing of them, whichever providers it names or rules out.
89
+
72
90
  ## The helpers
73
91
 
74
92
  Research and probing run in exec_tool, and one loader opens every exec (execs share no state —
@@ -118,6 +136,10 @@ One report: installed apps **with ids** (incl. Stores' catalog version — V1 vs
118
136
  endpoints), the OAuth app id (**also the visitor `clientId`**), locale, currency, CMS collections.
119
137
  An empty report = bad token, never an empty site.
120
138
 
139
+ No OAuth app in the report means the site has none **yet**. For anything a visitor operates,
140
+ create it with `wx.ensureOAuthApp` — one call, returns the `clientId` the frontend mints visitor
141
+ tokens from — and build the feature on the visitor flow.
142
+
121
143
  Reports over 4,000 characters are saved in full to a temporary Markdown file. The result includes
122
144
  its path, byte and line counts, and a heading outline. Read that file to inspect the site context.
123
145
 
@@ -124,6 +124,7 @@ Compute `<SKILL_ROOT>` from this file (`<SKILL_ROOT>/SKILL.md` — strip `/SKILL
124
124
  | Image generation (opt-in; agnostic) | `<SKILL_ROOT>/references/IMAGE_GENERATION.md` |
125
125
  | Design — fallback visual quality bar for the built frontend, when the user gave no design direction of their own (managed **create** only) | `<SKILL_ROOT>/references/DESIGN.md` |
126
126
  | Content — fallback copy quality bar for the built frontend, when the user gave no copy/voice direction of their own (managed **create** only) | `<SKILL_ROOT>/references/CONTENT.md` |
127
+ | Experience overlays — a vertical's own design + copy bar on top of DESIGN/CONTENT (optional per vertical; read when that vertical is loaded) | `<SKILL_ROOT>/references/inline-recipes/experience-<vertical>.md` (today: `experience-store.md`) |
127
128
  | AI features — text/chat + embeddings (opt-in; agnostic) | `<SKILL_ROOT>/references/AI_FEATURES.md` |
128
129
  | Feedback — relay the user's headless-experience feedback to Wix (opt-in; user-approved) | `<SKILL_ROOT>/references/FEEDBACK.md` |
129
130
  | **Authentication** — obtain `$TOKEN`/`$SITE_ID`/`clientId` (project-type-specific) | `<TYPE_DIR>/AUTHENTICATION.md` |