@brainerce/mcp-server 3.12.2 → 3.12.4

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/dist/bin/stdio.js CHANGED
@@ -217,7 +217,7 @@ async function startCheckout() {
217
217
  2. Customer optionally applies coupon on cart page \u2192 \`applyCoupon(cartId, code)\`
218
218
  3. Detect payment providers \u2192 \`getPaymentProviders()\`
219
219
  4. Start checkout session (\`startGuestCheckout()\` or \`createCheckout()\`)
220
- 5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\`
220
+ 5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\` \u2014 also pass \`notes\` from the **"Order notes" textarea that every checkout page should include by default** (optional field, max 2000 chars; lands on the order for the merchant)
221
221
  6. Select shipping method \u2192 \`selectShippingMethod()\`
222
222
  6b. (Optional) Set checkout custom fields \u2192 \`setCheckoutCustomFields()\` \u2014 surcharges auto-calculated
223
223
  7. Create payment intent \u2192 \`createPaymentIntent()\` \u2192 returns \`{ clientSecret, provider }\`
@@ -239,6 +239,8 @@ function CheckoutPage() {
239
239
  const [checkout, setCheckout] = useState<Checkout | null>(null);
240
240
  const [error, setError] = useState<string | null>(null);
241
241
  const [loading, setLoading] = useState(true);
242
+ // Optional "Order notes" textarea \u2014 render it on the checkout page by default
243
+ const [orderNotes, setOrderNotes] = useState('');
242
244
 
243
245
  useEffect(() => { startCheckout(); }, []);
244
246
 
@@ -273,6 +275,7 @@ function CheckoutPage() {
273
275
  postalCode: cart.shippingAddress.postalCode,
274
276
  country: cart.shippingAddress.country,
275
277
  phone: cart.shippingAddress.phone,
278
+ notes: orderNotes || undefined, // from the "Order notes" textarea \u2014 include one by default
276
279
  });
277
280
  setCheckout(checkoutData);
278
281
 
@@ -2123,6 +2126,7 @@ A useful order card includes ALL of the following when the data is present. Each
2123
2126
  | **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
2124
2127
  | **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
2125
2128
  | Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
2129
+ | **Order note** | \`order.notes\` | The shopper's own checkout note, echoed back read-only. Render when present ("Your order note"). |
2126
2130
  | Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. In VAT-inclusive mode \`taxAmount\` is 0 \u2014 read \`order.taxBreakdown.totalTax\` and label "Tax (incl.)". |
2127
2131
 
2128
2132
  #### Rendering \`order.items[i].customizations\` by type
@@ -2148,7 +2152,6 @@ The storefront scaffolded by \`create-brainerce-store\` already implements this.
2148
2152
  These fields are NOT returned to buyers by \`/customers/me/orders\`. If you see them in older examples, ignore them \u2014 they are for merchant/admin views only.
2149
2153
 
2150
2154
  - \`order.accountId\`, \`order.storeId\`, \`order.customerId\` (already known on the request)
2151
- - \`order.notes\` (internal merchant notes)
2152
2155
  - \`order.customFieldValues\` (order-level checkout fields \u2014 merchant concept; buyers see \`items[i].customizations\`)
2153
2156
  - \`order.appliedSurcharges\`, \`order.surchargeAmount\`, \`order.appliedRuleIds\`, \`order.downloadMeta\`, \`order.pickupLocationData\``;
2154
2157
  }
@@ -2196,7 +2199,22 @@ function OrderConfirmation() {
2196
2199
 
2197
2200
  **Common mistake:** Don't just check URL params and show success immediately!
2198
2201
  You MUST call \`waitForOrder(checkoutId)\` to verify the order was created.
2199
- Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.`;
2202
+ Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.
2203
+
2204
+ ### Optional: render full order details on the confirmation page
2205
+
2206
+ Want a richer thank-you page (items, shipping address, totals, the shopper's
2207
+ order note)? After \`waitForOrder\` succeeds, fetch the full buyer-safe order \u2014
2208
+ works for guests too (possession of the checkout id is the credential):
2209
+
2210
+ \`\`\`typescript
2211
+ const order = await client.getOrderByCheckout(checkoutId);
2212
+ // order.items, order.shippingAddress, order.notes, order.subtotal,
2213
+ // order.shippingAmount, order.taxAmount / order.taxBreakdown, order.totalAmount
2214
+ \`\`\`
2215
+
2216
+ Render whatever subset fits your design \u2014 same field shapes as the buyer
2217
+ order-history view (see "Buyer order view" section).`;
2200
2218
  }
