@doswiftly/storefront-operations 7.0.0 → 7.1.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/llms-full.txt ADDED
@@ -0,0 +1,4994 @@
1
+ # DoSwiftly Storefront Operations — Full Reference
2
+
3
+ > Schema version: **7.1.0**
4
+ > 48 queries · 44 mutations · 108 fragments
5
+
6
+ Auto-generated from `.graphql` source files. Do not edit by hand — this file is
7
+ regenerated on every release to match the published schema.
8
+
9
+ This file is the deep reference for AI agents and tools that need full operation
10
+ signatures with descriptions, typed variables, and ready-to-execute GraphQL bodies.
11
+
12
+ Companion files:
13
+ - `AGENTS.md` — entry point with critical conventions and progressive disclosure
14
+ - `schema.graphql` — full GraphQL schema (types, fields, enums)
15
+ - `operations.json` — same operations as structured JSON for programmatic tools
16
+
17
+ ---
18
+
19
+ ## Queries
20
+
21
+ ### Query: `Shop`
22
+
23
+ **Section**: Shop
24
+
25
+ **Description**: Returns shop configuration: name, base + supported currencies, supported locales, branding (logo, colors, fonts, social links), contact info, active payment methods, brand metadata, money format template, and the list of countries the shop ships to. Public; no auth required. Call once per session and cache — almost everything else is contextualized by the shop returned here.
26
+
27
+ **Variables**: none
28
+
29
+ **Fragments used**: `Shop`
30
+
31
+ **GraphQL**:
32
+ ```graphql
33
+ query Shop {
34
+ shop {
35
+ ...Shop
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### Query: `Product`
41
+
42
+ **Section**: Products
43
+
44
+ **Description**: Fetches a single product by `id` or `handle` (URL slug). Pass either — whichever is provided wins; if both are missing, returns null. Returns null if the product is not storefront-accessible (must be `ACTIVE` status with `PUBLIC` or `BUNDLE_ONLY` visibility).
45
+
46
+ **Variables**:
47
+ - `$id`: `ID`
48
+ - `$handle`: `String`
49
+
50
+ **Fragments used**: `ProductFull`
51
+
52
+ **GraphQL**:
53
+ ```graphql
54
+ query Product($id: ID, $handle: String) {
55
+ product(id: $id, handle: $handle) {
56
+ ...ProductFull
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### Query: `ProductConfigurator`
62
+
63
+ **Section**: Products
64
+
65
+ **Description**: Fetches a product together with its filtered attribute definitions, optimized for the configurator UI (e.g. customer-facing text fields, finishing options, scoped variants). `fillingMode: "CUSTOMER"` returns only customer-facing attributes; pass `"BOTH"` to also include attributes shared with the merchant admin. Single round-trip — saves a separate `attributes` query.
66
+
67
+ **Variables**:
68
+ - `$handle`: `String!`
69
+ - `$fillingMode`: `String` *(default: `"CUSTOMER"`)*
70
+
71
+ **Fragments used**: `ProductAttributeDefinition`, `ProductFull`
72
+
73
+ **GraphQL**:
74
+ ```graphql
75
+ query ProductConfigurator($handle: String!, $fillingMode: String = "CUSTOMER") {
76
+ product(handle: $handle) {
77
+ ...ProductFull
78
+ attributes(filter: {fillingMode: $fillingMode}) {
79
+ ...ProductAttributeDefinition
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### Query: `Products`
86
+
87
+ **Section**: Products
88
+
89
+ **Description**: Paginated product list (Relay Connection, default page size 20, max 100). The `query` argument supports a structured search syntax — `tag:summer`, `vendor:foo`, `product_type:shirts`, `variants.price:>10`, plus `AND`/`OR`/`NOT` — falling back to free-text title/content search. The `filters[]` array uses multi-filter logic: same field name appears multiple times → OR; different fields → AND. Sort: `RELEVANCE`, `TITLE`, `PRICE`, `NEWEST`, `OLDEST`, `BEST_SELLING`. The response includes a `filters` block for faceted navigation (counts per filterable attribute value).
90
+
91
+ **Variables**:
92
+ - `$first`: `Int` *(default: `20`)*
93
+ - `$after`: `String`
94
+ - `$query`: `String`
95
+ - `$sortKey`: `ProductSortKeys` *(default: `RELEVANCE`)*
96
+ - `$reverse`: `Boolean` *(default: `false`)*
97
+ - `$filters`: `[ProductFilter!]`
98
+
99
+ **Fragments used**: `PageInfo`, `ProductCard`
100
+
101
+ **GraphQL**:
102
+ ```graphql
103
+ query Products($first: Int = 20, $after: String, $query: String, $sortKey: ProductSortKeys = RELEVANCE, $reverse: Boolean = false, $filters: [ProductFilter!]) {
104
+ products(
105
+ first: $first
106
+ after: $after
107
+ query: $query
108
+ sortKey: $sortKey
109
+ reverse: $reverse
110
+ filters: $filters
111
+ ) {
112
+ edges {
113
+ node {
114
+ ...ProductCard
115
+ }
116
+ cursor
117
+ }
118
+ nodes {
119
+ ...ProductCard
120
+ }
121
+ pageInfo {
122
+ ...PageInfo
123
+ }
124
+ totalCount
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### Query: `ProductSearch`
130
+
131
+ **Section**: Products
132
+
133
+ **Description**: Full-text product search — `$query` is required. Functionally equivalent to `Products` with `$query` set, minus the `sortKey` argument (search defaults to relevance ranking). Use for the search results page; combine with `filters[]` for guided refinement.
134
+
135
+ **Variables**:
136
+ - `$query`: `String!`
137
+ - `$first`: `Int` *(default: `20`)*
138
+ - `$after`: `String`
139
+ - `$filters`: `[ProductFilter!]`
140
+
141
+ **Fragments used**: `PageInfo`, `ProductCard`
142
+
143
+ **GraphQL**:
144
+ ```graphql
145
+ query ProductSearch($query: String!, $first: Int = 20, $after: String, $filters: [ProductFilter!]) {
146
+ products(query: $query, first: $first, after: $after, filters: $filters) {
147
+ edges {
148
+ node {
149
+ ...ProductCard
150
+ }
151
+ cursor
152
+ }
153
+ nodes {
154
+ ...ProductCard
155
+ }
156
+ pageInfo {
157
+ ...PageInfo
158
+ }
159
+ totalCount
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### Query: `PredictiveSearch`
165
+
166
+ **Section**: Products
167
+
168
+ **Description**: Type-ahead suggestions for the storefront search input. Returns up to `$limit` matching products (hard cap 50) plus up to 5 styled query suggestions with `<mark>` tags around matched spans. Polish-language aware (handles morphology in suggestions). Run on each keystroke (debounce 200-300ms). The `$query` is capped at 100 characters server-side.
169
+
170
+ **Variables**:
171
+ - `$query`: `String!`
172
+ - `$limit`: `Int` *(default: `10`)*
173
+
174
+ **Fragments used**: `ProductCard`
175
+
176
+ **GraphQL**:
177
+ ```graphql
178
+ query PredictiveSearch($query: String!, $limit: Int = 10) {
179
+ predictiveSearch(query: $query, limit: $limit) {
180
+ products {
181
+ ...ProductCard
182
+ }
183
+ queries {
184
+ text
185
+ styledText
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ ### Query: `Collection`
192
+
193
+ **Section**: Collections
194
+
195
+ **Description**: Fetches a single collection by `id` or `handle`, with paginated products. Collections come in two types: **MANUAL** (curated — products explicitly added by the merchant) and **AUTO** (rule-based — products matched dynamically). Both surfaces use the same field selection.
196
+
197
+ **Variables**:
198
+ - `$id`: `ID`
199
+ - `$handle`: `String`
200
+ - `$productsFirst`: `Int` *(default: `20`)*
201
+ - `$productsAfter`: `String`
202
+ - `$productsFilters`: `[ProductFilter!]`
203
+
204
+ **Fragments used**: `Collection`, `PageInfo`, `ProductCard`
205
+
206
+ **GraphQL**:
207
+ ```graphql
208
+ query Collection($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsFilters: [ProductFilter!]) {
209
+ collection(id: $id, handle: $handle) {
210
+ ...Collection
211
+ products(
212
+ first: $productsFirst
213
+ after: $productsAfter
214
+ filters: $productsFilters
215
+ ) {
216
+ edges {
217
+ node {
218
+ ...ProductCard
219
+ }
220
+ cursor
221
+ }
222
+ nodes {
223
+ ...ProductCard
224
+ }
225
+ pageInfo {
226
+ ...PageInfo
227
+ }
228
+ totalCount
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ ### Query: `Collections`
235
+
236
+ **Section**: Collections
237
+
238
+ **Description**: Paginated list of all active collections (default 20, max 100). Sort by `TITLE` or `UPDATED_AT` via `sortKey`. Note: the `query` argument is reserved for future text filtering — it is currently accepted but ignored.
239
+
240
+ **Variables**:
241
+ - `$first`: `Int` *(default: `20`)*
242
+ - `$after`: `String`
243
+ - `$query`: `String`
244
+ - `$sortKey`: `CollectionSortKeys` *(default: `TITLE`)*
245
+ - `$reverse`: `Boolean` *(default: `false`)*
246
+
247
+ **Fragments used**: `Collection`, `PageInfo`
248
+
249
+ **GraphQL**:
250
+ ```graphql
251
+ query Collections($first: Int = 20, $after: String, $query: String, $sortKey: CollectionSortKeys = TITLE, $reverse: Boolean = false) {
252
+ collections(
253
+ first: $first
254
+ after: $after
255
+ query: $query
256
+ sortKey: $sortKey
257
+ reverse: $reverse
258
+ ) {
259
+ edges {
260
+ node {
261
+ ...Collection
262
+ }
263
+ cursor
264
+ }
265
+ pageInfo {
266
+ ...PageInfo
267
+ }
268
+ totalCount
269
+ }
270
+ }
271
+ ```
272
+
273
+ ### Query: `Category`
274
+
275
+ **Section**: Categories
276
+
277
+ **Description**: Fetches a single category by `id` or `slug` with its parent and immediate children. Use for breadcrumbs and sub-navigation. Nested queries on `parent` / `children` are batched server-side — safe to use in lists without N+1 concerns.
278
+
279
+ **Variables**:
280
+ - `$id`: `ID`
281
+ - `$slug`: `String`
282
+
283
+ **Fragments used**: `Category`
284
+
285
+ **GraphQL**:
286
+ ```graphql
287
+ query Category($id: ID, $slug: String) {
288
+ category(id: $id, slug: $slug) {
289
+ ...Category
290
+ parent {
291
+ ...Category
292
+ }
293
+ children {
294
+ ...Category
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
300
+ ### Query: `Categories`
301
+
302
+ **Section**: Categories
303
+
304
+ **Description**: Returns active categories for the shop. Each category exposes its `parent` and `children` — build the tree client-side by walking those fields (server batches the lookups, no N+1). The hierarchy is not depth-capped server-side. Use for nav mega-menus and category pages.
305
+
306
+ **Variables**: none
307
+
308
+ **Fragments used**: `Category`
309
+
310
+ **GraphQL**:
311
+ ```graphql
312
+ query Categories {
313
+ categories {
314
+ roots {
315
+ ...Category
316
+ children {
317
+ ...Category
318
+ children {
319
+ ...Category
320
+ }
321
+ }
322
+ }
323
+ totalCount
324
+ }
325
+ }
326
+ ```
327
+
328
+ ### Query: `Cart`
329
+
330
+ **Section**: Cart
331
+
332
+ **Description**: Fetches a cart by `id` (the value persisted by the SDK in the `cart-id` cookie). The cart query is public — no auth needed to read it — but once a customer logs in and gets associated with the cart, mutations enforce ownership. Returns line items (paginated up to 100), totals, applied discount codes, gift cards, buyer identity, note, attributes, and warnings. Refetch after every cart mutation.
333
+
334
+ **Variables**:
335
+ - `$id`: `ID!`
336
+
337
+ **Fragments used**: `Cart`
338
+
339
+ **GraphQL**:
340
+ ```graphql
341
+ query Cart($id: ID!) {
342
+ cart(id: $id) {
343
+ ...Cart
344
+ }
345
+ }
346
+ ```
347
+
348
+ ### Query: `Customer`
349
+
350
+ **Section**: Customer (requires auth)
351
+
352
+ **Description**: Full customer profile — basic info plus the first 10 addresses and first 10 orders. Heaviest customer query; for narrow use cases prefer `CustomerProfile` (no orders / addresses) or `CustomerOrder` (single order). Returns null if unauthenticated.
353
+
354
+ **Variables**: none
355
+
356
+ **Fragments used**: `Customer`, `MailingAddress`, `Order`, `PageInfo`
357
+
358
+ **GraphQL**:
359
+ ```graphql
360
+ query Customer {
361
+ customer {
362
+ ...Customer
363
+ addresses(first: 10) {
364
+ edges {
365
+ cursor
366
+ node {
367
+ ...MailingAddress
368
+ }
369
+ }
370
+ nodes {
371
+ ...MailingAddress
372
+ }
373
+ pageInfo {
374
+ ...PageInfo
375
+ }
376
+ totalCount
377
+ }
378
+ orders(first: 10) {
379
+ edges {
380
+ node {
381
+ ...Order
382
+ }
383
+ cursor
384
+ }
385
+ pageInfo {
386
+ ...PageInfo
387
+ }
388
+ totalCount
389
+ }
390
+ }
391
+ }
392
+ ```
393
+
394
+ ### Query: `CustomerProfile`
395
+
396
+ **Section**: Customer (requires auth)
397
+
398
+ **Description**: Lightweight customer profile (no orders, no addresses list). Use for settings / profile pages that only need basic customer info — much cheaper than `Customer`. Returns null if unauthenticated.
399
+
400
+ **Variables**: none
401
+
402
+ **Fragments used**: `Customer`
403
+
404
+ **GraphQL**:
405
+ ```graphql
406
+ query CustomerProfile {
407
+ customer {
408
+ ...Customer
409
+ }
410
+ }
411
+ ```
412
+
413
+ ### Query: `CustomerOrder`
414
+
415
+ **Section**: Customer (requires auth)
416
+
417
+ **Description**: Single order by `orderId`. Returns only orders that belong to the authenticated customer (cross-customer access returns null, not an error). Much cheaper than fetching the full `Customer` payload to access one order. Use on the order detail page.
418
+
419
+ **Variables**:
420
+ - `$orderId`: `ID!`
421
+
422
+ **Fragments used**: `Order`
423
+
424
+ **GraphQL**:
425
+ ```graphql
426
+ query CustomerOrder($orderId: ID!) {
427
+ customerOrder(orderId: $orderId) {
428
+ ...Order
429
+ }
430
+ }
431
+ ```
432
+
433
+ ### Query: `Checkout`
434
+
435
+ **Section**: Checkout
436
+
437
+ **Description**: Fetches a checkout session by `id`. **Important**: `checkoutId` and `cartId` are 1:1 — there is no separate "checkout" record, the response is built dynamically from the cart. Returns line items, addresses, selected shipping rate, available shipping rates + payment methods, applied discount/gift cards (gift card codes are masked for security), and totals (`cost`, `tax`, `paymentDue`). Public read; ownership enforced on mutations. Refetch after every checkout mutation.
438
+
439
+ **Variables**:
440
+ - `$id`: `ID!`
441
+
442
+ **Fragments used**: `Checkout`
443
+
444
+ **GraphQL**:
445
+ ```graphql
446
+ query Checkout($id: ID!) {
447
+ checkout(id: $id) {
448
+ ...Checkout
449
+ }
450
+ }
451
+ ```
452
+
453
+ ### Query: `AvailablePaymentMethods`
454
+
455
+ **Section**: Payment Methods
456
+
457
+ **Description**: Returns the active payment methods for the shop, sorted by the merchant-configured display position. Shop-level — does NOT vary by cart amount or currency. Each method exposes `type` (`CARD`, `BANK_TRANSFER`, `BLIK`, `PAYPAL`, `APPLE_PAY`, `GOOGLE_PAY`, `CASH_ON_DELIVERY`, `OTHER`), provider, icon, description, and supported currencies. Use to render the payment step of checkout.
458
+
459
+ **Variables**: none
460
+
461
+ **Fragments used**: `AvailablePaymentMethods`
462
+
463
+ **GraphQL**:
464
+ ```graphql
465
+ query AvailablePaymentMethods {
466
+ availablePaymentMethods {
467
+ ...AvailablePaymentMethods
468
+ }
469
+ }
470
+ ```
471
+
472
+ ### Query: `Shipment`
473
+
474
+ **Section**: Shipments / Tracking
475
+
476
+ **Description**: Fetches a shipment by `id` with status, tracking events, recipient address, and shipped/delivered timestamps. **Auth required** — customer access token plus ownership of the parent order. Wrapped response: `{ shipment, userErrors[] }`. Error codes: `INVALID_TOKEN`, `NOT_FOUND` (also returned on ownership mismatch to prevent enumeration), `FETCH_FAILED`.
477
+
478
+ **Variables**:
479
+ - `$id`: `ID!`
480
+
481
+ **Fragments used**: `Shipment`, `UserError`
482
+
483
+ **GraphQL**:
484
+ ```graphql
485
+ query Shipment($id: ID!) {
486
+ shipment(id: $id) {
487
+ shipment {
488
+ ...Shipment
489
+ }
490
+ userErrors {
491
+ ...UserError
492
+ }
493
+ }
494
+ }
495
+ ```
496
+
497
+ ### Query: `ShipmentByTrackingNumber`
498
+
499
+ **Section**: Shipments / Tracking
500
+
501
+ **Description**: **Public** shipment lookup by carrier tracking number — no auth required. Designed for "Track my order" landing pages reachable without login. Returns the basic shipment fragment including recipient address. Wrapped response: `{ shipment, userErrors[] }`. Error codes: `INVALID_INPUT`, `NOT_FOUND`, `FETCH_FAILED`.
502
+
503
+ **Variables**:
504
+ - `$trackingNumber`: `String!`
505
+
506
+ **Fragments used**: `ShipmentBasic`, `UserError`
507
+
508
+ **GraphQL**:
509
+ ```graphql
510
+ query ShipmentByTrackingNumber($trackingNumber: String!) {
511
+ shipmentByTrackingNumber(trackingNumber: $trackingNumber) {
512
+ shipment {
513
+ ...ShipmentBasic
514
+ }
515
+ userErrors {
516
+ ...UserError
517
+ }
518
+ }
519
+ }
520
+ ```
521
+
522
+ ### Query: `Return`
523
+
524
+ **Section**: Returns / RMA
525
+
526
+ **Description**: Fetches a single return (RMA) by `id` with line items, refund/compensation info, and history. **Auth required** — customer access token plus ownership of the return. Wrapped response: `{ return, userErrors[] }`. Error codes: `INVALID_TOKEN`, `NOT_FOUND` (also returned on ownership mismatch), `FETCH_FAILED`.
527
+
528
+ **Variables**:
529
+ - `$id`: `ID!`
530
+
531
+ **Fragments used**: `Return`, `UserError`
532
+
533
+ **GraphQL**:
534
+ ```graphql
535
+ query Return($id: ID!) {
536
+ return(id: $id) {
537
+ return {
538
+ ...Return
539
+ }
540
+ userErrors {
541
+ ...UserError
542
+ }
543
+ }
544
+ }
545
+ ```
546
+
547
+ ### Query: `ReturnsByOrder`
548
+
549
+ **Section**: Returns / RMA
550
+
551
+ **Description**: Lists returns for a given order (paginated, default page size 20, cursor-based). **Auth required** — customer access token plus ownership of the order; the connection is empty (no explicit error) on auth failure. Use on the order detail page to show return history.
552
+
553
+ **Variables**:
554
+ - `$orderId`: `ID!`
555
+
556
+ **Fragments used**: `PageInfo`, `Return`
557
+
558
+ **GraphQL**:
559
+ ```graphql
560
+ query ReturnsByOrder($orderId: ID!) {
561
+ returnsByOrder(orderId: $orderId) {
562
+ edges {
563
+ node {
564
+ ...Return
565
+ }
566
+ cursor
567
+ }
568
+ pageInfo {
569
+ ...PageInfo
570
+ }
571
+ totalCount
572
+ }
573
+ }
574
+ ```
575
+
576
+ ### Query: `ReturnReasons`
577
+
578
+ **Section**: Returns / RMA
579
+
580
+ **Description**: Returns the standard list of return reasons used by the RMA flow: `DEFECTIVE`, `NOT_AS_DESCRIBED`, `WRONG_ITEM`, `CHANGED_MIND`, `BETTER_PRICE`, `DAMAGED_SHIPPING`, `OTHER`. The list is fixed across all shops — not per-shop configurable. Public; no auth required.
581
+
582
+ **Variables**: none
583
+
584
+ **Fragments used**: `ReturnReasonOption`
585
+
586
+ **GraphQL**:
587
+ ```graphql
588
+ query ReturnReasons {
589
+ returnReasons {
590
+ ...ReturnReasonOption
591
+ }
592
+ }
593
+ ```
594
+
595
+ ### Query: `GiftCard`
596
+
597
+ **Section**: Gift Cards
598
+
599
+ **Description**: Public gift-card lookup by `code`. Returns balance, currency, expiry, and `maskedCode` (first 4 + last 4 chars only — the full code never leaks back). Returns null if the code is unknown (rather than an explicit error, to limit enumeration). **Rate-limited**: 10 requests per 60 seconds per IP.
600
+
601
+ **Variables**:
602
+ - `$code`: `String!`
603
+
604
+ **Fragments used**: `GiftCard`
605
+
606
+ **GraphQL**:
607
+ ```graphql
608
+ query GiftCard($code: String!) {
609
+ giftCard(code: $code) {
610
+ ...GiftCard
611
+ }
612
+ }
613
+ ```
614
+
615
+ ### Query: `GiftCardValidate`
616
+
617
+ **Section**: Gift Cards
618
+
619
+ **Description**: Validates whether a gift card is usable (and optionally for a given `$amount`). Checks status (`DISABLED`, `USED`, `EXPIRED`), expiry date, and — when `$amount` is provided — sufficient balance. Returns `{ validation: { isValid, availableBalance, error: { code, message } }, userErrors[] }`. Validation error codes: `NOT_FOUND`, `DISABLED`, `ALREADY_USED`, `EXPIRED`, `INSUFFICIENT_BALANCE`. **Rate-limited**: 10 / 60s.
620
+
621
+ **Variables**:
622
+ - `$code`: `String!`
623
+ - `$amount`: `Float`
624
+
625
+ **Fragments used**: `GiftCardValidation`, `UserError`
626
+
627
+ **GraphQL**:
628
+ ```graphql
629
+ query GiftCardValidate($code: String!, $amount: Float) {
630
+ giftCardValidate(code: $code, amount: $amount) {
631
+ validation {
632
+ ...GiftCardValidation
633
+ }
634
+ userErrors {
635
+ ...UserError
636
+ }
637
+ }
638
+ }
639
+ ```
640
+
641
+ ### Query: `AvailableShippingMethods`
642
+
643
+ **Section**: Shipping Methods
644
+
645
+ **Description**: Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price.
646
+
647
+ **Variables**:
648
+ - `$address`: `ShippingAddressInput!`
649
+ - `$cart`: `CartShippingInput`
650
+
651
+ **Fragments used**: `AvailableShippingMethod`, `FreeShippingProgress`, `UserError`
652
+
653
+ **GraphQL**:
654
+ ```graphql
655
+ query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShippingInput) {
656
+ availableShippingMethods(address: $address, cart: $cart) {
657
+ methods {
658
+ ...AvailableShippingMethod
659
+ }
660
+ freeShippingProgress {
661
+ ...FreeShippingProgress
662
+ }
663
+ userErrors {
664
+ ...UserError
665
+ }
666
+ }
667
+ }
668
+ ```
669
+
670
+ ### Query: `AvailableFilters`
671
+
672
+ **Section**: Attribute Filters
673
+
674
+ **Description**: Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`, or `searchQuery`. For each visible & filterable attribute, returns either discrete value counts (for `SELECT` / `CHECKBOX` types) or numeric range bounds (for `SLIDER` types). Plus `priceRange`, per-category counts, `activeCount` (length of `currentFilters` input), and `matchCount` (total products in the context). Use to render filter sidebars on listing/search pages.
675
+
676
+ **Variables**:
677
+ - `$input`: `AvailableFiltersInput`
678
+
679
+ **Fragments used**: `AvailableFilters`
680
+
681
+ **GraphQL**:
682
+ ```graphql
683
+ query AvailableFilters($input: AvailableFiltersInput) {
684
+ availableFilters(input: $input) {
685
+ ...AvailableFilters
686
+ }
687
+ }
688
+ ```
689
+
690
+ ### Query: `LoyaltyMember`
691
+
692
+ **Section**: Loyalty Program
693
+
694
+ **Description**: Returns the logged-in customer's loyalty membership: points (current, pending, redeemed, expired, expiring), current tier, tier progress, annual spend, last activity. Returns null if the customer is not enrolled — there is **no auto-enrollment** here (enrollment happens via signup or a first qualifying order). Auth required.
695
+
696
+ **Variables**: none
697
+
698
+ **Fragments used**: `LoyaltyMember`
699
+
700
+ **GraphQL**:
701
+ ```graphql
702
+ query LoyaltyMember {
703
+ loyaltyMember {
704
+ ...LoyaltyMember
705
+ }
706
+ }
707
+ ```
708
+
709
+ ### Query: `LoyaltyTiers`
710
+
711
+ **Section**: Loyalty Program
712
+
713
+ **Description**: Lists the loyalty tiers configured for the shop (`BRONZE`, `SILVER`, `GOLD`, `PLATINUM`, `DIAMOND` etc.) with their `minPoints`, `minAnnualSpend`, `pointsMultiplier`, and custom benefits. Sorted by `minPoints` ASC. Public; no auth required.
714
+
715
+ **Variables**: none
716
+
717
+ **Fragments used**: `LoyaltyTier`
718
+
719
+ **GraphQL**:
720
+ ```graphql
721
+ query LoyaltyTiers {
722
+ loyaltyTiers {
723
+ ...LoyaltyTier
724
+ }
725
+ }
726
+ ```
727
+
728
+ ### Query: `LoyaltyRewards`
729
+
730
+ **Section**: Loyalty Program
731
+
732
+ **Description**: Lists rewards customers can redeem (free shipping, percent off, free product, gift card). Filtered to **active** rewards only (`is_active = true` AND inside their `starts_at`/`ends_at` window). Public; no auth required.
733
+
734
+ **Variables**: none
735
+
736
+ **Fragments used**: `LoyaltyReward`
737
+
738
+ **GraphQL**:
739
+ ```graphql
740
+ query LoyaltyRewards {
741
+ loyaltyRewards {
742
+ ...LoyaltyReward
743
+ }
744
+ }
745
+ ```
746
+
747
+ ### Query: `LoyaltyTransactions`
748
+
749
+ **Section**: Loyalty Program
750
+
751
+ **Description**: Paginated history of loyalty point transactions for the logged-in customer (default 20). Transaction `type` enum: `EARN_PURCHASE`, `EARN_SIGNUP`, `EARN_REFERRAL`, `EARN_REVIEW`, `EARN_BIRTHDAY`, `EARN_BONUS`, `REDEEM`, `EXPIRE`, `ADJUST`, `REFUND_REVERSAL`. Auth required — empty connection if unauthenticated.
752
+
753
+ **Variables**:
754
+ - `$first`: `Int` *(default: `20`)*
755
+ - `$after`: `String`
756
+
757
+ **Fragments used**: `LoyaltyPageInfo`, `LoyaltyTransaction`
758
+
759
+ **GraphQL**:
760
+ ```graphql
761
+ query LoyaltyTransactions($first: Int = 20, $after: String) {
762
+ loyaltyTransactions(first: $first, after: $after) {
763
+ edges {
764
+ node {
765
+ ...LoyaltyTransaction
766
+ }
767
+ cursor
768
+ }
769
+ pageInfo {
770
+ ...LoyaltyPageInfo
771
+ }
772
+ totalCount
773
+ }
774
+ }
775
+ ```
776
+
777
+ ### Query: `LoyaltySettings`
778
+
779
+ **Section**: Loyalty Program
780
+
781
+ **Description**: Returns the loyalty program configuration: `isEnabled`, `pointsName` (e.g. "stars"), `pointsPerCurrency`, `pointsExpiryMonths`, available earn actions, referral settings. Use this at app boot to decide whether to render any loyalty UI at all. Public; no auth required.
782
+
783
+ **Variables**: none
784
+
785
+ **Fragments used**: `LoyaltySettings`
786
+
787
+ **GraphQL**:
788
+ ```graphql
789
+ query LoyaltySettings {
790
+ loyaltySettings {
791
+ ...LoyaltySettings
792
+ }
793
+ }
794
+ ```
795
+
796
+ ### Query: `EstimatePoints`
797
+
798
+ **Section**: Loyalty Program
799
+
800
+ **Description**: Estimates how many loyalty points the customer would earn for an order of `$orderTotal` (in major currency units). When the customer is authenticated, the result accounts for their current tier's points multiplier. Use on cart/checkout to show "Earn X points with this order".
801
+
802
+ **Variables**:
803
+ - `$orderTotal`: `Float!`
804
+
805
+ **Fragments used**: `PointsEstimate`
806
+
807
+ **GraphQL**:
808
+ ```graphql
809
+ query EstimatePoints($orderTotal: Float!) {
810
+ estimatePoints(orderTotal: $orderTotal) {
811
+ ...PointsEstimate
812
+ }
813
+ }
814
+ ```
815
+
816
+ ### Query: `ReferralStats`
817
+
818
+ **Section**: Loyalty Program
819
+
820
+ **Description**: Returns the customer's referral statistics: `referralCode`, `shareUrl`, `totalReferred`, `completedReferrals`, `pendingReferrals`, `totalPointsEarned`. Auth required. Returns null if unauthenticated or if the referral program is disabled for the shop.
821
+
822
+ **Variables**: none
823
+
824
+ **Fragments used**: `ReferralStats`
825
+
826
+ **GraphQL**:
827
+ ```graphql
828
+ query ReferralStats {
829
+ referralStats {
830
+ ...ReferralStats
831
+ }
832
+ }
833
+ ```
834
+
835
+ ### Query: `ProductReviews`
836
+
837
+ **Section**: Reviews
838
+
839
+ **Description**: Paginated list of customer reviews for a product, **filtered to APPROVED reviews only** (PENDING / REJECTED reviews are not exposed to the storefront). Sort by `CREATED_AT` (default), helpfulness, or rating. Public; no auth required.
840
+
841
+ **Variables**:
842
+ - `$productId`: `ID!`
843
+ - `$first`: `Int` *(default: `10`)*
844
+ - `$after`: `String`
845
+ - `$sortKey`: `ReviewSortKey` *(default: `CREATED_AT`)*
846
+ - `$reverse`: `Boolean` *(default: `true`)*
847
+
848
+ **Fragments used**: `PageInfo`, `ProductReview`
849
+
850
+ **GraphQL**:
851
+ ```graphql
852
+ query ProductReviews($productId: ID!, $first: Int = 10, $after: String, $sortKey: ReviewSortKey = CREATED_AT, $reverse: Boolean = true) {
853
+ productReviews(
854
+ productId: $productId
855
+ first: $first
856
+ after: $after
857
+ sortKey: $sortKey
858
+ reverse: $reverse
859
+ ) {
860
+ edges {
861
+ node {
862
+ ...ProductReview
863
+ }
864
+ cursor
865
+ }
866
+ pageInfo {
867
+ ...PageInfo
868
+ }
869
+ totalCount
870
+ }
871
+ }
872
+ ```
873
+
874
+ ### Query: `ReviewStats`
875
+
876
+ **Section**: Reviews
877
+
878
+ **Description**: Aggregate review statistics for a product: average rating, total count, distribution per star (1-5). Computed from APPROVED reviews only. Use for product card review summaries. Public; no auth required.
879
+
880
+ **Variables**:
881
+ - `$productId`: `ID!`
882
+
883
+ **Fragments used**: `ReviewStats`
884
+
885
+ **GraphQL**:
886
+ ```graphql
887
+ query ReviewStats($productId: ID!) {
888
+ reviewStats(productId: $productId) {
889
+ ...ReviewStats
890
+ }
891
+ }
892
+ ```
893
+
894
+ ### Query: `Wishlists`
895
+
896
+ **Section**: Wishlists
897
+
898
+ **Description**: Paginated list of the logged-in customer's wishlists (default 20). Auth required — empty connection if unauthenticated. Customers typically have a small set (<10).
899
+
900
+ **Variables**:
901
+ - `$first`: `Int` *(default: `20`)*
902
+ - `$after`: `String`
903
+
904
+ **Fragments used**: `Wishlist`
905
+
906
+ **GraphQL**:
907
+ ```graphql
908
+ query Wishlists($first: Int = 20, $after: String) {
909
+ wishlists(first: $first, after: $after) {
910
+ edges {
911
+ cursor
912
+ node {
913
+ ...Wishlist
914
+ }
915
+ }
916
+ nodes {
917
+ ...Wishlist
918
+ }
919
+ pageInfo {
920
+ hasNextPage
921
+ hasPreviousPage
922
+ startCursor
923
+ endCursor
924
+ }
925
+ totalCount
926
+ }
927
+ }
928
+ ```
929
+
930
+ ### Query: `WishlistById`
931
+
932
+ **Section**: Wishlists
933
+
934
+ **Description**: Fetches a single wishlist by `id`. Private wishlists are visible only to the owner; public wishlists are visible to anyone. Note: this query supports lookup by `id` only — there is currently no way to fetch a wishlist by its share token.
935
+
936
+ **Variables**:
937
+ - `$id`: `ID!`
938
+
939
+ **Fragments used**: `Wishlist`
940
+
941
+ **GraphQL**:
942
+ ```graphql
943
+ query WishlistById($id: ID!) {
944
+ wishlist(id: $id) {
945
+ ...Wishlist
946
+ }
947
+ }
948
+ ```
949
+
950
+ ### Query: `BlogPosts`
951
+
952
+ **Section**: Blog
953
+
954
+ **Description**: Paginated list of published blog posts. Filter by `categorySlug`, `tagSlug`, or `featured` (boolean flag, not enum). Sort: `PUBLISHED_AT` (default), `TITLE`, `VIEW_COUNT`, or `CREATED_AT`. Public; no auth required.
955
+
956
+ **Variables**:
957
+ - `$first`: `Int` *(default: `20`)*
958
+ - `$after`: `String`
959
+ - `$categorySlug`: `String`
960
+ - `$tagSlug`: `String`
961
+ - `$featured`: `Boolean`
962
+ - `$sortKey`: `BlogPostSortKey` *(default: `PUBLISHED_AT`)*
963
+ - `$reverse`: `Boolean` *(default: `false`)*
964
+
965
+ **Fragments used**: `BlogPost`, `PageInfo`
966
+
967
+ **GraphQL**:
968
+ ```graphql
969
+ query BlogPosts($first: Int = 20, $after: String, $categorySlug: String, $tagSlug: String, $featured: Boolean, $sortKey: BlogPostSortKey = PUBLISHED_AT, $reverse: Boolean = false) {
970
+ blogPosts(
971
+ first: $first
972
+ after: $after
973
+ categorySlug: $categorySlug
974
+ tagSlug: $tagSlug
975
+ featured: $featured
976
+ sortKey: $sortKey
977
+ reverse: $reverse
978
+ ) {
979
+ edges {
980
+ node {
981
+ ...BlogPost
982
+ }
983
+ cursor
984
+ }
985
+ pageInfo {
986
+ ...PageInfo
987
+ }
988
+ totalCount
989
+ }
990
+ }
991
+ ```
992
+
993
+ ### Query: `BlogPost`
994
+
995
+ **Section**: Blog
996
+
997
+ **Description**: Fetches a single blog post by `id` or `slug`. Visibility-gated: returns null if the post is not yet `PUBLISHED` or if its publish date is in the future (scheduled posts stay hidden until their publish time). Side effect: fetching a post increments its `view_count` asynchronously (does not block the response).
998
+
999
+ **Variables**:
1000
+ - `$id`: `ID`
1001
+ - `$slug`: `String`
1002
+
1003
+ **Fragments used**: `BlogPost`
1004
+
1005
+ **GraphQL**:
1006
+ ```graphql
1007
+ query BlogPost($id: ID, $slug: String) {
1008
+ blogPost(id: $id, slug: $slug) {
1009
+ ...BlogPost
1010
+ }
1011
+ }
1012
+ ```
1013
+
1014
+ ### Query: `BlogCategories`
1015
+
1016
+ **Section**: Blog
1017
+
1018
+ **Description**: Lists all blog categories with per-category `postCount` and SEO metadata. Use to render category navigation on blog pages. Public; no auth required.
1019
+
1020
+ **Variables**: none
1021
+
1022
+ **Fragments used**: `BlogCategory`
1023
+
1024
+ **GraphQL**:
1025
+ ```graphql
1026
+ query BlogCategories {
1027
+ blogCategories {
1028
+ ...BlogCategory
1029
+ }
1030
+ }
1031
+ ```
1032
+
1033
+ ### Query: `BlogTags`
1034
+
1035
+ **Section**: Blog
1036
+
1037
+ **Description**: Lists blog tags with usage counts (`postCount` per tag). Use to render a tag cloud. Public; no auth required.
1038
+
1039
+ **Variables**: none
1040
+
1041
+ **Fragments used**: `BlogTag`
1042
+
1043
+ **GraphQL**:
1044
+ ```graphql
1045
+ query BlogTags {
1046
+ blogTags {
1047
+ ...BlogTag
1048
+ }
1049
+ }
1050
+ ```
1051
+
1052
+ ### Query: `ProductRecommendations`
1053
+
1054
+ **Section**: Recommendations
1055
+
1056
+ **Description**: Returns up to `$limit` recommended products related to `$productId`. Default `$intent: SIMILAR` — products sharing categories or tags. Use on PDP "You may also like" sections. Public; no auth required.
1057
+
1058
+ **Variables**:
1059
+ - `$productId`: `ID!`
1060
+ - `$limit`: `Int` *(default: `8`)*
1061
+ - `$intent`: `RecommendationIntent` *(default: `SIMILAR`)*
1062
+
1063
+ **Fragments used**: `ProductCard`
1064
+
1065
+ **GraphQL**:
1066
+ ```graphql
1067
+ query ProductRecommendations($productId: ID!, $limit: Int = 8, $intent: RecommendationIntent = SIMILAR) {
1068
+ productRecommendations(productId: $productId, limit: $limit, intent: $intent) {
1069
+ ...ProductCard
1070
+ }
1071
+ }
1072
+ ```
1073
+
1074
+ ### Query: `Page`
1075
+
1076
+ **Section**: Content: Pages
1077
+
1078
+ **Description**: Fetches a single CMS page (About, Privacy, Returns Policy, Terms, etc.) by `handle` or `id`. Visibility-gated: returns null if the page is hidden or if its publish date is in the future. Public; no auth required.
1079
+
1080
+ **Variables**:
1081
+ - `$handle`: `String`
1082
+ - `$id`: `ID`
1083
+
1084
+ **Fragments used**: `ShopPage`
1085
+
1086
+ **GraphQL**:
1087
+ ```graphql
1088
+ query Page($handle: String, $id: ID) {
1089
+ page(handle: $handle, id: $id) {
1090
+ ...ShopPage
1091
+ }
1092
+ }
1093
+ ```
1094
+
1095
+ ### Query: `Pages`
1096
+
1097
+ **Section**: Content: Pages
1098
+
1099
+ **Description**: Paginated list of visible, already-published CMS pages. Use for sitemap, footer link list, or page directory. The `query` argument supports text search over the page title/handle. Public; no auth required.
1100
+
1101
+ **Variables**:
1102
+ - `$first`: `Int` *(default: `20`)*
1103
+ - `$after`: `String`
1104
+ - `$sortKey`: `PageSortKeys` *(default: `TITLE`)*
1105
+ - `$reverse`: `Boolean` *(default: `false`)*
1106
+ - `$query`: `String`
1107
+
1108
+ **Fragments used**: `PageInfo`, `ShopPage`
1109
+
1110
+ **GraphQL**:
1111
+ ```graphql
1112
+ query Pages($first: Int = 20, $after: String, $sortKey: PageSortKeys = TITLE, $reverse: Boolean = false, $query: String) {
1113
+ pages(
1114
+ first: $first
1115
+ after: $after
1116
+ sortKey: $sortKey
1117
+ reverse: $reverse
1118
+ query: $query
1119
+ ) {
1120
+ edges {
1121
+ node {
1122
+ ...ShopPage
1123
+ }
1124
+ cursor
1125
+ }
1126
+ pageInfo {
1127
+ ...PageInfo
1128
+ }
1129
+ totalCount
1130
+ }
1131
+ }
1132
+ ```
1133
+
1134
+ ### Query: `Menu`
1135
+
1136
+ **Section**: Content: Navigation Menus
1137
+
1138
+ **Description**: Fetches a navigation menu by `handle` (e.g. `"main-menu"`, `"footer"`, `"mobile"`). Returns the nested item tree. Each item is typed as one of: `HTTP`, `FRONTPAGE`, `SEARCH`, `CATALOG`, `BLOG`, `PRODUCT`, `COLLECTION`, `CATEGORY`, or `PAGE` — switch on the type to render the right link target. Linked resources and URLs are resolved on demand by the field selections in the `Menu` fragment.
1139
+
1140
+ **Variables**:
1141
+ - `$handle`: `String!`
1142
+
1143
+ **Fragments used**: `Menu`
1144
+
1145
+ **GraphQL**:
1146
+ ```graphql
1147
+ query Menu($handle: String!) {
1148
+ menu(handle: $handle) {
1149
+ ...Menu
1150
+ }
1151
+ }
1152
+ ```
1153
+
1154
+ ### Query: `UrlRedirects`
1155
+
1156
+ **Section**: Content: URL Redirects
1157
+
1158
+ **Description**: Returns the shop's URL redirects (legacy `path` → new `target` mappings). Use server-side at the edge or in SSR to issue 301 redirects for migrated routes (preserves SEO equity). Default page size 250 — most shops fit in a single page.
1159
+
1160
+ **Variables**:
1161
+ - `$first`: `Int` *(default: `250`)*
1162
+ - `$after`: `String`
1163
+
1164
+ **Fragments used**: `PageInfo`, `UrlRedirect`
1165
+
1166
+ **GraphQL**:
1167
+ ```graphql
1168
+ query UrlRedirects($first: Int = 250, $after: String) {
1169
+ urlRedirects(first: $first, after: $after) {
1170
+ nodes {
1171
+ ...UrlRedirect
1172
+ }
1173
+ pageInfo {
1174
+ ...PageInfo
1175
+ }
1176
+ }
1177
+ }
1178
+ ```
1179
+
1180
+ ### Query: `ProductStoreAvailability`
1181
+
1182
+ **Section**: Store Availability: per-location stock (BOPIS / multi-location)
1183
+
1184
+ **Description**: Fetches a product (by `handle` or `id`) along with per-variant availability across the merchant's physical locations — for the BOPIS / multi-location flow. The `storeAvailability` connection lives on each `ProductVariant`; its arguments (`first`, `after`, `near`, `locationType`) are set inside the `VariantStoreAvailability` fragment. The connection returns null for single-location shops (in which case the storefront can skip the store picker entirely). `availableStock` is null for anonymous users and an integer for authenticated customers. Apply `@inContext(preferredLocationId: ...)` on the operation to pin the customer's preferred location to the top of the result.
1185
+
1186
+ **Variables**:
1187
+ - `$handle`: `String`
1188
+ - `$id`: `ID`
1189
+
1190
+ **Fragments used**: `VariantStoreAvailability`
1191
+
1192
+ **GraphQL**:
1193
+ ```graphql
1194
+ query ProductStoreAvailability($handle: String, $id: ID) {
1195
+ product(handle: $handle, id: $id) {
1196
+ id
1197
+ handle
1198
+ title
1199
+ variants {
1200
+ ...VariantStoreAvailability
1201
+ }
1202
+ }
1203
+ }
1204
+ ```
1205
+
1206
+ ### Query: `Locations`
1207
+
1208
+ **Section**: Locations (store picker UI)
1209
+
1210
+ **Description**: Paginated list of active store locations (default 20, max 100). Filters: `near` (`{ latitude, longitude }`) for proximity search — sorts ascending by distance; `hasPickupEnabled` for pickup-only filtering; `locationType` (`RETAIL`, `WAREHOUSE`, `PICKUP_POINT`). When `near` is omitted, results are sorted by the merchant's `priority`, then name. Use for the BOPIS store picker UI. Public; no auth required.
1211
+
1212
+ **Variables**:
1213
+ - `$first`: `Int` *(default: `20`)*
1214
+ - `$after`: `String`
1215
+ - `$near`: `GeoCoordinateInput`
1216
+ - `$hasPickupEnabled`: `Boolean`
1217
+ - `$locationType`: `LocationType`
1218
+
1219
+ **Fragments used**: `Location`, `PageInfo`
1220
+
1221
+ **GraphQL**:
1222
+ ```graphql
1223
+ query Locations($first: Int = 20, $after: String, $near: GeoCoordinateInput, $hasPickupEnabled: Boolean, $locationType: LocationType) {
1224
+ locations(
1225
+ first: $first
1226
+ after: $after
1227
+ near: $near
1228
+ hasPickupEnabled: $hasPickupEnabled
1229
+ locationType: $locationType
1230
+ ) {
1231
+ totalCount
1232
+ pageInfo {
1233
+ ...PageInfo
1234
+ }
1235
+ edges {
1236
+ cursor
1237
+ node {
1238
+ ...Location
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+ ```
1244
+
1245
+ ### Query: `Location`
1246
+
1247
+ **Section**: Locations (store picker UI)
1248
+
1249
+ **Description**: Fetches a single store location by `id` — full address, coordinates, business hours, pickup config (lead time, hours, timezone), and services. Returns null if the location is not found, not active, or owned by another shop. Use on the location detail page. Public; no auth required.
1250
+
1251
+ **Variables**:
1252
+ - `$id`: `ID!`
1253
+
1254
+ **Fragments used**: `Location`
1255
+
1256
+ **GraphQL**:
1257
+ ```graphql
1258
+ query Location($id: ID!) {
1259
+ location(id: $id) {
1260
+ ...Location
1261
+ }
1262
+ }
1263
+ ```
1264
+
1265
+ ---
1266
+
1267
+ ## Mutations
1268
+
1269
+ ### Mutation: `CartCreate`
1270
+
1271
+ **Section**: Cart Mutations
1272
+
1273
+ **Description**: 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.
1274
+
1275
+ **Variables**:
1276
+ - `$input`: `CartCreateInput`
1277
+
1278
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1279
+
1280
+ **GraphQL**:
1281
+ ```graphql
1282
+ mutation CartCreate($input: CartCreateInput) {
1283
+ cartCreate(input: $input) {
1284
+ cart {
1285
+ ...Cart
1286
+ }
1287
+ userErrors {
1288
+ ...UserError
1289
+ }
1290
+ warnings {
1291
+ ...CartWarning
1292
+ }
1293
+ }
1294
+ }
1295
+ ```
1296
+
1297
+ ### Mutation: `CartAddLines`
1298
+
1299
+ **Section**: Cart Mutations
1300
+
1301
+ **Description**: 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.
1302
+
1303
+ **Variables**:
1304
+ - `$id`: `ID!`
1305
+ - `$lines`: `[CartLineInput!]!`
1306
+
1307
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1308
+
1309
+ **GraphQL**:
1310
+ ```graphql
1311
+ mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
1312
+ cartAddLines(id: $id, lines: $lines) {
1313
+ cart {
1314
+ ...Cart
1315
+ }
1316
+ userErrors {
1317
+ ...UserError
1318
+ }
1319
+ warnings {
1320
+ ...CartWarning
1321
+ }
1322
+ }
1323
+ }
1324
+ ```
1325
+
1326
+ ### Mutation: `CartUpdateLines`
1327
+
1328
+ **Section**: Cart Mutations
1329
+
1330
+ **Description**: 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.
1331
+
1332
+ **Variables**:
1333
+ - `$id`: `ID!`
1334
+ - `$lines`: `[CartLineUpdateInput!]!`
1335
+
1336
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1337
+
1338
+ **GraphQL**:
1339
+ ```graphql
1340
+ mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!) {
1341
+ cartUpdateLines(id: $id, lines: $lines) {
1342
+ cart {
1343
+ ...Cart
1344
+ }
1345
+ userErrors {
1346
+ ...UserError
1347
+ }
1348
+ warnings {
1349
+ ...CartWarning
1350
+ }
1351
+ }
1352
+ }
1353
+ ```
1354
+
1355
+ ### Mutation: `CartRemoveLines`
1356
+
1357
+ **Section**: Cart Mutations
1358
+
1359
+ **Description**: 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.
1360
+
1361
+ **Variables**:
1362
+ - `$id`: `ID!`
1363
+ - `$lineIds`: `[ID!]!`
1364
+
1365
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1366
+
1367
+ **GraphQL**:
1368
+ ```graphql
1369
+ mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!) {
1370
+ cartRemoveLines(id: $id, lineIds: $lineIds) {
1371
+ cart {
1372
+ ...Cart
1373
+ }
1374
+ userErrors {
1375
+ ...UserError
1376
+ }
1377
+ warnings {
1378
+ ...CartWarning
1379
+ }
1380
+ }
1381
+ }
1382
+ ```
1383
+
1384
+ ### Mutation: `CartApplyDiscountCodes`
1385
+
1386
+ **Section**: Cart Mutations
1387
+
1388
+ **Description**: 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`.
1389
+
1390
+ **Variables**:
1391
+ - `$id`: `ID!`
1392
+ - `$discountCodes`: `[String!]!`
1393
+
1394
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1395
+
1396
+ **GraphQL**:
1397
+ ```graphql
1398
+ mutation CartApplyDiscountCodes($id: ID!, $discountCodes: [String!]!) {
1399
+ cartApplyDiscountCodes(id: $id, discountCodes: $discountCodes) {
1400
+ cart {
1401
+ ...Cart
1402
+ }
1403
+ userErrors {
1404
+ ...UserError
1405
+ }
1406
+ warnings {
1407
+ ...CartWarning
1408
+ }
1409
+ }
1410
+ }
1411
+ ```
1412
+
1413
+ ### Mutation: `CartUpdateBuyerIdentity`
1414
+
1415
+ **Section**: Cart Mutations
1416
+
1417
+ **Description**: 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.
1418
+
1419
+ **Variables**:
1420
+ - `$id`: `ID!`
1421
+ - `$buyerIdentity`: `CartBuyerIdentityInput!`
1422
+
1423
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1424
+
1425
+ **GraphQL**:
1426
+ ```graphql
1427
+ mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
1428
+ cartUpdateBuyerIdentity(id: $id, buyerIdentity: $buyerIdentity) {
1429
+ cart {
1430
+ ...Cart
1431
+ }
1432
+ userErrors {
1433
+ ...UserError
1434
+ }
1435
+ warnings {
1436
+ ...CartWarning
1437
+ }
1438
+ }
1439
+ }
1440
+ ```
1441
+
1442
+ ### Mutation: `CartUpdateNote`
1443
+
1444
+ **Section**: Cart Mutations
1445
+
1446
+ **Description**: 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.
1447
+
1448
+ **Variables**:
1449
+ - `$id`: `ID!`
1450
+ - `$note`: `String!`
1451
+
1452
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1453
+
1454
+ **GraphQL**:
1455
+ ```graphql
1456
+ mutation CartUpdateNote($id: ID!, $note: String!) {
1457
+ cartUpdateNote(id: $id, note: $note) {
1458
+ cart {
1459
+ ...Cart
1460
+ }
1461
+ userErrors {
1462
+ ...UserError
1463
+ }
1464
+ warnings {
1465
+ ...CartWarning
1466
+ }
1467
+ }
1468
+ }
1469
+ ```
1470
+
1471
+ ### Mutation: `CustomerSignup`
1472
+
1473
+ **Section**: Customer Auth Mutations
1474
+
1475
+ **Description**: 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.
1476
+
1477
+ **Variables**:
1478
+ - `$input`: `CustomerCreateInput!`
1479
+
1480
+ **Fragments used**: `Customer`, `CustomerAccessToken`, `UserError`
1481
+
1482
+ **GraphQL**:
1483
+ ```graphql
1484
+ mutation CustomerSignup($input: CustomerCreateInput!) {
1485
+ customerSignup(input: $input) {
1486
+ customer {
1487
+ ...Customer
1488
+ }
1489
+ customerAccessToken {
1490
+ ...CustomerAccessToken
1491
+ }
1492
+ userErrors {
1493
+ ...UserError
1494
+ }
1495
+ }
1496
+ }
1497
+ ```
1498
+
1499
+ ### Mutation: `CustomerLogin`
1500
+
1501
+ **Section**: Customer Auth Mutations
1502
+
1503
+ **Description**: 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).
1504
+
1505
+ **Variables**:
1506
+ - `$input`: `CustomerAccessTokenCreateInput!`
1507
+
1508
+ **Fragments used**: `CustomerAccessToken`, `UserError`
1509
+
1510
+ **GraphQL**:
1511
+ ```graphql
1512
+ mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
1513
+ customerLogin(input: $input) {
1514
+ customerAccessToken {
1515
+ ...CustomerAccessToken
1516
+ }
1517
+ userErrors {
1518
+ ...UserError
1519
+ }
1520
+ }
1521
+ }
1522
+ ```
1523
+
1524
+ ### Mutation: `CustomerLogout`
1525
+
1526
+ **Section**: Customer Auth Mutations
1527
+
1528
+ **Description**: 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.
1529
+
1530
+ **Variables**: none
1531
+
1532
+ **Fragments used**: `UserError`
1533
+
1534
+ **GraphQL**:
1535
+ ```graphql
1536
+ mutation CustomerLogout {
1537
+ customerLogout {
1538
+ deletedAccessToken
1539
+ deletedCustomerAccessTokenId
1540
+ userErrors {
1541
+ ...UserError
1542
+ }
1543
+ }
1544
+ }
1545
+ ```
1546
+
1547
+ ### Mutation: `CustomerRefreshToken`
1548
+
1549
+ **Section**: Customer Auth Mutations
1550
+
1551
+ **Description**: 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.
1552
+
1553
+ **Variables**: none
1554
+
1555
+ **Fragments used**: `CustomerAccessToken`, `UserError`
1556
+
1557
+ **GraphQL**:
1558
+ ```graphql
1559
+ mutation CustomerRefreshToken {
1560
+ customerRefreshToken {
1561
+ customerAccessToken {
1562
+ ...CustomerAccessToken
1563
+ }
1564
+ userErrors {
1565
+ ...UserError
1566
+ }
1567
+ }
1568
+ }
1569
+ ```
1570
+
1571
+ ### Mutation: `CustomerUpdate`
1572
+
1573
+ **Section**: Customer Profile Mutations
1574
+
1575
+ **Description**: 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.
1576
+
1577
+ **Variables**:
1578
+ - `$customer`: `CustomerUpdateInput!`
1579
+
1580
+ **Fragments used**: `Customer`, `UserError`
1581
+
1582
+ **GraphQL**:
1583
+ ```graphql
1584
+ mutation CustomerUpdate($customer: CustomerUpdateInput!) {
1585
+ customerUpdate(customer: $customer) {
1586
+ customer {
1587
+ ...Customer
1588
+ }
1589
+ userErrors {
1590
+ ...UserError
1591
+ }
1592
+ }
1593
+ }
1594
+ ```
1595
+
1596
+ ### Mutation: `CustomerAddAddress`
1597
+
1598
+ **Section**: Customer Address Mutations
1599
+
1600
+ **Description**: 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.
1601
+
1602
+ **Variables**:
1603
+ - `$address`: `MailingAddressInput!`
1604
+
1605
+ **Fragments used**: `MailingAddress`, `UserError`
1606
+
1607
+ **GraphQL**:
1608
+ ```graphql
1609
+ mutation CustomerAddAddress($address: MailingAddressInput!) {
1610
+ customerAddAddress(address: $address) {
1611
+ address {
1612
+ ...MailingAddress
1613
+ }
1614
+ userErrors {
1615
+ ...UserError
1616
+ }
1617
+ }
1618
+ }
1619
+ ```
1620
+
1621
+ ### Mutation: `CustomerUpdateAddress`
1622
+
1623
+ **Section**: Customer Address Mutations
1624
+
1625
+ **Description**: 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.
1626
+
1627
+ **Variables**:
1628
+ - `$id`: `ID!`
1629
+ - `$address`: `MailingAddressInput!`
1630
+
1631
+ **Fragments used**: `MailingAddress`, `UserError`
1632
+
1633
+ **GraphQL**:
1634
+ ```graphql
1635
+ mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!) {
1636
+ customerUpdateAddress(id: $id, address: $address) {
1637
+ address {
1638
+ ...MailingAddress
1639
+ }
1640
+ userErrors {
1641
+ ...UserError
1642
+ }
1643
+ }
1644
+ }
1645
+ ```
1646
+
1647
+ ### Mutation: `CustomerRemoveAddress`
1648
+
1649
+ **Section**: Customer Address Mutations
1650
+
1651
+ **Description**: 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).
1652
+
1653
+ **Variables**:
1654
+ - `$id`: `ID!`
1655
+
1656
+ **Fragments used**: `UserError`
1657
+
1658
+ **GraphQL**:
1659
+ ```graphql
1660
+ mutation CustomerRemoveAddress($id: ID!) {
1661
+ customerRemoveAddress(id: $id) {
1662
+ deletedAddressId
1663
+ userErrors {
1664
+ ...UserError
1665
+ }
1666
+ }
1667
+ }
1668
+ ```
1669
+
1670
+ ### Mutation: `CustomerSetDefaultAddress`
1671
+
1672
+ **Section**: Customer Address Mutations
1673
+
1674
+ **Description**: 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.
1675
+
1676
+ **Variables**:
1677
+ - `$addressId`: `ID!`
1678
+
1679
+ **Fragments used**: `Customer`, `UserError`
1680
+
1681
+ **GraphQL**:
1682
+ ```graphql
1683
+ mutation CustomerSetDefaultAddress($addressId: ID!) {
1684
+ customerSetDefaultAddress(addressId: $addressId) {
1685
+ customer {
1686
+ ...Customer
1687
+ }
1688
+ userErrors {
1689
+ ...UserError
1690
+ }
1691
+ }
1692
+ }
1693
+ ```
1694
+
1695
+ ### Mutation: `CustomerRequestPasswordReset`
1696
+
1697
+ **Section**: Customer Password Mutations
1698
+
1699
+ **Description**: 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.
1700
+
1701
+ **Variables**:
1702
+ - `$email`: `String!`
1703
+
1704
+ **Fragments used**: `UserError`
1705
+
1706
+ **GraphQL**:
1707
+ ```graphql
1708
+ mutation CustomerRequestPasswordReset($email: String!) {
1709
+ customerRequestPasswordReset(email: $email) {
1710
+ userErrors {
1711
+ ...UserError
1712
+ }
1713
+ }
1714
+ }
1715
+ ```
1716
+
1717
+ ### Mutation: `CustomerActivate`
1718
+
1719
+ **Section**: Customer Password Mutations
1720
+
1721
+ **Description**: 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.
1722
+
1723
+ **Variables**:
1724
+ - `$token`: `String!`
1725
+ - `$password`: `String!`
1726
+
1727
+ **Fragments used**: `Customer`, `CustomerAccessToken`, `UserError`
1728
+
1729
+ **GraphQL**:
1730
+ ```graphql
1731
+ mutation CustomerActivate($token: String!, $password: String!) {
1732
+ customerActivate(token: $token, password: $password) {
1733
+ customer {
1734
+ ...Customer
1735
+ }
1736
+ customerAccessToken {
1737
+ ...CustomerAccessToken
1738
+ }
1739
+ userErrors {
1740
+ ...UserError
1741
+ }
1742
+ }
1743
+ }
1744
+ ```
1745
+
1746
+ ### Mutation: `CustomerResetPassword`
1747
+
1748
+ **Section**: Customer Password Mutations
1749
+
1750
+ **Description**: 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.
1751
+
1752
+ **Variables**:
1753
+ - `$token`: `String!`
1754
+ - `$newPassword`: `String!`
1755
+
1756
+ **Fragments used**: `Customer`, `CustomerAccessToken`, `UserError`
1757
+
1758
+ **GraphQL**:
1759
+ ```graphql
1760
+ mutation CustomerResetPassword($token: String!, $newPassword: String!) {
1761
+ customerResetPassword(token: $token, newPassword: $newPassword) {
1762
+ customer {
1763
+ ...Customer
1764
+ }
1765
+ customerAccessToken {
1766
+ ...CustomerAccessToken
1767
+ }
1768
+ userErrors {
1769
+ ...UserError
1770
+ }
1771
+ }
1772
+ }
1773
+ ```
1774
+
1775
+ ### Mutation: `CheckoutCreate`
1776
+
1777
+ **Section**: Checkout Mutations
1778
+
1779
+ **Description**: 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.
1780
+
1781
+ **Variables**:
1782
+ - `$input`: `CheckoutCreateInput!`
1783
+
1784
+ **Fragments used**: `Checkout`, `UserError`
1785
+
1786
+ **GraphQL**:
1787
+ ```graphql
1788
+ mutation CheckoutCreate($input: CheckoutCreateInput!) {
1789
+ checkoutCreate(input: $input) {
1790
+ checkout {
1791
+ ...Checkout
1792
+ }
1793
+ userErrors {
1794
+ ...UserError
1795
+ }
1796
+ }
1797
+ }
1798
+ ```
1799
+
1800
+ ### Mutation: `CheckoutShippingAddressUpdate`
1801
+
1802
+ **Section**: Checkout Mutations
1803
+
1804
+ **Description**: Sets the shipping address (full replace, not patch). Triggers cart re-pricing. Address format is NOT validated here — full validation runs at `checkoutComplete` (`validateOrderReadiness`).
1805
+
1806
+ **Variables**:
1807
+ - `$checkoutId`: `ID!`
1808
+ - `$shippingAddress`: `CheckoutAddressInput!`
1809
+
1810
+ **Fragments used**: `Checkout`, `UserError`
1811
+
1812
+ **GraphQL**:
1813
+ ```graphql
1814
+ mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: CheckoutAddressInput!) {
1815
+ checkoutShippingAddressUpdate(
1816
+ checkoutId: $checkoutId
1817
+ shippingAddress: $shippingAddress
1818
+ ) {
1819
+ checkout {
1820
+ ...Checkout
1821
+ }
1822
+ userErrors {
1823
+ ...UserError
1824
+ }
1825
+ }
1826
+ }
1827
+ ```
1828
+
1829
+ ### Mutation: `CheckoutBillingAddressUpdate`
1830
+
1831
+ **Section**: Checkout Mutations
1832
+
1833
+ **Description**: Sets the billing address (full replace). Independent of shipping address — pass it explicitly even when "billing same as shipping".
1834
+
1835
+ **Variables**:
1836
+ - `$checkoutId`: `ID!`
1837
+ - `$billingAddress`: `CheckoutAddressInput!`
1838
+
1839
+ **Fragments used**: `Checkout`, `UserError`
1840
+
1841
+ **GraphQL**:
1842
+ ```graphql
1843
+ mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: CheckoutAddressInput!) {
1844
+ checkoutBillingAddressUpdate(
1845
+ checkoutId: $checkoutId
1846
+ billingAddress: $billingAddress
1847
+ ) {
1848
+ checkout {
1849
+ ...Checkout
1850
+ }
1851
+ userErrors {
1852
+ ...UserError
1853
+ }
1854
+ }
1855
+ }
1856
+ ```
1857
+
1858
+ ### Mutation: `CheckoutEmailUpdate`
1859
+
1860
+ **Section**: Checkout Mutations
1861
+
1862
+ **Description**: 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.
1863
+
1864
+ **Variables**:
1865
+ - `$checkoutId`: `ID!`
1866
+ - `$email`: `String!`
1867
+
1868
+ **Fragments used**: `Checkout`, `UserError`
1869
+
1870
+ **GraphQL**:
1871
+ ```graphql
1872
+ mutation CheckoutEmailUpdate($checkoutId: ID!, $email: String!) {
1873
+ checkoutEmailUpdate(checkoutId: $checkoutId, email: $email) {
1874
+ checkout {
1875
+ ...Checkout
1876
+ }
1877
+ userErrors {
1878
+ ...UserError
1879
+ }
1880
+ }
1881
+ }
1882
+ ```
1883
+
1884
+ ### Mutation: `CheckoutShippingLineUpdate`
1885
+
1886
+ **Section**: Checkout Mutations
1887
+
1888
+ **Description**: Selects a shipping method by `shippingRateHandle` (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.
1889
+
1890
+ **Variables**:
1891
+ - `$checkoutId`: `ID!`
1892
+ - `$shippingRateHandle`: `String!`
1893
+
1894
+ **Fragments used**: `Checkout`, `UserError`
1895
+
1896
+ **GraphQL**:
1897
+ ```graphql
1898
+ mutation CheckoutShippingLineUpdate($checkoutId: ID!, $shippingRateHandle: String!) {
1899
+ checkoutShippingLineUpdate(
1900
+ checkoutId: $checkoutId
1901
+ shippingRateHandle: $shippingRateHandle
1902
+ ) {
1903
+ checkout {
1904
+ ...Checkout
1905
+ }
1906
+ userErrors {
1907
+ ...UserError
1908
+ }
1909
+ }
1910
+ }
1911
+ ```
1912
+
1913
+ ### Mutation: `CheckoutDiscountCodeApply`
1914
+
1915
+ **Section**: Checkout Mutations
1916
+
1917
+ **Description**: 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 via `discountService.validateDiscount`.
1918
+
1919
+ **Variables**:
1920
+ - `$checkoutId`: `ID!`
1921
+ - `$discountCode`: `String!`
1922
+
1923
+ **Fragments used**: `Checkout`, `UserError`
1924
+
1925
+ **GraphQL**:
1926
+ ```graphql
1927
+ mutation CheckoutDiscountCodeApply($checkoutId: ID!, $discountCode: String!) {
1928
+ checkoutDiscountCodeApply(checkoutId: $checkoutId, discountCode: $discountCode) {
1929
+ checkout {
1930
+ ...Checkout
1931
+ }
1932
+ userErrors {
1933
+ ...UserError
1934
+ }
1935
+ }
1936
+ }
1937
+ ```
1938
+
1939
+ ### Mutation: `CheckoutDiscountCodeRemove`
1940
+
1941
+ **Section**: Checkout Mutations
1942
+
1943
+ **Description**: Removes a code from the cart's `discount_codes` array (filters by exact match). Triggers re-pricing.
1944
+
1945
+ **Variables**:
1946
+ - `$checkoutId`: `ID!`
1947
+ - `$discountCode`: `String!`
1948
+
1949
+ **Fragments used**: `Checkout`, `UserError`
1950
+
1951
+ **GraphQL**:
1952
+ ```graphql
1953
+ mutation CheckoutDiscountCodeRemove($checkoutId: ID!, $discountCode: String!) {
1954
+ checkoutDiscountCodeRemove(checkoutId: $checkoutId, discountCode: $discountCode) {
1955
+ checkout {
1956
+ ...Checkout
1957
+ }
1958
+ userErrors {
1959
+ ...UserError
1960
+ }
1961
+ }
1962
+ }
1963
+ ```
1964
+
1965
+ ### Mutation: `CheckoutDiscountCodeValidate`
1966
+
1967
+ **Section**: Checkout Mutations
1968
+
1969
+ **Description**: 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).
1970
+
1971
+ **Variables**:
1972
+ - `$checkoutId`: `ID!`
1973
+ - `$discountCode`: `String!`
1974
+
1975
+ **Fragments used**: `UserError`
1976
+
1977
+ **GraphQL**:
1978
+ ```graphql
1979
+ mutation CheckoutDiscountCodeValidate($checkoutId: ID!, $discountCode: String!) {
1980
+ checkoutDiscountCodeValidate(
1981
+ checkoutId: $checkoutId
1982
+ discountCode: $discountCode
1983
+ ) {
1984
+ result {
1985
+ isValid
1986
+ discount {
1987
+ code
1988
+ title
1989
+ type
1990
+ value
1991
+ discountAmount {
1992
+ amount
1993
+ currencyCode
1994
+ }
1995
+ }
1996
+ error {
1997
+ code
1998
+ message
1999
+ }
2000
+ }
2001
+ userErrors {
2002
+ ...UserError
2003
+ }
2004
+ }
2005
+ }
2006
+ ```
2007
+
2008
+ ### Mutation: `CheckoutPaymentMethodUpdate`
2009
+
2010
+ **Section**: Checkout Mutations
2011
+
2012
+ **Description**: Selects a payment method by `paymentMethodId` (UUID from `availablePaymentMethods` query). Validates existence and active status; no pre-authorization is performed here.
2013
+
2014
+ **Variables**:
2015
+ - `$checkoutId`: `ID!`
2016
+ - `$paymentMethodId`: `ID!`
2017
+
2018
+ **Fragments used**: `Checkout`, `UserError`
2019
+
2020
+ **GraphQL**:
2021
+ ```graphql
2022
+ mutation CheckoutPaymentMethodUpdate($checkoutId: ID!, $paymentMethodId: ID!) {
2023
+ checkoutPaymentMethodUpdate(
2024
+ checkoutId: $checkoutId
2025
+ paymentMethodId: $paymentMethodId
2026
+ ) {
2027
+ checkout {
2028
+ ...Checkout
2029
+ }
2030
+ userErrors {
2031
+ ...UserError
2032
+ }
2033
+ }
2034
+ }
2035
+ ```
2036
+
2037
+ ### Mutation: `CheckoutComplete`
2038
+
2039
+ **Section**: Checkout Mutations
2040
+
2041
+ **Description**: Finalizes the checkout: creates the `Order`, marks the cart `CONVERTED`, deducts gift cards, emits `ORDER_CREATED` (and `ORDER_CONFIRMED` for COD) — all in a single serializable transaction. **Idempotent on `idempotencyKey`** (NOT on `checkoutId`); auto-generated from `cartId + timestamp` 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.
2042
+
2043
+ **Variables**:
2044
+ - `$checkoutId`: `ID!`
2045
+ - `$input`: `CheckoutCompleteInput`
2046
+
2047
+ **Fragments used**: `Checkout`, `Order`, `UserError`
2048
+
2049
+ **GraphQL**:
2050
+ ```graphql
2051
+ mutation CheckoutComplete($checkoutId: ID!, $input: CheckoutCompleteInput) {
2052
+ checkoutComplete(checkoutId: $checkoutId, input: $input) {
2053
+ checkout {
2054
+ ...Checkout
2055
+ }
2056
+ order {
2057
+ ...Order
2058
+ }
2059
+ paymentUrl
2060
+ userErrors {
2061
+ ...UserError
2062
+ }
2063
+ }
2064
+ }
2065
+ ```
2066
+
2067
+ ### Mutation: `CheckoutGiftCardApply`
2068
+
2069
+ **Section**: Gift Card Checkout Mutations
2070
+
2071
+ **Description**: 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 inside the `checkoutComplete` transaction. Errors: `GIFT_CARD_NOT_FOUND`, `GIFT_CARD_DEPLETED`, `GIFT_CARD_UNUSABLE`, `GIFT_CARD_ALREADY_APPLIED`.
2072
+
2073
+ **Variables**:
2074
+ - `$checkoutId`: `ID!`
2075
+ - `$giftCardCode`: `String!`
2076
+
2077
+ **Fragments used**: `Checkout`, `UserError`
2078
+
2079
+ **GraphQL**:
2080
+ ```graphql
2081
+ mutation CheckoutGiftCardApply($checkoutId: ID!, $giftCardCode: String!) {
2082
+ checkoutGiftCardApply(checkoutId: $checkoutId, giftCardCode: $giftCardCode) {
2083
+ checkout {
2084
+ ...Checkout
2085
+ }
2086
+ userErrors {
2087
+ ...UserError
2088
+ }
2089
+ }
2090
+ }
2091
+ ```
2092
+
2093
+ ### Mutation: `CheckoutGiftCardRemove`
2094
+
2095
+ **Section**: Gift Card Checkout Mutations
2096
+
2097
+ **Description**: 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.
2098
+
2099
+ **Variables**:
2100
+ - `$checkoutId`: `ID!`
2101
+ - `$giftCardCode`: `String!`
2102
+
2103
+ **Fragments used**: `Checkout`, `UserError`
2104
+
2105
+ **GraphQL**:
2106
+ ```graphql
2107
+ mutation CheckoutGiftCardRemove($checkoutId: ID!, $giftCardCode: String!) {
2108
+ checkoutGiftCardRemove(checkoutId: $checkoutId, giftCardCode: $giftCardCode) {
2109
+ checkout {
2110
+ ...Checkout
2111
+ }
2112
+ userErrors {
2113
+ ...UserError
2114
+ }
2115
+ }
2116
+ }
2117
+ ```
2118
+
2119
+ ### Mutation: `CheckoutGiftCardRecipientUpdate`
2120
+
2121
+ **Section**: Gift Card Checkout Mutations
2122
+
2123
+ **Description**: 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.
2124
+
2125
+ **Variables**:
2126
+ - `$input`: `CheckoutGiftCardRecipientInput!`
2127
+
2128
+ **Fragments used**: `Checkout`, `UserError`
2129
+
2130
+ **GraphQL**:
2131
+ ```graphql
2132
+ mutation CheckoutGiftCardRecipientUpdate($input: CheckoutGiftCardRecipientInput!) {
2133
+ checkoutGiftCardRecipientUpdate(input: $input) {
2134
+ checkout {
2135
+ ...Checkout
2136
+ }
2137
+ userErrors {
2138
+ ...UserError
2139
+ }
2140
+ }
2141
+ }
2142
+ ```
2143
+
2144
+ ### Mutation: `ReturnCreate`
2145
+
2146
+ **Section**: Return Mutations
2147
+
2148
+ **Description**: 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.
2149
+
2150
+ **Variables**:
2151
+ - `$input`: `ReturnCreateInput!`
2152
+
2153
+ **Fragments used**: `Return`, `UserError`
2154
+
2155
+ **GraphQL**:
2156
+ ```graphql
2157
+ mutation ReturnCreate($input: ReturnCreateInput!) {
2158
+ returnCreate(input: $input) {
2159
+ return {
2160
+ ...Return
2161
+ }
2162
+ userErrors {
2163
+ ...UserError
2164
+ }
2165
+ }
2166
+ }
2167
+ ```
2168
+
2169
+ ### Mutation: `ReturnCancel`
2170
+
2171
+ **Section**: Return Mutations
2172
+
2173
+ **Description**: 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.
2174
+
2175
+ **Variables**:
2176
+ - `$id`: `ID!`
2177
+
2178
+ **Fragments used**: `Return`, `UserError`
2179
+
2180
+ **GraphQL**:
2181
+ ```graphql
2182
+ mutation ReturnCancel($id: ID!) {
2183
+ returnCancel(id: $id) {
2184
+ return {
2185
+ ...Return
2186
+ }
2187
+ userErrors {
2188
+ ...UserError
2189
+ }
2190
+ }
2191
+ }
2192
+ ```
2193
+
2194
+ ### Mutation: `RedeemLoyaltyReward`
2195
+
2196
+ **Section**: Loyalty Program Mutations
2197
+
2198
+ **Description**: 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.
2199
+
2200
+ **Variables**:
2201
+ - `$input`: `RedeemRewardInput!`
2202
+
2203
+ **Fragments used**: `RedeemRewardPayload`
2204
+
2205
+ **GraphQL**:
2206
+ ```graphql
2207
+ mutation RedeemLoyaltyReward($input: RedeemRewardInput!) {
2208
+ redeemLoyaltyReward(input: $input) {
2209
+ ...RedeemRewardPayload
2210
+ }
2211
+ }
2212
+ ```
2213
+
2214
+ ### Mutation: `GenerateReferralCode`
2215
+
2216
+ **Section**: Loyalty Program Mutations
2217
+
2218
+ **Description**: 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.
2219
+
2220
+ **Variables**: none
2221
+
2222
+ **Fragments used**: `GenerateReferralCodePayload`
2223
+
2224
+ **GraphQL**:
2225
+ ```graphql
2226
+ mutation GenerateReferralCode {
2227
+ generateReferralCode {
2228
+ ...GenerateReferralCodePayload
2229
+ }
2230
+ }
2231
+ ```
2232
+
2233
+ ### Mutation: `ReviewCreate`
2234
+
2235
+ **Section**: Review Mutations
2236
+
2237
+ **Description**: 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).
2238
+
2239
+ **Variables**:
2240
+ - `$input`: `ReviewCreateInput!`
2241
+
2242
+ **Fragments used**: `ProductReview`, `UserError`
2243
+
2244
+ **GraphQL**:
2245
+ ```graphql
2246
+ mutation ReviewCreate($input: ReviewCreateInput!) {
2247
+ reviewCreate(input: $input) {
2248
+ review {
2249
+ ...ProductReview
2250
+ }
2251
+ userErrors {
2252
+ ...UserError
2253
+ }
2254
+ }
2255
+ }
2256
+ ```
2257
+
2258
+ ### Mutation: `ReviewUpvote`
2259
+
2260
+ **Section**: Review Mutations
2261
+
2262
+ **Description**: 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).
2263
+
2264
+ **Variables**:
2265
+ - `$reviewId`: `ID!`
2266
+
2267
+ **Fragments used**: `ProductReview`, `UserError`
2268
+
2269
+ **GraphQL**:
2270
+ ```graphql
2271
+ mutation ReviewUpvote($reviewId: ID!) {
2272
+ reviewUpvote(reviewId: $reviewId) {
2273
+ review {
2274
+ ...ProductReview
2275
+ }
2276
+ userErrors {
2277
+ ...UserError
2278
+ }
2279
+ }
2280
+ }
2281
+ ```
2282
+
2283
+ ### Mutation: `ReviewDownvote`
2284
+
2285
+ **Section**: Review Mutations
2286
+
2287
+ **Description**: 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`.
2288
+
2289
+ **Variables**:
2290
+ - `$reviewId`: `ID!`
2291
+
2292
+ **Fragments used**: `ProductReview`, `UserError`
2293
+
2294
+ **GraphQL**:
2295
+ ```graphql
2296
+ mutation ReviewDownvote($reviewId: ID!) {
2297
+ reviewDownvote(reviewId: $reviewId) {
2298
+ review {
2299
+ ...ProductReview
2300
+ }
2301
+ userErrors {
2302
+ ...UserError
2303
+ }
2304
+ }
2305
+ }
2306
+ ```
2307
+
2308
+ ### Mutation: `WishlistCreate`
2309
+
2310
+ **Section**: Wishlist Mutations
2311
+
2312
+ **Description**: 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.
2313
+
2314
+ **Variables**:
2315
+ - `$input`: `WishlistCreateInput!`
2316
+
2317
+ **Fragments used**: `Wishlist`
2318
+
2319
+ **GraphQL**:
2320
+ ```graphql
2321
+ mutation WishlistCreate($input: WishlistCreateInput!) {
2322
+ wishlistCreate(input: $input) {
2323
+ wishlist {
2324
+ ...Wishlist
2325
+ }
2326
+ userErrors
2327
+ }
2328
+ }
2329
+ ```
2330
+
2331
+ ### Mutation: `WishlistAddItem`
2332
+
2333
+ **Section**: Wishlist Mutations
2334
+
2335
+ **Description**: 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 (`ON CONFLICT DO NOTHING`). Captures `priceAtAdd` for price-drop notifications.
2336
+
2337
+ **Variables**:
2338
+ - `$wishlistId`: `ID!`
2339
+ - `$input`: `WishlistItemInput!`
2340
+
2341
+ **Fragments used**: `Wishlist`
2342
+
2343
+ **GraphQL**:
2344
+ ```graphql
2345
+ mutation WishlistAddItem($wishlistId: ID!, $input: WishlistItemInput!) {
2346
+ wishlistAddItem(wishlistId: $wishlistId, input: $input) {
2347
+ wishlist {
2348
+ ...Wishlist
2349
+ }
2350
+ userErrors
2351
+ }
2352
+ }
2353
+ ```
2354
+
2355
+ ### Mutation: `WishlistRemoveItem`
2356
+
2357
+ **Section**: Wishlist Mutations
2358
+
2359
+ **Description**: Hard-deletes a wishlist item by `itemId` (the `WishlistItem` row id, NOT the product id).
2360
+
2361
+ **Variables**:
2362
+ - `$wishlistId`: `ID!`
2363
+ - `$itemId`: `ID!`
2364
+
2365
+ **Fragments used**: `Wishlist`
2366
+
2367
+ **GraphQL**:
2368
+ ```graphql
2369
+ mutation WishlistRemoveItem($wishlistId: ID!, $itemId: ID!) {
2370
+ wishlistRemoveItem(wishlistId: $wishlistId, itemId: $itemId) {
2371
+ wishlist {
2372
+ ...Wishlist
2373
+ }
2374
+ userErrors
2375
+ }
2376
+ }
2377
+ ```
2378
+
2379
+ ### Mutation: `WishlistDelete`
2380
+
2381
+ **Section**: Wishlist Mutations
2382
+
2383
+ **Description**: Hard-deletes the wishlist row. All `WishlistItem` rows are removed via FK cascade. No soft-delete; cannot be undone.
2384
+
2385
+ **Variables**:
2386
+ - `$wishlistId`: `ID!`
2387
+
2388
+ **Fragments used**: `Wishlist`
2389
+
2390
+ **GraphQL**:
2391
+ ```graphql
2392
+ mutation WishlistDelete($wishlistId: ID!) {
2393
+ wishlistDelete(wishlistId: $wishlistId) {
2394
+ wishlist {
2395
+ ...Wishlist
2396
+ }
2397
+ userErrors
2398
+ }
2399
+ }
2400
+ ```
2401
+
2402
+ ### Mutation: `CartUpdateAttributes`
2403
+
2404
+ **Section**: Cart Attributes
2405
+
2406
+ **Description**: 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.
2407
+
2408
+ **Variables**:
2409
+ - `$id`: `ID!`
2410
+ - `$attributes`: `[CartAttributeInput!]!`
2411
+
2412
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
2413
+
2414
+ **GraphQL**:
2415
+ ```graphql
2416
+ mutation CartUpdateAttributes($id: ID!, $attributes: [CartAttributeInput!]!) {
2417
+ cartUpdateAttributes(id: $id, attributes: $attributes) {
2418
+ cart {
2419
+ ...Cart
2420
+ }
2421
+ userErrors {
2422
+ ...UserError
2423
+ }
2424
+ warnings {
2425
+ ...CartWarning
2426
+ }
2427
+ }
2428
+ }
2429
+ ```
2430
+
2431
+ ---
2432
+
2433
+ ## Fragments
2434
+
2435
+ Fragments are referenced by name in operations above. Each fragment is defined once below.
2436
+
2437
+ ### Fragment: `PageInfo` on `PageInfo`
2438
+
2439
+ **Section**: Common
2440
+
2441
+ **Description**: Standard Relay Connection page metadata. Spread on every paginated query's `pageInfo`.
2442
+
2443
+ **GraphQL**:
2444
+ ```graphql
2445
+ fragment PageInfo on PageInfo {
2446
+ hasNextPage
2447
+ hasPreviousPage
2448
+ startCursor
2449
+ endCursor
2450
+ }
2451
+ ```
2452
+
2453
+ ### Fragment: `UserError` on `UserError`
2454
+
2455
+ **Section**: Common
2456
+
2457
+ **Description**: Generic mutation error envelope — included in every mutation's `userErrors[]` field. Always check before consuming the happy-path payload.
2458
+
2459
+ **GraphQL**:
2460
+ ```graphql
2461
+ fragment UserError on UserError {
2462
+ message
2463
+ code
2464
+ field
2465
+ }
2466
+ ```
2467
+
2468
+ ### Fragment: `CartWarning` on `CartWarning`
2469
+
2470
+ **Section**: Common
2471
+
2472
+ **Description**: Non-blocking cart warning (e.g. low stock, partial availability). Cart mutations succeed but surface these for UI hints.
2473
+
2474
+ **GraphQL**:
2475
+ ```graphql
2476
+ fragment CartWarning on CartWarning {
2477
+ message
2478
+ code
2479
+ target
2480
+ }
2481
+ ```
2482
+
2483
+ ### Fragment: `Image` on `Image`
2484
+
2485
+ **Section**: Common
2486
+
2487
+ **Description**: Base image — original size, no transform. Use for logos, favicons, branding assets that are already sized correctly. ~few KB.
2488
+
2489
+ **GraphQL**:
2490
+ ```graphql
2491
+ fragment Image on Image {
2492
+ id
2493
+ url
2494
+ altText
2495
+ width
2496
+ height
2497
+ }
2498
+ ```
2499
+
2500
+ ### Fragment: `ImageThumbnail` on `Image`
2501
+
2502
+ **Section**: Common
2503
+
2504
+ **Description**: Thumbnail — cart line items, variant swatches, author avatars (rendered ~96px on screen, transform delivers up to 300px for 3x DPI). Includes `thumbhash` for blurred placeholder. Format auto-negotiated from the `Accept` header.
2505
+
2506
+ **GraphQL**:
2507
+ ```graphql
2508
+ fragment ImageThumbnail on Image {
2509
+ id
2510
+ url(transform: {maxWidth: 300})
2511
+ altText
2512
+ width
2513
+ height
2514
+ thumbhash
2515
+ }
2516
+ ```
2517
+
2518
+ ### Fragment: `ImageCard` on `Image`
2519
+
2520
+ **Section**: Common
2521
+
2522
+ **Description**: Card — product cards, collection / category tiles, blog post previews (rendered ~400px, transform delivers up to 800px for 2x DPI). Includes `thumbhash` placeholder.
2523
+
2524
+ **GraphQL**:
2525
+ ```graphql
2526
+ fragment ImageCard on Image {
2527
+ id
2528
+ url(transform: {maxWidth: 800})
2529
+ altText
2530
+ width
2531
+ height
2532
+ thumbhash
2533
+ }
2534
+ ```
2535
+
2536
+ ### Fragment: `ImageFull` on `Image`
2537
+
2538
+ **Section**: Common
2539
+
2540
+ **Description**: Full — product detail gallery, hero banners (rendered ~800px, transform delivers up to 1600px for 2x DPI). Includes `thumbhash` placeholder.
2541
+
2542
+ **GraphQL**:
2543
+ ```graphql
2544
+ fragment ImageFull on Image {
2545
+ id
2546
+ url(transform: {maxWidth: 1600})
2547
+ altText
2548
+ width
2549
+ height
2550
+ thumbhash
2551
+ }
2552
+ ```
2553
+
2554
+ ### Fragment: `Money` on `Money`
2555
+
2556
+ **Section**: Common
2557
+
2558
+ **Description**: Plain `{ amount, currencyCode }` price. Use for `compareAtPrice`, totals, and any field where currency conversion transparency is not needed. `amount` is a String.
2559
+
2560
+ **GraphQL**:
2561
+ ```graphql
2562
+ fragment Money on Money {
2563
+ amount
2564
+ currencyCode
2565
+ }
2566
+ ```
2567
+
2568
+ ### Fragment: `Price` on `PriceMoney`
2569
+
2570
+ **Section**: Common
2571
+
2572
+ **Description**: Lightweight price for listing views (cards, grids, search results) — same shape as `Money`. Spread when full conversion metadata is overkill.
2573
+
2574
+ **GraphQL**:
2575
+ ```graphql
2576
+ fragment Price on PriceMoney {
2577
+ amount
2578
+ currencyCode
2579
+ }
2580
+ ```
2581
+
2582
+ ### Fragment: `PriceMoney` on `PriceMoney`
2583
+
2584
+ **Section**: Common
2585
+
2586
+ **Description**: Full price with currency-conversion transparency — adds `baseAmount`, `baseCurrencyCode`, `exchangeRate`, `marginApplied`, `rateTimestamp`, `isConverted`. Spread on PDP / cart / checkout where the customer needs to see the original price + applied conversion.
2587
+
2588
+ **Uses fragments**: `Price`
2589
+
2590
+ **GraphQL**:
2591
+ ```graphql
2592
+ fragment PriceMoney on PriceMoney {
2593
+ ...Price
2594
+ baseAmount
2595
+ baseCurrencyCode
2596
+ exchangeRate
2597
+ marginApplied
2598
+ rateTimestamp
2599
+ isConverted
2600
+ }
2601
+ ```
2602
+
2603
+ ### Fragment: `SelectedOption` on `SelectedOption`
2604
+
2605
+ **Section**: Common
2606
+
2607
+ **Description**: Selected variant option (e.g. `{ name: "Color", value: "Red" }`) on a `ProductVariant`. Use to render variant labels in cart / order summaries.
2608
+
2609
+ **GraphQL**:
2610
+ ```graphql
2611
+ fragment SelectedOption on SelectedOption {
2612
+ name
2613
+ value
2614
+ }
2615
+ ```
2616
+
2617
+ ### Fragment: `ProductVariant` on `ProductVariant`
2618
+
2619
+ **Section**: Products
2620
+
2621
+ **Description**: Single variant of a product — price, compare-at, stock flags, sort, image, selected options. Spread on cart line items, PDP variant selectors, search result rows that need price.
2622
+
2623
+ **Uses fragments**: `ImageThumbnail`, `Money`, `SelectedOption`
2624
+
2625
+ **GraphQL**:
2626
+ ```graphql
2627
+ fragment ProductVariant on ProductVariant {
2628
+ id
2629
+ title
2630
+ sku
2631
+ price {
2632
+ ...Money
2633
+ }
2634
+ compareAtPrice {
2635
+ ...Money
2636
+ }
2637
+ isAvailable
2638
+ availableStock
2639
+ image {
2640
+ ...ImageThumbnail
2641
+ }
2642
+ selectedOptions {
2643
+ ...SelectedOption
2644
+ }
2645
+ barcode
2646
+ weight {
2647
+ value
2648
+ unit
2649
+ }
2650
+ sortOrder
2651
+ }
2652
+ ```
2653
+
2654
+ ### Fragment: `ProductCard` on `Product`
2655
+
2656
+ **Section**: Products
2657
+
2658
+ **Description**: Minimal product shape for listing views (collection grids, search results, "you may also like"). Includes price range, featured image (card size), rating + review count, vendor, tags. Use for any list of products where you don't need variants/description.
2659
+
2660
+ **Uses fragments**: `ImageCard`, `Money`
2661
+
2662
+ **GraphQL**:
2663
+ ```graphql
2664
+ fragment ProductCard on Product {
2665
+ id
2666
+ handle
2667
+ title
2668
+ vendor
2669
+ category
2670
+ isAvailable
2671
+ averageRating
2672
+ reviewCount
2673
+ tags
2674
+ featuredImage {
2675
+ ...ImageCard
2676
+ }
2677
+ priceRange {
2678
+ minVariantPrice {
2679
+ ...Money
2680
+ }
2681
+ maxVariantPrice {
2682
+ ...Money
2683
+ }
2684
+ }
2685
+ compareAtPriceRange {
2686
+ minVariantPrice {
2687
+ ...Money
2688
+ }
2689
+ maxVariantPrice {
2690
+ ...Money
2691
+ }
2692
+ }
2693
+ }
2694
+ ```
2695
+
2696
+ ### Fragment: `ProductBase` on `Product`
2697
+
2698
+ **Section**: Products
2699
+
2700
+ **Description**: `ProductCard` plus textual content (description, descriptionHtml), stock total, type, visibility flags, and timestamps. Use when you need full product copy but not the heavy variants/images lists. Sweet spot for blog post embeds, related-product cards with description.
2701
+
2702
+ **Uses fragments**: `ProductCard`
2703
+
2704
+ **GraphQL**:
2705
+ ```graphql
2706
+ fragment ProductBase on Product {
2707
+ ...ProductCard
2708
+ description
2709
+ descriptionHtml
2710
+ stockTotal
2711
+ type
2712
+ requiresRecipientInfo
2713
+ visibility
2714
+ attributeSetId
2715
+ createdAt
2716
+ updatedAt
2717
+ }
2718
+ ```
2719
+
2720
+ ### Fragment: `ProductFull` on `Product`
2721
+
2722
+ **Section**: Products
2723
+
2724
+ **Description**: Full product shape for the PDP — `ProductBase` plus the full image gallery (up to 20, full-size) and all variants (up to 100) with their pricing/stock. Spread on the product detail page.
2725
+
2726
+ **Uses fragments**: `ImageFull`, `PageInfo`, `ProductBase`, `ProductVariant`
2727
+
2728
+ **GraphQL**:
2729
+ ```graphql
2730
+ fragment ProductFull on Product {
2731
+ ...ProductBase
2732
+ images(first: 20) {
2733
+ edges {
2734
+ cursor
2735
+ node {
2736
+ ...ImageFull
2737
+ }
2738
+ }
2739
+ nodes {
2740
+ ...ImageFull
2741
+ }
2742
+ pageInfo {
2743
+ ...PageInfo
2744
+ }
2745
+ totalCount
2746
+ }
2747
+ variants(first: 100) {
2748
+ edges {
2749
+ cursor
2750
+ node {
2751
+ ...ProductVariant
2752
+ }
2753
+ }
2754
+ nodes {
2755
+ ...ProductVariant
2756
+ }
2757
+ pageInfo {
2758
+ ...PageInfo
2759
+ }
2760
+ totalCount
2761
+ }
2762
+ seo {
2763
+ title
2764
+ description
2765
+ }
2766
+ }
2767
+ ```
2768
+
2769
+ ### Fragment: `Collection` on `Collection`
2770
+
2771
+ **Section**: Collections
2772
+
2773
+ **Description**: Collection (manual or rule-based product grouping) — title, description, hero image, SEO. Spread on collection landing pages and collection cards. Does NOT include products — query `collection(id, handle).products` separately for those.
2774
+
2775
+ **Uses fragments**: `ImageCard`
2776
+
2777
+ **GraphQL**:
2778
+ ```graphql
2779
+ fragment Collection on Collection {
2780
+ id
2781
+ handle
2782
+ title
2783
+ description
2784
+ descriptionHtml
2785
+ image {
2786
+ ...ImageCard
2787
+ }
2788
+ seo {
2789
+ title
2790
+ description
2791
+ }
2792
+ createdAt
2793
+ updatedAt
2794
+ }
2795
+ ```
2796
+
2797
+ ### Fragment: `Category` on `Category`
2798
+
2799
+ **Section**: Categories
2800
+
2801
+ **Description**: Category node in the shop's taxonomy — name, slug, hero image, depth `level`, materialized `path`, product count, sort order. Spread on category cards and breadcrumb segments. Does NOT include parent / children — query those fields separately and let the server batch them.
2802
+
2803
+ **Uses fragments**: `ImageCard`
2804
+
2805
+ **GraphQL**:
2806
+ ```graphql
2807
+ fragment Category on Category {
2808
+ id
2809
+ name
2810
+ slug
2811
+ description
2812
+ image {
2813
+ ...ImageCard
2814
+ }
2815
+ level
2816
+ path
2817
+ productCount
2818
+ sortOrder
2819
+ }
2820
+ ```
2821
+
2822
+ ### Fragment: `MailingAddress` on `MailingAddress`
2823
+
2824
+ **Section**: Customer
2825
+
2826
+ **Description**: Customer mailing address — full street/city/state/country/postal code with names and phone. `isDefault` flips when this address is the default for shipping. Spread on the address book UI and order summaries.
2827
+
2828
+ **GraphQL**:
2829
+ ```graphql
2830
+ fragment MailingAddress on MailingAddress {
2831
+ id
2832
+ streetLine1
2833
+ streetLine2
2834
+ city
2835
+ company
2836
+ country
2837
+ countryCode
2838
+ firstName
2839
+ lastName
2840
+ phone
2841
+ state
2842
+ stateCode
2843
+ postalCode
2844
+ isDefault
2845
+ }
2846
+ ```
2847
+
2848
+ ### Fragment: `CustomerAccessToken` on `CustomerAccessToken`
2849
+
2850
+ **Section**: Customer
2851
+
2852
+ **Description**: Customer access token returned by `customerLogin` / `customerSignup` / `customerActivate` / `customerResetPassword` / `customerRefreshToken`. The token's JWT TTL is 24h; cookie max-age is 30d.
2853
+
2854
+ **GraphQL**:
2855
+ ```graphql
2856
+ fragment CustomerAccessToken on CustomerAccessToken {
2857
+ accessToken
2858
+ expiresAt
2859
+ }
2860
+ ```
2861
+
2862
+ ### Fragment: `Customer` on `Customer`
2863
+
2864
+ **Section**: Customer
2865
+
2866
+ **Description**: Customer profile — basic info plus B2B fields (`taxId`, `vatNumber`, `regon`, `companyName`), default address, lifetime stats (`orderCount`, `totalSpent`), marketing preferences. Use on profile / settings pages.
2867
+
2868
+ **Uses fragments**: `MailingAddress`, `Money`
2869
+
2870
+ **GraphQL**:
2871
+ ```graphql
2872
+ fragment Customer on Customer {
2873
+ id
2874
+ email
2875
+ firstName
2876
+ lastName
2877
+ displayName
2878
+ phone
2879
+ isEmailVerified
2880
+ emailMarketing
2881
+ tags
2882
+ customerType
2883
+ companyName
2884
+ taxId
2885
+ vatNumber
2886
+ regon
2887
+ defaultAddress {
2888
+ ...MailingAddress
2889
+ }
2890
+ orderCount
2891
+ totalSpent {
2892
+ ...Money
2893
+ }
2894
+ createdAt
2895
+ updatedAt
2896
+ }
2897
+ ```
2898
+
2899
+ ### Fragment: `Order` on `Order`
2900
+
2901
+ **Section**: Customer
2902
+
2903
+ **Description**: Order summary — number, totals (cost / tax / shipping), payment + fulfillment status, shipping address, item count, lifecycle timestamps. Spread on the order list and order detail pages. Line items are not included here.
2904
+
2905
+ **Uses fragments**: `MailingAddress`, `Money`
2906
+
2907
+ **GraphQL**:
2908
+ ```graphql
2909
+ fragment Order on Order {
2910
+ id
2911
+ orderNumber
2912
+ totals {
2913
+ total {
2914
+ ...Money
2915
+ }
2916
+ subtotal {
2917
+ ...Money
2918
+ }
2919
+ totalTax {
2920
+ ...Money
2921
+ }
2922
+ totalShipping {
2923
+ ...Money
2924
+ }
2925
+ }
2926
+ status
2927
+ paymentStatus
2928
+ fulfillmentStatus
2929
+ processedAt
2930
+ confirmedAt
2931
+ cancelledAt
2932
+ expiredAt
2933
+ shippingAddress {
2934
+ ...MailingAddress
2935
+ }
2936
+ itemCount
2937
+ }
2938
+ ```
2939
+
2940
+ ### Fragment: `CartCost` on `CartCost`
2941
+
2942
+ **Section**: Cart
2943
+
2944
+ **Description**: Cart-level totals — subtotal, total, tax, duty, checkout charge. Spread inside the `Cart` fragment.
2945
+
2946
+ **Uses fragments**: `Money`
2947
+
2948
+ **GraphQL**:
2949
+ ```graphql
2950
+ fragment CartCost on CartCost {
2951
+ total {
2952
+ ...Money
2953
+ }
2954
+ subtotal {
2955
+ ...Money
2956
+ }
2957
+ totalTax {
2958
+ ...Money
2959
+ }
2960
+ totalDuty {
2961
+ ...Money
2962
+ }
2963
+ checkoutCharge {
2964
+ ...Money
2965
+ }
2966
+ }
2967
+ ```
2968
+
2969
+ ### Fragment: `CartLineCost` on `CartLineCost`
2970
+
2971
+ **Section**: Cart
2972
+
2973
+ **Description**: Per-line cost breakdown — unit price, line subtotal/total, compare-at unit price (for showing strikethroughs). Spread inside `CartLine`.
2974
+
2975
+ **Uses fragments**: `Money`
2976
+
2977
+ **GraphQL**:
2978
+ ```graphql
2979
+ fragment CartLineCost on CartLineCost {
2980
+ pricePerUnit {
2981
+ ...Money
2982
+ }
2983
+ subtotal {
2984
+ ...Money
2985
+ }
2986
+ total {
2987
+ ...Money
2988
+ }
2989
+ compareAtPricePerUnit {
2990
+ ...Money
2991
+ }
2992
+ }
2993
+ ```
2994
+
2995
+ ### Fragment: `AttributeSelection` on `AttributeSelection`
2996
+
2997
+ **Section**: Cart
2998
+
2999
+ **Description**: Typed snapshot of a customer-filled product attribute (configurator selection) on a cart line. Includes the chosen option / text value, fillingMode, billingMode, surcharge, tax class, and any linked variant. Distinct from `CartLine.attributes` which holds raw key-value line item properties (free-form, untyped).
3000
+
3001
+ **GraphQL**:
3002
+ ```graphql
3003
+ fragment AttributeSelection on AttributeSelection {
3004
+ attributeDefinitionId
3005
+ attributeName
3006
+ type
3007
+ fillingMode
3008
+ billingMode
3009
+ optionId
3010
+ optionLabel
3011
+ optionIds
3012
+ textValue
3013
+ surchargeAmount
3014
+ surchargeType
3015
+ taxClassId
3016
+ linkedVariantId
3017
+ }
3018
+ ```
3019
+
3020
+ ### Fragment: `CartLine` on `CartLine`
3021
+
3022
+ **Section**: Cart
3023
+
3024
+ **Description**: A single line in the cart — variant + quantity + per-line cost, plus dual attribute storage: `attributes[]` for free-form key-value properties (gift wrap, engraving notes), `attributeSelections[]` for typed configurator answers.
3025
+
3026
+ **Uses fragments**: `AttributeSelection`, `CartLineCost`, `ProductVariant`
3027
+
3028
+ **GraphQL**:
3029
+ ```graphql
3030
+ fragment CartLine on CartLine {
3031
+ id
3032
+ quantity
3033
+ variant {
3034
+ ...ProductVariant
3035
+ }
3036
+ cost {
3037
+ ...CartLineCost
3038
+ }
3039
+ attributes {
3040
+ key
3041
+ value
3042
+ }
3043
+ attributeSelections {
3044
+ ...AttributeSelection
3045
+ }
3046
+ productId
3047
+ productTitle
3048
+ productHandle
3049
+ productType
3050
+ }
3051
+ ```
3052
+
3053
+ ### Fragment: `CartBuyerIdentity` on `CartBuyerIdentity`
3054
+
3055
+ **Section**: Cart
3056
+
3057
+ **Description**: Buyer identity associated with the cart. Note: only `customerId` is currently persisted server-side; `email`, `phone`, `countryCode` are accepted in the input but ignored.
3058
+
3059
+ **GraphQL**:
3060
+ ```graphql
3061
+ fragment CartBuyerIdentity on CartBuyerIdentity {
3062
+ email
3063
+ phone
3064
+ countryCode
3065
+ }
3066
+ ```
3067
+
3068
+ ### Fragment: `CartDiscountCode` on `CartDiscountCode`
3069
+
3070
+ **Section**: Cart
3071
+
3072
+ **Description**: Discount code applied to the cart with its `isApplicable` flag — false means the code was kept on the cart but does not currently qualify (e.g. minimum spend not reached).
3073
+
3074
+ **GraphQL**:
3075
+ ```graphql
3076
+ fragment CartDiscountCode on CartDiscountCode {
3077
+ code
3078
+ isApplicable
3079
+ }
3080
+ ```
3081
+
3082
+ ### Fragment: `CartDiscountAllocation` on `CartDiscountAllocation`
3083
+
3084
+ **Section**: Cart
3085
+
3086
+ **Description**: Allocation of a discount code to the cart total — pairs the code with the actual money discounted. Use to show "saved X with code Y" in cart UI.
3087
+
3088
+ **Uses fragments**: `Money`
3089
+
3090
+ **GraphQL**:
3091
+ ```graphql
3092
+ fragment CartDiscountAllocation on CartDiscountAllocation {
3093
+ discountCode
3094
+ amount {
3095
+ ...Money
3096
+ }
3097
+ }
3098
+ ```
3099
+
3100
+ ### Fragment: `Cart` on `Cart`
3101
+
3102
+ **Section**: Cart
3103
+
3104
+ **Description**: Full cart shape — totals, line items (paginated up to 100), buyer identity, applied discount codes + allocations, note, custom attributes. Spread on the cart drawer / cart page; refetch after every cart mutation.
3105
+
3106
+ **Uses fragments**: `CartBuyerIdentity`, `CartCost`, `CartDiscountAllocation`, `CartDiscountCode`, `CartLine`, `PageInfo`
3107
+
3108
+ **GraphQL**:
3109
+ ```graphql
3110
+ fragment Cart on Cart {
3111
+ id
3112
+ checkoutUrl
3113
+ totalQuantity
3114
+ cost {
3115
+ ...CartCost
3116
+ }
3117
+ lines(first: 100) {
3118
+ edges {
3119
+ cursor
3120
+ node {
3121
+ ... on CartLine {
3122
+ ...CartLine
3123
+ }
3124
+ }
3125
+ }
3126
+ nodes {
3127
+ ... on CartLine {
3128
+ ...CartLine
3129
+ }
3130
+ }
3131
+ pageInfo {
3132
+ ...PageInfo
3133
+ }
3134
+ totalCount
3135
+ }
3136
+ buyerIdentity {
3137
+ ...CartBuyerIdentity
3138
+ }
3139
+ discountCodes {
3140
+ ...CartDiscountCode
3141
+ }
3142
+ discountAllocations {
3143
+ ...CartDiscountAllocation
3144
+ }
3145
+ note
3146
+ attributes {
3147
+ key
3148
+ value
3149
+ }
3150
+ createdAt
3151
+ updatedAt
3152
+ }
3153
+ ```
3154
+
3155
+ ### Fragment: `ShopColors` on `ShopColors`
3156
+
3157
+ **Section**: Shop
3158
+
3159
+ **Description**: Shop branding colors — primary, secondary, accent, background, text. Use to theme the storefront from server-side config.
3160
+
3161
+ **GraphQL**:
3162
+ ```graphql
3163
+ fragment ShopColors on ShopColors {
3164
+ primary
3165
+ secondary
3166
+ accent
3167
+ background
3168
+ text
3169
+ }
3170
+ ```
3171
+
3172
+ ### Fragment: `ShopFonts` on `ShopFonts`
3173
+
3174
+ **Section**: Shop
3175
+
3176
+ **Description**: Shop branding fonts — body (`primary`) and `heading` font families.
3177
+
3178
+ **GraphQL**:
3179
+ ```graphql
3180
+ fragment ShopFonts on ShopFonts {
3181
+ primary
3182
+ heading
3183
+ }
3184
+ ```
3185
+
3186
+ ### Fragment: `SocialLinks` on `SocialLinks`
3187
+
3188
+ **Section**: Shop
3189
+
3190
+ **Description**: Social media links configured for the shop. Spread on the footer.
3191
+
3192
+ **GraphQL**:
3193
+ ```graphql
3194
+ fragment SocialLinks on SocialLinks {
3195
+ facebook
3196
+ instagram
3197
+ twitter
3198
+ youtube
3199
+ tiktok
3200
+ linkedin
3201
+ pinterest
3202
+ }
3203
+ ```
3204
+
3205
+ ### Fragment: `ShopAddress` on `ShopAddress`
3206
+
3207
+ **Section**: Shop
3208
+
3209
+ **Description**: Shop's physical / business address. Use on the contact page and footer.
3210
+
3211
+ **GraphQL**:
3212
+ ```graphql
3213
+ fragment ShopAddress on ShopAddress {
3214
+ streetLine1
3215
+ streetLine2
3216
+ city
3217
+ state
3218
+ postalCode
3219
+ country
3220
+ }
3221
+ ```
3222
+
3223
+ ### Fragment: `BusinessHour` on `BusinessHour`
3224
+
3225
+ **Section**: Shop
3226
+
3227
+ **Description**: One day's business hours window. Spread inside `Shop.businessHours[]`.
3228
+
3229
+ **GraphQL**:
3230
+ ```graphql
3231
+ fragment BusinessHour on BusinessHour {
3232
+ day
3233
+ openTime
3234
+ closeTime
3235
+ isClosed
3236
+ }
3237
+ ```
3238
+
3239
+ ### Fragment: `ShopBranding` on `ShopBranding`
3240
+
3241
+ **Section**: Shop
3242
+
3243
+ **Description**: Full shop branding bundle — logo + favicon + colors + fonts + social links. Spread inside the `Shop` fragment.
3244
+
3245
+ **Uses fragments**: `Image`, `ShopColors`, `ShopFonts`, `SocialLinks`
3246
+
3247
+ **GraphQL**:
3248
+ ```graphql
3249
+ fragment ShopBranding on ShopBranding {
3250
+ logo {
3251
+ ...Image
3252
+ }
3253
+ favicon {
3254
+ ...Image
3255
+ }
3256
+ colors {
3257
+ ...ShopColors
3258
+ }
3259
+ fonts {
3260
+ ...ShopFonts
3261
+ }
3262
+ socialLinks {
3263
+ ...SocialLinks
3264
+ }
3265
+ }
3266
+ ```
3267
+
3268
+ ### Fragment: `BotProtectionProvider` on `BotProtectionProviderInfo`
3269
+
3270
+ **Section**: Shop
3271
+
3272
+ **Description**: Bot-protection provider config (provider name, site key, script URL) for storefront-side challenge widgets.
3273
+
3274
+ **GraphQL**:
3275
+ ```graphql
3276
+ fragment BotProtectionProvider on BotProtectionProviderInfo {
3277
+ provider
3278
+ siteKey
3279
+ scriptUrl
3280
+ }
3281
+ ```
3282
+
3283
+ ### Fragment: `BotProtection` on `BotProtectionInfo`
3284
+
3285
+ **Section**: Shop
3286
+
3287
+ **Description**: Bot-protection setup for the shop — primary + fallback provider, and the list of `protectedOperations` (mutation names that require a challenge token). Spread inside `Shop`.
3288
+
3289
+ **Uses fragments**: `BotProtectionProvider`
3290
+
3291
+ **GraphQL**:
3292
+ ```graphql
3293
+ fragment BotProtection on BotProtectionInfo {
3294
+ primary {
3295
+ ...BotProtectionProvider
3296
+ }
3297
+ fallback {
3298
+ ...BotProtectionProvider
3299
+ }
3300
+ protectedOperations
3301
+ }
3302
+ ```
3303
+
3304
+ ### Fragment: `Shop` on `Shop`
3305
+
3306
+ **Section**: Shop
3307
+
3308
+ **Description**: The shop itself — name, primary domain, currencies + locales, branding, contact info, business hours, bot-protection config. Spread on the root `shop` query; cache for the session.
3309
+
3310
+ **Uses fragments**: `BotProtection`, `BusinessHour`, `Image`, `ShopAddress`, `ShopBranding`
3311
+
3312
+ **GraphQL**:
3313
+ ```graphql
3314
+ fragment Shop on Shop {
3315
+ id
3316
+ name
3317
+ description
3318
+ primaryDomain {
3319
+ host
3320
+ url
3321
+ isSslEnabled
3322
+ }
3323
+ currencyCode
3324
+ supportedCurrencies
3325
+ paymentCurrencies
3326
+ defaultLanguage
3327
+ supportedLanguages
3328
+ logo {
3329
+ ...Image
3330
+ }
3331
+ contactEmail
3332
+ contactPhone
3333
+ address {
3334
+ ...ShopAddress
3335
+ }
3336
+ businessHours {
3337
+ ...BusinessHour
3338
+ }
3339
+ branding {
3340
+ ...ShopBranding
3341
+ }
3342
+ botProtection {
3343
+ ...BotProtection
3344
+ }
3345
+ }
3346
+ ```
3347
+
3348
+ ### Fragment: `PaymentMethod` on `PaymentMethod`
3349
+
3350
+ **Section**: Payment Methods
3351
+
3352
+ **Description**: Single payment method enabled for the shop — type (CARD / BANK_TRANSFER / BLIK / PAYPAL / APPLE_PAY / GOOGLE_PAY / CASH_ON_DELIVERY / OTHER), provider, icon, supported currencies, default flag, sort position. Spread on the checkout payment step.
3353
+
3354
+ **GraphQL**:
3355
+ ```graphql
3356
+ fragment PaymentMethod on PaymentMethod {
3357
+ id
3358
+ name
3359
+ provider
3360
+ type
3361
+ icon
3362
+ description
3363
+ isDefault
3364
+ supportedCurrencies
3365
+ position
3366
+ }
3367
+ ```
3368
+
3369
+ ### Fragment: `AvailablePaymentMethods` on `AvailablePaymentMethods`
3370
+
3371
+ **Section**: Payment Methods
3372
+
3373
+ **Description**: Active payment methods list with the merchant's `defaultMethod`. Returned by the `availablePaymentMethods` query.
3374
+
3375
+ **Uses fragments**: `PaymentMethod`
3376
+
3377
+ **GraphQL**:
3378
+ ```graphql
3379
+ fragment AvailablePaymentMethods on AvailablePaymentMethods {
3380
+ methods {
3381
+ ...PaymentMethod
3382
+ }
3383
+ defaultMethod {
3384
+ ...PaymentMethod
3385
+ }
3386
+ }
3387
+ ```
3388
+
3389
+ ### Fragment: `ShippingRate` on `ShippingRate`
3390
+
3391
+ **Section**: Checkout
3392
+
3393
+ **Description**: Single shipping rate option — `handle` is the stable id you pass to `checkoutShippingLineUpdate`, plus title and price.
3394
+
3395
+ **Uses fragments**: `Money`
3396
+
3397
+ **GraphQL**:
3398
+ ```graphql
3399
+ fragment ShippingRate on ShippingRate {
3400
+ handle
3401
+ title
3402
+ price {
3403
+ ...Money
3404
+ }
3405
+ }
3406
+ ```
3407
+
3408
+ ### Fragment: `TaxLine` on `TaxLine`
3409
+
3410
+ **Section**: Checkout
3411
+
3412
+ **Description**: One tax line on the checkout — title, rate (decimal, e.g. 0.23 for 23%), computed amount. Spread on the order summary.
3413
+
3414
+ **Uses fragments**: `Money`
3415
+
3416
+ **GraphQL**:
3417
+ ```graphql
3418
+ fragment TaxLine on TaxLine {
3419
+ title
3420
+ rate
3421
+ price {
3422
+ ...Money
3423
+ }
3424
+ }
3425
+ ```
3426
+
3427
+ ### Fragment: `DiscountAffectedItem` on `DiscountAffectedItem`
3428
+
3429
+ **Section**: Checkout
3430
+
3431
+ **Description**: Item affected by a discount — the discounted product/variant with original + discounted prices and savings. Used on Buy-X-Get-Y promotions to highlight which items the discount applies to.
3432
+
3433
+ **Uses fragments**: `Money`
3434
+
3435
+ **GraphQL**:
3436
+ ```graphql
3437
+ fragment DiscountAffectedItem on DiscountAffectedItem {
3438
+ productId
3439
+ variantId
3440
+ title
3441
+ quantity
3442
+ originalPrice {
3443
+ ...Money
3444
+ }
3445
+ discountedPrice {
3446
+ ...Money
3447
+ }
3448
+ savings {
3449
+ ...Money
3450
+ }
3451
+ isFreeItem
3452
+ }
3453
+ ```
3454
+
3455
+ ### Fragment: `DiscountApplication` on `DiscountApplication`
3456
+
3457
+ **Section**: Checkout
3458
+
3459
+ **Description**: A discount currently applied to the checkout — code, type, value, plus BXGY (Buy X Get Y) metadata when applicable (`buyQuantity`, `getQuantity`, `getDiscountPercent`, `affectedItems`). Use to render a "Discounts applied" panel.
3460
+
3461
+ **Uses fragments**: `DiscountAffectedItem`, `Money`
3462
+
3463
+ **GraphQL**:
3464
+ ```graphql
3465
+ fragment DiscountApplication on DiscountApplication {
3466
+ code
3467
+ isApplicable
3468
+ type
3469
+ value {
3470
+ ...Money
3471
+ }
3472
+ title
3473
+ affectedItems {
3474
+ ...DiscountAffectedItem
3475
+ }
3476
+ buyQuantity
3477
+ getQuantity
3478
+ getDiscountPercent
3479
+ }
3480
+ ```
3481
+
3482
+ ### Fragment: `DiscountCode` on `DiscountCode`
3483
+
3484
+ **Section**: Checkout
3485
+
3486
+ **Description**: Lightweight discount code entry on the checkout — code + applicability flag.
3487
+
3488
+ **GraphQL**:
3489
+ ```graphql
3490
+ fragment DiscountCode on DiscountCode {
3491
+ code
3492
+ isApplicable
3493
+ }
3494
+ ```
3495
+
3496
+ ### Fragment: `CheckoutLineItem` on `CheckoutLineItem`
3497
+
3498
+ **Section**: Checkout
3499
+
3500
+ **Description**: Single line item in the checkout — variant snapshot, quantity, unit + total prices, image. Use on the order summary panel.
3501
+
3502
+ **Uses fragments**: `ImageThumbnail`, `Money`
3503
+
3504
+ **GraphQL**:
3505
+ ```graphql
3506
+ fragment CheckoutLineItem on CheckoutLineItem {
3507
+ id
3508
+ title
3509
+ variantTitle
3510
+ quantity
3511
+ pricePerUnit {
3512
+ ...Money
3513
+ }
3514
+ total {
3515
+ ...Money
3516
+ }
3517
+ variantId
3518
+ productId
3519
+ sku
3520
+ image {
3521
+ ...ImageThumbnail
3522
+ }
3523
+ }
3524
+ ```
3525
+
3526
+ ### Fragment: `AppliedGiftCard` on `AppliedGiftCard`
3527
+
3528
+ **Section**: Checkout
3529
+
3530
+ **Description**: Gift card applied to the checkout — `maskedCode` (first + last 4 chars), `lastCharacters` (for display matching), the amount applied to this checkout, and remaining balance after this order would complete.
3531
+
3532
+ **Uses fragments**: `Money`
3533
+
3534
+ **GraphQL**:
3535
+ ```graphql
3536
+ fragment AppliedGiftCard on AppliedGiftCard {
3537
+ maskedCode
3538
+ lastCharacters
3539
+ appliedAmount {
3540
+ ...Money
3541
+ }
3542
+ remainingBalance {
3543
+ ...Money
3544
+ }
3545
+ }
3546
+ ```
3547
+
3548
+ ### Fragment: `Checkout` on `Checkout`
3549
+
3550
+ **Section**: Checkout
3551
+
3552
+ **Description**: Full checkout shape — line items, shipping + billing addresses, selected + available shipping rates, available payment methods, applied discounts + gift cards, tax lines, totals (`cost`, `paymentDue`, `totalGiftCardAmount`). Spread on the checkout flow; refetch after every checkout mutation. `isReady` flips to true when the checkout has all required fields to call `checkoutComplete`.
3553
+
3554
+ **Uses fragments**: `AppliedGiftCard`, `CheckoutLineItem`, `DiscountApplication`, `DiscountCode`, `MailingAddress`, `Money`, `PaymentMethod`, `ShippingRate`, `TaxLine`
3555
+
3556
+ **GraphQL**:
3557
+ ```graphql
3558
+ fragment Checkout on Checkout {
3559
+ id
3560
+ isReady
3561
+ isCompleted
3562
+ completedOrderId
3563
+ webUrl
3564
+ lineItems {
3565
+ ...CheckoutLineItem
3566
+ }
3567
+ totalQuantity
3568
+ shippingAddress {
3569
+ ...MailingAddress
3570
+ }
3571
+ billingAddress {
3572
+ ...MailingAddress
3573
+ }
3574
+ shippingLine {
3575
+ ...ShippingRate
3576
+ }
3577
+ availableShippingRates {
3578
+ ...ShippingRate
3579
+ }
3580
+ shippingRatesReady
3581
+ email
3582
+ phone
3583
+ customerId
3584
+ discountCodes {
3585
+ ...DiscountCode
3586
+ }
3587
+ discountApplications {
3588
+ ...DiscountApplication
3589
+ }
3590
+ availablePaymentMethods {
3591
+ ...PaymentMethod
3592
+ }
3593
+ selectedPaymentMethodId
3594
+ cost {
3595
+ subtotal {
3596
+ ...Money
3597
+ }
3598
+ total {
3599
+ ...Money
3600
+ }
3601
+ totalTax {
3602
+ ...Money
3603
+ }
3604
+ totalShipping {
3605
+ ...Money
3606
+ }
3607
+ totalDiscounts {
3608
+ ...Money
3609
+ }
3610
+ }
3611
+ taxLines {
3612
+ ...TaxLine
3613
+ }
3614
+ appliedGiftCards {
3615
+ ...AppliedGiftCard
3616
+ }
3617
+ totalGiftCardAmount {
3618
+ ...Money
3619
+ }
3620
+ paymentDue {
3621
+ ...Money
3622
+ }
3623
+ currencyCode
3624
+ note
3625
+ createdAt
3626
+ updatedAt
3627
+ }
3628
+ ```
3629
+
3630
+ ### Fragment: `ShipmentEvent` on `ShipmentEvent`
3631
+
3632
+ **Section**: Shipments / Tracking
3633
+
3634
+ **Description**: One tracking event in the shipment timeline — status, description, location, timestamp. Spread on the tracking page timeline.
3635
+
3636
+ **GraphQL**:
3637
+ ```graphql
3638
+ fragment ShipmentEvent on ShipmentEvent {
3639
+ id
3640
+ status
3641
+ description
3642
+ location
3643
+ occurredAt
3644
+ providerEventCode
3645
+ }
3646
+ ```
3647
+
3648
+ ### Fragment: `ShipmentPackage` on `ShipmentPackage`
3649
+
3650
+ **Section**: Shipments / Tracking
3651
+
3652
+ **Description**: Physical package metadata (weight + dimensions). Used in the merchant's label generation; storefronts rarely render this directly.
3653
+
3654
+ **GraphQL**:
3655
+ ```graphql
3656
+ fragment ShipmentPackage on ShipmentPackage {
3657
+ weight
3658
+ length
3659
+ width
3660
+ height
3661
+ }
3662
+ ```
3663
+
3664
+ ### Fragment: `ShipmentItem` on `ShipmentItem`
3665
+
3666
+ **Section**: Shipments / Tracking
3667
+
3668
+ **Description**: One item in a shipment — title, variant, sku, quantity, image URL. Spread inside `Shipment.items[]` for the tracking page item list.
3669
+
3670
+ **GraphQL**:
3671
+ ```graphql
3672
+ fragment ShipmentItem on ShipmentItem {
3673
+ id
3674
+ title
3675
+ variantTitle
3676
+ sku
3677
+ quantity
3678
+ imageUrl
3679
+ }
3680
+ ```
3681
+
3682
+ ### Fragment: `Shipment` on `Shipment`
3683
+
3684
+ **Section**: Shipments / Tracking
3685
+
3686
+ **Description**: Full shipment shape — provider, tracking number + URL, label URL, status, ETA, recipient address, packages, items, full event timeline. Spread on the tracking page.
3687
+
3688
+ **Uses fragments**: `MailingAddress`, `ShipmentEvent`, `ShipmentItem`, `ShipmentPackage`
3689
+
3690
+ **GraphQL**:
3691
+ ```graphql
3692
+ fragment Shipment on Shipment {
3693
+ id
3694
+ orderId
3695
+ provider
3696
+ serviceCode
3697
+ trackingNumber
3698
+ trackingUrl
3699
+ labelUrl
3700
+ status
3701
+ estimatedDeliveryAt
3702
+ shippedAt
3703
+ deliveredAt
3704
+ recipientAddress {
3705
+ ...MailingAddress
3706
+ }
3707
+ packages {
3708
+ ...ShipmentPackage
3709
+ }
3710
+ items {
3711
+ ...ShipmentItem
3712
+ }
3713
+ events {
3714
+ ...ShipmentEvent
3715
+ }
3716
+ createdAt
3717
+ updatedAt
3718
+ }
3719
+ ```
3720
+
3721
+ ### Fragment: `ShipmentBasic` on `Shipment`
3722
+
3723
+ **Section**: Shipments / Tracking
3724
+
3725
+ **Description**: Lightweight shipment shape — id, tracking number + URL, status, dates. No items / events / address. Use for order summary cards or on the public `shipmentByTrackingNumber` query.
3726
+
3727
+ **GraphQL**:
3728
+ ```graphql
3729
+ fragment ShipmentBasic on Shipment {
3730
+ id
3731
+ orderId
3732
+ provider
3733
+ trackingNumber
3734
+ trackingUrl
3735
+ status
3736
+ estimatedDeliveryAt
3737
+ shippedAt
3738
+ deliveredAt
3739
+ createdAt
3740
+ }
3741
+ ```
3742
+
3743
+ ### Fragment: `ReturnShippingLabel` on `ReturnShippingLabel`
3744
+
3745
+ **Section**: Returns / RMA
3746
+
3747
+ **Description**: Return shipping label generated by the carrier — URL to download, carrier name, tracking number, expiry. Spread on the return detail page.
3748
+
3749
+ **GraphQL**:
3750
+ ```graphql
3751
+ fragment ReturnShippingLabel on ReturnShippingLabel {
3752
+ url
3753
+ carrier
3754
+ trackingNumber
3755
+ expiresAt
3756
+ }
3757
+ ```
3758
+
3759
+ ### Fragment: `ReturnItemPhoto` on `ReturnItemPhoto`
3760
+
3761
+ **Section**: Returns / RMA
3762
+
3763
+ **Description**: Photo evidence attached to a return item (e.g. damage photo). Spread inside `ReturnItem.photos[]`.
3764
+
3765
+ **GraphQL**:
3766
+ ```graphql
3767
+ fragment ReturnItemPhoto on ReturnItemPhoto {
3768
+ id
3769
+ url
3770
+ alt
3771
+ description
3772
+ createdAt
3773
+ }
3774
+ ```
3775
+
3776
+ ### Fragment: `ReturnItem` on `ReturnItem`
3777
+
3778
+ **Section**: Returns / RMA
3779
+
3780
+ **Description**: Single line in a return request — variant identity, quantity requested, reason, condition, photos, status, approved quantity (set by merchant after review). Spread inside `Return.items[]`.
3781
+
3782
+ **Uses fragments**: `Money`, `ReturnItemPhoto`
3783
+
3784
+ **GraphQL**:
3785
+ ```graphql
3786
+ fragment ReturnItem on ReturnItem {
3787
+ id
3788
+ variantId
3789
+ productTitle
3790
+ variantTitle
3791
+ sku
3792
+ imageUrl
3793
+ quantity
3794
+ reason
3795
+ condition
3796
+ unitPrice {
3797
+ ...Money
3798
+ }
3799
+ photos {
3800
+ ...ReturnItemPhoto
3801
+ }
3802
+ status
3803
+ approvedQuantity
3804
+ }
3805
+ ```
3806
+
3807
+ ### Fragment: `Return` on `Return`
3808
+
3809
+ **Section**: Returns / RMA
3810
+
3811
+ **Description**: Full return shape — number, parent order, status, reason, customer note, compensation type (`REFUND` / `STORE_CREDIT`), items, refund amount, shipping label, lifecycle timestamps. Spread on the return detail page.
3812
+
3813
+ **Uses fragments**: `Money`, `ReturnItem`, `ReturnShippingLabel`
3814
+
3815
+ **GraphQL**:
3816
+ ```graphql
3817
+ fragment Return on Return {
3818
+ id
3819
+ returnNumber
3820
+ orderId
3821
+ orderNumber
3822
+ status
3823
+ reason
3824
+ customerNote
3825
+ compensationType
3826
+ items {
3827
+ ...ReturnItem
3828
+ }
3829
+ refundAmount {
3830
+ ...Money
3831
+ }
3832
+ shippingLabel {
3833
+ ...ReturnShippingLabel
3834
+ }
3835
+ requestedAt
3836
+ approvedAt
3837
+ receivedAt
3838
+ refundedAt
3839
+ completedAt
3840
+ createdAt
3841
+ updatedAt
3842
+ }
3843
+ ```
3844
+
3845
+ ### Fragment: `ReturnReasonOption` on `ReturnReasonOption`
3846
+
3847
+ **Section**: Returns / RMA
3848
+
3849
+ **Description**: Single option in the return reasons dropdown — `value` enum, human-readable `label`, optional `description`. Returned by `returnReasons` query.
3850
+
3851
+ **GraphQL**:
3852
+ ```graphql
3853
+ fragment ReturnReasonOption on ReturnReasonOption {
3854
+ value
3855
+ label
3856
+ description
3857
+ }
3858
+ ```
3859
+
3860
+ ### Fragment: `GiftCard` on `GiftCard`
3861
+
3862
+ **Section**: Gift Cards
3863
+
3864
+ **Description**: Gift card detail — masked code (first + last 4 chars), status, initial + remaining balance, expiry, recipient/message. Returned by `giftCard($code)` query.
3865
+
3866
+ **Uses fragments**: `Money`
3867
+
3868
+ **GraphQL**:
3869
+ ```graphql
3870
+ fragment GiftCard on GiftCard {
3871
+ id
3872
+ maskedCode
3873
+ status
3874
+ initialAmount {
3875
+ ...Money
3876
+ }
3877
+ balance {
3878
+ ...Money
3879
+ }
3880
+ expiresAt
3881
+ recipientName
3882
+ message
3883
+ createdAt
3884
+ }
3885
+ ```
3886
+
3887
+ ### Fragment: `GiftCardError` on `GiftCardError`
3888
+
3889
+ **Section**: Gift Cards
3890
+
3891
+ **Description**: Validation error on a gift card check — code (`NOT_FOUND` / `DISABLED` / `ALREADY_USED` / `EXPIRED` / `INSUFFICIENT_BALANCE`) + human message.
3892
+
3893
+ **GraphQL**:
3894
+ ```graphql
3895
+ fragment GiftCardError on GiftCardError {
3896
+ code
3897
+ message
3898
+ }
3899
+ ```
3900
+
3901
+ ### Fragment: `GiftCardValidation` on `GiftCardValidation`
3902
+
3903
+ **Section**: Gift Cards
3904
+
3905
+ **Description**: Result of `giftCardValidate($code, $amount)` — `isValid` flag, available balance, optional `error` (when invalid), and the matched gift card (when found). Use to inline-validate gift card inputs before applying.
3906
+
3907
+ **Uses fragments**: `GiftCard`, `GiftCardError`, `Money`
3908
+
3909
+ **GraphQL**:
3910
+ ```graphql
3911
+ fragment GiftCardValidation on GiftCardValidation {
3912
+ isValid
3913
+ availableBalance {
3914
+ ...Money
3915
+ }
3916
+ error {
3917
+ ...GiftCardError
3918
+ }
3919
+ giftCard {
3920
+ ...GiftCard
3921
+ }
3922
+ }
3923
+ ```
3924
+
3925
+ ### Fragment: `ShippingCarrier` on `ShippingCarrier`
3926
+
3927
+ **Section**: Shipping Methods
3928
+
3929
+ **Description**: Carrier offering the shipping method — id, display name, logo URL, internal service code.
3930
+
3931
+ **GraphQL**:
3932
+ ```graphql
3933
+ fragment ShippingCarrier on ShippingCarrier {
3934
+ id
3935
+ name
3936
+ logoUrl
3937
+ serviceCode
3938
+ }
3939
+ ```
3940
+
3941
+ ### Fragment: `DeliveryEstimate` on `DeliveryEstimate`
3942
+
3943
+ **Section**: Shipping Methods
3944
+
3945
+ **Description**: Estimated delivery window — `minDays` to `maxDays` plus a human-readable description (e.g. "2-4 business days").
3946
+
3947
+ **GraphQL**:
3948
+ ```graphql
3949
+ fragment DeliveryEstimate on DeliveryEstimate {
3950
+ minDays
3951
+ maxDays
3952
+ description
3953
+ }
3954
+ ```
3955
+
3956
+ ### Fragment: `FreeShippingProgress` on `FreeShippingProgress`
3957
+
3958
+ **Section**: Shipping Methods
3959
+
3960
+ **Description**: Free-shipping progress info — `qualifies` flag, current cart amount, threshold, remaining to qualify, percent progress, and a localized message ("Add 20 PLN more for free shipping"). Use to render a free-shipping progress bar in the cart drawer.
3961
+
3962
+ **Uses fragments**: `Money`
3963
+
3964
+ **GraphQL**:
3965
+ ```graphql
3966
+ fragment FreeShippingProgress on FreeShippingProgress {
3967
+ qualifies
3968
+ currentAmount {
3969
+ ...Money
3970
+ }
3971
+ threshold {
3972
+ ...Money
3973
+ }
3974
+ remaining {
3975
+ ...Money
3976
+ }
3977
+ progressPercent
3978
+ message
3979
+ }
3980
+ ```
3981
+
3982
+ ### Fragment: `AvailableShippingMethod` on `AvailableShippingMethod`
3983
+
3984
+ **Section**: Shipping Methods
3985
+
3986
+ **Description**: One shipping method offered for the destination + cart — id, name, carrier, price, free-shipping progress, estimated delivery, sort order. Spread on the checkout shipping step.
3987
+
3988
+ **Uses fragments**: `DeliveryEstimate`, `FreeShippingProgress`, `Money`, `ShippingCarrier`
3989
+
3990
+ **GraphQL**:
3991
+ ```graphql
3992
+ fragment AvailableShippingMethod on AvailableShippingMethod {
3993
+ id
3994
+ name
3995
+ description
3996
+ carrier {
3997
+ ...ShippingCarrier
3998
+ }
3999
+ price {
4000
+ ...Money
4001
+ }
4002
+ isFree
4003
+ estimatedDelivery {
4004
+ ...DeliveryEstimate
4005
+ }
4006
+ freeShippingProgress {
4007
+ ...FreeShippingProgress
4008
+ }
4009
+ sortOrder
4010
+ }
4011
+ ```
4012
+
4013
+ ### Fragment: `AttributeSwatch` on `AttributeSwatch`
4014
+
4015
+ **Section**: Attribute Filters
4016
+
4017
+ **Description**: Visual swatch for an attribute filter value — color hex (for color filters) or image URL (for pattern/material swatches).
4018
+
4019
+ **GraphQL**:
4020
+ ```graphql
4021
+ fragment AttributeSwatch on AttributeSwatch {
4022
+ colorHex
4023
+ imageUrl
4024
+ }
4025
+ ```
4026
+
4027
+ ### Fragment: `AttributeRangeBounds` on `AttributeRangeBounds`
4028
+
4029
+ **Section**: Attribute Filters
4030
+
4031
+ **Description**: Numeric range bounds for slider-style filters — min, max, currency code (when relevant).
4032
+
4033
+ **GraphQL**:
4034
+ ```graphql
4035
+ fragment AttributeRangeBounds on AttributeRangeBounds {
4036
+ min
4037
+ max
4038
+ currencyCode
4039
+ }
4040
+ ```
4041
+
4042
+ ### Fragment: `AttributeFilterValue` on `AttributeFilterValue`
4043
+
4044
+ **Section**: Attribute Filters
4045
+
4046
+ **Description**: One discrete value in a filterable attribute (e.g. "Red" for the Color filter). Includes `productCount` in the current listing context (for "(12)" badges next to filter values), optional swatch and price modifier.
4047
+
4048
+ **Uses fragments**: `AttributeSwatch`, `Money`
4049
+
4050
+ **GraphQL**:
4051
+ ```graphql
4052
+ fragment AttributeFilterValue on AttributeFilterValue {
4053
+ id
4054
+ value
4055
+ label
4056
+ productCount
4057
+ swatch {
4058
+ ...AttributeSwatch
4059
+ }
4060
+ priceModifier {
4061
+ ...Money
4062
+ }
4063
+ sortOrder
4064
+ }
4065
+ ```
4066
+
4067
+ ### Fragment: `AttributeDefinition` on `AttributeDefinition`
4068
+
4069
+ **Section**: Attribute Filters
4070
+
4071
+ **Description**: Filterable attribute exposed on the storefront — name, type (SELECT / CHECKBOX / SLIDER / etc.), filterability flags, display order, and either discrete `filterValues[]` or numeric `rangeBounds`. Spread inside `availableFilters.attributes[]`.
4072
+
4073
+ **Uses fragments**: `AttributeFilterValue`, `AttributeRangeBounds`
4074
+
4075
+ **GraphQL**:
4076
+ ```graphql
4077
+ fragment AttributeDefinition on AttributeDefinition {
4078
+ id
4079
+ name
4080
+ handle
4081
+ type
4082
+ isFilterable
4083
+ isVisible
4084
+ displayOrder
4085
+ filterValues {
4086
+ ...AttributeFilterValue
4087
+ }
4088
+ rangeBounds {
4089
+ ...AttributeRangeBounds
4090
+ }
4091
+ }
4092
+ ```
4093
+
4094
+ ### Fragment: `PriceRangeFilter` on `PriceRange`
4095
+
4096
+ **Section**: Attribute Filters
4097
+
4098
+ **Description**: Min / max price range across products in the current listing context. Use to bound the price slider.
4099
+
4100
+ **Uses fragments**: `Money`
4101
+
4102
+ **GraphQL**:
4103
+ ```graphql
4104
+ fragment PriceRangeFilter on PriceRange {
4105
+ min {
4106
+ ...Money
4107
+ }
4108
+ max {
4109
+ ...Money
4110
+ }
4111
+ }
4112
+ ```
4113
+
4114
+ ### Fragment: `CategoryFilterOption` on `CategoryFilterOption`
4115
+
4116
+ **Section**: Attribute Filters
4117
+
4118
+ **Description**: One category option in the categories filter — id, name, slug, count of products in this category within the current listing context, level + parentId for tree rendering.
4119
+
4120
+ **GraphQL**:
4121
+ ```graphql
4122
+ fragment CategoryFilterOption on CategoryFilterOption {
4123
+ id
4124
+ name
4125
+ slug
4126
+ productCount
4127
+ level
4128
+ parentId
4129
+ }
4130
+ ```
4131
+
4132
+ ### Fragment: `AvailableFilters` on `AvailableFilters`
4133
+
4134
+ **Section**: Attribute Filters
4135
+
4136
+ **Description**: Full result of the `availableFilters($input)` query — attribute filters, price range, category filters, count of currently active filters, total products matching the context. Spread on listing pages to render filter sidebars.
4137
+
4138
+ **Uses fragments**: `AttributeDefinition`, `CategoryFilterOption`, `PriceRangeFilter`
4139
+
4140
+ **GraphQL**:
4141
+ ```graphql
4142
+ fragment AvailableFilters on AvailableFilters {
4143
+ attributes {
4144
+ ...AttributeDefinition
4145
+ }
4146
+ priceRange {
4147
+ ...PriceRangeFilter
4148
+ }
4149
+ categories {
4150
+ ...CategoryFilterOption
4151
+ }
4152
+ activeFilterCount
4153
+ totalProducts
4154
+ }
4155
+ ```
4156
+
4157
+ ### Fragment: `LoyaltyPageInfo` on `LoyaltyPageInfo`
4158
+
4159
+ **Section**: Loyalty Program
4160
+
4161
+ **Description**: Page metadata for the loyalty transactions connection (separate type from generic `PageInfo` for legacy reasons; identical shape).
4162
+
4163
+ **GraphQL**:
4164
+ ```graphql
4165
+ fragment LoyaltyPageInfo on LoyaltyPageInfo {
4166
+ hasNextPage
4167
+ hasPreviousPage
4168
+ startCursor
4169
+ endCursor
4170
+ }
4171
+ ```
4172
+
4173
+ ### Fragment: `LoyaltyTier` on `LoyaltyTier`
4174
+
4175
+ **Section**: Loyalty Program
4176
+
4177
+ **Description**: Loyalty tier definition — name, type enum (`BRONZE` / `SILVER` / `GOLD` / `PLATINUM` / `DIAMOND`), entry threshold (`minPoints` and/or `minAnnualSpend`), points multiplier, custom benefits list. Returned by `loyaltyTiers` and embedded in member tier data.
4178
+
4179
+ **Uses fragments**: `Money`
4180
+
4181
+ **GraphQL**:
4182
+ ```graphql
4183
+ fragment LoyaltyTier on LoyaltyTier {
4184
+ id
4185
+ name
4186
+ type
4187
+ minPoints
4188
+ minAnnualSpend {
4189
+ ...Money
4190
+ }
4191
+ pointsMultiplier
4192
+ customBenefits {
4193
+ name
4194
+ description
4195
+ icon
4196
+ }
4197
+ }
4198
+ ```
4199
+
4200
+ ### Fragment: `LoyaltyPointsSummary` on `LoyaltyPointsSummary`
4201
+
4202
+ **Section**: Loyalty Program
4203
+
4204
+ **Description**: Customer's points breakdown — total earned, currently spendable, pending (not yet vested), redeemed lifetime, expired lifetime, expiring soon, and the next expiry date. Spread inside `LoyaltyMember`.
4205
+
4206
+ **GraphQL**:
4207
+ ```graphql
4208
+ fragment LoyaltyPointsSummary on LoyaltyPointsSummary {
4209
+ totalPoints
4210
+ currentPoints
4211
+ pendingPoints
4212
+ redeemedPoints
4213
+ expiredPoints
4214
+ expiringPoints
4215
+ nextExpiryDate
4216
+ }
4217
+ ```
4218
+
4219
+ ### Fragment: `TierProgress` on `TierProgress`
4220
+
4221
+ **Section**: Loyalty Program
4222
+
4223
+ **Description**: Customer's progress toward the next tier — current tier, next tier, points + spend remaining, percent progress. Use to render the "X points to GOLD" tracker.
4224
+
4225
+ **Uses fragments**: `LoyaltyTier`, `Money`
4226
+
4227
+ **GraphQL**:
4228
+ ```graphql
4229
+ fragment TierProgress on TierProgress {
4230
+ currentTier {
4231
+ ...LoyaltyTier
4232
+ }
4233
+ nextTier {
4234
+ ...LoyaltyTier
4235
+ }
4236
+ pointsToNextTier
4237
+ progressPercent
4238
+ spendToNextTier {
4239
+ ...Money
4240
+ }
4241
+ }
4242
+ ```
4243
+
4244
+ ### Fragment: `LoyaltyMember` on `LoyaltyMember`
4245
+
4246
+ **Section**: Loyalty Program
4247
+
4248
+ **Description**: Customer's loyalty membership — points summary, current tier, tier progress, annual spend, last activity. Returned by `loyaltyMember` (null if not enrolled).
4249
+
4250
+ **Uses fragments**: `LoyaltyPointsSummary`, `LoyaltyTier`, `Money`, `TierProgress`
4251
+
4252
+ **GraphQL**:
4253
+ ```graphql
4254
+ fragment LoyaltyMember on LoyaltyMember {
4255
+ id
4256
+ customerId
4257
+ points {
4258
+ ...LoyaltyPointsSummary
4259
+ }
4260
+ tier {
4261
+ ...LoyaltyTier
4262
+ }
4263
+ tierProgress {
4264
+ ...TierProgress
4265
+ }
4266
+ annualSpend {
4267
+ ...Money
4268
+ }
4269
+ lastActivityAt
4270
+ enrolledAt
4271
+ }
4272
+ ```
4273
+
4274
+ ### Fragment: `LoyaltyTransaction` on `LoyaltyTransaction`
4275
+
4276
+ **Section**: Loyalty Program
4277
+
4278
+ **Description**: One row in the loyalty points history — type (`EARN_PURCHASE` / `EARN_SIGNUP` / `EARN_REFERRAL` / `EARN_REVIEW` / `EARN_BIRTHDAY` / `EARN_BONUS` / `REDEEM` / `EXPIRE` / `ADJUST` / `REFUND_REVERSAL`), points delta, balance after, source order, expiry. Spread on the loyalty dashboard transaction list.
4279
+
4280
+ **GraphQL**:
4281
+ ```graphql
4282
+ fragment LoyaltyTransaction on LoyaltyTransaction {
4283
+ id
4284
+ type
4285
+ points
4286
+ balanceAfter
4287
+ orderId
4288
+ description
4289
+ expiresAt
4290
+ createdAt
4291
+ }
4292
+ ```
4293
+
4294
+ ### Fragment: `LoyaltyReward` on `LoyaltyReward`
4295
+
4296
+ **Section**: Loyalty Program
4297
+
4298
+ **Description**: Reward customers can redeem — name, type, points cost, discount value (% or fixed), tier requirement, availability flag, remaining redemptions. Spread on the rewards page.
4299
+
4300
+ **Uses fragments**: `ImageCard`, `LoyaltyTier`, `Money`
4301
+
4302
+ **GraphQL**:
4303
+ ```graphql
4304
+ fragment LoyaltyReward on LoyaltyReward {
4305
+ id
4306
+ name
4307
+ slug
4308
+ type
4309
+ pointsCost
4310
+ discountPercent
4311
+ discountAmount {
4312
+ ...Money
4313
+ }
4314
+ description
4315
+ image {
4316
+ ...ImageCard
4317
+ }
4318
+ isAvailable
4319
+ tierRequired {
4320
+ ...LoyaltyTier
4321
+ }
4322
+ redemptionsRemaining
4323
+ }
4324
+ ```
4325
+
4326
+ ### Fragment: `PointsEstimate` on `PointsEstimate`
4327
+
4328
+ **Section**: Loyalty Program
4329
+
4330
+ **Description**: Estimated points for an order — base points, bonus points (tier multiplier applied), total, multiplier used. Returned by `estimatePoints($orderTotal)` for "Earn X points" UI on cart/checkout.
4331
+
4332
+ **GraphQL**:
4333
+ ```graphql
4334
+ fragment PointsEstimate on PointsEstimate {
4335
+ basePoints
4336
+ bonusPoints
4337
+ totalPoints
4338
+ multiplier
4339
+ }
4340
+ ```
4341
+
4342
+ ### Fragment: `LoyaltyAction` on `LoyaltyAction`
4343
+
4344
+ **Section**: Loyalty Program
4345
+
4346
+ **Description**: One earn action configured in the loyalty program — type (`PURCHASE` / `SIGNUP` / `REFERRAL` / `REVIEW` / `BIRTHDAY`), enabled flag, points granted.
4347
+
4348
+ **GraphQL**:
4349
+ ```graphql
4350
+ fragment LoyaltyAction on LoyaltyAction {
4351
+ type
4352
+ isEnabled
4353
+ points
4354
+ }
4355
+ ```
4356
+
4357
+ ### Fragment: `LoyaltySettings` on `LoyaltySettings`
4358
+
4359
+ **Section**: Loyalty Program
4360
+
4361
+ **Description**: Loyalty program configuration — `isEnabled` (use to gate UI rendering), points name (e.g. "stars"), earn rate, expiry policy, available actions, referral settings. Returned by `loyaltySettings`.
4362
+
4363
+ **Uses fragments**: `LoyaltyAction`
4364
+
4365
+ **GraphQL**:
4366
+ ```graphql
4367
+ fragment LoyaltySettings on LoyaltySettings {
4368
+ isEnabled
4369
+ pointsName
4370
+ pointsPerCurrency
4371
+ pointsExpiryMonths
4372
+ availableActions {
4373
+ ...LoyaltyAction
4374
+ }
4375
+ referralEnabled
4376
+ referralPoints
4377
+ referralBonusPoints
4378
+ }
4379
+ ```
4380
+
4381
+ ### Fragment: `ReferralStats` on `ReferralStats`
4382
+
4383
+ **Section**: Loyalty Program
4384
+
4385
+ **Description**: Customer referral statistics — code, share URL, counts of referred / completed / pending, lifetime points earned. Returned by `referralStats`.
4386
+
4387
+ **GraphQL**:
4388
+ ```graphql
4389
+ fragment ReferralStats on ReferralStats {
4390
+ referralCode
4391
+ shareUrl
4392
+ totalReferred
4393
+ completedReferrals
4394
+ pendingReferrals
4395
+ totalPointsEarned
4396
+ }
4397
+ ```
4398
+
4399
+ ### Fragment: `RedeemRewardPayload` on `RedeemRewardPayload`
4400
+
4401
+ **Section**: Loyalty Program
4402
+
4403
+ **Description**: Result of `redeemLoyaltyReward` — depending on the reward type, exactly one of `discountCode`, `productDiscountCode`, `giftCardCode` is populated. Apply the returned code on cart/checkout.
4404
+
4405
+ **GraphQL**:
4406
+ ```graphql
4407
+ fragment RedeemRewardPayload on RedeemRewardPayload {
4408
+ success
4409
+ discountCode
4410
+ productDiscountCode
4411
+ giftCardCode
4412
+ userErrors
4413
+ }
4414
+ ```
4415
+
4416
+ ### Fragment: `GenerateReferralCodePayload` on `GenerateReferralCodePayload`
4417
+
4418
+ **Section**: Loyalty Program
4419
+
4420
+ **Description**: Result of `generateReferralCode` — the customer's referral code and shareable URL.
4421
+
4422
+ **GraphQL**:
4423
+ ```graphql
4424
+ fragment GenerateReferralCodePayload on GenerateReferralCodePayload {
4425
+ success
4426
+ referralCode
4427
+ shareUrl
4428
+ userErrors
4429
+ }
4430
+ ```
4431
+
4432
+ ### Fragment: `ProductReview` on `ProductReview`
4433
+
4434
+ **Section**: Reviews
4435
+
4436
+ **Description**: Single product review — rating, title, body, optional pros/cons, image attachments, author, verified-purchase flag, helpful/unhelpful counts, optional merchant response. Only `APPROVED` reviews are exposed to the storefront.
4437
+
4438
+ **GraphQL**:
4439
+ ```graphql
4440
+ fragment ProductReview on ProductReview {
4441
+ id
4442
+ productId
4443
+ rating
4444
+ title
4445
+ content
4446
+ pros
4447
+ cons
4448
+ images
4449
+ authorName
4450
+ isVerifiedPurchase
4451
+ helpfulCount
4452
+ unhelpfulCount
4453
+ response
4454
+ responseAt
4455
+ createdAt
4456
+ }
4457
+ ```
4458
+
4459
+ ### Fragment: `ReviewStats` on `ReviewStats`
4460
+
4461
+ **Section**: Reviews
4462
+
4463
+ **Description**: Aggregate review stats for a product — average rating, total count, distribution per star (1-5). Returned by `reviewStats($productId)`.
4464
+
4465
+ **GraphQL**:
4466
+ ```graphql
4467
+ fragment ReviewStats on ReviewStats {
4468
+ averageRating
4469
+ totalCount
4470
+ fiveStarCount
4471
+ fourStarCount
4472
+ threeStarCount
4473
+ twoStarCount
4474
+ oneStarCount
4475
+ }
4476
+ ```
4477
+
4478
+ ### Fragment: `WishlistItem` on `WishlistItem`
4479
+
4480
+ **Section**: Wishlists
4481
+
4482
+ **Description**: One item in a wishlist — references the product and (optionally) a specific variant, snapshots the price at the time it was added, plus notification preferences (`notifyOnSale`, `notifyOnRestock`).
4483
+
4484
+ **Uses fragments**: `Money`, `ProductCard`
4485
+
4486
+ **GraphQL**:
4487
+ ```graphql
4488
+ fragment WishlistItem on WishlistItem {
4489
+ id
4490
+ productId
4491
+ variantId
4492
+ product {
4493
+ ...ProductCard
4494
+ }
4495
+ priceWhenAdded {
4496
+ ...Money
4497
+ }
4498
+ notifyOnSale
4499
+ notifyOnRestock
4500
+ addedAt
4501
+ }
4502
+ ```
4503
+
4504
+ ### Fragment: `Wishlist` on `Wishlist`
4505
+
4506
+ **Section**: Wishlists
4507
+
4508
+ **Description**: A customer's wishlist — name, public/private flag, share token (when public), items, item count. Spread on the wishlists page.
4509
+
4510
+ **Uses fragments**: `WishlistItem`
4511
+
4512
+ **GraphQL**:
4513
+ ```graphql
4514
+ fragment Wishlist on Wishlist {
4515
+ id
4516
+ name
4517
+ isPublic
4518
+ shareToken
4519
+ items {
4520
+ ...WishlistItem
4521
+ }
4522
+ itemCount
4523
+ createdAt
4524
+ updatedAt
4525
+ }
4526
+ ```
4527
+
4528
+ ### Fragment: `BlogCategory` on `BlogCategory`
4529
+
4530
+ **Section**: Blog
4531
+
4532
+ **Description**: Blog category — name, slug, description, post count. Spread on category navigation and post category labels.
4533
+
4534
+ **GraphQL**:
4535
+ ```graphql
4536
+ fragment BlogCategory on BlogCategory {
4537
+ id
4538
+ name
4539
+ slug
4540
+ description
4541
+ postCount
4542
+ }
4543
+ ```
4544
+
4545
+ ### Fragment: `BlogTag` on `BlogTag`
4546
+
4547
+ **Section**: Blog
4548
+
4549
+ **Description**: Blog tag — name, slug, post count. Spread on tag clouds and post tag labels.
4550
+
4551
+ **GraphQL**:
4552
+ ```graphql
4553
+ fragment BlogTag on BlogTag {
4554
+ id
4555
+ name
4556
+ slug
4557
+ postCount
4558
+ }
4559
+ ```
4560
+
4561
+ ### Fragment: `BlogPost` on `BlogPost`
4562
+
4563
+ **Section**: Blog
4564
+
4565
+ **Description**: Full blog post — title, slug, excerpt, content (with `contentFormat` indicating HTML/Markdown), featured image, author, category, tags, publish date, reading time, view + comment counts, SEO metadata. Spread on the post detail page.
4566
+
4567
+ **Uses fragments**: `BlogCategory`, `BlogTag`, `ImageCard`, `ImageThumbnail`
4568
+
4569
+ **GraphQL**:
4570
+ ```graphql
4571
+ fragment BlogPost on BlogPost {
4572
+ id
4573
+ title
4574
+ slug
4575
+ excerpt
4576
+ content
4577
+ contentFormat
4578
+ featuredImage {
4579
+ ...ImageCard
4580
+ }
4581
+ author {
4582
+ id
4583
+ name
4584
+ bio
4585
+ avatar {
4586
+ ...ImageThumbnail
4587
+ }
4588
+ }
4589
+ category {
4590
+ ...BlogCategory
4591
+ }
4592
+ tags {
4593
+ ...BlogTag
4594
+ }
4595
+ publishedAt
4596
+ readingTimeMinutes
4597
+ viewCount
4598
+ commentCount
4599
+ allowComments
4600
+ isFeatured
4601
+ status
4602
+ seo {
4603
+ title
4604
+ description
4605
+ }
4606
+ createdAt
4607
+ updatedAt
4608
+ }
4609
+ ```
4610
+
4611
+ ### Fragment: `ProductRecommendation` on `ProductRecommendation`
4612
+
4613
+ **Section**: Recommendations
4614
+
4615
+ **Description**: One recommended product — the product itself (card-level), recommendation type (e.g. SIMILAR / FREQUENTLY_BOUGHT_TOGETHER), score (0-100), human reason text. Spread on PDP "You may also like" sections.
4616
+
4617
+ **Uses fragments**: `ProductCard`
4618
+
4619
+ **GraphQL**:
4620
+ ```graphql
4621
+ fragment ProductRecommendation on ProductRecommendation {
4622
+ product {
4623
+ ...ProductCard
4624
+ }
4625
+ type
4626
+ score
4627
+ reason
4628
+ }
4629
+ ```
4630
+
4631
+ ### Fragment: `CartRecommendation` on `CartRecommendations`
4632
+
4633
+ **Section**: Recommendations
4634
+
4635
+ **Description**: Recommendations bundle for the cart drawer — `frequentlyBoughtTogether` (cross-sell) and `youMayAlsoLike` (related). Each entry has the same shape as `ProductRecommendation`.
4636
+
4637
+ **Uses fragments**: `ProductCard`
4638
+
4639
+ **GraphQL**:
4640
+ ```graphql
4641
+ fragment CartRecommendation on CartRecommendations {
4642
+ frequentlyBoughtTogether {
4643
+ product {
4644
+ ...ProductCard
4645
+ }
4646
+ type
4647
+ score
4648
+ reason
4649
+ }
4650
+ youMayAlsoLike {
4651
+ product {
4652
+ ...ProductCard
4653
+ }
4654
+ type
4655
+ score
4656
+ reason
4657
+ }
4658
+ }
4659
+ ```
4660
+
4661
+ ### Fragment: `LocationAddress` on `LocationAddress`
4662
+
4663
+ **Section**: Store Availability (BOPIS / multi-location)
4664
+
4665
+ **Description**: Field-access notes: - `isAvailable` — always public. - `pickupTime` — public, localized merchant-configured string. - `availableStock` — token-gated (null for anonymous, integer for authenticated customers). - `location` — public, includes coords + business hours when configured. Physical location address — street, city, country, state, postal code, phone, lat/lng, formatted single-line representation. Spread inside `Location.address`.
4666
+
4667
+ **GraphQL**:
4668
+ ```graphql
4669
+ fragment LocationAddress on LocationAddress {
4670
+ streetLine1
4671
+ streetLine2
4672
+ city
4673
+ country
4674
+ countryCode
4675
+ state
4676
+ stateCode
4677
+ postalCode
4678
+ phone
4679
+ latitude
4680
+ longitude
4681
+ formatted
4682
+ }
4683
+ ```
4684
+
4685
+ ### Fragment: `BusinessHoursWindow` on `BusinessHoursWindow`
4686
+
4687
+ **Section**: Store Availability (BOPIS / multi-location)
4688
+
4689
+ **Description**: One day's open/close window inside `BusinessHours` (24h clock).
4690
+
4691
+ **GraphQL**:
4692
+ ```graphql
4693
+ fragment BusinessHoursWindow on BusinessHoursWindow {
4694
+ openHour
4695
+ closeHour
4696
+ }
4697
+ ```
4698
+
4699
+ ### Fragment: `BusinessHours` on `BusinessHours`
4700
+
4701
+ **Section**: Store Availability (BOPIS / multi-location)
4702
+
4703
+ **Description**: Weekly business hours — one window per day. Spread inside `Location.businessHours`.
4704
+
4705
+ **Uses fragments**: `BusinessHoursWindow`
4706
+
4707
+ **GraphQL**:
4708
+ ```graphql
4709
+ fragment BusinessHours on BusinessHours {
4710
+ monday {
4711
+ ...BusinessHoursWindow
4712
+ }
4713
+ tuesday {
4714
+ ...BusinessHoursWindow
4715
+ }
4716
+ wednesday {
4717
+ ...BusinessHoursWindow
4718
+ }
4719
+ thursday {
4720
+ ...BusinessHoursWindow
4721
+ }
4722
+ friday {
4723
+ ...BusinessHoursWindow
4724
+ }
4725
+ saturday {
4726
+ ...BusinessHoursWindow
4727
+ }
4728
+ sunday {
4729
+ ...BusinessHoursWindow
4730
+ }
4731
+ }
4732
+ ```
4733
+
4734
+ ### Fragment: `Location` on `Location`
4735
+
4736
+ **Section**: Store Availability (BOPIS / multi-location)
4737
+
4738
+ **Description**: Physical store / pickup location — name, type (RETAIL / WAREHOUSE / PICKUP_POINT), address, business hours, pickup support flag + instructions, timezone. Spread on the store picker and location detail pages.
4739
+
4740
+ **Uses fragments**: `BusinessHours`, `LocationAddress`
4741
+
4742
+ **GraphQL**:
4743
+ ```graphql
4744
+ fragment Location on Location {
4745
+ id
4746
+ name
4747
+ type
4748
+ supportsPickup
4749
+ timezone
4750
+ pickupInstructions
4751
+ address {
4752
+ ...LocationAddress
4753
+ }
4754
+ businessHours {
4755
+ ...BusinessHours
4756
+ }
4757
+ }
4758
+ ```
4759
+
4760
+ ### Fragment: `StoreAvailability` on `StoreAvailability`
4761
+
4762
+ **Section**: Store Availability (BOPIS / multi-location)
4763
+
4764
+ **Description**: Per-location stock entry for one variant — availability flag, stock quantity (token-gated), pickup time, full location detail. Spread inside `StoreAvailabilityConnection.edges[].node`.
4765
+
4766
+ **Uses fragments**: `Location`
4767
+
4768
+ **GraphQL**:
4769
+ ```graphql
4770
+ fragment StoreAvailability on StoreAvailability {
4771
+ isAvailable
4772
+ availableStock
4773
+ pickupTime
4774
+ location {
4775
+ ...Location
4776
+ }
4777
+ }
4778
+ ```
4779
+
4780
+ ### Fragment: `StoreAvailabilityConnection` on `StoreAvailabilityConnection`
4781
+
4782
+ **Section**: Store Availability (BOPIS / multi-location)
4783
+
4784
+ **Description**: Relay connection of `StoreAvailability` entries for a variant — used inside the `VariantStoreAvailability` fragment. Pre-paginated to 10 by default.
4785
+
4786
+ **Uses fragments**: `PageInfo`, `StoreAvailability`
4787
+
4788
+ **GraphQL**:
4789
+ ```graphql
4790
+ fragment StoreAvailabilityConnection on StoreAvailabilityConnection {
4791
+ totalCount
4792
+ pageInfo {
4793
+ ...PageInfo
4794
+ }
4795
+ edges {
4796
+ cursor
4797
+ node {
4798
+ ...StoreAvailability
4799
+ }
4800
+ }
4801
+ }
4802
+ ```
4803
+
4804
+ ### Fragment: `VariantStoreAvailability` on `ProductVariant`
4805
+
4806
+ **Section**: Store Availability (BOPIS / multi-location)
4807
+
4808
+ **Description**: Slim variant projection optimized for the store-picker UI — id, title, sku, isAvailable, total `availableStock` plus per-location `storeAvailability` connection. Use in `productStoreAvailability` query.
4809
+
4810
+ **Uses fragments**: `StoreAvailabilityConnection`
4811
+
4812
+ **GraphQL**:
4813
+ ```graphql
4814
+ fragment VariantStoreAvailability on ProductVariant {
4815
+ id
4816
+ title
4817
+ sku
4818
+ isAvailable
4819
+ availableStock
4820
+ storeAvailability(first: 10) {
4821
+ ...StoreAvailabilityConnection
4822
+ }
4823
+ }
4824
+ ```
4825
+
4826
+ ### Fragment: `ShopPage` on `ShopPage`
4827
+
4828
+ **Section**: Pages
4829
+
4830
+ **Description**: CMS page — handle (URL slug), title, body (HTML), excerpt, SEO metadata, publish date. Spread on About / Privacy / Terms / Returns Policy pages.
4831
+
4832
+ **GraphQL**:
4833
+ ```graphql
4834
+ fragment ShopPage on ShopPage {
4835
+ id
4836
+ handle
4837
+ title
4838
+ body
4839
+ excerpt
4840
+ seo {
4841
+ title
4842
+ description
4843
+ }
4844
+ publishedAt
4845
+ createdAt
4846
+ updatedAt
4847
+ }
4848
+ ```
4849
+
4850
+ ### Fragment: `MenuItem` on `MenuItem`
4851
+
4852
+ **Section**: Navigation Menus
4853
+
4854
+ **Description**: One menu item with up to 3 levels of nested children — title, URL, type (`HTTP` / `FRONTPAGE` / `SEARCH` / `CATALOG` / `BLOG` / `PRODUCT` / `COLLECTION` / `CATEGORY` / `PAGE`), `resourceId` for typed items, optional image. Switch on `type` to render the right link target.
4855
+
4856
+ **Uses fragments**: `Image`
4857
+
4858
+ **GraphQL**:
4859
+ ```graphql
4860
+ fragment MenuItem on MenuItem {
4861
+ id
4862
+ title
4863
+ url
4864
+ type
4865
+ resourceId
4866
+ image {
4867
+ ...Image
4868
+ }
4869
+ items {
4870
+ id
4871
+ title
4872
+ url
4873
+ type
4874
+ resourceId
4875
+ items {
4876
+ id
4877
+ title
4878
+ url
4879
+ type
4880
+ resourceId
4881
+ }
4882
+ }
4883
+ }
4884
+ ```
4885
+
4886
+ ### Fragment: `Menu` on `Menu`
4887
+
4888
+ **Section**: Navigation Menus
4889
+
4890
+ **Description**: Navigation menu — handle (e.g. "main-menu", "footer", "mobile"), title, nested item tree. Returned by `menu($handle)` query.
4891
+
4892
+ **Uses fragments**: `MenuItem`
4893
+
4894
+ **GraphQL**:
4895
+ ```graphql
4896
+ fragment Menu on Menu {
4897
+ id
4898
+ handle
4899
+ title
4900
+ items {
4901
+ ...MenuItem
4902
+ }
4903
+ }
4904
+ ```
4905
+
4906
+ ### Fragment: `UrlRedirect` on `UrlRedirect`
4907
+
4908
+ **Section**: URL Redirects
4909
+
4910
+ **Description**: Single legacy `path` → new `target` redirect entry. Use on the server / edge to issue 301 redirects for migrated routes.
4911
+
4912
+ **GraphQL**:
4913
+ ```graphql
4914
+ fragment UrlRedirect on UrlRedirect {
4915
+ path
4916
+ target
4917
+ }
4918
+ ```
4919
+
4920
+ ### Fragment: `LinkedVariantSummary` on `LinkedVariantSummary`
4921
+
4922
+ **Section**: Product Configurator (per-product attributes)
4923
+
4924
+ **Description**: Slim variant snapshot linked from a configurator option — when an attribute option (e.g. "256 GB" storage) maps to a specific variant, this fragment exposes that variant's id, title, sku, stock flags. Spread inside `ProductAttributeOption.linkedVariant`.
4925
+
4926
+ **GraphQL**:
4927
+ ```graphql
4928
+ fragment LinkedVariantSummary on LinkedVariantSummary {
4929
+ id
4930
+ productId
4931
+ title
4932
+ sku
4933
+ availableStock
4934
+ isAvailable
4935
+ trackQuantity
4936
+ }
4937
+ ```
4938
+
4939
+ ### Fragment: `ProductAttributeOption` on `ProductAttributeOption`
4940
+
4941
+ **Section**: Product Configurator (per-product attributes)
4942
+
4943
+ **Description**: One option (choice) within a configurator attribute — value, label, sort order, optional color hex, surcharge (amount + type), default flag, optional linked variant for variant-mapped options.
4944
+
4945
+ **Uses fragments**: `LinkedVariantSummary`
4946
+
4947
+ **GraphQL**:
4948
+ ```graphql
4949
+ fragment ProductAttributeOption on ProductAttributeOption {
4950
+ id
4951
+ value
4952
+ label
4953
+ sortOrder
4954
+ colorHex
4955
+ surchargeAmount
4956
+ surchargeType
4957
+ isDefault
4958
+ linkedVariantId
4959
+ linkedVariant {
4960
+ ...LinkedVariantSummary
4961
+ }
4962
+ }
4963
+ ```
4964
+
4965
+ ### Fragment: `ProductAttributeDefinition` on `ProductAttributeDefinition`
4966
+
4967
+ **Section**: Product Configurator (per-product attributes)
4968
+
4969
+ **Description**: Configurator attribute definition — name, type, fillingMode (CUSTOMER / MERCHANT / BOTH), billingMode, tax class, validation bounds, options (for choice-type attributes), required + visibility flags. Returned by `product.attributes(filter)` for the configurator UI.
4970
+
4971
+ **Uses fragments**: `ProductAttributeOption`
4972
+
4973
+ **GraphQL**:
4974
+ ```graphql
4975
+ fragment ProductAttributeDefinition on ProductAttributeDefinition {
4976
+ id
4977
+ name
4978
+ slug
4979
+ description
4980
+ type
4981
+ fillingMode
4982
+ billingMode
4983
+ taxClassId
4984
+ scopeProductId
4985
+ isRequired
4986
+ isVisible
4987
+ displayOrder
4988
+ minValue
4989
+ maxValue
4990
+ options {
4991
+ ...ProductAttributeOption
4992
+ }
4993
+ }
4994
+ ```