@doswiftly/storefront-operations 7.0.0 → 8.0.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/mutations.graphql CHANGED
@@ -7,6 +7,7 @@
7
7
  # Cart Mutations
8
8
  # ============================================
9
9
 
10
+ # Creates a new cart and optionally pre-populates it with line items. Cart ID is a UUID stored by the SDK in the `cart-id` cookie (30-day TTL); the cart itself expires server-side after 72 hours of inactivity. The `warnings` field is reserved for non-blocking issues — current implementation returns it empty in this path.
10
11
  mutation CartCreate($input: CartCreateInput) {
11
12
  cartCreate(input: $input) {
12
13
  cart {
@@ -21,6 +22,7 @@ mutation CartCreate($input: CartCreateInput) {
21
22
  }
22
23
  }
23
24
 
25
+ # Adds line items to a cart. Each line is `{ merchandiseId, quantity, attributes?, attributeSelections? }`. If the same variant + identical attributes are added twice, quantities merge into one row instead of duplicating. Validates stock (`INSUFFICIENT_STOCK`) and configurator attributes (`ATTRIBUTE_REQUIRED`, `ATTRIBUTE_OPTION_INVALID`). Triggers cart re-pricing including discount recalculation.
24
26
  mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
25
27
  cartAddLines(id: $id, lines: $lines) {
26
28
  cart {
@@ -35,6 +37,7 @@ mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
35
37
  }
36
38
  }
37
39
 
40
+ # Updates quantity and/or attributes of existing cart lines by `id`. Setting `quantity: 0` auto-deletes the line. Passing `attributes: []` clears them; omitting the field preserves existing values. Re-validates stock and re-prices the cart after each update.
38
41
  mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!) {
39
42
  cartUpdateLines(id: $id, lines: $lines) {
40
43
  cart {
@@ -49,6 +52,7 @@ mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!) {
49
52
  }
50
53
  }
51
54
 
55
+ # Removes specific lines from cart by `lineIds[]`. Internally delegates to `cartUpdateLines` with `quantity: 0` — both endpoints are functionally equivalent; this one exists for API ergonomics when intent is explicit removal. Triggers cart re-pricing.
52
56
  mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!) {
53
57
  cartRemoveLines(id: $id, lineIds: $lineIds) {
54
58
  cart {
@@ -63,6 +67,7 @@ mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!) {
63
67
  }
64
68
  }
65
69
 
70
+ # Replaces (NOT appends) the cart's discount codes with the given list. Pass `[]` to clear all codes. Each code is validated against `discounts` table (existence, active status); invalid codes appear in `userErrors[]` as `DISCOUNT_CODE_INVALID`. Triggers cart re-pricing — discount allocations are recomputed and stored in `cart.discountAmount`.
66
71
  mutation CartApplyDiscountCodes($id: ID!, $discountCodes: [String!]!) {
67
72
  cartApplyDiscountCodes(id: $id, discountCodes: $discountCodes) {
68
73
  cart {
@@ -77,6 +82,7 @@ mutation CartApplyDiscountCodes($id: ID!, $discountCodes: [String!]!) {
77
82
  }
78
83
  }
79
84
 
85
+ # Associates a customer with the cart. Despite the input shape accepting `email`, `phone`, `countryCode`, `languageCode`, only `customerId` is currently persisted — other fields are silently ignored. Does NOT trigger tax / shipping recalculation; pure cart-to-customer linking. Use during login or guest-to-account upgrade.
80
86
  mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
81
87
  cartUpdateBuyerIdentity(id: $id, buyerIdentity: $buyerIdentity) {
82
88
  cart {
@@ -91,6 +97,7 @@ mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInpu
91
97
  }
92
98
  }
93
99
 
100
+ # Sets a free-text note on the cart (gift message, special instructions). Pass empty string to clear. Stored on the `Cart` row, propagated to the `Order` at checkout completion, visible to merchant in admin.
94
101
  mutation CartUpdateNote($id: ID!, $note: String!) {
95
102
  cartUpdateNote(id: $id, note: $note) {
96
103
  cart {
@@ -109,6 +116,7 @@ mutation CartUpdateNote($id: ID!, $note: String!) {
109
116
  # Customer Auth Mutations
110
117
  # ============================================
111
118
 
119
+ # Registers a new customer. Returns `customerAccessToken` immediately — the customer record is created with status `ACTIVE` (no pending state). The activation email containing `customerActivate` token is sent for `emailVerified=true` confirmation, but is NOT required for login. Cookie: `customerAccessToken`, 30-day max-age, httpOnly. JWT TTL: 24h. Bot-protection guarded.
112
120
  mutation CustomerSignup($input: CustomerCreateInput!) {
113
121
  customerSignup(input: $input) {
114
122
  customer {
@@ -123,6 +131,7 @@ mutation CustomerSignup($input: CustomerCreateInput!) {
123
131
  }
124
132
  }
125
133
 
134
+ # Logs in with email + password. JWT lifetime 24h; cookie max-age 30d (cookie outlives JWT — call `customerRefreshToken` before JWT expiry to extend session). Brute-force protected: 10 failed attempts per email = 15-min Redis-backed lockout. Failed attempts are recorded for non-existent emails too (timing-attack safe).
126
135
  mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
127
136
  customerLogin(input: $input) {
128
137
  customerAccessToken {
@@ -134,6 +143,7 @@ mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
134
143
  }
135
144
  }
136
145
 
146
+ # Clears the `customerAccessToken` cookie. Note: the JWT itself is NOT server-side invalidated — it remains valid until its 24h expiry. Server-side token revocation is on the roadmap. Idempotent.
137
147
  mutation CustomerLogout {
138
148
  customerLogout {
139
149
  deletedAccessToken
@@ -144,6 +154,7 @@ mutation CustomerLogout {
144
154
  }
145
155
  }
146
156
 
157
+ # Issues a fresh JWT (24h TTL) for the currently-authenticated customer. Reads identity from the current cookie/Bearer token; takes no input. Use proactively before JWT expiry or reactively on a 401 retry. The new token replaces the cookie value.
147
158
  mutation CustomerRefreshToken {
148
159
  customerRefreshToken {
149
160
  customerAccessToken {
@@ -159,6 +170,7 @@ mutation CustomerRefreshToken {
159
170
  # Customer Profile Mutations
160
171
  # ============================================
161
172
 
173
+ # Updates the logged-in customer's profile. Supported fields include `firstName`, `lastName`, `phone`, marketing preferences, and B2B identity (`customerType`, `companyName`, `taxId`, `vatNumber`, `regon`). Concurrent updates from the storefront and the merchant admin are reconciled safely — the loser of a race retries against the latest version. Marketing consent changes are recorded separately for audit purposes.
162
174
  mutation CustomerUpdate($customer: CustomerUpdateInput!) {
163
175
  customerUpdate(customer: $customer) {
164
176
  customer {
@@ -174,6 +186,7 @@ mutation CustomerUpdate($customer: CustomerUpdateInput!) {
174
186
  # Customer Address Mutations
175
187
  # ============================================
176
188
 
189
+ # Adds a new mailing address. If `isDefaultShipping` or `isDefaultBilling` is `true` in the input, the new address is set as default and any other address for this customer holding that flag is atomically cleared in the same transaction.
177
190
  mutation CustomerAddAddress($address: MailingAddressInput!) {
178
191
  customerAddAddress(address: $address) {
179
192
  address {
@@ -185,6 +198,7 @@ mutation CustomerAddAddress($address: MailingAddressInput!) {
185
198
  }
186
199
  }
187
200
 
201
+ # Updates an existing address owned by the logged-in customer. If `isDefaultShipping` or `isDefaultBilling` toggles to `true`, default flag is atomically cleared on all other addresses for this customer.
188
202
  mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!) {
189
203
  customerUpdateAddress(id: $id, address: $address) {
190
204
  address {
@@ -196,6 +210,7 @@ mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!) {
196
210
  }
197
211
  }
198
212
 
213
+ # Hard-deletes an address row from `customer_addresses`. Historical orders that referenced this address are unaffected (address is snapshotted into the order at checkout completion).
199
214
  mutation CustomerRemoveAddress($id: ID!) {
200
215
  customerRemoveAddress(id: $id) {
201
216
  deletedAddressId
@@ -205,6 +220,7 @@ mutation CustomerRemoveAddress($id: ID!) {
205
220
  }
206
221
  }
207
222
 
223
+ # Marks the given address as the customer's default **shipping** address. Atomically clears the shipping-default flag from all other addresses. Note: there is no separate setter for default billing — set `isDefaultBilling: true` via `customerAddAddress` / `customerUpdateAddress` instead.
208
224
  mutation CustomerSetDefaultAddress($addressId: ID!) {
209
225
  customerSetDefaultAddress(addressId: $addressId) {
210
226
  customer {
@@ -220,6 +236,7 @@ mutation CustomerSetDefaultAddress($addressId: ID!) {
220
236
  # Customer Password Mutations
221
237
  # ============================================
222
238
 
239
+ # Sends a password reset email. Always returns success regardless of whether the email exists (no account enumeration). The email is dispatched asynchronously, so a small delay between request and inbox arrival is normal. Rate-limited to 3 requests per 10 minutes.
223
240
  mutation CustomerRequestPasswordReset($email: String!) {
224
241
  customerRequestPasswordReset(email: $email) {
225
242
  userErrors {
@@ -228,6 +245,7 @@ mutation CustomerRequestPasswordReset($email: String!) {
228
245
  }
229
246
  }
230
247
 
248
+ # Activates a newly-created account using the 64-hex activation token from the welcome email + a chosen password. Token TTL is 24h, single-use (atomically marked `used_at`). On success: sets `email_verified=true`, transitions status `INACTIVE`→`ACTIVE`, returns a fresh JWT for auto-login. Rate-limited.
231
249
  mutation CustomerActivate($token: String!, $password: String!) {
232
250
  customerActivate(token: $token, password: $password) {
233
251
  customer {
@@ -242,6 +260,7 @@ mutation CustomerActivate($token: String!, $password: String!) {
242
260
  }
243
261
  }
244
262
 
263
+ # Resets the password using the 64-hex reset token from the password-reset email. Token TTL is 1h, single-use (atomically marked `used_at`). On success: updates the password hash and returns a fresh JWT for auto-login (no second login step needed). Rate-limited.
245
264
  mutation CustomerResetPassword($token: String!, $newPassword: String!) {
246
265
  customerResetPassword(token: $token, newPassword: $newPassword) {
247
266
  customer {
@@ -260,6 +279,7 @@ mutation CustomerResetPassword($token: String!, $newPassword: String!) {
260
279
  # Checkout Mutations
261
280
  # ============================================
262
281
 
282
+ # Creates a new checkout session for the given cart (or a fresh cart if `cartId` is omitted). Inherits applied discounts and gift cards from the cart by reference (not snapshot). Errors: `CART_NOT_FOUND`, `EMPTY_CART`, `ALREADY_COMPLETED` (cart already converted to order). NOT idempotent — multiple calls create multiple checkouts.
263
283
  mutation CheckoutCreate($input: CheckoutCreateInput!) {
264
284
  checkoutCreate(input: $input) {
265
285
  checkout {
@@ -271,8 +291,9 @@ mutation CheckoutCreate($input: CheckoutCreateInput!) {
271
291
  }
272
292
  }
273
293
 
274
- mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: CheckoutAddressInput!) {
275
- checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) {
294
+ # Sets the shipping address (full replace, not patch). Triggers cart re-pricing. Address format is NOT validated here — full validation runs at `checkoutComplete`.
295
+ mutation CheckoutUpdateShippingAddress($id: ID!, $shippingAddress: CheckoutAddressInput!) {
296
+ checkoutUpdateShippingAddress(id: $id, shippingAddress: $shippingAddress) {
276
297
  checkout {
277
298
  ...Checkout
278
299
  }
@@ -282,8 +303,9 @@ mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: Check
282
303
  }
283
304
  }
284
305
 
285
- mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: CheckoutAddressInput!) {
286
- checkoutBillingAddressUpdate(checkoutId: $checkoutId, billingAddress: $billingAddress) {
306
+ # Sets the billing address (full replace). Independent of shipping address — pass it explicitly even when "billing same as shipping".
307
+ mutation CheckoutUpdateBillingAddress($id: ID!, $billingAddress: CheckoutAddressInput!) {
308
+ checkoutUpdateBillingAddress(id: $id, billingAddress: $billingAddress) {
287
309
  checkout {
288
310
  ...Checkout
289
311
  }
@@ -293,8 +315,9 @@ mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: Checkou
293
315
  }
294
316
  }
295
317
 
296
- mutation CheckoutEmailUpdate($checkoutId: ID!, $email: String!) {
297
- checkoutEmailUpdate(checkoutId: $checkoutId, email: $email) {
318
+ # Sets or updates the contact email on the checkout (used for guest checkout, order confirmation, and tracking emails). Validated against a regex; returns `INVALID` for malformed format.
319
+ mutation CheckoutUpdateEmail($id: ID!, $email: String!) {
320
+ checkoutUpdateEmail(id: $id, email: $email) {
298
321
  checkout {
299
322
  ...Checkout
300
323
  }
@@ -304,8 +327,9 @@ mutation CheckoutEmailUpdate($checkoutId: ID!, $email: String!) {
304
327
  }
305
328
  }
306
329
 
307
- mutation CheckoutShippingLineUpdate($checkoutId: ID!, $shippingRateHandle: String!) {
308
- checkoutShippingLineUpdate(checkoutId: $checkoutId, shippingRateHandle: $shippingRateHandle) {
330
+ # Selects a shipping method by `rateId` (a stable shipping-method UUID, NOT an opaque per-request token). The id comes from `availableShippingMethods` query, computed for the current address + cart subtotal at request time.
331
+ mutation CheckoutSelectShippingRate($id: ID!, $rateId: String!) {
332
+ checkoutSelectShippingRate(id: $id, rateId: $rateId) {
309
333
  checkout {
310
334
  ...Checkout
311
335
  }
@@ -315,8 +339,9 @@ mutation CheckoutShippingLineUpdate($checkoutId: ID!, $shippingRateHandle: Strin
315
339
  }
316
340
  }
317
341
 
318
- mutation CheckoutDiscountCodeApply($checkoutId: ID!, $discountCode: String!) {
319
- checkoutDiscountCodeApply(checkoutId: $checkoutId, discountCode: $discountCode) {
342
+ # Appends a discount code to the cart's `discount_codes` array. Note: while multiple codes can be stored, the pricing engine currently uses **only the first applied code** — codes do not stack. Validated for existence, active status, and customer usage limits.
343
+ mutation CheckoutApplyDiscountCode($id: ID!, $discountCode: String!) {
344
+ checkoutApplyDiscountCode(id: $id, discountCode: $discountCode) {
320
345
  checkout {
321
346
  ...Checkout
322
347
  }
@@ -326,8 +351,9 @@ mutation CheckoutDiscountCodeApply($checkoutId: ID!, $discountCode: String!) {
326
351
  }
327
352
  }
328
353
 
329
- mutation CheckoutDiscountCodeRemove($checkoutId: ID!, $discountCode: String!) {
330
- checkoutDiscountCodeRemove(checkoutId: $checkoutId, discountCode: $discountCode) {
354
+ # Removes a code from the cart's `discount_codes` array (filters by exact match). Triggers re-pricing.
355
+ mutation CheckoutRemoveDiscountCode($id: ID!, $discountCode: String!) {
356
+ checkoutRemoveDiscountCode(id: $id, discountCode: $discountCode) {
331
357
  checkout {
332
358
  ...Checkout
333
359
  }
@@ -337,8 +363,9 @@ mutation CheckoutDiscountCodeRemove($checkoutId: ID!, $discountCode: String!) {
337
363
  }
338
364
  }
339
365
 
340
- mutation CheckoutDiscountCodeValidate($checkoutId: ID!, $discountCode: String!) {
341
- checkoutDiscountCodeValidate(checkoutId: $checkoutId, discountCode: $discountCode) {
366
+ # READ-ONLY validation of a discount code against the current checkout — does NOT modify state. Returns `{ isValid, discount, error }` for previewing the effect (e.g. inline UI feedback as the user types).
367
+ mutation CheckoutValidateDiscountCode($id: ID!, $discountCode: String!) {
368
+ checkoutValidateDiscountCode(id: $id, discountCode: $discountCode) {
342
369
  result {
343
370
  isValid
344
371
  discount {
@@ -362,8 +389,9 @@ mutation CheckoutDiscountCodeValidate($checkoutId: ID!, $discountCode: String!)
362
389
  }
363
390
  }
364
391
 
365
- mutation CheckoutPaymentMethodUpdate($checkoutId: ID!, $paymentMethodId: ID!) {
366
- checkoutPaymentMethodUpdate(checkoutId: $checkoutId, paymentMethodId: $paymentMethodId) {
392
+ # Selects a payment method by `paymentMethodId` (UUID from `availablePaymentMethods` query). Validates existence and active status; no pre-authorization is performed here.
393
+ mutation CheckoutSelectPaymentMethod($id: ID!, $paymentMethodId: ID!) {
394
+ checkoutSelectPaymentMethod(id: $id, paymentMethodId: $paymentMethodId) {
367
395
  checkout {
368
396
  ...Checkout
369
397
  }
@@ -373,8 +401,9 @@ mutation CheckoutPaymentMethodUpdate($checkoutId: ID!, $paymentMethodId: ID!) {
373
401
  }
374
402
  }
375
403
 
376
- mutation CheckoutComplete($checkoutId: ID!, $input: CheckoutCompleteInput) {
377
- checkoutComplete(checkoutId: $checkoutId, input: $input) {
404
+ # Finalizes the checkout: creates the `Order`, deducts gift cards, sends order-created confirmation — all atomically. **Idempotent on `idempotencyKey`** (NOT on the checkout `id`); auto-generated if the caller omits it. The `paymentUrl` field is reserved but is NOT populated here — for hosted gateways (PayU, P24) the storefront calls a separate `paymentCreate` mutation after this returns.
405
+ mutation CheckoutComplete($id: ID!, $input: CheckoutCompleteInput) {
406
+ checkoutComplete(id: $id, input: $input) {
378
407
  checkout {
379
408
  ...Checkout
380
409
  }
@@ -389,11 +418,12 @@ mutation CheckoutComplete($checkoutId: ID!, $input: CheckoutCompleteInput) {
389
418
  }
390
419
 
391
420
  # ============================================
392
- # Gift Card Checkout Mutations (GAP-001)
421
+ # Gift Card Checkout Mutations
393
422
  # ============================================
394
423
 
395
- mutation CheckoutGiftCardApply($checkoutId: ID!, $giftCardCode: String!) {
396
- checkoutGiftCardApply(checkoutId: $checkoutId, giftCardCode: $giftCardCode) {
424
+ # Applies a gift card to the cart, stackable with discount codes. Consumption is FIFO: each card consumes `min(remainingBalance, paymentDue)` against the current cart total in the order they were applied. The gift card balance is NOT debited yet — actual deduction happens atomically at `checkoutComplete`. Errors: `GIFT_CARD_NOT_FOUND`, `GIFT_CARD_DEPLETED`, `GIFT_CARD_UNUSABLE`, `GIFT_CARD_ALREADY_APPLIED`.
425
+ mutation CheckoutApplyGiftCard($id: ID!, $giftCardCode: String!) {
426
+ checkoutApplyGiftCard(id: $id, giftCardCode: $giftCardCode) {
397
427
  checkout {
398
428
  ...Checkout
399
429
  }
@@ -403,8 +433,9 @@ mutation CheckoutGiftCardApply($checkoutId: ID!, $giftCardCode: String!) {
403
433
  }
404
434
  }
405
435
 
406
- mutation CheckoutGiftCardRemove($checkoutId: ID!, $giftCardCode: String!) {
407
- checkoutGiftCardRemove(checkoutId: $checkoutId, giftCardCode: $giftCardCode) {
436
+ # Removes a gift card from the applied list and recalculates FIFO `appliedAmount` for the remaining cards. Since gift card balances are only debited at `checkoutComplete`, removing before completion has no effect on the underlying gift card balance.
437
+ mutation CheckoutRemoveGiftCard($id: ID!, $giftCardCode: String!) {
438
+ checkoutRemoveGiftCard(id: $id, giftCardCode: $giftCardCode) {
408
439
  checkout {
409
440
  ...Checkout
410
441
  }
@@ -414,8 +445,9 @@ mutation CheckoutGiftCardRemove($checkoutId: ID!, $giftCardCode: String!) {
414
445
  }
415
446
  }
416
447
 
417
- mutation CheckoutGiftCardRecipientUpdate($input: CheckoutGiftCardRecipientInput!) {
418
- checkoutGiftCardRecipientUpdate(input: $input) {
448
+ # Sets per-line-item recipient details (name, email, message, delivery date) for digital gift card products in the cart (variants with `type: GIFT_CARD`). Required before `checkoutComplete` for any line item with a gift-card variant. Recipient details are associated with each gift-card line item and propagated to the resulting order.
449
+ mutation CheckoutUpdateGiftCardRecipient($input: CheckoutGiftCardRecipientInput!) {
450
+ checkoutUpdateGiftCardRecipient(input: $input) {
419
451
  checkout {
420
452
  ...Checkout
421
453
  }
@@ -426,9 +458,10 @@ mutation CheckoutGiftCardRecipientUpdate($input: CheckoutGiftCardRecipientInput!
426
458
  }
427
459
 
428
460
  # ============================================
429
- # Return Mutations (R30 - Returns/RMA)
461
+ # Return Mutations
430
462
  # ============================================
431
463
 
464
+ # Creates an RMA in `REQUESTED` status (awaits merchant approval — NOT auto-approved). Input: `orderId`, `reason`, `items[{ variantId, quantity, reason, condition }]`, optional `compensationType` (REFUND or STORE_CREDIT) and `customerNote`. Validates the order's `fulfillmentStatus` permits returns and that requested quantities don't exceed already-shipped/unreturned quantities. Supports optional `idempotencyKey` for retry-safe creation.
432
465
  mutation ReturnCreate($input: ReturnCreateInput!) {
433
466
  returnCreate(input: $input) {
434
467
  return {
@@ -440,6 +473,7 @@ mutation ReturnCreate($input: ReturnCreateInput!) {
440
473
  }
441
474
  }
442
475
 
476
+ # Cancels a return that is currently in `REQUESTED`, `APPROVED`, or `DRAFT` status (cancellation is allowed even AFTER merchant approval, as long as the return shipment hasn't been processed). Sets `cancelled_at` timestamp. Customer can only cancel returns they own.
443
477
  mutation ReturnCancel($id: ID!) {
444
478
  returnCancel(id: $id) {
445
479
  return {
@@ -452,17 +486,19 @@ mutation ReturnCancel($id: ID!) {
452
486
  }
453
487
 
454
488
  # ============================================
455
- # Loyalty Program Mutations (R8, R10.4)
489
+ # Loyalty Program Mutations
456
490
  # ============================================
457
491
 
492
+ # Redeems a loyalty reward by `rewardId`. Three reward types are supported, distinguished by which output field is populated: `discountCode` (issues a `LOYALTY-XXXX` code with 30-day expiry), `productDiscountCode` (issues a single-use 100%-off code for a specific product), or `giftCardCode` (creates a new gift card for the customer). Points are deducted atomically inside a transaction — if external creation (e.g. gift card service) fails after deduction, points are reversed.
458
493
  mutation RedeemLoyaltyReward($input: RedeemRewardInput!) {
459
- redeemLoyaltyReward(input: $input) {
494
+ loyaltyRedeemReward(input: $input) {
460
495
  ...RedeemRewardPayload
461
496
  }
462
497
  }
463
498
 
499
+ # Returns the customer's referral code, generating one on first call. Idempotent UPSERT — subsequent calls return the existing code from `customers.referral_code`. Format: `REF-XXXXXXXX` (8 random alphanumeric chars). Output also includes a `shareUrl` built from the shop's domain.
464
500
  mutation GenerateReferralCode {
465
- generateReferralCode {
501
+ loyaltyGenerateReferralCode {
466
502
  ...GenerateReferralCodePayload
467
503
  }
468
504
  }
@@ -471,6 +507,7 @@ mutation GenerateReferralCode {
471
507
  # Review Mutations
472
508
  # ============================================
473
509
 
510
+ # Submits a product review (rating 1-5, content 10-5000 chars, sanitized via `sanitizePlainText`). Default state is `PENDING` — review is hidden from public until merchant approves. If the input includes `orderId`, `isVerifiedPurchase` is auto-set to `true`. Bot-protected and rate-limited (10/min by default).
474
511
  mutation ReviewCreate($input: ReviewCreateInput!) {
475
512
  reviewCreate(input: $input) {
476
513
  review {
@@ -482,6 +519,7 @@ mutation ReviewCreate($input: ReviewCreateInput!) {
482
519
  }
483
520
  }
484
521
 
522
+ # Records an upvote (helpful) on a review. UPSERT semantics — one `ReviewVote` row per `(reviewId, customerId)`. Calling upvote twice is a no-op; calling downvote afterwards replaces the vote. Increments `helpful_count` on the review (and decrements `unhelpful_count` if replacing a downvote).
485
523
  mutation ReviewUpvote($reviewId: ID!) {
486
524
  reviewUpvote(reviewId: $reviewId) {
487
525
  review {
@@ -493,6 +531,7 @@ mutation ReviewUpvote($reviewId: ID!) {
493
531
  }
494
532
  }
495
533
 
534
+ # Records a downvote (unhelpful) on a review. Same UPSERT semantics as `reviewUpvote` — one vote row per `(reviewId, customerId)`, replacing any prior vote. Increments `unhelpful_count`.
496
535
  mutation ReviewDownvote($reviewId: ID!) {
497
536
  reviewDownvote(reviewId: $reviewId) {
498
537
  review {
@@ -508,39 +547,51 @@ mutation ReviewDownvote($reviewId: ID!) {
508
547
  # Wishlist Mutations
509
548
  # ============================================
510
549
 
550
+ # Creates a new wishlist for the logged-in customer. `name` is optional (defaults to "My Wishlist"); name uniqueness is NOT enforced — customers can have multiple lists with the same name. Setting `isPublic: true` generates a 16-byte hex `shareToken` for public sharing.
511
551
  mutation WishlistCreate($input: WishlistCreateInput!) {
512
552
  wishlistCreate(input: $input) {
513
553
  wishlist {
514
554
  ...Wishlist
515
555
  }
516
- userErrors
556
+ userErrors {
557
+ ...UserError
558
+ }
517
559
  }
518
560
  }
519
561
 
520
- mutation WishlistAddItem($wishlistId: ID!, $input: WishlistItemInput!) {
521
- wishlistAddItem(wishlistId: $wishlistId, input: $input) {
562
+ # Adds an item by `productId` (and optional `variantId`) to a wishlist. Idempotent on the `(wishlist_id, product_id, variant_id)` unique constraint — adding an already-present item is a silent no-op. Captures `priceAtAdd` for price-drop notifications.
563
+ mutation WishlistAddItem($id: ID!, $input: WishlistItemInput!) {
564
+ wishlistAddItem(id: $id, input: $input) {
522
565
  wishlist {
523
566
  ...Wishlist
524
567
  }
525
- userErrors
568
+ userErrors {
569
+ ...UserError
570
+ }
526
571
  }
527
572
  }
528
573
 
529
- mutation WishlistRemoveItem($wishlistId: ID!, $itemId: ID!) {
530
- wishlistRemoveItem(wishlistId: $wishlistId, itemId: $itemId) {
574
+ # Hard-deletes a wishlist item by `itemId` (the `WishlistItem` row id, NOT the product id).
575
+ mutation WishlistRemoveItem($id: ID!, $itemId: ID!) {
576
+ wishlistRemoveItem(id: $id, itemId: $itemId) {
531
577
  wishlist {
532
578
  ...Wishlist
533
579
  }
534
- userErrors
580
+ userErrors {
581
+ ...UserError
582
+ }
535
583
  }
536
584
  }
537
585
 
538
- mutation WishlistDelete($wishlistId: ID!) {
539
- wishlistDelete(wishlistId: $wishlistId) {
586
+ # Hard-deletes the wishlist row. All wishlist items are removed via cascade. No soft-delete; cannot be undone.
587
+ mutation WishlistDelete($id: ID!) {
588
+ wishlistDelete(id: $id) {
540
589
  wishlist {
541
590
  ...Wishlist
542
591
  }
543
- userErrors
592
+ userErrors {
593
+ ...UserError
594
+ }
544
595
  }
545
596
  }
546
597
 
@@ -548,6 +599,7 @@ mutation WishlistDelete($wishlistId: ID!) {
548
599
  # Cart Attributes
549
600
  # ============================================
550
601
 
602
+ # Replaces (NOT merges) the cart's custom attributes — free-form `[{ key, value }]` pairs visible to merchant in admin. Use for delivery instructions, gift wrap flags, B2B PO numbers, etc. Limit: 250 pairs per cart (returns `CART_ATTRIBUTES_LIMIT_EXCEEDED`); each `key` max 255 chars.
551
603
  mutation CartUpdateAttributes($id: ID!, $attributes: [CartAttributeInput!]!) {
552
604
  cartUpdateAttributes(id: $id, attributes: $attributes) {
553
605
  cart {