2201
2219
  function getI18nSection() {
2202
2220
  return `## Internationalization (i18n)
@@ -2526,6 +2544,8 @@ dashboard or the admin SDK).
2526
2544
 
2527
2545
  \`\`\`typescript
2528
2546
  // Publish to a specific vibe-coded site (admin mode):
2547
+ await admin.publishProductToSalesChannel('prod_id', 'vc_conn_id');
2548
+ await admin.publishCouponToSalesChannel('coupon_id', 'vc_conn_id');
2529
2549
  await admin.publishCategoryToVibeCodedSite('cat_id', 'conn_id');
2530
2550
  await admin.publishTagToVibeCodedSite('tag_id', 'conn_id');
2531
2551
  await admin.publishBrandToVibeCodedSite('brand_id', 'conn_id');
@@ -3321,11 +3341,18 @@ Points are earned automatically by the platform when an order is paid \u2014 the
3321
3341
  \`\`\`typescript
3322
3342
  // requires client.setCustomerToken(auth.token)
3323
3343
  const status = await client.getLoyaltyStatus();
3324
- // { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
3344
+ // {
3345
+ // enrolled, pointsBalance, lifetimeEarned,
3346
+ // program: { pointsName, currencyRatio, status } | null,
3347
+ // tier: { id, name, level, pointsMultiplier } | null,
3348
+ // nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
3349
+ // progressToNextTier, pointsToNextTier,
3350
+ // }
3325
3351
  \`\`\`
3326
3352
 
3327
3353
  - \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
3328
3354
  - \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
3355
+ - \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
3329
3356
 
3330
3357
  ## Enroll (only if auto-enroll is off)
3331
3358
 
@@ -3333,19 +3360,67 @@ const status = await client.getLoyaltyStatus();
3333
3360
  await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
3334
3361
  \`\`\`
3335
3362
 
3363
+ May grant a one-time "joined the program" bonus if configured \u2014 distinct from any account-signup bonus (granted automatically on registration, not by this call).
3364
+
3336
3365
  ## Rewards & redemption
3337
3366
 
3338
3367
  \`\`\`typescript
3339
3368
  const rewards = await client.getAvailableRewards();
3340
- // [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
3369
+ // [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
3370
+ // Already filtered to rewards the customer's current tier qualifies for.
3341
3371
 
3342
- const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
3372
+ const { couponCode, discountType, discountValue, pointsBalance } =
3373
+ await client.redeemLoyaltyReward(rewardId);
3343
3374
  await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
3344
3375
  \`\`\`
3345
3376
 
3346
- - \`redeemLoyaltyReward\` mints a one-time fixed-amount coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
3347
- - Throws if the customer has insufficient points. If minting fails, points are refunded automatically.
3377
+ - \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
3378
+ - \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
3379
+ - Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
3348
3380
  - Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
3381
+
3382
+ ## Social share (optional)
3383
+
3384
+ \`\`\`typescript
3385
+ await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
3386
+ \`\`\`
3387
+
3388
+ ## Referrals (when the store enables them)
3389
+
3390
+ \`getLoyaltyStatus()\` also returns \`referralCode\` (the member's share code, null when referrals are disabled or the customer isn't enrolled) and \`referralWelcomeCoupon\` (the still-unused welcome coupon this customer got for signing up via a referral link).
3391
+
3392
+ \`\`\`typescript
3393
+ // 1. Referrer: build a share link around their code.
3394
+ const { referralCode } = await client.getLoyaltyStatus();
3395
+ const shareUrl = \`https://your-store.com/?ref=\${referralCode}\`;
3396
+
3397
+ // 2. Landing page: PUBLIC lookup \u2014 no customerToken needed.
3398
+ const info = await client.getReferralInfo(refFromQuery);
3399
+ // { valid, referrerFirstName, reward: { name, type, value, minOrderAmount } | null }
3400
+ if (info.valid) {
3401
+ // "Jane sent you a gift: 10% off your first order"
3402
+ }
3403
+
3404
+ // 3. Registration: pass the code \u2014 invalid codes never fail registration
3405
+ // (validated asynchronously server-side with fraud checks).
3406
+ await client.registerCustomer({ email, password, referralCode: refFromQuery });
3407
+
3408
+ // 4. Show the referee their welcome coupon after login:
3409
+ const { referralWelcomeCoupon } = await client.getLoyaltyStatus();
3410
+ if (referralWelcomeCoupon) {
3411
+ await client.applyCoupon(cartId, referralWelcomeCoupon.code);
3412
+ }
3413
+ \`\`\`
3414
+
3415
+ The referrer earns a points bonus after the referee's first qualifying order (held through the program's pending window, like order points). Everything server-side \u2014 no storefront earn calls.
3416
+
3417
+ ## Birthday gifts (when the store enables them)
3418
+
3419
+ No dedicated calls \u2014 collect \`birthMonth\`/\`birthDay\` (1-12 / 1-31, **no year**, must be sent together) via \`updateMyProfile()\` and the platform emails a one-time gift coupon ahead of the customer's birthday.
3420
+
3421
+ \`\`\`typescript
3422
+ await client.updateMyProfile({ birthMonth: 4, birthDay: 17 });
3423
+ \`\`\`
3349
3424
  `;
3350
3425
  }
