@doswiftly/storefront-operations 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms-full.txt ADDED
@@ -0,0 +1,4916 @@
1
+ # DoSwiftly Storefront Operations — Full Reference
2
+
3
+ > Schema version: **8.0.0**
4
+ > 48 queries · 44 mutations · 104 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`: `AttributeFillingMode` *(default: `CUSTOMER`)*
70
+
71
+ **Fragments used**: `ProductAttributeDefinition`, `ProductFull`
72
+
73
+ **GraphQL**:
74
+ ```graphql
75
+ query ProductConfigurator($handle: String!, $fillingMode: AttributeFillingMode = 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: `SearchSuggestions`
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 SearchSuggestions($query: String!, $limit: Int = 10) {
179
+ searchSuggestions(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 root categories for the shop. Each root exposes its `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(rootsOnly: true) {
314
+ nodes {
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: `ProductFilters`
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 ProductFilters($input: AvailableFiltersInput) {
684
+ productFilters(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
+ nodes {
1201
+ ...VariantStoreAvailability
1202
+ }
1203
+ }
1204
+ }
1205
+ }
1206
+ ```
1207
+
1208
+ ### Query: `Locations`
1209
+
1210
+ **Section**: Locations (store picker UI)
1211
+
1212
+ **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.
1213
+
1214
+ **Variables**:
1215
+ - `$first`: `Int` *(default: `20`)*
1216
+ - `$after`: `String`
1217
+ - `$near`: `GeoCoordinateInput`
1218
+ - `$hasPickupEnabled`: `Boolean`
1219
+ - `$locationType`: `LocationType`
1220
+
1221
+ **Fragments used**: `Location`, `PageInfo`
1222
+
1223
+ **GraphQL**:
1224
+ ```graphql
1225
+ query Locations($first: Int = 20, $after: String, $near: GeoCoordinateInput, $hasPickupEnabled: Boolean, $locationType: LocationType) {
1226
+ locations(
1227
+ first: $first
1228
+ after: $after
1229
+ near: $near
1230
+ hasPickupEnabled: $hasPickupEnabled
1231
+ locationType: $locationType
1232
+ ) {
1233
+ totalCount
1234
+ pageInfo {
1235
+ ...PageInfo
1236
+ }
1237
+ edges {
1238
+ cursor
1239
+ node {
1240
+ ...Location
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+ ```
1246
+
1247
+ ### Query: `Location`
1248
+
1249
+ **Section**: Locations (store picker UI)
1250
+
1251
+ **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.
1252
+
1253
+ **Variables**:
1254
+ - `$id`: `ID!`
1255
+
1256
+ **Fragments used**: `Location`
1257
+
1258
+ **GraphQL**:
1259
+ ```graphql
1260
+ query Location($id: ID!) {
1261
+ location(id: $id) {
1262
+ ...Location
1263
+ }
1264
+ }
1265
+ ```
1266
+
1267
+ ---
1268
+
1269
+ ## Mutations
1270
+
1271
+ ### Mutation: `CartCreate`
1272
+
1273
+ **Section**: Cart Mutations
1274
+
1275
+ **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.
1276
+
1277
+ **Variables**:
1278
+ - `$input`: `CartCreateInput`
1279
+
1280
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1281
+
1282
+ **GraphQL**:
1283
+ ```graphql
1284
+ mutation CartCreate($input: CartCreateInput) {
1285
+ cartCreate(input: $input) {
1286
+ cart {
1287
+ ...Cart
1288
+ }
1289
+ userErrors {
1290
+ ...UserError
1291
+ }
1292
+ warnings {
1293
+ ...CartWarning
1294
+ }
1295
+ }
1296
+ }
1297
+ ```
1298
+
1299
+ ### Mutation: `CartAddLines`
1300
+
1301
+ **Section**: Cart Mutations
1302
+
1303
+ **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.
1304
+
1305
+ **Variables**:
1306
+ - `$id`: `ID!`
1307
+ - `$lines`: `[CartLineInput!]!`
1308
+
1309
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1310
+
1311
+ **GraphQL**:
1312
+ ```graphql
1313
+ mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
1314
+ cartAddLines(id: $id, lines: $lines) {
1315
+ cart {
1316
+ ...Cart
1317
+ }
1318
+ userErrors {
1319
+ ...UserError
1320
+ }
1321
+ warnings {
1322
+ ...CartWarning
1323
+ }
1324
+ }
1325
+ }
1326
+ ```
1327
+
1328
+ ### Mutation: `CartUpdateLines`
1329
+
1330
+ **Section**: Cart Mutations
1331
+
1332
+ **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.
1333
+
1334
+ **Variables**:
1335
+ - `$id`: `ID!`
1336
+ - `$lines`: `[CartLineUpdateInput!]!`
1337
+
1338
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1339
+
1340
+ **GraphQL**:
1341
+ ```graphql
1342
+ mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!) {
1343
+ cartUpdateLines(id: $id, lines: $lines) {
1344
+ cart {
1345
+ ...Cart
1346
+ }
1347
+ userErrors {
1348
+ ...UserError
1349
+ }
1350
+ warnings {
1351
+ ...CartWarning
1352
+ }
1353
+ }
1354
+ }
1355
+ ```
1356
+
1357
+ ### Mutation: `CartRemoveLines`
1358
+
1359
+ **Section**: Cart Mutations
1360
+
1361
+ **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.
1362
+
1363
+ **Variables**:
1364
+ - `$id`: `ID!`
1365
+ - `$lineIds`: `[ID!]!`
1366
+
1367
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1368
+
1369
+ **GraphQL**:
1370
+ ```graphql
1371
+ mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!) {
1372
+ cartRemoveLines(id: $id, lineIds: $lineIds) {
1373
+ cart {
1374
+ ...Cart
1375
+ }
1376
+ userErrors {
1377
+ ...UserError
1378
+ }
1379
+ warnings {
1380
+ ...CartWarning
1381
+ }
1382
+ }
1383
+ }
1384
+ ```
1385
+
1386
+ ### Mutation: `CartApplyDiscountCodes`
1387
+
1388
+ **Section**: Cart Mutations
1389
+
1390
+ **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`.
1391
+
1392
+ **Variables**:
1393
+ - `$id`: `ID!`
1394
+ - `$discountCodes`: `[String!]!`
1395
+
1396
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1397
+
1398
+ **GraphQL**:
1399
+ ```graphql
1400
+ mutation CartApplyDiscountCodes($id: ID!, $discountCodes: [String!]!) {
1401
+ cartApplyDiscountCodes(id: $id, discountCodes: $discountCodes) {
1402
+ cart {
1403
+ ...Cart
1404
+ }
1405
+ userErrors {
1406
+ ...UserError
1407
+ }
1408
+ warnings {
1409
+ ...CartWarning
1410
+ }
1411
+ }
1412
+ }
1413
+ ```
1414
+
1415
+ ### Mutation: `CartUpdateBuyerIdentity`
1416
+
1417
+ **Section**: Cart Mutations
1418
+
1419
+ **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.
1420
+
1421
+ **Variables**:
1422
+ - `$id`: `ID!`
1423
+ - `$buyerIdentity`: `CartBuyerIdentityInput!`
1424
+
1425
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1426
+
1427
+ **GraphQL**:
1428
+ ```graphql
1429
+ mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
1430
+ cartUpdateBuyerIdentity(id: $id, buyerIdentity: $buyerIdentity) {
1431
+ cart {
1432
+ ...Cart
1433
+ }
1434
+ userErrors {
1435
+ ...UserError
1436
+ }
1437
+ warnings {
1438
+ ...CartWarning
1439
+ }
1440
+ }
1441
+ }
1442
+ ```
1443
+
1444
+ ### Mutation: `CartUpdateNote`
1445
+
1446
+ **Section**: Cart Mutations
1447
+
1448
+ **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.
1449
+
1450
+ **Variables**:
1451
+ - `$id`: `ID!`
1452
+ - `$note`: `String!`
1453
+
1454
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
1455
+
1456
+ **GraphQL**:
1457
+ ```graphql
1458
+ mutation CartUpdateNote($id: ID!, $note: String!) {
1459
+ cartUpdateNote(id: $id, note: $note) {
1460
+ cart {
1461
+ ...Cart
1462
+ }
1463
+ userErrors {
1464
+ ...UserError
1465
+ }
1466
+ warnings {
1467
+ ...CartWarning
1468
+ }
1469
+ }
1470
+ }
1471
+ ```
1472
+
1473
+ ### Mutation: `CustomerSignup`
1474
+
1475
+ **Section**: Customer Auth Mutations
1476
+
1477
+ **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.
1478
+
1479
+ **Variables**:
1480
+ - `$input`: `CustomerCreateInput!`
1481
+
1482
+ **Fragments used**: `Customer`, `CustomerAccessToken`, `UserError`
1483
+
1484
+ **GraphQL**:
1485
+ ```graphql
1486
+ mutation CustomerSignup($input: CustomerCreateInput!) {
1487
+ customerSignup(input: $input) {
1488
+ customer {
1489
+ ...Customer
1490
+ }
1491
+ customerAccessToken {
1492
+ ...CustomerAccessToken
1493
+ }
1494
+ userErrors {
1495
+ ...UserError
1496
+ }
1497
+ }
1498
+ }
1499
+ ```
1500
+
1501
+ ### Mutation: `CustomerLogin`
1502
+
1503
+ **Section**: Customer Auth Mutations
1504
+
1505
+ **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).
1506
+
1507
+ **Variables**:
1508
+ - `$input`: `CustomerAccessTokenCreateInput!`
1509
+
1510
+ **Fragments used**: `CustomerAccessToken`, `UserError`
1511
+
1512
+ **GraphQL**:
1513
+ ```graphql
1514
+ mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
1515
+ customerLogin(input: $input) {
1516
+ customerAccessToken {
1517
+ ...CustomerAccessToken
1518
+ }
1519
+ userErrors {
1520
+ ...UserError
1521
+ }
1522
+ }
1523
+ }
1524
+ ```
1525
+
1526
+ ### Mutation: `CustomerLogout`
1527
+
1528
+ **Section**: Customer Auth Mutations
1529
+
1530
+ **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.
1531
+
1532
+ **Variables**: none
1533
+
1534
+ **Fragments used**: `UserError`
1535
+
1536
+ **GraphQL**:
1537
+ ```graphql
1538
+ mutation CustomerLogout {
1539
+ customerLogout {
1540
+ deletedAccessToken
1541
+ deletedCustomerAccessTokenId
1542
+ userErrors {
1543
+ ...UserError
1544
+ }
1545
+ }
1546
+ }
1547
+ ```
1548
+
1549
+ ### Mutation: `CustomerRefreshToken`
1550
+
1551
+ **Section**: Customer Auth Mutations
1552
+
1553
+ **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.
1554
+
1555
+ **Variables**: none
1556
+
1557
+ **Fragments used**: `CustomerAccessToken`, `UserError`
1558
+
1559
+ **GraphQL**:
1560
+ ```graphql
1561
+ mutation CustomerRefreshToken {
1562
+ customerRefreshToken {
1563
+ customerAccessToken {
1564
+ ...CustomerAccessToken
1565
+ }
1566
+ userErrors {
1567
+ ...UserError
1568
+ }
1569
+ }
1570
+ }
1571
+ ```
1572
+
1573
+ ### Mutation: `CustomerUpdate`
1574
+
1575
+ **Section**: Customer Profile Mutations
1576
+
1577
+ **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.
1578
+
1579
+ **Variables**:
1580
+ - `$customer`: `CustomerUpdateInput!`
1581
+
1582
+ **Fragments used**: `Customer`, `UserError`
1583
+
1584
+ **GraphQL**:
1585
+ ```graphql
1586
+ mutation CustomerUpdate($customer: CustomerUpdateInput!) {
1587
+ customerUpdate(customer: $customer) {
1588
+ customer {
1589
+ ...Customer
1590
+ }
1591
+ userErrors {
1592
+ ...UserError
1593
+ }
1594
+ }
1595
+ }
1596
+ ```
1597
+
1598
+ ### Mutation: `CustomerAddAddress`
1599
+
1600
+ **Section**: Customer Address Mutations
1601
+
1602
+ **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.
1603
+
1604
+ **Variables**:
1605
+ - `$address`: `MailingAddressInput!`
1606
+
1607
+ **Fragments used**: `MailingAddress`, `UserError`
1608
+
1609
+ **GraphQL**:
1610
+ ```graphql
1611
+ mutation CustomerAddAddress($address: MailingAddressInput!) {
1612
+ customerAddAddress(address: $address) {
1613
+ address {
1614
+ ...MailingAddress
1615
+ }
1616
+ userErrors {
1617
+ ...UserError
1618
+ }
1619
+ }
1620
+ }
1621
+ ```
1622
+
1623
+ ### Mutation: `CustomerUpdateAddress`
1624
+
1625
+ **Section**: Customer Address Mutations
1626
+
1627
+ **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.
1628
+
1629
+ **Variables**:
1630
+ - `$id`: `ID!`
1631
+ - `$address`: `MailingAddressInput!`
1632
+
1633
+ **Fragments used**: `MailingAddress`, `UserError`
1634
+
1635
+ **GraphQL**:
1636
+ ```graphql
1637
+ mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!) {
1638
+ customerUpdateAddress(id: $id, address: $address) {
1639
+ address {
1640
+ ...MailingAddress
1641
+ }
1642
+ userErrors {
1643
+ ...UserError
1644
+ }
1645
+ }
1646
+ }
1647
+ ```
1648
+
1649
+ ### Mutation: `CustomerRemoveAddress`
1650
+
1651
+ **Section**: Customer Address Mutations
1652
+
1653
+ **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).
1654
+
1655
+ **Variables**:
1656
+ - `$id`: `ID!`
1657
+
1658
+ **Fragments used**: `UserError`
1659
+
1660
+ **GraphQL**:
1661
+ ```graphql
1662
+ mutation CustomerRemoveAddress($id: ID!) {
1663
+ customerRemoveAddress(id: $id) {
1664
+ deletedAddressId
1665
+ userErrors {
1666
+ ...UserError
1667
+ }
1668
+ }
1669
+ }
1670
+ ```
1671
+
1672
+ ### Mutation: `CustomerSetDefaultAddress`
1673
+
1674
+ **Section**: Customer Address Mutations
1675
+
1676
+ **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.
1677
+
1678
+ **Variables**:
1679
+ - `$addressId`: `ID!`
1680
+
1681
+ **Fragments used**: `Customer`, `UserError`
1682
+
1683
+ **GraphQL**:
1684
+ ```graphql
1685
+ mutation CustomerSetDefaultAddress($addressId: ID!) {
1686
+ customerSetDefaultAddress(addressId: $addressId) {
1687
+ customer {
1688
+ ...Customer
1689
+ }
1690
+ userErrors {
1691
+ ...UserError
1692
+ }
1693
+ }
1694
+ }
1695
+ ```
1696
+
1697
+ ### Mutation: `CustomerRequestPasswordReset`
1698
+
1699
+ **Section**: Customer Password Mutations
1700
+
1701
+ **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.
1702
+
1703
+ **Variables**:
1704
+ - `$email`: `String!`
1705
+
1706
+ **Fragments used**: `UserError`
1707
+
1708
+ **GraphQL**:
1709
+ ```graphql
1710
+ mutation CustomerRequestPasswordReset($email: String!) {
1711
+ customerRequestPasswordReset(email: $email) {
1712
+ userErrors {
1713
+ ...UserError
1714
+ }
1715
+ }
1716
+ }
1717
+ ```
1718
+
1719
+ ### Mutation: `CustomerActivate`
1720
+
1721
+ **Section**: Customer Password Mutations
1722
+
1723
+ **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.
1724
+
1725
+ **Variables**:
1726
+ - `$token`: `String!`
1727
+ - `$password`: `String!`
1728
+
1729
+ **Fragments used**: `Customer`, `CustomerAccessToken`, `UserError`
1730
+
1731
+ **GraphQL**:
1732
+ ```graphql
1733
+ mutation CustomerActivate($token: String!, $password: String!) {
1734
+ customerActivate(token: $token, password: $password) {
1735
+ customer {
1736
+ ...Customer
1737
+ }
1738
+ customerAccessToken {
1739
+ ...CustomerAccessToken
1740
+ }
1741
+ userErrors {
1742
+ ...UserError
1743
+ }
1744
+ }
1745
+ }
1746
+ ```
1747
+
1748
+ ### Mutation: `CustomerResetPassword`
1749
+
1750
+ **Section**: Customer Password Mutations
1751
+
1752
+ **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.
1753
+
1754
+ **Variables**:
1755
+ - `$token`: `String!`
1756
+ - `$newPassword`: `String!`
1757
+
1758
+ **Fragments used**: `Customer`, `CustomerAccessToken`, `UserError`
1759
+
1760
+ **GraphQL**:
1761
+ ```graphql
1762
+ mutation CustomerResetPassword($token: String!, $newPassword: String!) {
1763
+ customerResetPassword(token: $token, newPassword: $newPassword) {
1764
+ customer {
1765
+ ...Customer
1766
+ }
1767
+ customerAccessToken {
1768
+ ...CustomerAccessToken
1769
+ }
1770
+ userErrors {
1771
+ ...UserError
1772
+ }
1773
+ }
1774
+ }
1775
+ ```
1776
+
1777
+ ### Mutation: `CheckoutCreate`
1778
+
1779
+ **Section**: Checkout Mutations
1780
+
1781
+ **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.
1782
+
1783
+ **Variables**:
1784
+ - `$input`: `CheckoutCreateInput!`
1785
+
1786
+ **Fragments used**: `Checkout`, `UserError`
1787
+
1788
+ **GraphQL**:
1789
+ ```graphql
1790
+ mutation CheckoutCreate($input: CheckoutCreateInput!) {
1791
+ checkoutCreate(input: $input) {
1792
+ checkout {
1793
+ ...Checkout
1794
+ }
1795
+ userErrors {
1796
+ ...UserError
1797
+ }
1798
+ }
1799
+ }
1800
+ ```
1801
+
1802
+ ### Mutation: `CheckoutUpdateShippingAddress`
1803
+
1804
+ **Section**: Checkout Mutations
1805
+
1806
+ **Description**: Sets the shipping address (full replace, not patch). Triggers cart re-pricing. Address format is NOT validated here — full validation runs at `checkoutComplete`.
1807
+
1808
+ **Variables**:
1809
+ - `$id`: `ID!`
1810
+ - `$shippingAddress`: `CheckoutAddressInput!`
1811
+
1812
+ **Fragments used**: `Checkout`, `UserError`
1813
+
1814
+ **GraphQL**:
1815
+ ```graphql
1816
+ mutation CheckoutUpdateShippingAddress($id: ID!, $shippingAddress: CheckoutAddressInput!) {
1817
+ checkoutUpdateShippingAddress(id: $id, shippingAddress: $shippingAddress) {
1818
+ checkout {
1819
+ ...Checkout
1820
+ }
1821
+ userErrors {
1822
+ ...UserError
1823
+ }
1824
+ }
1825
+ }
1826
+ ```
1827
+
1828
+ ### Mutation: `CheckoutUpdateBillingAddress`
1829
+
1830
+ **Section**: Checkout Mutations
1831
+
1832
+ **Description**: Sets the billing address (full replace). Independent of shipping address — pass it explicitly even when "billing same as shipping".
1833
+
1834
+ **Variables**:
1835
+ - `$id`: `ID!`
1836
+ - `$billingAddress`: `CheckoutAddressInput!`
1837
+
1838
+ **Fragments used**: `Checkout`, `UserError`
1839
+
1840
+ **GraphQL**:
1841
+ ```graphql
1842
+ mutation CheckoutUpdateBillingAddress($id: ID!, $billingAddress: CheckoutAddressInput!) {
1843
+ checkoutUpdateBillingAddress(id: $id, billingAddress: $billingAddress) {
1844
+ checkout {
1845
+ ...Checkout
1846
+ }
1847
+ userErrors {
1848
+ ...UserError
1849
+ }
1850
+ }
1851
+ }
1852
+ ```
1853
+
1854
+ ### Mutation: `CheckoutUpdateEmail`
1855
+
1856
+ **Section**: Checkout Mutations
1857
+
1858
+ **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.
1859
+
1860
+ **Variables**:
1861
+ - `$id`: `ID!`
1862
+ - `$email`: `String!`
1863
+
1864
+ **Fragments used**: `Checkout`, `UserError`
1865
+
1866
+ **GraphQL**:
1867
+ ```graphql
1868
+ mutation CheckoutUpdateEmail($id: ID!, $email: String!) {
1869
+ checkoutUpdateEmail(id: $id, email: $email) {
1870
+ checkout {
1871
+ ...Checkout
1872
+ }
1873
+ userErrors {
1874
+ ...UserError
1875
+ }
1876
+ }
1877
+ }
1878
+ ```
1879
+
1880
+ ### Mutation: `CheckoutSelectShippingRate`
1881
+
1882
+ **Section**: Checkout Mutations
1883
+
1884
+ **Description**: Selects a shipping method by `rateId` (a stable shipping-method UUID, NOT an opaque per-request token). The id comes from `availableShippingMethods` query, computed for the current address + cart subtotal at request time.
1885
+
1886
+ **Variables**:
1887
+ - `$id`: `ID!`
1888
+ - `$rateId`: `String!`
1889
+
1890
+ **Fragments used**: `Checkout`, `UserError`
1891
+
1892
+ **GraphQL**:
1893
+ ```graphql
1894
+ mutation CheckoutSelectShippingRate($id: ID!, $rateId: String!) {
1895
+ checkoutSelectShippingRate(id: $id, rateId: $rateId) {
1896
+ checkout {
1897
+ ...Checkout
1898
+ }
1899
+ userErrors {
1900
+ ...UserError
1901
+ }
1902
+ }
1903
+ }
1904
+ ```
1905
+
1906
+ ### Mutation: `CheckoutApplyDiscountCode`
1907
+
1908
+ **Section**: Checkout Mutations
1909
+
1910
+ **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.
1911
+
1912
+ **Variables**:
1913
+ - `$id`: `ID!`
1914
+ - `$discountCode`: `String!`
1915
+
1916
+ **Fragments used**: `Checkout`, `UserError`
1917
+
1918
+ **GraphQL**:
1919
+ ```graphql
1920
+ mutation CheckoutApplyDiscountCode($id: ID!, $discountCode: String!) {
1921
+ checkoutApplyDiscountCode(id: $id, discountCode: $discountCode) {
1922
+ checkout {
1923
+ ...Checkout
1924
+ }
1925
+ userErrors {
1926
+ ...UserError
1927
+ }
1928
+ }
1929
+ }
1930
+ ```
1931
+
1932
+ ### Mutation: `CheckoutRemoveDiscountCode`
1933
+
1934
+ **Section**: Checkout Mutations
1935
+
1936
+ **Description**: Removes a code from the cart's `discount_codes` array (filters by exact match). Triggers re-pricing.
1937
+
1938
+ **Variables**:
1939
+ - `$id`: `ID!`
1940
+ - `$discountCode`: `String!`
1941
+
1942
+ **Fragments used**: `Checkout`, `UserError`
1943
+
1944
+ **GraphQL**:
1945
+ ```graphql
1946
+ mutation CheckoutRemoveDiscountCode($id: ID!, $discountCode: String!) {
1947
+ checkoutRemoveDiscountCode(id: $id, discountCode: $discountCode) {
1948
+ checkout {
1949
+ ...Checkout
1950
+ }
1951
+ userErrors {
1952
+ ...UserError
1953
+ }
1954
+ }
1955
+ }
1956
+ ```
1957
+
1958
+ ### Mutation: `CheckoutValidateDiscountCode`
1959
+
1960
+ **Section**: Checkout Mutations
1961
+
1962
+ **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).
1963
+
1964
+ **Variables**:
1965
+ - `$id`: `ID!`
1966
+ - `$discountCode`: `String!`
1967
+
1968
+ **Fragments used**: `UserError`
1969
+
1970
+ **GraphQL**:
1971
+ ```graphql
1972
+ mutation CheckoutValidateDiscountCode($id: ID!, $discountCode: String!) {
1973
+ checkoutValidateDiscountCode(id: $id, discountCode: $discountCode) {
1974
+ result {
1975
+ isValid
1976
+ discount {
1977
+ code
1978
+ title
1979
+ type
1980
+ value
1981
+ discountAmount {
1982
+ amount
1983
+ currencyCode
1984
+ }
1985
+ }
1986
+ error {
1987
+ code
1988
+ message
1989
+ }
1990
+ }
1991
+ userErrors {
1992
+ ...UserError
1993
+ }
1994
+ }
1995
+ }
1996
+ ```
1997
+
1998
+ ### Mutation: `CheckoutSelectPaymentMethod`
1999
+
2000
+ **Section**: Checkout Mutations
2001
+
2002
+ **Description**: Selects a payment method by `paymentMethodId` (UUID from `availablePaymentMethods` query). Validates existence and active status; no pre-authorization is performed here.
2003
+
2004
+ **Variables**:
2005
+ - `$id`: `ID!`
2006
+ - `$paymentMethodId`: `ID!`
2007
+
2008
+ **Fragments used**: `Checkout`, `UserError`
2009
+
2010
+ **GraphQL**:
2011
+ ```graphql
2012
+ mutation CheckoutSelectPaymentMethod($id: ID!, $paymentMethodId: ID!) {
2013
+ checkoutSelectPaymentMethod(id: $id, paymentMethodId: $paymentMethodId) {
2014
+ checkout {
2015
+ ...Checkout
2016
+ }
2017
+ userErrors {
2018
+ ...UserError
2019
+ }
2020
+ }
2021
+ }
2022
+ ```
2023
+
2024
+ ### Mutation: `CheckoutComplete`
2025
+
2026
+ **Section**: Checkout Mutations
2027
+
2028
+ **Description**: Finalizes the checkout: creates the `Order`, deducts gift cards, sends order-created confirmation — all atomically. **Idempotent on `idempotencyKey`** (NOT on the checkout `id`); auto-generated if the caller omits it. The `paymentUrl` field is reserved but is NOT populated here — for hosted gateways (PayU, P24) the storefront calls a separate `paymentCreate` mutation after this returns.
2029
+
2030
+ **Variables**:
2031
+ - `$id`: `ID!`
2032
+ - `$input`: `CheckoutCompleteInput`
2033
+
2034
+ **Fragments used**: `Checkout`, `Order`, `UserError`
2035
+
2036
+ **GraphQL**:
2037
+ ```graphql
2038
+ mutation CheckoutComplete($id: ID!, $input: CheckoutCompleteInput) {
2039
+ checkoutComplete(id: $id, input: $input) {
2040
+ checkout {
2041
+ ...Checkout
2042
+ }
2043
+ order {
2044
+ ...Order
2045
+ }
2046
+ paymentUrl
2047
+ userErrors {
2048
+ ...UserError
2049
+ }
2050
+ }
2051
+ }
2052
+ ```
2053
+
2054
+ ### Mutation: `CheckoutApplyGiftCard`
2055
+
2056
+ **Section**: Gift Card Checkout Mutations
2057
+
2058
+ **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 at `checkoutComplete`. Errors: `GIFT_CARD_NOT_FOUND`, `GIFT_CARD_DEPLETED`, `GIFT_CARD_UNUSABLE`, `GIFT_CARD_ALREADY_APPLIED`.
2059
+
2060
+ **Variables**:
2061
+ - `$id`: `ID!`
2062
+ - `$giftCardCode`: `String!`
2063
+
2064
+ **Fragments used**: `Checkout`, `UserError`
2065
+
2066
+ **GraphQL**:
2067
+ ```graphql
2068
+ mutation CheckoutApplyGiftCard($id: ID!, $giftCardCode: String!) {
2069
+ checkoutApplyGiftCard(id: $id, giftCardCode: $giftCardCode) {
2070
+ checkout {
2071
+ ...Checkout
2072
+ }
2073
+ userErrors {
2074
+ ...UserError
2075
+ }
2076
+ }
2077
+ }
2078
+ ```
2079
+
2080
+ ### Mutation: `CheckoutRemoveGiftCard`
2081
+
2082
+ **Section**: Gift Card Checkout Mutations
2083
+
2084
+ **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.
2085
+
2086
+ **Variables**:
2087
+ - `$id`: `ID!`
2088
+ - `$giftCardCode`: `String!`
2089
+
2090
+ **Fragments used**: `Checkout`, `UserError`
2091
+
2092
+ **GraphQL**:
2093
+ ```graphql
2094
+ mutation CheckoutRemoveGiftCard($id: ID!, $giftCardCode: String!) {
2095
+ checkoutRemoveGiftCard(id: $id, giftCardCode: $giftCardCode) {
2096
+ checkout {
2097
+ ...Checkout
2098
+ }
2099
+ userErrors {
2100
+ ...UserError
2101
+ }
2102
+ }
2103
+ }
2104
+ ```
2105
+
2106
+ ### Mutation: `CheckoutUpdateGiftCardRecipient`
2107
+
2108
+ **Section**: Gift Card Checkout Mutations
2109
+
2110
+ **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.
2111
+
2112
+ **Variables**:
2113
+ - `$input`: `CheckoutGiftCardRecipientInput!`
2114
+
2115
+ **Fragments used**: `Checkout`, `UserError`
2116
+
2117
+ **GraphQL**:
2118
+ ```graphql
2119
+ mutation CheckoutUpdateGiftCardRecipient($input: CheckoutGiftCardRecipientInput!) {
2120
+ checkoutUpdateGiftCardRecipient(input: $input) {
2121
+ checkout {
2122
+ ...Checkout
2123
+ }
2124
+ userErrors {
2125
+ ...UserError
2126
+ }
2127
+ }
2128
+ }
2129
+ ```
2130
+
2131
+ ### Mutation: `ReturnCreate`
2132
+
2133
+ **Section**: Return Mutations
2134
+
2135
+ **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.
2136
+
2137
+ **Variables**:
2138
+ - `$input`: `ReturnCreateInput!`
2139
+
2140
+ **Fragments used**: `Return`, `UserError`
2141
+
2142
+ **GraphQL**:
2143
+ ```graphql
2144
+ mutation ReturnCreate($input: ReturnCreateInput!) {
2145
+ returnCreate(input: $input) {
2146
+ return {
2147
+ ...Return
2148
+ }
2149
+ userErrors {
2150
+ ...UserError
2151
+ }
2152
+ }
2153
+ }
2154
+ ```
2155
+
2156
+ ### Mutation: `ReturnCancel`
2157
+
2158
+ **Section**: Return Mutations
2159
+
2160
+ **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.
2161
+
2162
+ **Variables**:
2163
+ - `$id`: `ID!`
2164
+
2165
+ **Fragments used**: `Return`, `UserError`
2166
+
2167
+ **GraphQL**:
2168
+ ```graphql
2169
+ mutation ReturnCancel($id: ID!) {
2170
+ returnCancel(id: $id) {
2171
+ return {
2172
+ ...Return
2173
+ }
2174
+ userErrors {
2175
+ ...UserError
2176
+ }
2177
+ }
2178
+ }
2179
+ ```
2180
+
2181
+ ### Mutation: `RedeemLoyaltyReward`
2182
+
2183
+ **Section**: Loyalty Program Mutations
2184
+
2185
+ **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.
2186
+
2187
+ **Variables**:
2188
+ - `$input`: `RedeemRewardInput!`
2189
+
2190
+ **Fragments used**: `RedeemRewardPayload`
2191
+
2192
+ **GraphQL**:
2193
+ ```graphql
2194
+ mutation RedeemLoyaltyReward($input: RedeemRewardInput!) {
2195
+ loyaltyRedeemReward(input: $input) {
2196
+ ...RedeemRewardPayload
2197
+ }
2198
+ }
2199
+ ```
2200
+
2201
+ ### Mutation: `GenerateReferralCode`
2202
+
2203
+ **Section**: Loyalty Program Mutations
2204
+
2205
+ **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.
2206
+
2207
+ **Variables**: none
2208
+
2209
+ **Fragments used**: `GenerateReferralCodePayload`
2210
+
2211
+ **GraphQL**:
2212
+ ```graphql
2213
+ mutation GenerateReferralCode {
2214
+ loyaltyGenerateReferralCode {
2215
+ ...GenerateReferralCodePayload
2216
+ }
2217
+ }
2218
+ ```
2219
+
2220
+ ### Mutation: `ReviewCreate`
2221
+
2222
+ **Section**: Review Mutations
2223
+
2224
+ **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).
2225
+
2226
+ **Variables**:
2227
+ - `$input`: `ReviewCreateInput!`
2228
+
2229
+ **Fragments used**: `ProductReview`, `UserError`
2230
+
2231
+ **GraphQL**:
2232
+ ```graphql
2233
+ mutation ReviewCreate($input: ReviewCreateInput!) {
2234
+ reviewCreate(input: $input) {
2235
+ review {
2236
+ ...ProductReview
2237
+ }
2238
+ userErrors {
2239
+ ...UserError
2240
+ }
2241
+ }
2242
+ }
2243
+ ```
2244
+
2245
+ ### Mutation: `ReviewUpvote`
2246
+
2247
+ **Section**: Review Mutations
2248
+
2249
+ **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).
2250
+
2251
+ **Variables**:
2252
+ - `$reviewId`: `ID!`
2253
+
2254
+ **Fragments used**: `ProductReview`, `UserError`
2255
+
2256
+ **GraphQL**:
2257
+ ```graphql
2258
+ mutation ReviewUpvote($reviewId: ID!) {
2259
+ reviewUpvote(reviewId: $reviewId) {
2260
+ review {
2261
+ ...ProductReview
2262
+ }
2263
+ userErrors {
2264
+ ...UserError
2265
+ }
2266
+ }
2267
+ }
2268
+ ```
2269
+
2270
+ ### Mutation: `ReviewDownvote`
2271
+
2272
+ **Section**: Review Mutations
2273
+
2274
+ **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`.
2275
+
2276
+ **Variables**:
2277
+ - `$reviewId`: `ID!`
2278
+
2279
+ **Fragments used**: `ProductReview`, `UserError`
2280
+
2281
+ **GraphQL**:
2282
+ ```graphql
2283
+ mutation ReviewDownvote($reviewId: ID!) {
2284
+ reviewDownvote(reviewId: $reviewId) {
2285
+ review {
2286
+ ...ProductReview
2287
+ }
2288
+ userErrors {
2289
+ ...UserError
2290
+ }
2291
+ }
2292
+ }
2293
+ ```
2294
+
2295
+ ### Mutation: `WishlistCreate`
2296
+
2297
+ **Section**: Wishlist Mutations
2298
+
2299
+ **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.
2300
+
2301
+ **Variables**:
2302
+ - `$input`: `WishlistCreateInput!`
2303
+
2304
+ **Fragments used**: `UserError`, `Wishlist`
2305
+
2306
+ **GraphQL**:
2307
+ ```graphql
2308
+ mutation WishlistCreate($input: WishlistCreateInput!) {
2309
+ wishlistCreate(input: $input) {
2310
+ wishlist {
2311
+ ...Wishlist
2312
+ }
2313
+ userErrors {
2314
+ ...UserError
2315
+ }
2316
+ }
2317
+ }
2318
+ ```
2319
+
2320
+ ### Mutation: `WishlistAddItem`
2321
+
2322
+ **Section**: Wishlist Mutations
2323
+
2324
+ **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. Captures `priceAtAdd` for price-drop notifications.
2325
+
2326
+ **Variables**:
2327
+ - `$id`: `ID!`
2328
+ - `$input`: `WishlistItemInput!`
2329
+
2330
+ **Fragments used**: `UserError`, `Wishlist`
2331
+
2332
+ **GraphQL**:
2333
+ ```graphql
2334
+ mutation WishlistAddItem($id: ID!, $input: WishlistItemInput!) {
2335
+ wishlistAddItem(id: $id, input: $input) {
2336
+ wishlist {
2337
+ ...Wishlist
2338
+ }
2339
+ userErrors {
2340
+ ...UserError
2341
+ }
2342
+ }
2343
+ }
2344
+ ```
2345
+
2346
+ ### Mutation: `WishlistRemoveItem`
2347
+
2348
+ **Section**: Wishlist Mutations
2349
+
2350
+ **Description**: Hard-deletes a wishlist item by `itemId` (the `WishlistItem` row id, NOT the product id).
2351
+
2352
+ **Variables**:
2353
+ - `$id`: `ID!`
2354
+ - `$itemId`: `ID!`
2355
+
2356
+ **Fragments used**: `UserError`, `Wishlist`
2357
+
2358
+ **GraphQL**:
2359
+ ```graphql
2360
+ mutation WishlistRemoveItem($id: ID!, $itemId: ID!) {
2361
+ wishlistRemoveItem(id: $id, itemId: $itemId) {
2362
+ wishlist {
2363
+ ...Wishlist
2364
+ }
2365
+ userErrors {
2366
+ ...UserError
2367
+ }
2368
+ }
2369
+ }
2370
+ ```
2371
+
2372
+ ### Mutation: `WishlistDelete`
2373
+
2374
+ **Section**: Wishlist Mutations
2375
+
2376
+ **Description**: Hard-deletes the wishlist row. All wishlist items are removed via cascade. No soft-delete; cannot be undone.
2377
+
2378
+ **Variables**:
2379
+ - `$id`: `ID!`
2380
+
2381
+ **Fragments used**: `UserError`, `Wishlist`
2382
+
2383
+ **GraphQL**:
2384
+ ```graphql
2385
+ mutation WishlistDelete($id: ID!) {
2386
+ wishlistDelete(id: $id) {
2387
+ wishlist {
2388
+ ...Wishlist
2389
+ }
2390
+ userErrors {
2391
+ ...UserError
2392
+ }
2393
+ }
2394
+ }
2395
+ ```
2396
+
2397
+ ### Mutation: `CartUpdateAttributes`
2398
+
2399
+ **Section**: Cart Attributes
2400
+
2401
+ **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.
2402
+
2403
+ **Variables**:
2404
+ - `$id`: `ID!`
2405
+ - `$attributes`: `[CartAttributeInput!]!`
2406
+
2407
+ **Fragments used**: `Cart`, `CartWarning`, `UserError`
2408
+
2409
+ **GraphQL**:
2410
+ ```graphql
2411
+ mutation CartUpdateAttributes($id: ID!, $attributes: [CartAttributeInput!]!) {
2412
+ cartUpdateAttributes(id: $id, attributes: $attributes) {
2413
+ cart {
2414
+ ...Cart
2415
+ }
2416
+ userErrors {
2417
+ ...UserError
2418
+ }
2419
+ warnings {
2420
+ ...CartWarning
2421
+ }
2422
+ }
2423
+ }
2424
+ ```
2425
+
2426
+ ---
2427
+
2428
+ ## Fragments
2429
+
2430
+ Fragments are referenced by name in operations above. Each fragment is defined once below.
2431
+
2432
+ ### Fragment: `PageInfo` on `PageInfo`
2433
+
2434
+ **Section**: Common
2435
+
2436
+ **Description**: Standard Relay Connection page metadata. Spread on every paginated query's `pageInfo`.
2437
+
2438
+ **GraphQL**:
2439
+ ```graphql
2440
+ fragment PageInfo on PageInfo {
2441
+ hasNextPage
2442
+ hasPreviousPage
2443
+ startCursor
2444
+ endCursor
2445
+ }
2446
+ ```
2447
+
2448
+ ### Fragment: `UserError` on `UserError`
2449
+
2450
+ **Section**: Common
2451
+
2452
+ **Description**: Generic mutation error envelope — included in every mutation's `userErrors[]` field. Always check before consuming the happy-path payload.
2453
+
2454
+ **GraphQL**:
2455
+ ```graphql
2456
+ fragment UserError on UserError {
2457
+ message
2458
+ code
2459
+ field
2460
+ }
2461
+ ```
2462
+
2463
+ ### Fragment: `CartWarning` on `CartWarning`
2464
+
2465
+ **Section**: Common
2466
+
2467
+ **Description**: Non-blocking cart warning (e.g. low stock, partial availability). Cart mutations succeed but surface these for UI hints.
2468
+
2469
+ **GraphQL**:
2470
+ ```graphql
2471
+ fragment CartWarning on CartWarning {
2472
+ message
2473
+ code
2474
+ target
2475
+ }
2476
+ ```
2477
+
2478
+ ### Fragment: `Image` on `Image`
2479
+
2480
+ **Section**: Common
2481
+
2482
+ **Description**: Base image — original size, no transform. Use for logos, favicons, branding assets that are already sized correctly. ~few KB.
2483
+
2484
+ **GraphQL**:
2485
+ ```graphql
2486
+ fragment Image on Image {
2487
+ id
2488
+ url
2489
+ altText
2490
+ width
2491
+ height
2492
+ }
2493
+ ```
2494
+
2495
+ ### Fragment: `ImageThumbnail` on `Image`
2496
+
2497
+ **Section**: Common
2498
+
2499
+ **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.
2500
+
2501
+ **GraphQL**:
2502
+ ```graphql
2503
+ fragment ImageThumbnail on Image {
2504
+ id
2505
+ url(transform: {maxWidth: 300})
2506
+ altText
2507
+ width
2508
+ height
2509
+ thumbhash
2510
+ }
2511
+ ```
2512
+
2513
+ ### Fragment: `ImageCard` on `Image`
2514
+
2515
+ **Section**: Common
2516
+
2517
+ **Description**: Card — product cards, collection / category tiles, blog post previews (rendered ~400px, transform delivers up to 800px for 2x DPI). Includes `thumbhash` placeholder.
2518
+
2519
+ **GraphQL**:
2520
+ ```graphql
2521
+ fragment ImageCard on Image {
2522
+ id
2523
+ url(transform: {maxWidth: 800})
2524
+ altText
2525
+ width
2526
+ height
2527
+ thumbhash
2528
+ }
2529
+ ```
2530
+
2531
+ ### Fragment: `ImageFull` on `Image`
2532
+
2533
+ **Section**: Common
2534
+
2535
+ **Description**: Full — product detail gallery, hero banners (rendered ~800px, transform delivers up to 1600px for 2x DPI). Includes `thumbhash` placeholder.
2536
+
2537
+ **GraphQL**:
2538
+ ```graphql
2539
+ fragment ImageFull on Image {
2540
+ id
2541
+ url(transform: {maxWidth: 1600})
2542
+ altText
2543
+ width
2544
+ height
2545
+ thumbhash
2546
+ }
2547
+ ```
2548
+
2549
+ ### Fragment: `Money` on `Money`
2550
+
2551
+ **Section**: Common
2552
+
2553
+ **Description**: Plain `{ amount, currencyCode }` price. Use for `compareAtPrice`, totals, and any field where currency conversion transparency is not needed. `amount` is a String.
2554
+
2555
+ **GraphQL**:
2556
+ ```graphql
2557
+ fragment Money on Money {
2558
+ amount
2559
+ currencyCode
2560
+ }
2561
+ ```
2562
+
2563
+ ### Fragment: `SelectedOption` on `SelectedOption`
2564
+
2565
+ **Section**: Common
2566
+
2567
+ **Description**: Selected variant option (e.g. `{ name: "Color", value: "Red" }`) on a `ProductVariant`. Use to render variant labels in cart / order summaries.
2568
+
2569
+ **GraphQL**:
2570
+ ```graphql
2571
+ fragment SelectedOption on SelectedOption {
2572
+ name
2573
+ value
2574
+ }
2575
+ ```
2576
+
2577
+ ### Fragment: `ProductVariant` on `ProductVariant`
2578
+
2579
+ **Section**: Products
2580
+
2581
+ **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.
2582
+
2583
+ **Uses fragments**: `ImageThumbnail`, `Money`, `SelectedOption`
2584
+
2585
+ **GraphQL**:
2586
+ ```graphql
2587
+ fragment ProductVariant on ProductVariant {
2588
+ id
2589
+ title
2590
+ sku
2591
+ price {
2592
+ ...Money
2593
+ }
2594
+ compareAtPrice {
2595
+ ...Money
2596
+ }
2597
+ isAvailable
2598
+ availableStock
2599
+ image {
2600
+ ...ImageThumbnail
2601
+ }
2602
+ selectedOptions {
2603
+ ...SelectedOption
2604
+ }
2605
+ barcode
2606
+ weight {
2607
+ value
2608
+ unit
2609
+ }
2610
+ sortOrder
2611
+ }
2612
+ ```
2613
+
2614
+ ### Fragment: `ProductCard` on `Product`
2615
+
2616
+ **Section**: Products
2617
+
2618
+ **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.
2619
+
2620
+ **Uses fragments**: `ImageCard`, `Money`
2621
+
2622
+ **GraphQL**:
2623
+ ```graphql
2624
+ fragment ProductCard on Product {
2625
+ id
2626
+ handle
2627
+ title
2628
+ vendor
2629
+ categories {
2630
+ id
2631
+ slug
2632
+ name
2633
+ }
2634
+ isAvailable
2635
+ averageRating
2636
+ reviewCount
2637
+ tags
2638
+ featuredImage {
2639
+ ...ImageCard
2640
+ }
2641
+ priceRange {
2642
+ minVariantPrice {
2643
+ ...Money
2644
+ }
2645
+ maxVariantPrice {
2646
+ ...Money
2647
+ }
2648
+ }
2649
+ compareAtPriceRange {
2650
+ minVariantPrice {
2651
+ ...Money
2652
+ }
2653
+ maxVariantPrice {
2654
+ ...Money
2655
+ }
2656
+ }
2657
+ }
2658
+ ```
2659
+
2660
+ ### Fragment: `ProductBase` on `Product`
2661
+
2662
+ **Section**: Products
2663
+
2664
+ **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.
2665
+
2666
+ **Uses fragments**: `ProductCard`
2667
+
2668
+ **GraphQL**:
2669
+ ```graphql
2670
+ fragment ProductBase on Product {
2671
+ ...ProductCard
2672
+ description
2673
+ descriptionHtml
2674
+ stockTotal
2675
+ type
2676
+ requiresRecipientInfo
2677
+ visibility
2678
+ attributeSetId
2679
+ createdAt
2680
+ updatedAt
2681
+ }
2682
+ ```
2683
+
2684
+ ### Fragment: `ProductFull` on `Product`
2685
+
2686
+ **Section**: Products
2687
+
2688
+ **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.
2689
+
2690
+ **Uses fragments**: `ImageFull`, `PageInfo`, `ProductBase`, `ProductVariant`
2691
+
2692
+ **GraphQL**:
2693
+ ```graphql
2694
+ fragment ProductFull on Product {
2695
+ ...ProductBase
2696
+ images(first: 20) {
2697
+ edges {
2698
+ cursor
2699
+ node {
2700
+ ...ImageFull
2701
+ }
2702
+ }
2703
+ nodes {
2704
+ ...ImageFull
2705
+ }
2706
+ pageInfo {
2707
+ ...PageInfo
2708
+ }
2709
+ totalCount
2710
+ }
2711
+ variants(first: 100) {
2712
+ edges {
2713
+ cursor
2714
+ node {
2715
+ ...ProductVariant
2716
+ }
2717
+ }
2718
+ nodes {
2719
+ ...ProductVariant
2720
+ }
2721
+ pageInfo {
2722
+ ...PageInfo
2723
+ }
2724
+ totalCount
2725
+ }
2726
+ seo {
2727
+ title
2728
+ description
2729
+ }
2730
+ }
2731
+ ```
2732
+
2733
+ ### Fragment: `Collection` on `Collection`
2734
+
2735
+ **Section**: Collections
2736
+
2737
+ **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.
2738
+
2739
+ **Uses fragments**: `ImageCard`
2740
+
2741
+ **GraphQL**:
2742
+ ```graphql
2743
+ fragment Collection on Collection {
2744
+ id
2745
+ handle
2746
+ title
2747
+ description
2748
+ descriptionHtml
2749
+ image {
2750
+ ...ImageCard
2751
+ }
2752
+ seo {
2753
+ title
2754
+ description
2755
+ }
2756
+ createdAt
2757
+ updatedAt
2758
+ }
2759
+ ```
2760
+
2761
+ ### Fragment: `Category` on `Category`
2762
+
2763
+ **Section**: Categories
2764
+
2765
+ **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.
2766
+
2767
+ **Uses fragments**: `ImageCard`
2768
+
2769
+ **GraphQL**:
2770
+ ```graphql
2771
+ fragment Category on Category {
2772
+ id
2773
+ name
2774
+ slug
2775
+ description
2776
+ image {
2777
+ ...ImageCard
2778
+ }
2779
+ level
2780
+ path
2781
+ productCount
2782
+ sortOrder
2783
+ }
2784
+ ```
2785
+
2786
+ ### Fragment: `MailingAddress` on `MailingAddress`
2787
+
2788
+ **Section**: Customer
2789
+
2790
+ **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.
2791
+
2792
+ **GraphQL**:
2793
+ ```graphql
2794
+ fragment MailingAddress on MailingAddress {
2795
+ id
2796
+ streetLine1
2797
+ streetLine2
2798
+ city
2799
+ company
2800
+ country
2801
+ countryCode
2802
+ firstName
2803
+ lastName
2804
+ phone
2805
+ state
2806
+ stateCode
2807
+ postalCode
2808
+ isDefault
2809
+ }
2810
+ ```
2811
+
2812
+ ### Fragment: `CustomerAccessToken` on `CustomerAccessToken`
2813
+
2814
+ **Section**: Customer
2815
+
2816
+ **Description**: Customer access token returned by `customerLogin` / `customerSignup` / `customerActivate` / `customerResetPassword` / `customerRefreshToken`. The token's JWT TTL is 24h; cookie max-age is 30d.
2817
+
2818
+ **GraphQL**:
2819
+ ```graphql
2820
+ fragment CustomerAccessToken on CustomerAccessToken {
2821
+ accessToken
2822
+ expiresAt
2823
+ }
2824
+ ```
2825
+
2826
+ ### Fragment: `Customer` on `Customer`
2827
+
2828
+ **Section**: Customer
2829
+
2830
+ **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.
2831
+
2832
+ **Uses fragments**: `MailingAddress`, `Money`
2833
+
2834
+ **GraphQL**:
2835
+ ```graphql
2836
+ fragment Customer on Customer {
2837
+ id
2838
+ email
2839
+ firstName
2840
+ lastName
2841
+ displayName
2842
+ phone
2843
+ isEmailVerified
2844
+ emailMarketing
2845
+ tags
2846
+ customerType
2847
+ companyName
2848
+ taxId
2849
+ vatNumber
2850
+ regon
2851
+ defaultAddress {
2852
+ ...MailingAddress
2853
+ }
2854
+ orderCount
2855
+ totalSpent {
2856
+ ...Money
2857
+ }
2858
+ createdAt
2859
+ updatedAt
2860
+ }
2861
+ ```
2862
+
2863
+ ### Fragment: `Order` on `Order`
2864
+
2865
+ **Section**: Customer
2866
+
2867
+ **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.
2868
+
2869
+ **Uses fragments**: `MailingAddress`, `Money`
2870
+
2871
+ **GraphQL**:
2872
+ ```graphql
2873
+ fragment Order on Order {
2874
+ id
2875
+ orderNumber
2876
+ totals {
2877
+ total {
2878
+ ...Money
2879
+ }
2880
+ subtotal {
2881
+ ...Money
2882
+ }
2883
+ totalTax {
2884
+ ...Money
2885
+ }
2886
+ totalShipping {
2887
+ ...Money
2888
+ }
2889
+ }
2890
+ status
2891
+ paymentStatus
2892
+ fulfillmentStatus
2893
+ processedAt
2894
+ confirmedAt
2895
+ cancelledAt
2896
+ expiredAt
2897
+ shippingAddress {
2898
+ ...MailingAddress
2899
+ }
2900
+ itemCount
2901
+ }
2902
+ ```
2903
+
2904
+ ### Fragment: `CartCost` on `CartCost`
2905
+
2906
+ **Section**: Cart
2907
+
2908
+ **Description**: Cart-level totals — subtotal, total, tax, duty, checkout charge. Spread inside the `Cart` fragment.
2909
+
2910
+ **Uses fragments**: `Money`
2911
+
2912
+ **GraphQL**:
2913
+ ```graphql
2914
+ fragment CartCost on CartCost {
2915
+ total {
2916
+ ...Money
2917
+ }
2918
+ subtotal {
2919
+ ...Money
2920
+ }
2921
+ totalTax {
2922
+ ...Money
2923
+ }
2924
+ totalDuty {
2925
+ ...Money
2926
+ }
2927
+ checkoutCharge {
2928
+ ...Money
2929
+ }
2930
+ }
2931
+ ```
2932
+
2933
+ ### Fragment: `CartLineCost` on `CartLineCost`
2934
+
2935
+ **Section**: Cart
2936
+
2937
+ **Description**: Per-line cost breakdown — unit price, line subtotal/total, compare-at unit price (for showing strikethroughs). Spread inside `CartLine`.
2938
+
2939
+ **Uses fragments**: `Money`
2940
+
2941
+ **GraphQL**:
2942
+ ```graphql
2943
+ fragment CartLineCost on CartLineCost {
2944
+ pricePerUnit {
2945
+ ...Money
2946
+ }
2947
+ subtotal {
2948
+ ...Money
2949
+ }
2950
+ total {
2951
+ ...Money
2952
+ }
2953
+ compareAtPricePerUnit {
2954
+ ...Money
2955
+ }
2956
+ }
2957
+ ```
2958
+
2959
+ ### Fragment: `AttributeSelection` on `AttributeSelection`
2960
+
2961
+ **Section**: Cart
2962
+
2963
+ **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).
2964
+
2965
+ **GraphQL**:
2966
+ ```graphql
2967
+ fragment AttributeSelection on AttributeSelection {
2968
+ attributeDefinitionId
2969
+ attributeName
2970
+ type
2971
+ fillingMode
2972
+ billingMode
2973
+ optionId
2974
+ optionLabel
2975
+ optionIds
2976
+ textValue
2977
+ surchargeAmount
2978
+ surchargeType
2979
+ taxClassId
2980
+ linkedVariantId
2981
+ }
2982
+ ```
2983
+
2984
+ ### Fragment: `CartLine` on `CartLine`
2985
+
2986
+ **Section**: Cart
2987
+
2988
+ **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.
2989
+
2990
+ **Uses fragments**: `AttributeSelection`, `CartLineCost`, `ProductVariant`
2991
+
2992
+ **GraphQL**:
2993
+ ```graphql
2994
+ fragment CartLine on CartLine {
2995
+ id
2996
+ quantity
2997
+ variant {
2998
+ ...ProductVariant
2999
+ }
3000
+ cost {
3001
+ ...CartLineCost
3002
+ }
3003
+ attributes {
3004
+ key
3005
+ value
3006
+ }
3007
+ attributeSelections {
3008
+ ...AttributeSelection
3009
+ }
3010
+ productId
3011
+ productTitle
3012
+ productHandle
3013
+ productType
3014
+ }
3015
+ ```
3016
+
3017
+ ### Fragment: `CartBuyerIdentity` on `CartBuyerIdentity`
3018
+
3019
+ **Section**: Cart
3020
+
3021
+ **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.
3022
+
3023
+ **GraphQL**:
3024
+ ```graphql
3025
+ fragment CartBuyerIdentity on CartBuyerIdentity {
3026
+ email
3027
+ phone
3028
+ countryCode
3029
+ }
3030
+ ```
3031
+
3032
+ ### Fragment: `CartDiscountCode` on `CartDiscountCode`
3033
+
3034
+ **Section**: Cart
3035
+
3036
+ **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).
3037
+
3038
+ **GraphQL**:
3039
+ ```graphql
3040
+ fragment CartDiscountCode on CartDiscountCode {
3041
+ code
3042
+ isApplicable
3043
+ }
3044
+ ```
3045
+
3046
+ ### Fragment: `CartDiscountAllocation` on `CartDiscountAllocation`
3047
+
3048
+ **Section**: Cart
3049
+
3050
+ **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.
3051
+
3052
+ **Uses fragments**: `Money`
3053
+
3054
+ **GraphQL**:
3055
+ ```graphql
3056
+ fragment CartDiscountAllocation on CartDiscountAllocation {
3057
+ discountCode
3058
+ amount {
3059
+ ...Money
3060
+ }
3061
+ }
3062
+ ```
3063
+
3064
+ ### Fragment: `Cart` on `Cart`
3065
+
3066
+ **Section**: Cart
3067
+
3068
+ **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.
3069
+
3070
+ **Uses fragments**: `CartBuyerIdentity`, `CartCost`, `CartDiscountAllocation`, `CartDiscountCode`, `CartLine`, `PageInfo`
3071
+
3072
+ **GraphQL**:
3073
+ ```graphql
3074
+ fragment Cart on Cart {
3075
+ id
3076
+ checkoutUrl
3077
+ totalQuantity
3078
+ cost {
3079
+ ...CartCost
3080
+ }
3081
+ lines(first: 100) {
3082
+ edges {
3083
+ cursor
3084
+ node {
3085
+ ... on CartLine {
3086
+ ...CartLine
3087
+ }
3088
+ }
3089
+ }
3090
+ nodes {
3091
+ ... on CartLine {
3092
+ ...CartLine
3093
+ }
3094
+ }
3095
+ pageInfo {
3096
+ ...PageInfo
3097
+ }
3098
+ totalCount
3099
+ }
3100
+ buyerIdentity {
3101
+ ...CartBuyerIdentity
3102
+ }
3103
+ discountCodes {
3104
+ ...CartDiscountCode
3105
+ }
3106
+ discountAllocations {
3107
+ ...CartDiscountAllocation
3108
+ }
3109
+ note
3110
+ attributes {
3111
+ key
3112
+ value
3113
+ }
3114
+ createdAt
3115
+ updatedAt
3116
+ }
3117
+ ```
3118
+
3119
+ ### Fragment: `ShopColors` on `ShopColors`
3120
+
3121
+ **Section**: Shop
3122
+
3123
+ **Description**: Shop branding colors — primary, secondary, accent, background, text. Use to theme the storefront from server-side config.
3124
+
3125
+ **GraphQL**:
3126
+ ```graphql
3127
+ fragment ShopColors on ShopColors {
3128
+ primary
3129
+ secondary
3130
+ accent
3131
+ background
3132
+ text
3133
+ }
3134
+ ```
3135
+
3136
+ ### Fragment: `ShopFonts` on `ShopFonts`
3137
+
3138
+ **Section**: Shop
3139
+
3140
+ **Description**: Shop branding fonts — body (`primary`) and `heading` font families.
3141
+
3142
+ **GraphQL**:
3143
+ ```graphql
3144
+ fragment ShopFonts on ShopFonts {
3145
+ primary
3146
+ heading
3147
+ }
3148
+ ```
3149
+
3150
+ ### Fragment: `SocialLinks` on `SocialLinks`
3151
+
3152
+ **Section**: Shop
3153
+
3154
+ **Description**: Social media links configured for the shop. Spread on the footer.
3155
+
3156
+ **GraphQL**:
3157
+ ```graphql
3158
+ fragment SocialLinks on SocialLinks {
3159
+ facebook
3160
+ instagram
3161
+ twitter
3162
+ youtube
3163
+ tiktok
3164
+ linkedin
3165
+ pinterest
3166
+ }
3167
+ ```
3168
+
3169
+ ### Fragment: `ShopAddress` on `ShopAddress`
3170
+
3171
+ **Section**: Shop
3172
+
3173
+ **Description**: Shop's physical / business address. Use on the contact page and footer.
3174
+
3175
+ **GraphQL**:
3176
+ ```graphql
3177
+ fragment ShopAddress on ShopAddress {
3178
+ streetLine1
3179
+ streetLine2
3180
+ city
3181
+ state
3182
+ postalCode
3183
+ country
3184
+ }
3185
+ ```
3186
+
3187
+ ### Fragment: `BusinessHour` on `BusinessHour`
3188
+
3189
+ **Section**: Shop
3190
+
3191
+ **Description**: One day's business hours window. Spread inside `Shop.businessHours[]`.
3192
+
3193
+ **GraphQL**:
3194
+ ```graphql
3195
+ fragment BusinessHour on BusinessHour {
3196
+ day
3197
+ openTime
3198
+ closeTime
3199
+ isClosed
3200
+ }
3201
+ ```
3202
+
3203
+ ### Fragment: `ShopBranding` on `ShopBranding`
3204
+
3205
+ **Section**: Shop
3206
+
3207
+ **Description**: Full shop branding bundle — logo + favicon + colors + fonts + social links. Spread inside the `Shop` fragment.
3208
+
3209
+ **Uses fragments**: `Image`, `ShopColors`, `ShopFonts`, `SocialLinks`
3210
+
3211
+ **GraphQL**:
3212
+ ```graphql
3213
+ fragment ShopBranding on ShopBranding {
3214
+ logo {
3215
+ ...Image
3216
+ }
3217
+ favicon {
3218
+ ...Image
3219
+ }
3220
+ colors {
3221
+ ...ShopColors
3222
+ }
3223
+ fonts {
3224
+ ...ShopFonts
3225
+ }
3226
+ socialLinks {
3227
+ ...SocialLinks
3228
+ }
3229
+ }
3230
+ ```
3231
+
3232
+ ### Fragment: `BotProtectionProvider` on `BotProtectionProviderInfo`
3233
+
3234
+ **Section**: Shop
3235
+
3236
+ **Description**: Bot-protection provider config (provider name, site key, script URL) for storefront-side challenge widgets.
3237
+
3238
+ **GraphQL**:
3239
+ ```graphql
3240
+ fragment BotProtectionProvider on BotProtectionProviderInfo {
3241
+ provider
3242
+ siteKey
3243
+ scriptUrl
3244
+ }
3245
+ ```
3246
+
3247
+ ### Fragment: `BotProtection` on `BotProtectionInfo`
3248
+
3249
+ **Section**: Shop
3250
+
3251
+ **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`.
3252
+
3253
+ **Uses fragments**: `BotProtectionProvider`
3254
+
3255
+ **GraphQL**:
3256
+ ```graphql
3257
+ fragment BotProtection on BotProtectionInfo {
3258
+ primary {
3259
+ ...BotProtectionProvider
3260
+ }
3261
+ fallback {
3262
+ ...BotProtectionProvider
3263
+ }
3264
+ protectedOperations
3265
+ }
3266
+ ```
3267
+
3268
+ ### Fragment: `Shop` on `Shop`
3269
+
3270
+ **Section**: Shop
3271
+
3272
+ **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.
3273
+
3274
+ **Uses fragments**: `BotProtection`, `BusinessHour`, `Image`, `ShopAddress`, `ShopBranding`
3275
+
3276
+ **GraphQL**:
3277
+ ```graphql
3278
+ fragment Shop on Shop {
3279
+ id
3280
+ name
3281
+ description
3282
+ primaryDomain {
3283
+ host
3284
+ url
3285
+ isSslEnabled
3286
+ }
3287
+ currencyCode
3288
+ supportedCurrencies
3289
+ paymentCurrencies
3290
+ defaultLanguage
3291
+ supportedLanguages
3292
+ logo {
3293
+ ...Image
3294
+ }
3295
+ contactEmail
3296
+ contactPhone
3297
+ address {
3298
+ ...ShopAddress
3299
+ }
3300
+ businessHours {
3301
+ ...BusinessHour
3302
+ }
3303
+ branding {
3304
+ ...ShopBranding
3305
+ }
3306
+ botProtection {
3307
+ ...BotProtection
3308
+ }
3309
+ }
3310
+ ```
3311
+
3312
+ ### Fragment: `PaymentMethod` on `PaymentMethod`
3313
+
3314
+ **Section**: Payment Methods
3315
+
3316
+ **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.
3317
+
3318
+ **GraphQL**:
3319
+ ```graphql
3320
+ fragment PaymentMethod on PaymentMethod {
3321
+ id
3322
+ name
3323
+ provider
3324
+ type
3325
+ icon
3326
+ description
3327
+ isDefault
3328
+ supportedCurrencies
3329
+ position
3330
+ }
3331
+ ```
3332
+
3333
+ ### Fragment: `AvailablePaymentMethods` on `AvailablePaymentMethods`
3334
+
3335
+ **Section**: Payment Methods
3336
+
3337
+ **Description**: Active payment methods list with the merchant's `defaultMethod`. Returned by the `availablePaymentMethods` query.
3338
+
3339
+ **Uses fragments**: `PaymentMethod`
3340
+
3341
+ **GraphQL**:
3342
+ ```graphql
3343
+ fragment AvailablePaymentMethods on AvailablePaymentMethods {
3344
+ methods {
3345
+ ...PaymentMethod
3346
+ }
3347
+ defaultMethod {
3348
+ ...PaymentMethod
3349
+ }
3350
+ }
3351
+ ```
3352
+
3353
+ ### Fragment: `ShippingRate` on `ShippingRate`
3354
+
3355
+ **Section**: Checkout
3356
+
3357
+ **Description**: Single shipping rate option — `handle` is the stable id you pass to `checkoutSelectShippingRate` as `rateId`, plus title and price.
3358
+
3359
+ **Uses fragments**: `Money`
3360
+
3361
+ **GraphQL**:
3362
+ ```graphql
3363
+ fragment ShippingRate on ShippingRate {
3364
+ handle
3365
+ title
3366
+ price {
3367
+ ...Money
3368
+ }
3369
+ }
3370
+ ```
3371
+
3372
+ ### Fragment: `TaxLine` on `TaxLine`
3373
+
3374
+ **Section**: Checkout
3375
+
3376
+ **Description**: One tax line on the checkout — title, rate (decimal, e.g. 0.23 for 23%), computed amount. Spread on the order summary.
3377
+
3378
+ **Uses fragments**: `Money`
3379
+
3380
+ **GraphQL**:
3381
+ ```graphql
3382
+ fragment TaxLine on TaxLine {
3383
+ title
3384
+ rate
3385
+ price {
3386
+ ...Money
3387
+ }
3388
+ }
3389
+ ```
3390
+
3391
+ ### Fragment: `DiscountAffectedItem` on `DiscountAffectedItem`
3392
+
3393
+ **Section**: Checkout
3394
+
3395
+ **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.
3396
+
3397
+ **Uses fragments**: `Money`
3398
+
3399
+ **GraphQL**:
3400
+ ```graphql
3401
+ fragment DiscountAffectedItem on DiscountAffectedItem {
3402
+ productId
3403
+ variantId
3404
+ title
3405
+ quantity
3406
+ originalPrice {
3407
+ ...Money
3408
+ }
3409
+ discountedPrice {
3410
+ ...Money
3411
+ }
3412
+ savings {
3413
+ ...Money
3414
+ }
3415
+ isFreeItem
3416
+ }
3417
+ ```
3418
+
3419
+ ### Fragment: `DiscountApplication` on `DiscountApplication`
3420
+
3421
+ **Section**: Checkout
3422
+
3423
+ **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.
3424
+
3425
+ **Uses fragments**: `DiscountAffectedItem`, `Money`
3426
+
3427
+ **GraphQL**:
3428
+ ```graphql
3429
+ fragment DiscountApplication on DiscountApplication {
3430
+ code
3431
+ isApplicable
3432
+ type
3433
+ value {
3434
+ ...Money
3435
+ }
3436
+ title
3437
+ affectedItems {
3438
+ ...DiscountAffectedItem
3439
+ }
3440
+ buyQuantity
3441
+ getQuantity
3442
+ getDiscountPercent
3443
+ }
3444
+ ```
3445
+
3446
+ ### Fragment: `DiscountCode` on `DiscountCode`
3447
+
3448
+ **Section**: Checkout
3449
+
3450
+ **Description**: Lightweight discount code entry on the checkout — code + applicability flag.
3451
+
3452
+ **GraphQL**:
3453
+ ```graphql
3454
+ fragment DiscountCode on DiscountCode {
3455
+ code
3456
+ isApplicable
3457
+ }
3458
+ ```
3459
+
3460
+ ### Fragment: `CheckoutLineItem` on `CheckoutLineItem`
3461
+
3462
+ **Section**: Checkout
3463
+
3464
+ **Description**: Single line item in the checkout — variant snapshot, quantity, unit + total prices, image. Use on the order summary panel.
3465
+
3466
+ **Uses fragments**: `ImageThumbnail`, `Money`
3467
+
3468
+ **GraphQL**:
3469
+ ```graphql
3470
+ fragment CheckoutLineItem on CheckoutLineItem {
3471
+ id
3472
+ title
3473
+ variantTitle
3474
+ quantity
3475
+ pricePerUnit {
3476
+ ...Money
3477
+ }
3478
+ total {
3479
+ ...Money
3480
+ }
3481
+ variantId
3482
+ productId
3483
+ sku
3484
+ image {
3485
+ ...ImageThumbnail
3486
+ }
3487
+ }
3488
+ ```
3489
+
3490
+ ### Fragment: `AppliedGiftCard` on `AppliedGiftCard`
3491
+
3492
+ **Section**: Checkout
3493
+
3494
+ **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.
3495
+
3496
+ **Uses fragments**: `Money`
3497
+
3498
+ **GraphQL**:
3499
+ ```graphql
3500
+ fragment AppliedGiftCard on AppliedGiftCard {
3501
+ maskedCode
3502
+ lastCharacters
3503
+ appliedAmount {
3504
+ ...Money
3505
+ }
3506
+ remainingBalance {
3507
+ ...Money
3508
+ }
3509
+ }
3510
+ ```
3511
+
3512
+ ### Fragment: `Checkout` on `Checkout`
3513
+
3514
+ **Section**: Checkout
3515
+
3516
+ **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`.
3517
+
3518
+ **Uses fragments**: `AppliedGiftCard`, `CheckoutLineItem`, `DiscountApplication`, `DiscountCode`, `MailingAddress`, `Money`, `PaymentMethod`, `ShippingRate`, `TaxLine`
3519
+
3520
+ **GraphQL**:
3521
+ ```graphql
3522
+ fragment Checkout on Checkout {
3523
+ id
3524
+ isReady
3525
+ isCompleted
3526
+ completedOrderId
3527
+ webUrl
3528
+ lineItems {
3529
+ ...CheckoutLineItem
3530
+ }
3531
+ totalQuantity
3532
+ shippingAddress {
3533
+ ...MailingAddress
3534
+ }
3535
+ billingAddress {
3536
+ ...MailingAddress
3537
+ }
3538
+ shippingLine {
3539
+ ...ShippingRate
3540
+ }
3541
+ availableShippingRates {
3542
+ ...ShippingRate
3543
+ }
3544
+ shippingRatesReady
3545
+ email
3546
+ phone
3547
+ customerId
3548
+ discountCodes {
3549
+ ...DiscountCode
3550
+ }
3551
+ discountApplications {
3552
+ ...DiscountApplication
3553
+ }
3554
+ availablePaymentMethods {
3555
+ ...PaymentMethod
3556
+ }
3557
+ selectedPaymentMethodId
3558
+ cost {
3559
+ subtotal {
3560
+ ...Money
3561
+ }
3562
+ total {
3563
+ ...Money
3564
+ }
3565
+ totalTax {
3566
+ ...Money
3567
+ }
3568
+ totalShipping {
3569
+ ...Money
3570
+ }
3571
+ totalDiscounts {
3572
+ ...Money
3573
+ }
3574
+ }
3575
+ taxLines {
3576
+ ...TaxLine
3577
+ }
3578
+ appliedGiftCards {
3579
+ ...AppliedGiftCard
3580
+ }
3581
+ totalGiftCardAmount {
3582
+ ...Money
3583
+ }
3584
+ paymentDue {
3585
+ ...Money
3586
+ }
3587
+ currencyCode
3588
+ note
3589
+ createdAt
3590
+ updatedAt
3591
+ }
3592
+ ```
3593
+
3594
+ ### Fragment: `ShipmentEvent` on `ShipmentEvent`
3595
+
3596
+ **Section**: Shipments / Tracking
3597
+
3598
+ **Description**: One tracking event in the shipment timeline — status, description, location, timestamp. Spread on the tracking page timeline.
3599
+
3600
+ **GraphQL**:
3601
+ ```graphql
3602
+ fragment ShipmentEvent on ShipmentEvent {
3603
+ id
3604
+ status
3605
+ description
3606
+ location
3607
+ occurredAt
3608
+ providerEventCode
3609
+ }
3610
+ ```
3611
+
3612
+ ### Fragment: `ShipmentPackage` on `ShipmentPackage`
3613
+
3614
+ **Section**: Shipments / Tracking
3615
+
3616
+ **Description**: Physical package metadata (weight + dimensions). Used in the merchant's label generation; storefronts rarely render this directly.
3617
+
3618
+ **GraphQL**:
3619
+ ```graphql
3620
+ fragment ShipmentPackage on ShipmentPackage {
3621
+ weight
3622
+ length
3623
+ width
3624
+ height
3625
+ }
3626
+ ```
3627
+
3628
+ ### Fragment: `ShipmentItem` on `ShipmentItem`
3629
+
3630
+ **Section**: Shipments / Tracking
3631
+
3632
+ **Description**: One item in a shipment — title, variant, sku, quantity, image URL. Spread inside `Shipment.items[]` for the tracking page item list.
3633
+
3634
+ **GraphQL**:
3635
+ ```graphql
3636
+ fragment ShipmentItem on ShipmentItem {
3637
+ id
3638
+ title
3639
+ variantTitle
3640
+ sku
3641
+ quantity
3642
+ imageUrl
3643
+ }
3644
+ ```
3645
+
3646
+ ### Fragment: `Shipment` on `Shipment`
3647
+
3648
+ **Section**: Shipments / Tracking
3649
+
3650
+ **Description**: Full shipment shape — provider, tracking number + URL, label URL, status, ETA, recipient address, packages, items, full event timeline. Spread on the tracking page.
3651
+
3652
+ **Uses fragments**: `MailingAddress`, `ShipmentEvent`, `ShipmentItem`, `ShipmentPackage`
3653
+
3654
+ **GraphQL**:
3655
+ ```graphql
3656
+ fragment Shipment on Shipment {
3657
+ id
3658
+ orderId
3659
+ provider
3660
+ serviceCode
3661
+ trackingNumber
3662
+ trackingUrl
3663
+ labelUrl
3664
+ status
3665
+ estimatedDeliveryAt
3666
+ shippedAt
3667
+ deliveredAt
3668
+ recipientAddress {
3669
+ ...MailingAddress
3670
+ }
3671
+ packages {
3672
+ ...ShipmentPackage
3673
+ }
3674
+ items {
3675
+ ...ShipmentItem
3676
+ }
3677
+ events {
3678
+ ...ShipmentEvent
3679
+ }
3680
+ createdAt
3681
+ updatedAt
3682
+ }
3683
+ ```
3684
+
3685
+ ### Fragment: `ShipmentBasic` on `Shipment`
3686
+
3687
+ **Section**: Shipments / Tracking
3688
+
3689
+ **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.
3690
+
3691
+ **GraphQL**:
3692
+ ```graphql
3693
+ fragment ShipmentBasic on Shipment {
3694
+ id
3695
+ orderId
3696
+ provider
3697
+ trackingNumber
3698
+ trackingUrl
3699
+ status
3700
+ estimatedDeliveryAt
3701
+ shippedAt
3702
+ deliveredAt
3703
+ createdAt
3704
+ }
3705
+ ```
3706
+
3707
+ ### Fragment: `ReturnShippingLabel` on `ReturnShippingLabel`
3708
+
3709
+ **Section**: Returns / RMA
3710
+
3711
+ **Description**: Return shipping label generated by the carrier — URL to download, carrier name, tracking number, expiry. Spread on the return detail page.
3712
+
3713
+ **GraphQL**:
3714
+ ```graphql
3715
+ fragment ReturnShippingLabel on ReturnShippingLabel {
3716
+ url
3717
+ carrier
3718
+ trackingNumber
3719
+ expiresAt
3720
+ }
3721
+ ```
3722
+
3723
+ ### Fragment: `ReturnItemPhoto` on `ReturnItemPhoto`
3724
+
3725
+ **Section**: Returns / RMA
3726
+
3727
+ **Description**: Photo evidence attached to a return item (e.g. damage photo). Spread inside `ReturnItem.photos[]`.
3728
+
3729
+ **GraphQL**:
3730
+ ```graphql
3731
+ fragment ReturnItemPhoto on ReturnItemPhoto {
3732
+ id
3733
+ url
3734
+ alt
3735
+ description
3736
+ createdAt
3737
+ }
3738
+ ```
3739
+
3740
+ ### Fragment: `ReturnItem` on `ReturnItem`
3741
+
3742
+ **Section**: Returns / RMA
3743
+
3744
+ **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[]`.
3745
+
3746
+ **Uses fragments**: `Money`, `ReturnItemPhoto`
3747
+
3748
+ **GraphQL**:
3749
+ ```graphql
3750
+ fragment ReturnItem on ReturnItem {
3751
+ id
3752
+ variantId
3753
+ productTitle
3754
+ variantTitle
3755
+ sku
3756
+ imageUrl
3757
+ quantity
3758
+ reason
3759
+ condition
3760
+ unitPrice {
3761
+ ...Money
3762
+ }
3763
+ photos {
3764
+ ...ReturnItemPhoto
3765
+ }
3766
+ status
3767
+ approvedQuantity
3768
+ }
3769
+ ```
3770
+
3771
+ ### Fragment: `Return` on `Return`
3772
+
3773
+ **Section**: Returns / RMA
3774
+
3775
+ **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.
3776
+
3777
+ **Uses fragments**: `Money`, `ReturnItem`, `ReturnShippingLabel`
3778
+
3779
+ **GraphQL**:
3780
+ ```graphql
3781
+ fragment Return on Return {
3782
+ id
3783
+ returnNumber
3784
+ orderId
3785
+ orderNumber
3786
+ status
3787
+ reason
3788
+ customerNote
3789
+ compensationType
3790
+ items {
3791
+ ...ReturnItem
3792
+ }
3793
+ refundAmount {
3794
+ ...Money
3795
+ }
3796
+ shippingLabel {
3797
+ ...ReturnShippingLabel
3798
+ }
3799
+ requestedAt
3800
+ approvedAt
3801
+ receivedAt
3802
+ refundedAt
3803
+ completedAt
3804
+ createdAt
3805
+ updatedAt
3806
+ }
3807
+ ```
3808
+
3809
+ ### Fragment: `ReturnReasonOption` on `ReturnReasonOption`
3810
+
3811
+ **Section**: Returns / RMA
3812
+
3813
+ **Description**: Single option in the return reasons dropdown — `value` enum, human-readable `label`, optional `description`. Returned by `returnReasons` query.
3814
+
3815
+ **GraphQL**:
3816
+ ```graphql
3817
+ fragment ReturnReasonOption on ReturnReasonOption {
3818
+ value
3819
+ label
3820
+ description
3821
+ }
3822
+ ```
3823
+
3824
+ ### Fragment: `GiftCard` on `GiftCard`
3825
+
3826
+ **Section**: Gift Cards
3827
+
3828
+ **Description**: Gift card detail — masked code (first + last 4 chars), status, initial + remaining balance, expiry, recipient/message. Returned by `giftCard($code)` query.
3829
+
3830
+ **Uses fragments**: `Money`
3831
+
3832
+ **GraphQL**:
3833
+ ```graphql
3834
+ fragment GiftCard on GiftCard {
3835
+ id
3836
+ maskedCode
3837
+ status
3838
+ initialAmount {
3839
+ ...Money
3840
+ }
3841
+ balance {
3842
+ ...Money
3843
+ }
3844
+ expiresAt
3845
+ recipientName
3846
+ message
3847
+ createdAt
3848
+ }
3849
+ ```
3850
+
3851
+ ### Fragment: `GiftCardError` on `GiftCardError`
3852
+
3853
+ **Section**: Gift Cards
3854
+
3855
+ **Description**: Validation error on a gift card check — code (`NOT_FOUND` / `DISABLED` / `ALREADY_USED` / `EXPIRED` / `INSUFFICIENT_BALANCE`) + human message.
3856
+
3857
+ **GraphQL**:
3858
+ ```graphql
3859
+ fragment GiftCardError on GiftCardError {
3860
+ code
3861
+ message
3862
+ }
3863
+ ```
3864
+
3865
+ ### Fragment: `GiftCardValidation` on `GiftCardValidation`
3866
+
3867
+ **Section**: Gift Cards
3868
+
3869
+ **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.
3870
+
3871
+ **Uses fragments**: `GiftCard`, `GiftCardError`, `Money`
3872
+
3873
+ **GraphQL**:
3874
+ ```graphql
3875
+ fragment GiftCardValidation on GiftCardValidation {
3876
+ isValid
3877
+ availableBalance {
3878
+ ...Money
3879
+ }
3880
+ error {
3881
+ ...GiftCardError
3882
+ }
3883
+ giftCard {
3884
+ ...GiftCard
3885
+ }
3886
+ }
3887
+ ```
3888
+
3889
+ ### Fragment: `ShippingCarrier` on `ShippingCarrier`
3890
+
3891
+ **Section**: Shipping Methods
3892
+
3893
+ **Description**: Carrier offering the shipping method — id, display name, logo URL, internal service code.
3894
+
3895
+ **GraphQL**:
3896
+ ```graphql
3897
+ fragment ShippingCarrier on ShippingCarrier {
3898
+ id
3899
+ name
3900
+ logoUrl
3901
+ serviceCode
3902
+ }
3903
+ ```
3904
+
3905
+ ### Fragment: `DeliveryEstimate` on `DeliveryEstimate`
3906
+
3907
+ **Section**: Shipping Methods
3908
+
3909
+ **Description**: Estimated delivery window — `minDays` to `maxDays` plus a human-readable description (e.g. "2-4 business days").
3910
+
3911
+ **GraphQL**:
3912
+ ```graphql
3913
+ fragment DeliveryEstimate on DeliveryEstimate {
3914
+ minDays
3915
+ maxDays
3916
+ description
3917
+ }
3918
+ ```
3919
+
3920
+ ### Fragment: `FreeShippingProgress` on `FreeShippingProgress`
3921
+
3922
+ **Section**: Shipping Methods
3923
+
3924
+ **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.
3925
+
3926
+ **Uses fragments**: `Money`
3927
+
3928
+ **GraphQL**:
3929
+ ```graphql
3930
+ fragment FreeShippingProgress on FreeShippingProgress {
3931
+ qualifies
3932
+ currentAmount {
3933
+ ...Money
3934
+ }
3935
+ threshold {
3936
+ ...Money
3937
+ }
3938
+ remaining {
3939
+ ...Money
3940
+ }
3941
+ progressPercent
3942
+ message
3943
+ }
3944
+ ```
3945
+
3946
+ ### Fragment: `AvailableShippingMethod` on `AvailableShippingMethod`
3947
+
3948
+ **Section**: Shipping Methods
3949
+
3950
+ **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.
3951
+
3952
+ **Uses fragments**: `DeliveryEstimate`, `FreeShippingProgress`, `Money`, `ShippingCarrier`
3953
+
3954
+ **GraphQL**:
3955
+ ```graphql
3956
+ fragment AvailableShippingMethod on AvailableShippingMethod {
3957
+ id
3958
+ name
3959
+ description
3960
+ carrier {
3961
+ ...ShippingCarrier
3962
+ }
3963
+ price {
3964
+ ...Money
3965
+ }
3966
+ isFree
3967
+ estimatedDelivery {
3968
+ ...DeliveryEstimate
3969
+ }
3970
+ freeShippingProgress {
3971
+ ...FreeShippingProgress
3972
+ }
3973
+ sortOrder
3974
+ }
3975
+ ```
3976
+
3977
+ ### Fragment: `AttributeSwatch` on `AttributeSwatch`
3978
+
3979
+ **Section**: Attribute Filters
3980
+
3981
+ **Description**: Visual swatch for an attribute filter value — color hex (for color filters) or image URL (for pattern/material swatches).
3982
+
3983
+ **GraphQL**:
3984
+ ```graphql
3985
+ fragment AttributeSwatch on AttributeSwatch {
3986
+ colorHex
3987
+ imageUrl
3988
+ }
3989
+ ```
3990
+
3991
+ ### Fragment: `AttributeRangeBounds` on `AttributeRangeBounds`
3992
+
3993
+ **Section**: Attribute Filters
3994
+
3995
+ **Description**: Numeric range bounds for slider-style filters — min, max, currency code (when relevant).
3996
+
3997
+ **GraphQL**:
3998
+ ```graphql
3999
+ fragment AttributeRangeBounds on AttributeRangeBounds {
4000
+ min
4001
+ max
4002
+ currencyCode
4003
+ }
4004
+ ```
4005
+
4006
+ ### Fragment: `AttributeFilterValue` on `AttributeFilterValue`
4007
+
4008
+ **Section**: Attribute Filters
4009
+
4010
+ **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.
4011
+
4012
+ **Uses fragments**: `AttributeSwatch`, `Money`
4013
+
4014
+ **GraphQL**:
4015
+ ```graphql
4016
+ fragment AttributeFilterValue on AttributeFilterValue {
4017
+ id
4018
+ value
4019
+ label
4020
+ productCount
4021
+ swatch {
4022
+ ...AttributeSwatch
4023
+ }
4024
+ priceModifier {
4025
+ ...Money
4026
+ }
4027
+ sortOrder
4028
+ }
4029
+ ```
4030
+
4031
+ ### Fragment: `AttributeDefinition` on `AttributeDefinition`
4032
+
4033
+ **Section**: Attribute Filters
4034
+
4035
+ **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[]`.
4036
+
4037
+ **Uses fragments**: `AttributeFilterValue`, `AttributeRangeBounds`
4038
+
4039
+ **GraphQL**:
4040
+ ```graphql
4041
+ fragment AttributeDefinition on AttributeDefinition {
4042
+ id
4043
+ name
4044
+ handle
4045
+ type
4046
+ isFilterable
4047
+ isVisible
4048
+ displayOrder
4049
+ filterValues {
4050
+ ...AttributeFilterValue
4051
+ }
4052
+ rangeBounds {
4053
+ ...AttributeRangeBounds
4054
+ }
4055
+ }
4056
+ ```
4057
+
4058
+ ### Fragment: `PriceRangeFilter` on `PriceRange`
4059
+
4060
+ **Section**: Attribute Filters
4061
+
4062
+ **Description**: Min / max price range across products in the current listing context. Use to bound the price slider.
4063
+
4064
+ **Uses fragments**: `Money`
4065
+
4066
+ **GraphQL**:
4067
+ ```graphql
4068
+ fragment PriceRangeFilter on PriceRange {
4069
+ min {
4070
+ ...Money
4071
+ }
4072
+ max {
4073
+ ...Money
4074
+ }
4075
+ }
4076
+ ```
4077
+
4078
+ ### Fragment: `CategoryFilterOption` on `CategoryFilterOption`
4079
+
4080
+ **Section**: Attribute Filters
4081
+
4082
+ **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.
4083
+
4084
+ **GraphQL**:
4085
+ ```graphql
4086
+ fragment CategoryFilterOption on CategoryFilterOption {
4087
+ id
4088
+ name
4089
+ slug
4090
+ productCount
4091
+ level
4092
+ parentId
4093
+ }
4094
+ ```
4095
+
4096
+ ### Fragment: `AvailableFilters` on `AvailableFilters`
4097
+
4098
+ **Section**: Attribute Filters
4099
+
4100
+ **Description**: Full result of the `productFilters($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.
4101
+
4102
+ **Uses fragments**: `AttributeDefinition`, `CategoryFilterOption`, `PriceRangeFilter`
4103
+
4104
+ **GraphQL**:
4105
+ ```graphql
4106
+ fragment AvailableFilters on AvailableFilters {
4107
+ attributes {
4108
+ ...AttributeDefinition
4109
+ }
4110
+ priceRange {
4111
+ ...PriceRangeFilter
4112
+ }
4113
+ categories {
4114
+ ...CategoryFilterOption
4115
+ }
4116
+ activeCount
4117
+ matchCount
4118
+ }
4119
+ ```
4120
+
4121
+ ### Fragment: `LoyaltyPageInfo` on `LoyaltyPageInfo`
4122
+
4123
+ **Section**: Loyalty Program
4124
+
4125
+ **Description**: Page metadata for the loyalty transactions connection (separate type from generic `PageInfo` for legacy reasons; identical shape).
4126
+
4127
+ **GraphQL**:
4128
+ ```graphql
4129
+ fragment LoyaltyPageInfo on LoyaltyPageInfo {
4130
+ hasNextPage
4131
+ hasPreviousPage
4132
+ startCursor
4133
+ endCursor
4134
+ }
4135
+ ```
4136
+
4137
+ ### Fragment: `LoyaltyTier` on `LoyaltyTier`
4138
+
4139
+ **Section**: Loyalty Program
4140
+
4141
+ **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.
4142
+
4143
+ **Uses fragments**: `Money`
4144
+
4145
+ **GraphQL**:
4146
+ ```graphql
4147
+ fragment LoyaltyTier on LoyaltyTier {
4148
+ id
4149
+ name
4150
+ type
4151
+ minPoints
4152
+ minAnnualSpend {
4153
+ ...Money
4154
+ }
4155
+ pointsMultiplier
4156
+ customBenefits {
4157
+ name
4158
+ description
4159
+ icon
4160
+ }
4161
+ }
4162
+ ```
4163
+
4164
+ ### Fragment: `LoyaltyPointsSummary` on `LoyaltyPointsSummary`
4165
+
4166
+ **Section**: Loyalty Program
4167
+
4168
+ **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`.
4169
+
4170
+ **GraphQL**:
4171
+ ```graphql
4172
+ fragment LoyaltyPointsSummary on LoyaltyPointsSummary {
4173
+ totalPoints
4174
+ currentPoints
4175
+ pendingPoints
4176
+ redeemedPoints
4177
+ expiredPoints
4178
+ expiringPoints
4179
+ nextExpiryDate
4180
+ }
4181
+ ```
4182
+
4183
+ ### Fragment: `TierProgress` on `TierProgress`
4184
+
4185
+ **Section**: Loyalty Program
4186
+
4187
+ **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.
4188
+
4189
+ **Uses fragments**: `LoyaltyTier`, `Money`
4190
+
4191
+ **GraphQL**:
4192
+ ```graphql
4193
+ fragment TierProgress on TierProgress {
4194
+ currentTier {
4195
+ ...LoyaltyTier
4196
+ }
4197
+ nextTier {
4198
+ ...LoyaltyTier
4199
+ }
4200
+ pointsToNextTier
4201
+ progressPercent
4202
+ spendToNextTier {
4203
+ ...Money
4204
+ }
4205
+ }
4206
+ ```
4207
+
4208
+ ### Fragment: `LoyaltyMember` on `LoyaltyMember`
4209
+
4210
+ **Section**: Loyalty Program
4211
+
4212
+ **Description**: Customer's loyalty membership — points summary, current tier, tier progress, annual spend, last activity. Returned by `loyaltyMember` (null if not enrolled).
4213
+
4214
+ **Uses fragments**: `LoyaltyPointsSummary`, `LoyaltyTier`, `Money`, `TierProgress`
4215
+
4216
+ **GraphQL**:
4217
+ ```graphql
4218
+ fragment LoyaltyMember on LoyaltyMember {
4219
+ id
4220
+ customerId
4221
+ points {
4222
+ ...LoyaltyPointsSummary
4223
+ }
4224
+ tier {
4225
+ ...LoyaltyTier
4226
+ }
4227
+ tierProgress {
4228
+ ...TierProgress
4229
+ }
4230
+ annualSpend {
4231
+ ...Money
4232
+ }
4233
+ lastActivityAt
4234
+ enrolledAt
4235
+ }
4236
+ ```
4237
+
4238
+ ### Fragment: `LoyaltyTransaction` on `LoyaltyTransaction`
4239
+
4240
+ **Section**: Loyalty Program
4241
+
4242
+ **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.
4243
+
4244
+ **GraphQL**:
4245
+ ```graphql
4246
+ fragment LoyaltyTransaction on LoyaltyTransaction {
4247
+ id
4248
+ type
4249
+ points
4250
+ balanceAfter
4251
+ orderId
4252
+ description
4253
+ expiresAt
4254
+ createdAt
4255
+ }
4256
+ ```
4257
+
4258
+ ### Fragment: `LoyaltyReward` on `LoyaltyReward`
4259
+
4260
+ **Section**: Loyalty Program
4261
+
4262
+ **Description**: Reward customers can redeem — name, type, points cost, discount value (% or fixed), tier requirement, availability flag, remaining redemptions. Spread on the rewards page.
4263
+
4264
+ **Uses fragments**: `ImageCard`, `LoyaltyTier`, `Money`
4265
+
4266
+ **GraphQL**:
4267
+ ```graphql
4268
+ fragment LoyaltyReward on LoyaltyReward {
4269
+ id
4270
+ name
4271
+ slug
4272
+ type
4273
+ pointsCost
4274
+ discountPercent
4275
+ discountAmount {
4276
+ ...Money
4277
+ }
4278
+ description
4279
+ image {
4280
+ ...ImageCard
4281
+ }
4282
+ isAvailable
4283
+ tierRequired {
4284
+ ...LoyaltyTier
4285
+ }
4286
+ redemptionsRemaining
4287
+ }
4288
+ ```
4289
+
4290
+ ### Fragment: `PointsEstimate` on `PointsEstimate`
4291
+
4292
+ **Section**: Loyalty Program
4293
+
4294
+ **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.
4295
+
4296
+ **GraphQL**:
4297
+ ```graphql
4298
+ fragment PointsEstimate on PointsEstimate {
4299
+ basePoints
4300
+ bonusPoints
4301
+ totalPoints
4302
+ multiplier
4303
+ }
4304
+ ```
4305
+
4306
+ ### Fragment: `LoyaltyAction` on `LoyaltyAction`
4307
+
4308
+ **Section**: Loyalty Program
4309
+
4310
+ **Description**: One earn action configured in the loyalty program — type (`PURCHASE` / `SIGNUP` / `REFERRAL` / `REVIEW` / `BIRTHDAY`), enabled flag, points granted.
4311
+
4312
+ **GraphQL**:
4313
+ ```graphql
4314
+ fragment LoyaltyAction on LoyaltyAction {
4315
+ type
4316
+ isEnabled
4317
+ points
4318
+ }
4319
+ ```
4320
+
4321
+ ### Fragment: `LoyaltySettings` on `LoyaltySettings`
4322
+
4323
+ **Section**: Loyalty Program
4324
+
4325
+ **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`.
4326
+
4327
+ **Uses fragments**: `LoyaltyAction`
4328
+
4329
+ **GraphQL**:
4330
+ ```graphql
4331
+ fragment LoyaltySettings on LoyaltySettings {
4332
+ isEnabled
4333
+ pointsName
4334
+ pointsPerCurrency
4335
+ pointsExpiryMonths
4336
+ availableActions {
4337
+ ...LoyaltyAction
4338
+ }
4339
+ referralEnabled
4340
+ referralPoints
4341
+ referralBonusPoints
4342
+ }
4343
+ ```
4344
+
4345
+ ### Fragment: `ReferralStats` on `ReferralStats`
4346
+
4347
+ **Section**: Loyalty Program
4348
+
4349
+ **Description**: Customer referral statistics — code, share URL, counts of referred / completed / pending, lifetime points earned. Returned by `referralStats`.
4350
+
4351
+ **GraphQL**:
4352
+ ```graphql
4353
+ fragment ReferralStats on ReferralStats {
4354
+ referralCode
4355
+ shareUrl
4356
+ totalReferred
4357
+ completedReferrals
4358
+ pendingReferrals
4359
+ totalPointsEarned
4360
+ }
4361
+ ```
4362
+
4363
+ ### Fragment: `RedeemRewardPayload` on `RedeemRewardPayload`
4364
+
4365
+ **Section**: Loyalty Program
4366
+
4367
+ **Description**: Result of `loyaltyRedeemReward` — depending on the reward type, exactly one of `discountCode`, `productDiscountCode`, `giftCardCode` is populated. Apply the returned code on cart/checkout.
4368
+
4369
+ **Uses fragments**: `UserError`
4370
+
4371
+ **GraphQL**:
4372
+ ```graphql
4373
+ fragment RedeemRewardPayload on RedeemRewardPayload {
4374
+ success
4375
+ discountCode
4376
+ productDiscountCode
4377
+ giftCardCode
4378
+ userErrors {
4379
+ ...UserError
4380
+ }
4381
+ }
4382
+ ```
4383
+
4384
+ ### Fragment: `GenerateReferralCodePayload` on `GenerateReferralCodePayload`
4385
+
4386
+ **Section**: Loyalty Program
4387
+
4388
+ **Description**: Result of `loyaltyGenerateReferralCode` — the customer's referral code and shareable URL.
4389
+
4390
+ **Uses fragments**: `UserError`
4391
+
4392
+ **GraphQL**:
4393
+ ```graphql
4394
+ fragment GenerateReferralCodePayload on GenerateReferralCodePayload {
4395
+ success
4396
+ referralCode
4397
+ shareUrl
4398
+ userErrors {
4399
+ ...UserError
4400
+ }
4401
+ }
4402
+ ```
4403
+
4404
+ ### Fragment: `ProductReview` on `ProductReview`
4405
+
4406
+ **Section**: Reviews
4407
+
4408
+ **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.
4409
+
4410
+ **GraphQL**:
4411
+ ```graphql
4412
+ fragment ProductReview on ProductReview {
4413
+ id
4414
+ productId
4415
+ rating
4416
+ title
4417
+ content
4418
+ pros
4419
+ cons
4420
+ images
4421
+ authorName
4422
+ isVerifiedPurchase
4423
+ helpfulCount
4424
+ unhelpfulCount
4425
+ response
4426
+ responseAt
4427
+ createdAt
4428
+ }
4429
+ ```
4430
+
4431
+ ### Fragment: `ReviewStats` on `ReviewStats`
4432
+
4433
+ **Section**: Reviews
4434
+
4435
+ **Description**: Aggregate review stats for a product — average rating, total count, distribution per star (1-5). Returned by `reviewStats($productId)`.
4436
+
4437
+ **GraphQL**:
4438
+ ```graphql
4439
+ fragment ReviewStats on ReviewStats {
4440
+ averageRating
4441
+ totalCount
4442
+ fiveStarCount
4443
+ fourStarCount
4444
+ threeStarCount
4445
+ twoStarCount
4446
+ oneStarCount
4447
+ }
4448
+ ```
4449
+
4450
+ ### Fragment: `WishlistItem` on `WishlistItem`
4451
+
4452
+ **Section**: Wishlists
4453
+
4454
+ **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`).
4455
+
4456
+ **Uses fragments**: `Money`, `ProductCard`
4457
+
4458
+ **GraphQL**:
4459
+ ```graphql
4460
+ fragment WishlistItem on WishlistItem {
4461
+ id
4462
+ productId
4463
+ variantId
4464
+ product {
4465
+ ...ProductCard
4466
+ }
4467
+ priceWhenAdded {
4468
+ ...Money
4469
+ }
4470
+ notifyOnSale
4471
+ notifyOnRestock
4472
+ addedAt
4473
+ }
4474
+ ```
4475
+
4476
+ ### Fragment: `Wishlist` on `Wishlist`
4477
+
4478
+ **Section**: Wishlists
4479
+
4480
+ **Description**: A customer's wishlist — name, public/private flag, share token (when public), items, item count. Spread on the wishlists page.
4481
+
4482
+ **Uses fragments**: `WishlistItem`
4483
+
4484
+ **GraphQL**:
4485
+ ```graphql
4486
+ fragment Wishlist on Wishlist {
4487
+ id
4488
+ name
4489
+ isPublic
4490
+ shareToken
4491
+ items {
4492
+ ...WishlistItem
4493
+ }
4494
+ itemCount
4495
+ createdAt
4496
+ updatedAt
4497
+ }
4498
+ ```
4499
+
4500
+ ### Fragment: `BlogCategory` on `BlogCategory`
4501
+
4502
+ **Section**: Blog
4503
+
4504
+ **Description**: Blog category — name, slug, description, post count. Spread on category navigation and post category labels.
4505
+
4506
+ **GraphQL**:
4507
+ ```graphql
4508
+ fragment BlogCategory on BlogCategory {
4509
+ id
4510
+ name
4511
+ slug
4512
+ description
4513
+ postCount
4514
+ }
4515
+ ```
4516
+
4517
+ ### Fragment: `BlogTag` on `BlogTag`
4518
+
4519
+ **Section**: Blog
4520
+
4521
+ **Description**: Blog tag — name, slug, post count. Spread on tag clouds and post tag labels.
4522
+
4523
+ **GraphQL**:
4524
+ ```graphql
4525
+ fragment BlogTag on BlogTag {
4526
+ id
4527
+ name
4528
+ slug
4529
+ postCount
4530
+ }
4531
+ ```
4532
+
4533
+ ### Fragment: `BlogPost` on `BlogPost`
4534
+
4535
+ **Section**: Blog
4536
+
4537
+ **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.
4538
+
4539
+ **Uses fragments**: `BlogCategory`, `BlogTag`, `ImageCard`, `ImageThumbnail`
4540
+
4541
+ **GraphQL**:
4542
+ ```graphql
4543
+ fragment BlogPost on BlogPost {
4544
+ id
4545
+ title
4546
+ slug
4547
+ excerpt
4548
+ content
4549
+ contentFormat
4550
+ featuredImage {
4551
+ ...ImageCard
4552
+ }
4553
+ author {
4554
+ id
4555
+ name
4556
+ bio
4557
+ avatar {
4558
+ ...ImageThumbnail
4559
+ }
4560
+ }
4561
+ category {
4562
+ ...BlogCategory
4563
+ }
4564
+ tags {
4565
+ ...BlogTag
4566
+ }
4567
+ publishedAt
4568
+ readingTimeMinutes
4569
+ viewCount
4570
+ commentCount
4571
+ allowComments
4572
+ isFeatured
4573
+ status
4574
+ seo {
4575
+ title
4576
+ description
4577
+ }
4578
+ createdAt
4579
+ updatedAt
4580
+ }
4581
+ ```
4582
+
4583
+ ### Fragment: `LocationAddress` on `LocationAddress`
4584
+
4585
+ **Section**: Store Availability (BOPIS / multi-location)
4586
+
4587
+ **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`.
4588
+
4589
+ **GraphQL**:
4590
+ ```graphql
4591
+ fragment LocationAddress on LocationAddress {
4592
+ streetLine1
4593
+ streetLine2
4594
+ city
4595
+ country
4596
+ countryCode
4597
+ state
4598
+ stateCode
4599
+ postalCode
4600
+ phone
4601
+ latitude
4602
+ longitude
4603
+ formatted
4604
+ }
4605
+ ```
4606
+
4607
+ ### Fragment: `BusinessHoursWindow` on `BusinessHoursWindow`
4608
+
4609
+ **Section**: Store Availability (BOPIS / multi-location)
4610
+
4611
+ **Description**: One day's open/close window inside `BusinessHours` (24h clock).
4612
+
4613
+ **GraphQL**:
4614
+ ```graphql
4615
+ fragment BusinessHoursWindow on BusinessHoursWindow {
4616
+ openHour
4617
+ closeHour
4618
+ }
4619
+ ```
4620
+
4621
+ ### Fragment: `BusinessHours` on `BusinessHours`
4622
+
4623
+ **Section**: Store Availability (BOPIS / multi-location)
4624
+
4625
+ **Description**: Weekly business hours — one window per day. Spread inside `Location.businessHours`.
4626
+
4627
+ **Uses fragments**: `BusinessHoursWindow`
4628
+
4629
+ **GraphQL**:
4630
+ ```graphql
4631
+ fragment BusinessHours on BusinessHours {
4632
+ monday {
4633
+ ...BusinessHoursWindow
4634
+ }
4635
+ tuesday {
4636
+ ...BusinessHoursWindow
4637
+ }
4638
+ wednesday {
4639
+ ...BusinessHoursWindow
4640
+ }
4641
+ thursday {
4642
+ ...BusinessHoursWindow
4643
+ }
4644
+ friday {
4645
+ ...BusinessHoursWindow
4646
+ }
4647
+ saturday {
4648
+ ...BusinessHoursWindow
4649
+ }
4650
+ sunday {
4651
+ ...BusinessHoursWindow
4652
+ }
4653
+ }
4654
+ ```
4655
+
4656
+ ### Fragment: `Location` on `Location`
4657
+
4658
+ **Section**: Store Availability (BOPIS / multi-location)
4659
+
4660
+ **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.
4661
+
4662
+ **Uses fragments**: `BusinessHours`, `LocationAddress`
4663
+
4664
+ **GraphQL**:
4665
+ ```graphql
4666
+ fragment Location on Location {
4667
+ id
4668
+ name
4669
+ type
4670
+ supportsPickup
4671
+ timezone
4672
+ pickupInstructions
4673
+ address {
4674
+ ...LocationAddress
4675
+ }
4676
+ businessHours {
4677
+ ...BusinessHours
4678
+ }
4679
+ }
4680
+ ```
4681
+
4682
+ ### Fragment: `StoreAvailability` on `StoreAvailability`
4683
+
4684
+ **Section**: Store Availability (BOPIS / multi-location)
4685
+
4686
+ **Description**: Per-location stock entry for one variant — availability flag, stock quantity (token-gated), pickup time, full location detail. Spread inside `StoreAvailabilityConnection.edges[].node`.
4687
+
4688
+ **Uses fragments**: `Location`
4689
+
4690
+ **GraphQL**:
4691
+ ```graphql
4692
+ fragment StoreAvailability on StoreAvailability {
4693
+ isAvailable
4694
+ availableStock
4695
+ pickupTime
4696
+ location {
4697
+ ...Location
4698
+ }
4699
+ }
4700
+ ```
4701
+
4702
+ ### Fragment: `StoreAvailabilityConnection` on `StoreAvailabilityConnection`
4703
+
4704
+ **Section**: Store Availability (BOPIS / multi-location)
4705
+
4706
+ **Description**: Relay connection of `StoreAvailability` entries for a variant — used inside the `VariantStoreAvailability` fragment. Pre-paginated to 10 by default.
4707
+
4708
+ **Uses fragments**: `PageInfo`, `StoreAvailability`
4709
+
4710
+ **GraphQL**:
4711
+ ```graphql
4712
+ fragment StoreAvailabilityConnection on StoreAvailabilityConnection {
4713
+ totalCount
4714
+ pageInfo {
4715
+ ...PageInfo
4716
+ }
4717
+ edges {
4718
+ cursor
4719
+ node {
4720
+ ...StoreAvailability
4721
+ }
4722
+ }
4723
+ }
4724
+ ```
4725
+
4726
+ ### Fragment: `VariantStoreAvailability` on `ProductVariant`
4727
+
4728
+ **Section**: Store Availability (BOPIS / multi-location)
4729
+
4730
+ **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.
4731
+
4732
+ **Uses fragments**: `StoreAvailabilityConnection`
4733
+
4734
+ **GraphQL**:
4735
+ ```graphql
4736
+ fragment VariantStoreAvailability on ProductVariant {
4737
+ id
4738
+ title
4739
+ sku
4740
+ isAvailable
4741
+ availableStock
4742
+ storeAvailability(first: 10) {
4743
+ ...StoreAvailabilityConnection
4744
+ }
4745
+ }
4746
+ ```
4747
+
4748
+ ### Fragment: `ShopPage` on `ShopPage`
4749
+
4750
+ **Section**: Pages
4751
+
4752
+ **Description**: CMS page — handle (URL slug), title, body (HTML), excerpt, SEO metadata, publish date. Spread on About / Privacy / Terms / Returns Policy pages.
4753
+
4754
+ **GraphQL**:
4755
+ ```graphql
4756
+ fragment ShopPage on ShopPage {
4757
+ id
4758
+ handle
4759
+ title
4760
+ body
4761
+ excerpt
4762
+ seo {
4763
+ title
4764
+ description
4765
+ }
4766
+ publishedAt
4767
+ createdAt
4768
+ updatedAt
4769
+ }
4770
+ ```
4771
+
4772
+ ### Fragment: `MenuItem` on `MenuItem`
4773
+
4774
+ **Section**: Navigation Menus
4775
+
4776
+ **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.
4777
+
4778
+ **Uses fragments**: `Image`
4779
+
4780
+ **GraphQL**:
4781
+ ```graphql
4782
+ fragment MenuItem on MenuItem {
4783
+ id
4784
+ title
4785
+ url
4786
+ type
4787
+ resourceId
4788
+ image {
4789
+ ...Image
4790
+ }
4791
+ items {
4792
+ id
4793
+ title
4794
+ url
4795
+ type
4796
+ resourceId
4797
+ items {
4798
+ id
4799
+ title
4800
+ url
4801
+ type
4802
+ resourceId
4803
+ }
4804
+ }
4805
+ }
4806
+ ```
4807
+
4808
+ ### Fragment: `Menu` on `Menu`
4809
+
4810
+ **Section**: Navigation Menus
4811
+
4812
+ **Description**: Navigation menu — handle (e.g. "main-menu", "footer", "mobile"), title, nested item tree. Returned by `menu($handle)` query.
4813
+
4814
+ **Uses fragments**: `MenuItem`
4815
+
4816
+ **GraphQL**:
4817
+ ```graphql
4818
+ fragment Menu on Menu {
4819
+ id
4820
+ handle
4821
+ title
4822
+ items {
4823
+ ...MenuItem
4824
+ }
4825
+ }
4826
+ ```
4827
+
4828
+ ### Fragment: `UrlRedirect` on `UrlRedirect`
4829
+
4830
+ **Section**: URL Redirects
4831
+
4832
+ **Description**: Single legacy `path` → new `target` redirect entry. Use on the server / edge to issue 301 redirects for migrated routes.
4833
+
4834
+ **GraphQL**:
4835
+ ```graphql
4836
+ fragment UrlRedirect on UrlRedirect {
4837
+ path
4838
+ target
4839
+ }
4840
+ ```
4841
+
4842
+ ### Fragment: `LinkedVariantSummary` on `LinkedVariantSummary`
4843
+
4844
+ **Section**: Product Configurator (per-product attributes)
4845
+
4846
+ **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`.
4847
+
4848
+ **GraphQL**:
4849
+ ```graphql
4850
+ fragment LinkedVariantSummary on LinkedVariantSummary {
4851
+ id
4852
+ productId
4853
+ title
4854
+ sku
4855
+ availableStock
4856
+ isAvailable
4857
+ trackQuantity
4858
+ }
4859
+ ```
4860
+
4861
+ ### Fragment: `ProductAttributeOption` on `ProductAttributeOption`
4862
+
4863
+ **Section**: Product Configurator (per-product attributes)
4864
+
4865
+ **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.
4866
+
4867
+ **Uses fragments**: `LinkedVariantSummary`
4868
+
4869
+ **GraphQL**:
4870
+ ```graphql
4871
+ fragment ProductAttributeOption on ProductAttributeOption {
4872
+ id
4873
+ value
4874
+ label
4875
+ sortOrder
4876
+ colorHex
4877
+ surchargeAmount
4878
+ surchargeType
4879
+ isDefault
4880
+ linkedVariantId
4881
+ linkedVariant {
4882
+ ...LinkedVariantSummary
4883
+ }
4884
+ }
4885
+ ```
4886
+
4887
+ ### Fragment: `ProductAttributeDefinition` on `ProductAttributeDefinition`
4888
+
4889
+ **Section**: Product Configurator (per-product attributes)
4890
+
4891
+ **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.
4892
+
4893
+ **Uses fragments**: `ProductAttributeOption`
4894
+
4895
+ **GraphQL**:
4896
+ ```graphql
4897
+ fragment ProductAttributeDefinition on ProductAttributeDefinition {
4898
+ id
4899
+ name
4900
+ slug
4901
+ description
4902
+ type
4903
+ fillingMode
4904
+ billingMode
4905
+ taxClassId
4906
+ scopeProductId
4907
+ isRequired
4908
+ isVisible
4909
+ displayOrder
4910
+ minValue
4911
+ maxValue
4912
+ options {
4913
+ ...ProductAttributeOption
4914
+ }
4915
+ }
4916
+ ```