3351
3426
  function getSectionByTopic(topic, connectionId, currency) {
@@ -3870,6 +3945,7 @@ interface Checkout {
3870
3945
  appliedSurcharges?: Array<{ key: string; name: string; value: unknown; amount: string }> | null;
3871
3946
  customFieldValues?: Record<string, unknown> | null;
3872
3947
  couponCode?: string | null;
3948
+ notes?: string | null; // Order-level shopper note \u2014 copied onto the order at completion
3873
3949
  lineItems: CheckoutLineItem[]; // Use THIS for order summary, NOT cart.items!
3874
3950
  itemCount: number;
3875
3951
  availableShippingRates?: ShippingRate[];
@@ -3938,6 +4014,7 @@ interface SetShippingAddressDto {
3938
4014
  postalCode: string;
3939
4015
  country: string;
3940
4016
  phone?: string;
4017
+ notes?: string; // Order notes textarea \u2014 include one on checkout by default! Max 2000 chars, lands on the order
3941
4018
  }
3942
4019
 
3943
4020
  interface CreateCheckoutDto {
@@ -4881,6 +4958,18 @@ interface TaxEstimateResponse {
4881
4958
  // full shipping address. The country comes from your edge runtime
4882
4959
  // (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
4883
4960
  var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
4961
+ interface LoyaltyTierSummary {
4962
+ id: string;
4963
+ name: string;
4964
+ level: number; // ordering; higher = better tier
4965
+ pointsMultiplier: number;
4966
+ }
4967
+
4968
+ interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
4969
+ qualificationType: 'SPEND' | 'POINTS';
4970
+ qualificationThreshold: number;
4971
+ }
4972
+
4884
4973
  interface LoyaltyStatus {
4885
4974
  enrolled: boolean;
4886
4975
  pointsBalance: number; // redeemable points (pending earns excluded)
@@ -4890,6 +4979,28 @@ interface LoyaltyStatus {
4890
4979
  currencyRatio: number; // points needed to redeem 1 unit of store currency
4891
4980
  status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
4892
4981
  } | null; // null when the store has no loyalty program
4982
+ tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
4983
+ nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
4984
+ progressToNextTier: number; // 0..1
4985
+ pointsToNextTier: number | null;
4986
+ referralCode?: string | null; // member's share code; null when referrals disabled / not enrolled
4987
+ referralWelcomeCoupon?: { // unused welcome coupon from signing up via a referral link
4988
+ code: string;
4989
+ type: string; // 'PERCENTAGE' | 'FIXED_AMOUNT'
4990
+ value: number;
4991
+ } | null;
4992
+ }
4993
+
4994
+ // Public referral-link lookup \u2014 NO customerToken needed (safe pre-signup)
4995
+ interface ReferralInfo {
4996
+ valid: boolean; // false for unknown/disabled codes
4997
+ referrerFirstName: string | null; // for "Jane sent you a gift" copy \u2014 no other PII
4998
+ reward: {
4999
+ name: string;
5000
+ type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
5001
+ value: number;
5002
+ minOrderAmount: number | null;
5003
+ } | null;
4893
5004
  }
4894
5005
 
4895
5006
  interface LoyaltyReward {
@@ -4897,16 +5008,19 @@ interface LoyaltyReward {
4897
5008
  name: string;
4898
5009
  description: string | null;
4899
5010
  pointsCost: number;
4900
- discountValue: number; // fixed amount off, store currency
5011
+ type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
5012
+ discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
4901
5013
  minOrderAmount: number | null;
4902
5014
  maxUsesPerUser: number;
4903
5015
  isActive: boolean;
5016
+ minTierLevel: number | null; // minimum tier level required, or null for no restriction
4904
5017
  createdAt: string;
4905
5018
  updatedAt: string;
4906
5019
  }
4907
5020
 
4908
5021
  interface RedeemRewardResult {
4909
5022
  couponCode: string; // one-time coupon to apply at checkout
5023
+ discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
4910
5024
  discountValue: number;
4911
5025
  pointsSpent: number;
4912
5026
  pointsBalance: number; // remaining balance after redemption
@@ -4916,6 +5030,10 @@ interface RedeemRewardResult {
4916
5030
  // client.enrollInLoyalty(): Promise<LoyaltyStatus>
4917
5031
  // client.getAvailableRewards(): Promise<LoyaltyReward[]>
4918
5032
  // client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
5033
+ // client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
5034
+ // client.getReferralInfo(code: string): Promise<ReferralInfo> // PUBLIC \u2014 no customerToken
5035
+ // Referral signup: client.registerCustomer({ ..., referralCode })
5036
+ // Birthday gift data: client.updateMyProfile({ birthMonth, birthDay }) // 1-12 / 1-31, no year, both together
4919
5037
  `;
4920
5038
  var TYPES_BY_DOMAIN = {
4921
5039
  products: PRODUCTS_TYPES,
@@ -6414,6 +6532,14 @@ function formatCapabilities(caps) {
6414
6532
  lines.push(
6415
6533
  caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
6416
6534
  );
6535
+ if (caps.features.hasLoyaltyProgram) {
6536
+ lines.push(
6537
+ caps.features.hasReferralProgram ? "\u2705 Referral program enabled (share codes + welcome reward \u2014 storefront-mode only)" : "\u274C Referral program not enabled"
6538
+ );
6539
+ lines.push(
6540
+ caps.features.hasBirthdayRewards ? "\u2705 Birthday gifts enabled (set birthMonth/birthDay via updateMyProfile)" : "\u274C Birthday gifts not enabled"
6541
+ );
6542
+ }
6417
6543
  lines.push("");
6418
6544
  lines.push("## Connection Settings");
6419
6545
  lines.push(
@@ -6461,7 +6587,7 @@ function formatCapabilities(caps) {
6461
6587
  }
6462
6588
  if (caps.features.hasLoyaltyProgram) {
6463
6589
  suggestions.push(
6464
- "A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus(), getAvailableRewards(), and redeemLoyaltyReward(id). Note: loyalty is NOT available in vibe-coded mode."
6590
+ "A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance, tier progress, and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus() (includes tier/nextTier/progressToNextTier), getAvailableRewards(), redeemLoyaltyReward(id), and optionally reportSocialShare(). Note: loyalty is NOT available in vibe-coded mode."
6465
6591
  );
6466
6592
  }
6467
6593
  if (caps.features.hasCheckoutCustomFields) {
package/dist/index.d.mts CHANGED
@@ -77,6 +77,10 @@ interface StoreCapabilities {
77
77
  hasContent?: boolean;
78
78
  /** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
79
79
  hasLoyaltyProgram?: boolean;
80
+ /** True when the loyalty program has the referral program enabled. Storefront-mode only. */
81
+ hasReferralProgram?: boolean;
82
+ /** True when the loyalty program has birthday gifts enabled. */
83
+ hasBirthdayRewards?: boolean;
80
84
  };
81
85
  }
82
86
  declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
package/dist/index.d.ts CHANGED
@@ -77,6 +77,10 @@ interface StoreCapabilities {
77
77
  hasContent?: boolean;
78
78
  /** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
79
79
  hasLoyaltyProgram?: boolean;
80
+ /** True when the loyalty program has the referral program enabled. Storefront-mode only. */
81
+ hasReferralProgram?: boolean;
82
+ /** True when the loyalty program has birthday gifts enabled. */
83
+ hasBirthdayRewards?: boolean;
80
84
  };
81
85
  }
82
86
  declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
package/dist/index.js CHANGED
@@ -243,7 +243,7 @@ async function startCheckout() {
243
243
  2. Customer optionally applies coupon on cart page \u2192 \`applyCoupon(cartId, code)\`
244
244
  3. Detect payment providers \u2192 \`getPaymentProviders()\`
245
245
  4. Start checkout session (\`startGuestCheckout()\` or \`createCheckout()\`)
246
- 5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\`
246
+ 5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\` \u2014 also pass \`notes\` from the **"Order notes" textarea that every checkout page should include by default** (optional field, max 2000 chars; lands on the order for the merchant)
247
247
  6. Select shipping method \u2192 \`selectShippingMethod()\`
248
248
  6b. (Optional) Set checkout custom fields \u2192 \`setCheckoutCustomFields()\` \u2014 surcharges auto-calculated
249
249
  7. Create payment intent \u2192 \`createPaymentIntent()\` \u2192 returns \`{ clientSecret, provider }\`
@@ -265,6 +265,8 @@ function CheckoutPage() {
265
265
  const [checkout, setCheckout] = useState<Checkout | null>(null);
266
266
  const [error, setError] = useState<string | null>(null);
267
267
  const [loading, setLoading] = useState(true);
268
+ // Optional "Order notes" textarea \u2014 render it on the checkout page by default
269
+ const [orderNotes, setOrderNotes] = useState('');
268
270
 
269
271
  useEffect(() => { startCheckout(); }, []);
270
272
 
@@ -299,6 +301,7 @@ function CheckoutPage() {
299
301
  postalCode: cart.shippingAddress.postalCode,
300
302
  country: cart.shippingAddress.country,
301
303
  phone: cart.shippingAddress.phone,
304
+ notes: orderNotes || undefined, // from the "Order notes" textarea \u2014 include one by default
302
305
  });
303
306
  setCheckout(checkoutData);
304
307
 
@@ -2149,6 +2152,7 @@ A useful order card includes ALL of the following when the data is present. Each
2149
2152
  | **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
2150
2153
  | **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
2151
2154
  | Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
2155
+ | **Order note** | \`order.notes\` | The shopper's own checkout note, echoed back read-only. Render when present ("Your order note"). |
2152
2156
  | Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. In VAT-inclusive mode \`taxAmount\` is 0 \u2014 read \`order.taxBreakdown.totalTax\` and label "Tax (incl.)". |
2153
2157
 
2154
2158
  #### Rendering \`order.items[i].customizations\` by type
@@ -2174,7 +2178,6 @@ The storefront scaffolded by \`create-brainerce-store\` already implements this.
2174
2178
  These fields are NOT returned to buyers by \`/customers/me/orders\`. If you see them in older examples, ignore them \u2014 they are for merchant/admin views only.
2175
2179
 
2176
2180
  - \`order.accountId\`, \`order.storeId\`, \`order.customerId\` (already known on the request)
2177
- - \`order.notes\` (internal merchant notes)
2178
2181
  - \`order.customFieldValues\` (order-level checkout fields \u2014 merchant concept; buyers see \`items[i].customizations\`)
2179
2182
  - \`order.appliedSurcharges\`, \`order.surchargeAmount\`, \`order.appliedRuleIds\`, \`order.downloadMeta\`, \`order.pickupLocationData\``;
2180
2183
  }
@@ -2222,7 +2225,22 @@ function OrderConfirmation() {
2222
2225
 
2223
2226
  **Common mistake:** Don't just check URL params and show success immediately!
2224
2227
  You MUST call \`waitForOrder(checkoutId)\` to verify the order was created.
2225
- Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.`;
2228
+ Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.
2229
+
2230
+ ### Optional: render full order details on the confirmation page
2231
+
2232
+ Want a richer thank-you page (items, shipping address, totals, the shopper's
2233
+ order note)? After \`waitForOrder\` succeeds, fetch the full buyer-safe order \u2014
2234
+ works for guests too (possession of the checkout id is the credential):
2235
+
2236
+ \`\`\`typescript
2237
+ const order = await client.getOrderByCheckout(checkoutId);
2238
+ // order.items, order.shippingAddress, order.notes, order.subtotal,
2239
+ // order.shippingAmount, order.taxAmount / order.taxBreakdown, order.totalAmount
2240
+ \`\`\`
2241
+
2242
+ Render whatever subset fits your design \u2014 same field shapes as the buyer
2243
+ order-history view (see "Buyer order view" section).`;
2226
2244
  }
2227
2245
  function getI18nSection() {
2228
2246
  return `## Internationalization (i18n)
@@ -2552,6 +2570,8 @@ dashboard or the admin SDK).
2552
2570
 
2553
2571
  \`\`\`typescript
2554
2572
  // Publish to a specific vibe-coded site (admin mode):
2573
+ await admin.publishProductToSalesChannel('prod_id', 'vc_conn_id');
2574
+ await admin.publishCouponToSalesChannel('coupon_id', 'vc_conn_id');
2555
2575
  await admin.publishCategoryToVibeCodedSite('cat_id', 'conn_id');
2556
2576
  await admin.publishTagToVibeCodedSite('tag_id', 'conn_id');
2557
2577
  await admin.publishBrandToVibeCodedSite('brand_id', 'conn_id');
@@ -3347,11 +3367,18 @@ Points are earned automatically by the platform when an order is paid \u2014 the
3347
3367
  \`\`\`typescript
3348
3368
  // requires client.setCustomerToken(auth.token)
3349
3369
  const status = await client.getLoyaltyStatus();
3350
- // { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
3370
+ // {
3371
+ // enrolled, pointsBalance, lifetimeEarned,
3372
+ // program: { pointsName, currencyRatio, status } | null,
3373
+ // tier: { id, name, level, pointsMultiplier } | null,
3374
+ // nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
3375
+ // progressToNextTier, pointsToNextTier,
3376
+ // }
3351
3377
  \`\`\`
3352
3378
 
3353
3379
  - \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
3354
3380
  - \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
3381
+ - \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
3355
3382
 
3356
3383
  ## Enroll (only if auto-enroll is off)
3357
3384
 
@@ -3359,19 +3386,67 @@ const status = await client.getLoyaltyStatus();
3359
3386
  await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
3360
3387
  \`\`\`
3361
3388
 
3389
+ May grant a one-time "joined the program" bonus if configured \u2014 distinct from any account-signup bonus (granted automatically on registration, not by this call).
3390
+
3362
3391
  ## Rewards & redemption
3363
3392
 
3364
3393
  \`\`\`typescript
3365
3394
  const rewards = await client.getAvailableRewards();
3366
- // [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
3395
+ // [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
3396
+ // Already filtered to rewards the customer's current tier qualifies for.
3367
3397
 
3368
- const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
3398
+ const { couponCode, discountType, discountValue, pointsBalance } =
3399
+ await client.redeemLoyaltyReward(rewardId);
3369
3400
  await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
3370
3401
  \`\`\`
3371
3402
 
3372
- - \`redeemLoyaltyReward\` mints a one-time fixed-amount coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
3373
- - Throws if the customer has insufficient points. If minting fails, points are refunded automatically.
3403
+ - \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
3404
+ - \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
3405
+ - Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
3374
3406
  - Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
3407
+
3408
+ ## Social share (optional)
3409
+
3410
+ \`\`\`typescript
3411
+ await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
3412
+ \`\`\`
3413
+
3414
+ ## Referrals (when the store enables them)
3415
+
3416
+ \`getLoyaltyStatus()\` also returns \`referralCode\` (the member's share code, null when referrals are disabled or the customer isn't enrolled) and \`referralWelcomeCoupon\` (the still-unused welcome coupon this customer got for signing up via a referral link).
3417
+
3418
+ \`\`\`typescript
3419
+ // 1. Referrer: build a share link around their code.
3420
+ const { referralCode } = await client.getLoyaltyStatus();
3421
+ const shareUrl = \`https://your-store.com/?ref=\${referralCode}\`;
3422
+
3423
+ // 2. Landing page: PUBLIC lookup \u2014 no customerToken needed.
3424
+ const info = await client.getReferralInfo(refFromQuery);
3425
+ // { valid, referrerFirstName, reward: { name, type, value, minOrderAmount } | null }
3426
+ if (info.valid) {
3427
+ // "Jane sent you a gift: 10% off your first order"
3428
+ }
3429
+
3430
+ // 3. Registration: pass the code \u2014 invalid codes never fail registration
3431
+ // (validated asynchronously server-side with fraud checks).
3432
+ await client.registerCustomer({ email, password, referralCode: refFromQuery });
3433
+
3434
+ // 4. Show the referee their welcome coupon after login:
3435
+ const { referralWelcomeCoupon } = await client.getLoyaltyStatus();
3436
+ if (referralWelcomeCoupon) {
3437
+ await client.applyCoupon(cartId, referralWelcomeCoupon.code);
3438
+ }
3439
+ \`\`\`
3440
+
3441
+ The referrer earns a points bonus after the referee's first qualifying order (held through the program's pending window, like order points). Everything server-side \u2014 no storefront earn calls.
3442
+
3443
+ ## Birthday gifts (when the store enables them)
3444
+
3445
+ No dedicated calls \u2014 collect \`birthMonth\`/\`birthDay\` (1-12 / 1-31, **no year**, must be sent together) via \`updateMyProfile()\` and the platform emails a one-time gift coupon ahead of the customer's birthday.
3446
+
3447
+ \`\`\`typescript
3448
+ await client.updateMyProfile({ birthMonth: 4, birthDay: 17 });
3449
+ \`\`\`
3375
3450
  `;
3376
3451
  }
3377
3452
  function getSectionByTopic(topic, connectionId, currency) {
@@ -3896,6 +3971,7 @@ interface Checkout {
3896
3971
  appliedSurcharges?: Array<{ key: string; name: string; value: unknown; amount: string }> | null;
3897
3972
  customFieldValues?: Record<string, unknown> | null;
3898
3973
  couponCode?: string | null;
3974
+ notes?: string | null; // Order-level shopper note \u2014 copied onto the order at completion
3899
3975
  lineItems: CheckoutLineItem[]; // Use THIS for order summary, NOT cart.items!
3900
3976
  itemCount: number;
3901
3977
  availableShippingRates?: ShippingRate[];
@@ -3964,6 +4040,7 @@ interface SetShippingAddressDto {
3964
4040
  postalCode: string;
3965
4041
  country: string;
3966
4042
  phone?: string;
4043
+ notes?: string; // Order notes textarea \u2014 include one on checkout by default! Max 2000 chars, lands on the order
3967
4044
  }
3968
4045
 
3969
4046
  interface CreateCheckoutDto {
@@ -4907,6 +4984,18 @@ interface TaxEstimateResponse {
4907
4984
  // full shipping address. The country comes from your edge runtime
4908
4985
  // (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
4909
4986
  var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
4987
+ interface LoyaltyTierSummary {
4988
+ id: string;
4989
+ name: string;
4990
+ level: number; // ordering; higher = better tier
4991
+ pointsMultiplier: number;
4992
+ }
4993
+
4994
+ interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
4995
+ qualificationType: 'SPEND' | 'POINTS';
4996
+ qualificationThreshold: number;
4997
+ }
4998
+
4910
4999
  interface LoyaltyStatus {
4911
5000
  enrolled: boolean;
4912
5001
  pointsBalance: number; // redeemable points (pending earns excluded)
@@ -4916,6 +5005,28 @@ interface LoyaltyStatus {
4916
5005
  currencyRatio: number; // points needed to redeem 1 unit of store currency
4917
5006
  status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
4918
5007
  } | null; // null when the store has no loyalty program
5008
+ tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
5009
+ nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
5010
+ progressToNextTier: number; // 0..1
5011
+ pointsToNextTier: number | null;
5012
+ referralCode?: string | null; // member's share code; null when referrals disabled / not enrolled
5013
+ referralWelcomeCoupon?: { // unused welcome coupon from signing up via a referral link
5014
+ code: string;
5015
+ type: string; // 'PERCENTAGE' | 'FIXED_AMOUNT'
5016
+ value: number;
5017
+ } | null;
5018
+ }
5019
+
5020
+ // Public referral-link lookup \u2014 NO customerToken needed (safe pre-signup)
5021
+ interface ReferralInfo {
5022
+ valid: boolean; // false for unknown/disabled codes
5023
+ referrerFirstName: string | null; // for "Jane sent you a gift" copy \u2014 no other PII
5024
+ reward: {
5025
+ name: string;
5026
+ type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
5027
+ value: number;
5028
+ minOrderAmount: number | null;
5029
+ } | null;
4919
5030
  }
4920
5031
 
4921
5032
  interface LoyaltyReward {
@@ -4923,16 +5034,19 @@ interface LoyaltyReward {
4923
5034
  name: string;
4924
5035
  description: string | null;
4925
5036
  pointsCost: number;
4926
- discountValue: number; // fixed amount off, store currency
5037
+ type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
5038
+ discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
4927
5039
  minOrderAmount: number | null;
4928
5040
  maxUsesPerUser: number;
4929
5041
  isActive: boolean;
5042
+ minTierLevel: number | null; // minimum tier level required, or null for no restriction
4930
5043
  createdAt: string;
4931
5044
  updatedAt: string;
4932
5045
  }
4933
5046
 
4934
5047
  interface RedeemRewardResult {
4935
5048
  couponCode: string; // one-time coupon to apply at checkout
5049
+ discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
4936
5050
  discountValue: number;
4937
5051
  pointsSpent: number;
4938
5052
  pointsBalance: number; // remaining balance after redemption
@@ -4942,6 +5056,10 @@ interface RedeemRewardResult {
4942
5056
  // client.enrollInLoyalty(): Promise<LoyaltyStatus>
4943
5057
  // client.getAvailableRewards(): Promise<LoyaltyReward[]>
4944
5058
  // client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
5059
+ // client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
5060
+ // client.getReferralInfo(code: string): Promise<ReferralInfo> // PUBLIC \u2014 no customerToken
5061
+ // Referral signup: client.registerCustomer({ ..., referralCode })
5062
+ // Birthday gift data: client.updateMyProfile({ birthMonth, birthDay }) // 1-12 / 1-31, no year, both together
4945
5063
  `;
4946
5064
  var TYPES_BY_DOMAIN = {
4947
5065
  products: PRODUCTS_TYPES,
@@ -6440,6 +6558,14 @@ function formatCapabilities(caps) {
6440
6558
  lines.push(
6441
6559
  caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
6442
6560
  );
6561
+ if (caps.features.hasLoyaltyProgram) {
6562
+ lines.push(
6563
+ caps.features.hasReferralProgram ? "\u2705 Referral program enabled (share codes + welcome reward \u2014 storefront-mode only)" : "\u274C Referral program not enabled"
6564
+ );
6565
+ lines.push(
6566
+ caps.features.hasBirthdayRewards ? "\u2705 Birthday gifts enabled (set birthMonth/birthDay via updateMyProfile)" : "\u274C Birthday gifts not enabled"
6567
+ );
6568
+ }
6443
6569
  lines.push("");
6444
6570
  lines.push("## Connection Settings");
6445
6571
  lines.push(
@@ -6487,7 +6613,7 @@ function formatCapabilities(caps) {
6487
6613
  }
6488
6614
  if (caps.features.hasLoyaltyProgram) {
6489
6615
  suggestions.push(
6490
- "A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus(), getAvailableRewards(), and redeemLoyaltyReward(id). Note: loyalty is NOT available in vibe-coded mode."
6616
+ "A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance, tier progress, and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus() (includes tier/nextTier/progressToNextTier), getAvailableRewards(), redeemLoyaltyReward(id), and optionally reportSocialShare(). Note: loyalty is NOT available in vibe-coded mode."
6491
6617
  );
6492
6618
  }
6493
6619
  if (caps.features.hasCheckoutCustomFields) {