Przejdź do głównej zawartości

Mutacje (Mutations)

Storefront API wystawia mutacje pogrupowane w workflowy domenowe. Ta strona to use-case guide po polsku — krótkie wprowadzenie "kiedy/dlaczego" + inline reference każdej operacji generowany z @doswiftly/storefront-operations/operations.json (drift-safe). Pełny katalog typów: Types reference.

Konwencja błędów

Każda mutacja zwraca payload z dwoma wspólnymi polami:

PoleTypZnaczenie
userErrors[UserError!]! lub [CartUserError!]!Błędy walidacji do pokazania użytkownikowi — { message, code, field }. Mutacja zakończyła się logicznie niepowodzeniem mimo HTTP 200.
warnings[CartWarning!]! (cart) lub [PaymentWarning!]! (payment)Ostrzeżenia nieblokujące — np. zmiana stanu magazynu, automatyczne wyczyszczenie preselekcji instrumentu po nieudanej autoryzacji.

Domena cart (Cart* + PaymentCreate) używa typed enuma CartErrorCode (~30 kodów), domena klienta używa CustomerErrorCode (~22 kody), pozostałe domeny — generycznego UserError. Pełną listę kodów znajdziesz w Types reference.

Wykrywanie błędów po code, nie po message

Pole message jest tłumaczone backendowo wg locale klienta — nie matchuj go regexami. Logika storefronta rozgałęzia się wyłącznie po userErrors[].code.


Koszyk — lifecycle

Pełen cykl życia koszyka — od stworzenia, przez dodawanie linii i kodów rabatowych, do checkoutu i finalizacji. Backend trzyma cart-id w cookie 30 dni, sam koszyk wygasa po 72h bezczynności.

Tworzenie koszyka

CartCreate może być wywołany bez argumentów (pusty koszyk) lub z input zawierającym lines / buyer identity / kody rabatowe / atrybuty — to skraca następne round-tripy. Cart ID to UUID zapisywany w cookie SDK.

CartCreatemutationCart Mutations
mutation CartCreate($input: CartCreateInput)

Creates a new cart and optionally pre-populates it with line items. Returns a one-time `secret` (the cart access capability) alongside the cart — the SDK persists it in the `cart-id` cookie and sends it as the `x-cart-secret` header on later cart operations; a direct API caller must store it immediately, as it cannot be retrieved again. The cart ID is a UUID; the cart 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.

Variables

NameTypeDefaultRequired
$inputCartCreateInputNo
GraphQL operation
mutation CartCreate($input: CartCreateInput) {
cartCreate(input: $input) {
cart {
...Cart
}
secret
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

Mutacje linii — add / update / remove

CartAddLines dodaje nowe linie z walidacją magazynu (INSUFFICIENT_STOCK) i konfiguratora (ATTRIBUTE_REQUIRED, ATTRIBUTE_OPTION_INVALID). Jeśli ten sam wariant + identyczne atrybuty dodasz dwa razy — quantity się merguje zamiast duplikować linię.

CartAddLinesmutationCart Mutations
mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
$lines[CartLineInput!]!Yes
GraphQL operation
mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
cartAddLines(id: $id, lines: $lines) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

CartUpdateLines aktualizuje istniejące linie po CartLine.id — quantity 0 usuwa linię, a w attributeSelections null zachowuje stan, [] czyści, a niepusta tablica zastępuje całość (semantyka REPLACE).

CartUpdateLinesmutationCart Mutations
mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
$lines[CartLineUpdateInput!]!Yes
GraphQL operation
mutation CartUpdateLines($id: ID!, $lines: [CartLineUpdateInput!]!) {
cartUpdateLines(id: $id, lines: $lines) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError
CartRemoveLinesmutationCart Mutations
mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
$lineIds[ID!]!Yes
GraphQL operation
mutation CartRemoveLines($id: ID!, $lineIds: [ID!]!) {
cartRemoveLines(id: $id, lineIds: $lineIds) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError
Konfigurowalny produkt (bundle z linked variants)

CartLineInput.attributeSelections obsługuje konfigurator produktu — każdy wybór (np. Finiszer / Podstawa / Podajnik drukarki) wskazuje attributeDefinitionId + optionId. Backend rozpoznaje linkedVariantId w opcji i przy checkoucie materializuje każdy komponent jako osobny OrderItem (parent + N children, wspólna grupa "Komponenty konfiguracji" w admin UI).

Pole wielokrotnego wyboru — optionIds nalicza dopłaty

Pole typu CHECKBOX przyjmuje listę zaznaczeń w optionIds zamiast pojedynczego optionId. Warto znać cztery rzeczy:

  1. Odpowiedź zawiera jeden wpis NA ZAZNACZENIE, nie jeden wpis na pole. Zaznaczenie trzech opcji daje trzy elementy attributeSelections z tym samym attributeDefinitionId i różnymi optionId — każdy z własną etykietą i dopłatą. Jeżeli renderujesz wybory klienta, kluczuj listę parą (pole + opcja); sam identyfikator pola się powtórzy.
  2. optionIds NIE wraca w odpowiedzi. Każdy wpis opisuje dokładnie jedno zaznaczenie, więc stan odczytujesz z powtórzonych wpisów, nie z listy na jednym z nich.
  3. Wysyłaj optionId ALBO optionIds, nigdy oba naraz — i listę wyłącznie do pola, które wielokrotny wybór dopuszcza. Inaczej dostaniesz ATTRIBUTE_SELECTION_SHAPE_INVALID. Powtórzony identyfikator w liście liczy się raz; ile sztuk klient chce, mówi quantity linii.
  4. Zmiana zachowania (2026-08). Wcześniej optionIds było przyjmowane, ale nie wpływało na cenę. Od tej zmiany każde zaznaczenie nalicza swoją dopłatę i — dla komponentów magazynowych — tworzy własną pozycję zamówienia z własną rezerwacją. Jeżeli Twój sklep wysyłał optionIds, to samo żądanie da teraz inną cenę. Kształt schematu się nie zmienił, więc bramka zgodności tego nie wykryje — sprawdź to sam.

Kody rabatowe, buyer identity, notatka, atrybuty

CartDiscountCodesUpdate zastępuje (NIE scala) listę kodów rabatowych. Pusta tablica = usuń wszystkie. Backend re-priceuje koszyk po każdej zmianie.

Stosowalność jest per-kod — nie zakładaj, że rozpoznany kod obniża cenę. Każdy wpis cart.discountCodes[] ma pole isApplicable (true tylko gdy kod realnie obniża cenę lub daje darmową wysyłkę). Kod może być w pełni ważny (istnieje, aktywny, w terminie), a mimo to isApplicable:false — np. nie osiągnięto minimum zamówienia, koszyk nie zawiera produktu w zasięgu kodu, albo BUY_X_GET_Y nie ma kompletu. Taki kod zostaje na koszyku (klient może dodać kwalifikujący produkt i kod się aktywuje), a cartDiscountCodesUpdate.warnings[] niesie powód per kod: code: DISCOUNT_CODE_NOT_APPLICABLE, target = kod, message zlokalizowany do języka koszyka. Twardy userError dostajesz wyłącznie dla kodu nieistniejącego (NOT_FOUND) — wtedy nic nie zostaje zapisane.

cart.discountAllocations[] to rozbicie rabatu per zastosowany kod (discountCode + amount); ich suma równa się cart.cost.totalDiscount. Renderuj listę alokacji, by pokazać który kod ile dał, zamiast pojedynczej łącznej kwoty.

Anti-pattern (źródło „kod applied, 0 zł")

Branżowanie UI na samej liście discountCodes (np. „pierwszy kod = zastosowany") pokaże kod jako działający przy 0 zł rabatu i bez powodu. Zawsze czytaj discountCodes[].isApplicable i renderuj warnings. Tę samą prawdę przed kliknięciem Apply daje preview CartValidateDiscountCode.

CartDiscountCodesUpdatemutationCart Mutations
mutation CartDiscountCodesUpdate($id: ID!, $discountCodes: [String!]!)

Replaces (NOT appends) the cart's discount codes with the given list. Pass `[]` to clear all codes. Each code is validated against the 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`. Single canonical replace-all entry point — prior append/single-remove variants were removed in favor of this explicit caller-controlled list semantics.

Variables

NameTypeDefaultRequired
$idID!Yes
$discountCodes[String!]!Yes
GraphQL operation
mutation CartDiscountCodesUpdate($id: ID!, $discountCodes: [String!]!) {
cartDiscountCodesUpdate(id: $id, discountCodes: $discountCodes) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

CartUpdateBuyerIdentity ustawia dane kupującego (email/phone, customer ID dla logged-in flow, country/language code wpływające na @inContext directive na queries).

CartUpdateBuyerIdentitymutationCart Mutations
mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInput!)

Set the buyer's email and phone on the cart (guest checkout contact details). The cart is bound to a customer automatically from the authenticated session — there is no `customerId` input, so a guest cannot claim another shopper's account. Sign in and the cart attaches to that customer (re-binding overwrites, last-write-wins); the `customerId` is then readable on `cart.buyerIdentity`. Use during guest checkout to capture contact info and after login to attach the buyer. Does not trigger tax / shipping recalculation.

Variables

NameTypeDefaultRequired
$idID!Yes
$buyerIdentityCartBuyerIdentityInput!Yes
GraphQL operation
mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
cartUpdateBuyerIdentity(id: $id, buyerIdentity: $buyerIdentity) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

CartUpdateNote ustawia notatkę kupującego na koszyku (cart.note) — instrukcje dostawy, wiadomość prezentowa itp. Po złożeniu zamówienia notatka trafia na Order.customerNote (dostępne w API zamówienia) i jest widoczna dla merchanta w panelu.

CartUpdateNotemutationCart Mutations
mutation CartUpdateNote($id: ID!, $note: String!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
$noteString!Yes
GraphQL operation
mutation CartUpdateNote($id: ID!, $note: String!) {
cartUpdateNote(id: $id, note: $note) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

CartUpdateAttributes zastępuje (NIE scala) custom atrybuty { key, value } widoczne w admin panelu — instrukcje dostawy, flagi gift-packaging, numery B2B. Limit: 250 par/koszyk (przekroczenie → CART_ATTRIBUTES_LIMIT_EXCEEDED), key max 255 znaków.

CartUpdateAttributesmutationCart Attributes
mutation CartUpdateAttributes($id: ID!, $attributes: [CartAttributeInput!]!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
$attributes[CartAttributeInput!]!Yes
GraphQL operation
mutation CartUpdateAttributes($id: ID!, $attributes: [CartAttributeInput!]!) {
cartUpdateAttributes(id: $id, attributes: $attributes) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

Koszyk a sesja klienta — merge, logout, odzyskiwanie

Trzy operacje obsługujące zdarzenia cyklu życia koszyka powiązane z autoryzacją i odzyskiwaniem porzuconego koszyka.

CartMerge scala koszyk gościa (guestCartId) z istniejącym koszykiem zalogowanego klienta zaraz po logowaniu. Klient pobierany jest z sesji auth, nie z parametru — ID i secret koszyka gościa zostają bez zmian, dzięki czemu cookie cart-id pozostaje ważne bez reemisji. Ilości linii sumują się per wariant; dane checkout-u klienta wygrywają; poprzedni koszyk klienta jest porzucany. Wymaga uwierzytelnienia — brak zwraca CART_MERGE_REQUIRES_AUTH. Koszyki w różnych walutach zwracają CART_CURRENCY_MISMATCH.

CartMergemutationCart Mutations
mutation CartMerge($guestCartId: ID!)

Merge a guest cart into the signed-in customer's existing cart right after login. Pass the guest cart id; its secret travels in the cart credential header and the customer is taken from the authenticated session (never the client). Line quantities are summed per variant, the buyer's in-session checkout fields win, and the prior customer cart is discarded. The returned cart keeps the SAME id and secret as the guest cart, so the stored cart-id stays valid — no cookie re-issue. Requires authentication (returns `CART_MERGE_REQUIRES_AUTH` otherwise) and refuses carts held in different currencies (`CART_CURRENCY_MISMATCH`).

Variables

NameTypeDefaultRequired
$guestCartIdID!Yes
GraphQL operation
mutation CartMerge($guestCartId: ID!) {
cartMerge(guestCartId: $guestCartId) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

CartDowngradeOnLogout wywołaj przed zniszczeniem sesji auth. Czyści przypisanie klienta, dane kontaktowe, adresy i metodę płatności, ale zachowuje linie, kody rabatowe, metodę wysyłki, walutę i notatki. ID i secret koszyka nie zmieniają się — kupujący po wylogowaniu widzi swój koszyk jako gość (nic nie gubi na urządzeniu dzielonym). Gated przez cart secret (bez auth) — brak lub błędny secret zwraca CART_NOT_FOUND.

CartDowngradeOnLogoutmutationCart Mutations
mutation CartDowngradeOnLogout($cartId: ID!)

Downgrade a cart to guest on logout. Pass the cart id (its secret travels in the cart credential header). Clears the customer association, contact details, addresses and payment selection, but keeps line items, discount codes, the selected shipping method, currency and notes. The cart id and secret are unchanged (no rotation), so the stored cart-id stays valid and the buyer keeps their items as a guest. Call from the logout flow before tearing down the auth session so the next person on a shared device sees none of the previous buyer's data. Gated by the cart secret only (no auth) — a missing/wrong secret returns `CART_NOT_FOUND`.

Variables

NameTypeDefaultRequired
$cartIdID!Yes
GraphQL operation
mutation CartDowngradeOnLogout($cartId: ID!) {
cartDowngradeOnLogout(cartId: $cartId) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

CartRecoveryRedeem umarza link odzyskiwania porzuconego koszyka z emaila retencyjnego. Przekaż token z query parametru linka. Po sukcesie koszyk wraca do aktywnego stanu, a jego secret jest rotowany — nowy secret zwracany jednorazowo w polu secret (SDK zapisuje go do cookie; API caller musi go utrwalić natychmiast — poprzedni secret przestaje działać). Błędny lub przeterminowany link zwraca CART_RECOVERY_LINK_EXPIRED lub CART_RECOVERY_LINK_INVALID bez ujawniania zawartości koszyka; koszyk nieistniejący — CART_NOT_FOUND.

CartRecoveryRedeemmutationCart Mutations
mutation CartRecoveryRedeem($token: String!)

Redeem a signed cart recovery link (from an abandoned-cart email). Pass the token taken from the link's query parameter. On success the cart is recovered (made active again) and its access secret is ROTATED — the NEW secret is returned once in `secret` (persist it immediately; the previous secret stops working), and the SDK sets the cart-id cookie to the recovered cart. Buyer self-service: the merchant only sends the link, never takes over the cart. A bad link returns `CART_RECOVERY_LINK_EXPIRED` or `CART_RECOVERY_LINK_INVALID` without exposing any cart content; `CART_NOT_FOUND` if the cart no longer exists.

Variables

NameTypeDefaultRequired
$tokenString!Yes
GraphQL operation
mutation CartRecoveryRedeem($token: String!) {
cartRecoveryRedeem(token: $token) {
cart {
...Cart
}
secret
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

Adresy i metody — shipping / billing / payment

Checkout w DoSwiftly nie jest osobną encją — operacje wystawione są bezpośrednio na Cart. Ustaw shipping address PRZED zapytaniem o dostępne metody wysyłki — koszty zależą od kraju/regionu.

CartSetShippingAddressmutationCart Completion Mutations
mutation CartSetShippingAddress($input: CartSetShippingAddressInput!)

Phase 3 unify-cart-graphql-surface: wszystkie fulfillment + payment + completion operations teraz na Cart aggregate (zamiast Checkout dual-aggregate). Klient robi typowy checkout flow: cart create/add items → setShipping/Billing/Method → selectPayment → (optional) applyGiftCard → cartComplete → Order created. Sets the shipping address on the cart (full replace, not patch). Triggers cart re-pricing (tax recalculation per address country/region). Address format validated against `CartAddressInput` constraints (firstName/lastName/streetLine1/city/country/postalCode required). Errors: `INVALID_ADDRESS`, `CART_NOT_FOUND`.

Variables

NameTypeDefaultRequired
$inputCartSetShippingAddressInput!Yes
GraphQL operation
mutation CartSetShippingAddress($input: CartSetShippingAddressInput!) {
cartSetShippingAddress(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError
CartSetBillingAddressmutationCart Completion Mutations
mutation CartSetBillingAddress($input: CartSetBillingAddressInput!)

Sets the billing address on the cart (full replace). Independent of shipping address — pass it explicitly even when "billing same as shipping". Errors: `INVALID_ADDRESS`, `CART_NOT_FOUND`.

Variables

NameTypeDefaultRequired
$inputCartSetBillingAddressInput!Yes
GraphQL operation
mutation CartSetBillingAddress($input: CartSetBillingAddressInput!) {
cartSetBillingAddress(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError
CartSelectShippingMethodmutationCart Completion Mutations
mutation CartSelectShippingMethod($input: CartSelectShippingMethodInput!)

Selects a shipping method by `shippingMethodId` (typed `ID!`, a stable shipping-method UUID — NOT a per-request token). The id comes from a list of methods available for the current address + cart subtotal (queryable separately). Errors: `SHIPPING_METHOD_REQUIRED`, `ZIP_CODE_NOT_SUPPORTED`, `CART_NOT_FOUND`.

Variables

NameTypeDefaultRequired
$inputCartSelectShippingMethodInput!Yes
GraphQL operation
mutation CartSelectShippingMethod($input: CartSelectShippingMethodInput!) {
cartSelectShippingMethod(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError
CartSelectPaymentMethodmutationCart Completion Mutations
mutation CartSelectPaymentMethod($input: CartSelectPaymentMethodInput!)

Selects a payment method on the cart by category (`methodType` — BLIK, CARD, BANK_TRANSFER, ...). Optional `preferredProvider` overrides the merchant priority when the buyer explicitly picks a gateway from `PaymentMethod.providersAvailable`. The backend resolves the gateway routing from `MerchantPaymentConfig` at `cartComplete`; the selection persisted here is the category. Errors: `PAYMENT_METHOD_REQUIRED`, `CART_NOT_FOUND`.

Variables

NameTypeDefaultRequired
$inputCartSelectPaymentMethodInput!Yes
GraphQL operation
mutation CartSelectPaymentMethod($input: CartSelectPaymentMethodInput!) {
cartSelectPaymentMethod(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

CartClearPaymentSelection przydaje się gdy storefront chce zresetować preselekcję instrumentu (np. po nieudanej autoryzacji karty zapisanej w wallecie).

CartClearPaymentSelectionmutationCart Completion Mutations
mutation CartClearPaymentSelection($input: CartClearPaymentSelectionInput!)

Clears all payment selection state on the cart in a single atomic operation. Use for accordion "back to method picker" flows w storefront UI. Idempotent — calling twice yields the same end state. Cart MUSI być `ACTIVE` (CONVERTED carts reject z `ALREADY_COMPLETED`). Rate limit: 30 requests/minute per IP+shop.

Variables

NameTypeDefaultRequired
$inputCartClearPaymentSelectionInput!Yes
GraphQL operation
mutation CartClearPaymentSelection($input: CartClearPaymentSelectionInput!) {
cartClearPaymentSelection(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

Karty podarunkowe

CartApplyGiftCard przykleja kartę po kodzie do koszyka, CartRemoveGiftCard zdejmuje ją po CartAppliedGiftCard.id. CartUpdateGiftCardRecipient ustawia odbiorcę gdy klient kupuje gift card jako prezent (data dostarczenia, wiadomość, email odbiorcy).

CartApplyGiftCardmutationCart Completion Mutations
mutation CartApplyGiftCard($input: CartApplyGiftCardInput!)

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 `cartComplete`. Errors: `GIFT_CARD_NOT_FOUND`, `GIFT_CARD_DEPLETED`, `GIFT_CARD_UNUSABLE`.

Variables

NameTypeDefaultRequired
$inputCartApplyGiftCardInput!Yes
GraphQL operation
mutation CartApplyGiftCard($input: CartApplyGiftCardInput!) {
cartApplyGiftCard(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError
CartRemoveGiftCardmutationCart Completion Mutations
mutation CartRemoveGiftCard($input: CartRemoveGiftCardInput!)

Removes a gift card from the applied list and recalculates FIFO `appliedAmount` for the remaining cards. Since gift card balances are only debited at `cartComplete`, removing before completion has no effect on the underlying gift card balance.

Variables

NameTypeDefaultRequired
$inputCartRemoveGiftCardInput!Yes
GraphQL operation
mutation CartRemoveGiftCard($input: CartRemoveGiftCardInput!) {
cartRemoveGiftCard(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError
CartUpdateGiftCardRecipientmutationCart Completion Mutations
mutation CartUpdateGiftCardRecipient($input: CartUpdateGiftCardRecipientInput!)

Sets per-line-item recipient details (name, email, message) for digital gift card products in the cart (line items where the variant represents a gift-card SKU). Required before `cartComplete` for any line item with a gift-card variant. Recipient details propagated to the resulting order.

Variables

NameTypeDefaultRequired
$inputCartUpdateGiftCardRecipientInput!Yes
GraphQL operation
mutation CartUpdateGiftCardRecipient($input: CartUpdateGiftCardRecipientInput!) {
cartUpdateGiftCardRecipient(input: $input) {
cart {
...Cart
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: Cart, CartWarning, UserError

Finalizacja zamówienia

CartComplete atomowo tworzy Order, dezaktywuje karty podarunkowe, wysyła order-created confirmation. Idempotentna na idempotencyKey (auto-generated z cartId + minute timestamp jeśli pominiesz). Po sukcesie koszyk ma status CONVERTED (nie wraca już w payloadzie).

CartCompletemutationCart Completion Mutations
mutation CartComplete($input: CartCompleteInput!)

Finalizes the cart — creates the `Order`, deducts gift cards, sends order-created confirmation — all atomically. **Idempotent on `idempotencyKey`** (auto-generated from cartId + minute timestamp if caller omits it). Returns `order` field after completion. Note: `paymentUrl` is intentionally NOT in payload — for hosted gateways (online providers) the storefront calls a separate `paymentCreate` mutation after this returns (check `order.canCreatePayment` first). Errors: `EMAIL_REQUIRED`, `SHIPPING_ADDRESS_REQUIRED`, `SHIPPING_METHOD_REQUIRED`, `PAYMENT_METHOD_REQUIRED`, `INSUFFICIENT_STOCK`, `ALREADY_COMPLETED`.

Variables

NameTypeDefaultRequired
$inputCartCompleteInput!Yes
GraphQL operation
mutation CartComplete($input: CartCompleteInput!) {
cartComplete(input: $input) {
order {
...Order
}
userErrors {
...UserError
}
warnings {
...CartWarning
}
}
}
Uses fragments: CartWarning, Order, UserError
Brak pola cart w payloadzie

Od wersji @doswiftly/storefront-sdk 9.x payload cartComplete zwraca wyłącznie order (breaking change). Storefront pracuje dalej z obiektem orderorder.canCreatePayment jest jedynym branchem dla następnego kroku.

Najczęstsze userErrors[].code: EMAIL_REQUIRED, SHIPPING_ADDRESS_REQUIRED, SHIPPING_METHOD_REQUIRED, PAYMENT_METHOD_REQUIRED, INSUFFICIENT_STOCK, ALREADY_COMPLETED. Dla dostawy do paczkomatu z płatnością za pobraniem dochodzi COD_PICKUP_POINT_NOT_SUPPORTED (wybrany punkt nie obsługuje inkasa — patrz Checkout → Pobranie (COD) do paczkomatu).

Inicjacja sesji płatności

PaymentCreate wywołujesz po CartComplete gdy order.canCreatePayment === true (offline metody jak COD / przelew pomijają ten krok). Mutacja jest idempotentna — retry zwraca istniejącą wciąż-ważną sesję, nie tworzy duplikatu. returnUrl / cancelUrl (opcjonalne) muszą wskazywać na zweryfikowaną domenę sklepu (open-redirect protection — backend nigdy nie ufa notifyUrl od klienta, buduje go server-side).

PaymentCreatemutationCart Completion Mutations
mutation PaymentCreate($input: PaymentCreateInput!)

Initiates a payment session for an order created by `cartComplete` — call this when `order.canCreatePayment` is `true` (orders with an offline payment method like cash-on-delivery skip this step). The session charges the order's **outstanding balance** (total minus settled captures plus settled refunds), not necessarily the full total — a gift-card-covered part or an earlier partial payment is never charged twice; pending (unfinished) sessions do not reduce the balance. `orderId` is required; `returnUrl` / `cancelUrl` are optional and, when supplied, must point to a verified domain of the shop (open-redirect protected). Branch on the returned `payment.flow`: `ONLINE_REDIRECT` → redirect to `payment.redirectUrl`, `ONLINE_EMBEDDED` → render a widget with `payment.clientSecret`, `INSTANT_DIRECT` → already settled, read `payment.status`. Public, but ownership-checked — an authenticated customer cannot pay for another customer's order. **Idempotent** — calling it again for the same order returns the existing still-valid session **as long as its amount still equals the outstanding balance**; when the balance changed in the meantime (e.g. the shop edited the order items), a fresh session on the current balance is created instead (safe to retry either way). Rate limit: 5 requests/minute. `userErrors[].code`: `ORDER_NOT_FOUND`, `ORDER_ALREADY_PAID`, `ORDER_NOT_PAYABLE`, `PAYMENT_PROVIDER_NOT_CONFIGURED`, `RETURN_URL_INVALID`, `INVALID_ID_FORMAT`, `PAYMENT_FAILED`, `INSTRUMENT_PRESELECTION_FAILED`. `warnings[]` array zawiera `INSTRUMENT_CLEARED_FOR_RETRY` post-auto-clear instrument-specific failure (storefront retry hint: next attempt uses gateway default landing).

Variables

NameTypeDefaultRequired
$inputPaymentCreateInput!Yes
GraphQL operation
mutation PaymentCreate($input: PaymentCreateInput!) {
paymentCreate(input: $input) {
payment {
...PaymentSession
}
userErrors {
...UserError
}
warnings {
...PaymentWarning
}
}
}
Rozgałęzienie po payment.flow

Storefront robi switch (payment.flow):

  • ONLINE_REDIRECT → przekieruj na payment.redirectUrl (PayU hosted, Stripe Checkout)
  • ONLINE_EMBEDDED → wyrenderuj widget in-page z payment.clientSecret (Stripe Elements)
  • INSTANT_DIRECT → płatność rozliczona bez UI — odczytaj payment.status i przejdź do potwierdzenia
  • OFFLINE_MANUAL → nie wystąpi (canCreatePayment jest false dla offline)

Najczęstsze userErrors[].code: ORDER_NOT_FOUND, ORDER_ALREADY_PAID, ORDER_NOT_PAYABLE, PAYMENT_PROVIDER_NOT_CONFIGURED, RETURN_URL_INVALID, INVALID_ID_FORMAT, PAYMENT_FAILED, INSTRUMENT_PRESELECTION_FAILED. warnings[] zawiera INSTRUMENT_CLEARED_FOR_RETRY po auto-czyszczeniu preselekcji instrumentu.

Throttling: 5 req/min (limit finansowy — taki sam jak CartComplete). Workflow end-to-end: SDK / Cart — Completion Lifecycle.


Klient — autoryzacja i profil

Autoryzacja odbywa się przez httpOnly cookie customerAccessToken (30 dni max-age, JWT TTL 24h). Mutacje nie przyjmują tokena jako parametru — backend odczytuje go z headera cookie wysyłanego automatycznie. Wszystkie zwracają userErrors { message code field }.

Rejestracja, logowanie, sesja

CustomerSignup od razu zwraca customerAccessToken — konto jest tworzone w statusie ACTIVE (brak pending state). Email aktywacyjny z tokenem do CustomerActivate jest wysyłany, ale nie jest wymagany do logowania (potwierdzenie emailVerified=true jest opcjonalne). Bot-protection guarded.

CustomerSignupmutationCustomer Auth Mutations
mutation CustomerSignup($input: CustomerCreateInput!)

Registers a new customer. On shops with the default flow the buyer is signed in immediately: `accountStatus` is `ACTIVE` and `customerAccessToken` is returned. On shops that verify new accounts manually (approval gate) `accountStatus` is `PENDING_APPROVAL`, `customerAccessToken` is null and login is refused until the store approves the account — show `pendingApprovalMessage` to the buyer. A welcome e-mail with an e-mail verification link (consumed by `customerVerifyEmail` or the built-in confirmation page) is sent asynchronously; verification is NOT required for login. Cookie: `customerAccessToken`, 30-day max-age, httpOnly. JWT TTL: 24h. Bot-protection guarded.

Variables

NameTypeDefaultRequired
$inputCustomerCreateInput!Yes
GraphQL operation
mutation CustomerSignup($input: CustomerCreateInput!) {
customerSignup(input: $input) {
customer {
...Customer
}
customerAccessToken {
...CustomerAccessToken
}
accountStatus
pendingApprovalMessage
userErrors {
...UserError
}
}
}
CustomerLoginmutationCustomer Auth Mutations
mutation CustomerLogin($input: CustomerAccessTokenCreateInput!)

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).

Variables

NameTypeDefaultRequired
$inputCustomerAccessTokenCreateInput!Yes
GraphQL operation
mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
customerLogin(input: $input) {
customerAccessToken {
...CustomerAccessToken
}
userErrors {
...UserError
}
}
}
CustomerLogoutmutationCustomer Auth Mutations
mutation CustomerLogout

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.

GraphQL operation
mutation CustomerLogout {
customerLogout {
deletedAccessToken
deletedCustomerAccessTokenId
userErrors {
...UserError
}
}
}
Uses fragments: UserError

CustomerRefreshToken odnawia JWT przed jego wygaśnięciem (token żyje 24h, refresh w SDK leci proaktywnie ~5min przed expiry).

CustomerRefreshTokenmutationCustomer Auth Mutations
mutation CustomerRefreshToken

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.

GraphQL operation
mutation CustomerRefreshToken {
customerRefreshToken {
customerAccessToken {
...CustomerAccessToken
}
userErrors {
...UserError
}
}
}

Aktualizacja profilu

CustomerUpdatemutationCustomer Profile Mutations
mutation CustomerUpdate($customer: CustomerUpdateInput!)

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.

Variables

NameTypeDefaultRequired
$customerCustomerUpdateInput!Yes
GraphQL operation
mutation CustomerUpdate($customer: CustomerUpdateInput!) {
customerUpdate(customer: $customer) {
customer {
...Customer
}
userErrors {
...UserError
}
}
}
Uses fragments: Customer, UserError

Adresy klienta

Klient może mieć dowolnie wiele adresów; jeden jest oznaczony jako domyślny i automatycznie wczytywany do checkoutu.

CustomerAddAddressmutationCustomer Address Mutations
mutation CustomerAddAddress($address: MailingAddressInput!)

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.

Variables

NameTypeDefaultRequired
$addressMailingAddressInput!Yes
GraphQL operation
mutation CustomerAddAddress($address: MailingAddressInput!) {
customerAddAddress(address: $address) {
address {
...MailingAddress
}
userErrors {
...UserError
}
}
}
Uses fragments: MailingAddress, UserError
CustomerUpdateAddressmutationCustomer Address Mutations
mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
$addressMailingAddressInput!Yes
GraphQL operation
mutation CustomerUpdateAddress($id: ID!, $address: MailingAddressInput!) {
customerUpdateAddress(id: $id, address: $address) {
address {
...MailingAddress
}
userErrors {
...UserError
}
}
}
Uses fragments: MailingAddress, UserError
CustomerRemoveAddressmutationCustomer Address Mutations
mutation CustomerRemoveAddress($id: ID!)

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).

Variables

NameTypeDefaultRequired
$idID!Yes
GraphQL operation
mutation CustomerRemoveAddress($id: ID!) {
customerRemoveAddress(id: $id) {
deletedAddressId
userErrors {
...UserError
}
}
}
Uses fragments: UserError
CustomerSetDefaultAddressmutationCustomer Address Mutations
mutation CustomerSetDefaultAddress($addressId: ID!)

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.

Variables

NameTypeDefaultRequired
$addressIdID!Yes
GraphQL operation
mutation CustomerSetDefaultAddress($addressId: ID!) {
customerSetDefaultAddress(addressId: $addressId) {
customer {
...Customer
}
userErrors {
...UserError
}
}
}
Uses fragments: Customer, UserError

Reset i aktywacja hasła

CustomerRequestPasswordReset wysyła email z linkiem zawierającym token jednorazowy (24h TTL). CustomerResetPassword konsumuje token + ustawia nowe hasło + zwraca świeży JWT (auto-login). CustomerActivate służy do aktywacji konta utworzonego w stanie INACTIVE (np. import migracyjny) — token 64-hex z welcome emaila, single-use (atomic mark used_at), na sukces flippuje status → ACTIVE i emailVerified=true.

CustomerRequestPasswordResetmutationCustomer Password Mutations
mutation CustomerRequestPasswordReset($email: String!)

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.

Variables

NameTypeDefaultRequired
$emailString!Yes
GraphQL operation
mutation CustomerRequestPasswordReset($email: String!) {
customerRequestPasswordReset(email: $email) {
userErrors {
...UserError
}
}
}
Uses fragments: UserError
CustomerActivatemutationCustomer Password Mutations
mutation CustomerActivate($token: String!, $password: String!)

Activates a newly-created account using the 64-hex activation token from the account-activation email (sent when the merchant creates the account) + 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.

Variables

NameTypeDefaultRequired
$tokenString!Yes
$passwordString!Yes
GraphQL operation
mutation CustomerActivate($token: String!, $password: String!) {
customerActivate(token: $token, password: $password) {
customer {
...Customer
}
customerAccessToken {
...CustomerAccessToken
}
userErrors {
...UserError
}
}
}
CustomerResetPasswordmutationCustomer Password Mutations
mutation CustomerResetPassword($token: String!, $newPassword: String!)

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, marks the e-mail address as verified (completing the reset proves mailbox ownership) and returns a fresh JWT for auto-login (no second login step needed). Rate-limited.

Variables

NameTypeDefaultRequired
$tokenString!Yes
$newPasswordString!Yes
GraphQL operation
mutation CustomerResetPassword($token: String!, $newPassword: String!) {
customerResetPassword(token: $token, newPassword: $newPassword) {
customer {
...Customer
}
customerAccessToken {
...CustomerAccessToken
}
userErrors {
...UserError
}
}
}

Weryfikacja adresu e-mail

CustomerVerifyEmail potwierdza adres tokenem z wiadomości weryfikacyjnej (64 znaki szesnastkowe, ważny 24 godziny, jednorazowy). Nie wymaga sesji — klient może otworzyć link na dowolnym urządzeniu. Ponowne wywołanie po udanej weryfikacji też zwraca success: true, więc odświeżenie strony bezpiecznie pokazuje ekran sukcesu. Kody błędów: TOKEN_INVALID, TOKEN_EXPIRED (zaproponuj wtedy ponowne wysłanie), TOKEN_USED.

CustomerResendVerificationEmail nie przyjmuje argumentów — adresata bierze z sesji zalogowanego klienta, więc nie da się nim sprawdzić, czy dowolny adres ma konto. Kody błędów: TOKEN_INVALID (brak sesji), ALREADY_VERIFIED (adres już potwierdzony, wiadomość nie zostaje wysłana). E-mail wychodzi asynchronicznie, więc licz się z niewielkim opóźnieniem.

CustomerVerifyEmailmutationCustomer Password Mutations
mutation CustomerVerifyEmail($token: String!)

Confirms the customer's e-mail address using the 64-hex verification token from the verification e-mail. Public (no auth needed — the customer may open the link on any device). Token TTL is 24h, single-use; calling again after a successful verification returns `success: true` (safe to show a success screen on refresh). Error codes: `TOKEN_INVALID`, `TOKEN_EXPIRED` (offer `customerResendVerificationEmail`), `TOKEN_USED`. Rate-limited.

Variables

NameTypeDefaultRequired
$tokenString!Yes
GraphQL operation
mutation CustomerVerifyEmail($token: String!) {
customerVerifyEmail(token: $token) {
success
userErrors {
...UserError
}
}
}
Uses fragments: UserError
CustomerResendVerificationEmailmutationCustomer Password Mutations
mutation CustomerResendVerificationEmail

Queues a fresh e-mail verification message for the currently authenticated customer. Takes no arguments — the recipient is derived from the session, so the mutation cannot probe whether an arbitrary address has an account. Error codes: `TOKEN_INVALID` (not signed in), `ALREADY_VERIFIED` (address already confirmed — no e-mail sent). The e-mail is sent asynchronously (small delay). Rate-limited.

GraphQL operation
mutation CustomerResendVerificationEmail {
customerResendVerificationEmail {
success
userErrors {
...UserError
}
}
}
Uses fragments: UserError

Zwroty (RMA)

Zwroty są tworzone w statusie REQUESTED i wymagają approvalu merchanta (NIE są auto-approve). Backend waliduje że Order.fulfillmentStatus pozwala na zwrot i że ilości w request nie przekraczają już-wysłanych/nie-zwróconych.

ReturnCreate przyjmuje listę pozycji [{ variantId, quantity, reason, condition }], opcjonalny compensationType (REFUND / STORE_CREDIT) oraz customerNote. Obsługuje idempotencyKey dla retry-safe creation.

ReturnCreatemutationReturn Mutations
mutation ReturnCreate($input: ReturnCreateInput!)

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.

Variables

NameTypeDefaultRequired
$inputReturnCreateInput!Yes
GraphQL operation
mutation ReturnCreate($input: ReturnCreateInput!) {
returnCreate(input: $input) {
return {
...Return
}
userErrors {
...UserError
}
}
}
Uses fragments: Return, UserError
ReturnCancelmutationReturn Mutations
mutation ReturnCancel($id: ID!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
GraphQL operation
mutation ReturnCancel($id: ID!) {
returnCancel(id: $id) {
return {
...Return
}
userErrors {
...UserError
}
}
}
Uses fragments: Return, UserError

Program lojalnościowy

RedeemLoyaltyReward wymienia punkty na nagrodę. W zależności od typu nagrody payload wypełnia discountCode (kod LOYALTY-XXXX, 30-dniowy TTL), productDiscountCode (single-use 100%-off dla konkretnego produktu) lub giftCardCode (nowa karta podarunkowa). Punkty są odejmowane atomowo — jeśli zewnętrzna operacja (np. wystawienie gift card) się nie powiedzie, punkty wracają.

RedeemLoyaltyRewardmutationLoyalty Program Mutations
mutation RedeemLoyaltyReward($input: RedeemRewardInput!)

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.

Variables

NameTypeDefaultRequired
$inputRedeemRewardInput!Yes
GraphQL operation
mutation RedeemLoyaltyReward($input: RedeemRewardInput!) {
loyaltyRedeemReward(input: $input) {
...RedeemRewardPayload
}
}
Uses fragments: RedeemRewardPayload

GenerateReferralCode to UPSERT — pierwsze wywołanie generuje kod REF-XXXXXXXX (8 alfanumeryków) i zapisuje w customers.referral_code, kolejne wywołania zwracają ten sam kod plus shareUrl zbudowany z domeny sklepu.

GenerateReferralCodemutationLoyalty Program Mutations
mutation GenerateReferralCode

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.

GraphQL operation
mutation GenerateReferralCode {
loyaltyGenerateReferralCode {
...GenerateReferralCodePayload
}
}

Recenzje

ReviewCreate przyjmuje rating 1-5, content 10-5000 znaków (sanitized przez sanitizePlainText). Domyślny stan to PENDING — recenzja jest ukryta od publiki dopóki merchant nie zatwierdzi. Gdy input zawiera orderId, backend ustawia isVerifiedPurchase=true automatycznie. Bot-protected, rate-limited (10 req/min).

ReviewCreatemutationReview Mutations
mutation ReviewCreate($input: ReviewCreateInput!)

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).

Variables

NameTypeDefaultRequired
$inputReviewCreateInput!Yes
GraphQL operation
mutation ReviewCreate($input: ReviewCreateInput!) {
reviewCreate(input: $input) {
review {
...ProductReview
}
userErrors {
...UserError
}
}
}
Uses fragments: ProductReview, UserError

Pomocność recenzji — ReviewUpvote / ReviewDownvote aktualizują liczniki helpfulCount / unhelpfulCount. Każdy klient może oddać jeden głos per recenzja (toggle / cancel po wielokrotnym wywołaniu).

ReviewUpvotemutationReview Mutations
mutation ReviewUpvote($reviewId: ID!)

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).

Variables

NameTypeDefaultRequired
$reviewIdID!Yes
GraphQL operation
mutation ReviewUpvote($reviewId: ID!) {
reviewUpvote(reviewId: $reviewId) {
review {
...ProductReview
}
userErrors {
...UserError
}
}
}
Uses fragments: ProductReview, UserError
ReviewDownvotemutationReview Mutations
mutation ReviewDownvote($reviewId: ID!)

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`.

Variables

NameTypeDefaultRequired
$reviewIdID!Yes
GraphQL operation
mutation ReviewDownvote($reviewId: ID!) {
reviewDownvote(reviewId: $reviewId) {
review {
...ProductReview
}
userErrors {
...UserError
}
}
}
Uses fragments: ProductReview, UserError

Lista życzeń (Wishlist)

Klient może mieć wiele list (np. "Prezenty urodzinowe", "Do kupienia później"). Lista może być publiczna (isPublic=true — dostępna po publicznym slug) lub prywatna.

WishlistCreatemutationWishlist Mutations
mutation WishlistCreate($input: WishlistCreateInput!)

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.

Variables

NameTypeDefaultRequired
$inputWishlistCreateInput!Yes
GraphQL operation
mutation WishlistCreate($input: WishlistCreateInput!) {
wishlistCreate(input: $input) {
wishlist {
...Wishlist
}
userErrors {
...UserError
}
}
}
Uses fragments: UserError, Wishlist

WishlistAddItem jest idempotentne na unique constraint (wishlist_id, product_id, variant_id) — dodanie już-obecnego elementu to silent no-op. Pole priceAtAdd jest captured do powiadomień price-drop.

WishlistAddItemmutationWishlist Mutations
mutation WishlistAddItem($id: ID!, $input: WishlistItemInput!)

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.

Variables

NameTypeDefaultRequired
$idID!Yes
$inputWishlistItemInput!Yes
GraphQL operation
mutation WishlistAddItem($id: ID!, $input: WishlistItemInput!) {
wishlistAddItem(id: $id, input: $input) {
wishlist {
...Wishlist
}
userErrors {
...UserError
}
}
}
Uses fragments: UserError, Wishlist

WishlistRemoveItem usuwa po itemId (ID rzędu WishlistItem), NIE po productId. WishlistDelete kaskadowo usuwa listę razem z elementami — operacja nieodwracalna.

WishlistRemoveItemmutationWishlist Mutations
mutation WishlistRemoveItem($id: ID!, $itemId: ID!)

Hard-deletes a wishlist item by `itemId` (the `WishlistItem` row id, NOT the product id).

Variables

NameTypeDefaultRequired
$idID!Yes
$itemIdID!Yes
GraphQL operation
mutation WishlistRemoveItem($id: ID!, $itemId: ID!) {
wishlistRemoveItem(id: $id, itemId: $itemId) {
wishlist {
...Wishlist
}
userErrors {
...UserError
}
}
}
Uses fragments: UserError, Wishlist
WishlistDeletemutationWishlist Mutations
mutation WishlistDelete($id: ID!)

Hard-deletes the wishlist row. All wishlist items are removed via cascade. No soft-delete; cannot be undone.

Variables

NameTypeDefaultRequired
$idID!Yes
GraphQL operation
mutation WishlistDelete($id: ID!) {
wishlistDelete(id: $id) {
wishlist {
...Wishlist
}
userErrors {
...UserError
}
}
}
Uses fragments: UserError, Wishlist

Newsletter

Obie mutacje są publiczne — nie wymagają sesji klienta, więc nadają się do widgetu w stopce.

CustomerSubscribeToMarketing działa w modelu podwójnego potwierdzenia: adres zostaje zapisany jako oczekujący, a na skrzynkę idzie wiadomość z linkiem. Na listę trafia dopiero po jego kliknięciu.

accepted: true nie znaczy „adres jest na liście"

Oznacza wyłącznie, że żądanie zostało przyjęte. Adres już istniejący lub już zapisany zwraca dokładnie taką samą odpowiedź — dzięki temu nie da się tą drogą sprawdzać, kto jest na liście. Nie buduj na tej wartości komunikatu w rodzaju „ten adres jest już zapisany".

accepted: false oznacza odrzucenie adresu — powód znajdziesz w userErrors[].code (INVALID_EMAIL_FORMAT, TOO_LONG). Jeden adres potrafi dać więcej niż jeden wpis, więc przeglądaj całą tablicę zamiast czytać pierwszy element.

CustomerUnsubscribeFromMarketing ma ten sam kontrakt odpowiedzi. Osobna strona wypisu nie jest konieczna, żeby zachować zgodność z przepisami — wiadomości marketingowe niosą własny link wypisujący jednym kliknięciem.

Obie mutacje mają limit 10 żądań na minutę i wymagają tokenu ochrony przed botami, jeśli sklep ma skonfigurowanego dostawcę.

CustomerSubscribeToMarketingmutationNewsletter Mutations
mutation CustomerSubscribeToMarketing($input: CustomerSubscribeToMarketingInput!)

Subscribes an e-mail address to the shop newsletter. Public — no customer session required, so it fits a footer widget. Double opt-in: the address is stored as pending and a confirmation e-mail is sent; it joins the list only after the recipient clicks the link. `accepted: true` means the request was taken, NOT that the address is on the list — an address that already exists or is already subscribed returns exactly the same response, so the endpoint cannot be used to probe the list. `accepted: false` means the submitted address was rejected: read `userErrors[].code` (`INVALID_EMAIL_FORMAT`, `TOO_LONG`) — one address can produce more than one entry, so scan the array instead of reading the first element. Rate limited to 10 requests per minute; requires a bot-protection token when the shop has a provider configured.

Variables

NameTypeDefaultRequired
$inputCustomerSubscribeToMarketingInput!Yes
GraphQL operation
mutation CustomerSubscribeToMarketing($input: CustomerSubscribeToMarketingInput!) {
customerSubscribeToMarketing(input: $input) {
accepted
userErrors {
...UserError
}
}
}
Uses fragments: UserError
CustomerUnsubscribeFromMarketingmutationNewsletter Mutations
mutation CustomerUnsubscribeFromMarketing($input: CustomerUnsubscribeFromMarketingInput!)

Removes an e-mail address from the shop newsletter. Public — no customer session required. Same response contract as the subscribe mutation: `accepted: true` for every address that passed validation, whether or not it was ever subscribed; `accepted: false` only for a rejected address, with the reason in `userErrors[].code`. Marketing e-mails also carry their own one-click unsubscribe link, so you do not need to build an unsubscribe page to stay compliant. Rate limited to 10 requests per minute; requires a bot-protection token when the shop has a provider configured.

Variables

NameTypeDefaultRequired
$inputCustomerUnsubscribeFromMarketingInput!Yes
GraphQL operation
mutation CustomerUnsubscribeFromMarketing($input: CustomerUnsubscribeFromMarketingInput!) {
customerUnsubscribeFromMarketing(input: $input) {
accepted
userErrors {
...UserError
}
}
}
Uses fragments: UserError

Formularze sklepu

Wysyłka wartości zebranych z formularza pobranego zapytaniem form. Wszystkie wartości są stringami: CHECKBOX jako "true"/"false", NUMBER jako liczba w stringu, MULTI_SELECT przez tablicę values. Błędy walidacji wracają w userErrors ze stabilnymi kodami (UNKNOWN_FIELD, REQUIRED_FIELD_MISSING, INVALID_OPTION, INVALID_VALUE, FORM_NOT_FOUND, FORM_INACTIVE) i kluczem pola w field. Mutacja jest publiczna, chroniona przed botami i limitowana (5 żądań / minutę / sklep + IP).

FormSubmitmutationTreści cyfrowe
mutation FormSubmit($slug: String!, $values: [FormFieldValueInput!]!)

Submits a store-defined contact form. Values are strings (CHECKBOX "true"/"false", NUMBER a decimal string, MULTI_SELECT via `values`). Validation errors come back in `userErrors` with stable codes (UNKNOWN_FIELD, REQUIRED_FIELD_MISSING, INVALID_OPTION, INVALID_VALUE, FORM_NOT_FOUND, FORM_INACTIVE) and the offending field key in `field`. Bot-protection guarded and rate-limited (5/min per shop+IP).

Variables

NameTypeDefaultRequired
$slugString!Yes
$values[FormFieldValueInput!]!Yes
GraphQL operation
mutation FormSubmit($slug: String!, $values: [FormFieldValueInput!]!) {
formSubmit(slug: $slug, values: $values) {
success
successMessage
userErrors {
...UserError
}
}
}
Uses fragments: UserError

Pobieranie treści cyfrowej

Kupiony plik celowo nie ma stałego adresu. Zamiast tego prosisz o jednorazowy odnośnik, który wygasa w ciągu kilku minut. Gdyby adres był stały, każdy, kto raz zobaczy odpowiedź — w przekazanej dalej wiadomości, w historii przeglądarki, w logu serwera pośredniczącego — miałby dostęp do towaru bezterminowo.

Identyfikatory bierz z OrderLineItem.digitalDownloads: pozycję zamówienia i plik. Mutacja nie przyjmuje żadnego identyfikatora uprawnienia — to pojęcie wewnętrzne platformy i nie musisz go znać.

Czego nie robić z otrzymanym odnośnikiem: nie zapisuj go w pamięci podręcznej, nie umieszczaj w adresie strony, nie wysyłaj mailem. Zadziała dokładnie raz.

Wyczerpany limit pobrań i koniec okresu dostępu wracają w userErrors, a nie jako błąd zapytania. To normalne stany handlowe, które masz pokazać kupującemu — nie awarie do zalogowania. Odpowiedź nie rozróżnia „nie ma takiej pozycji" od „ta pozycja nie obejmuje tego pliku", żeby po odpowiedziach nie dało się zgadywać cudzych zakupów.

DigitalDownloadLinkCreatemutationTreści cyfrowe
mutation DigitalDownloadLinkCreate($orderLineItemId: ID!, $assetId: ID!)

Tworzy jednorazowy odnośnik do pobrania kupionej treści cyfrowej. Odnośnik działa dokładnie raz i wygasa po kilku minutach — nie zapisuj go w pamięci podręcznej, nie umieszczaj w adresie strony i nie wysyłaj mailem. Identyfikatory bierz z `OrderLineItem.digitalDownloads`. Wyczerpany limit pobrań i wygaśnięcie dostępu wracają jako `userErrors`, a nie jako błąd zapytania — pokaż je klientowi.

Variables

NameTypeDefaultRequired
$orderLineItemIdID!Yes
$assetIdID!Yes
GraphQL operation
mutation DigitalDownloadLinkCreate($orderLineItemId: ID!, $assetId: ID!) {
digitalDownloadLinkCreate(orderLineItemId: $orderLineItemId, assetId: $assetId) {
link {
url
expiresInSeconds
}
userErrors {
...UserError
}
}
}
Uses fragments: UserError

Pełna lista mutacji per sekcja

Cart Mutations

OperationKindDescription
CartCreatemutationCreates a new cart and optionally pre-populates it with line items. Returns a one-time `secret` (the cart access capability) alongside the cart — the SDK persists it in the `cart-id` cookie and sends it as the `x-cart-secret` header on later cart operations; a direct API caller must store it immediately, as it cannot be retrieved again. The cart ID is a UUID; the cart 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.
CartAddLinesmutationAdds 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.
CartUpdateLinesmutationUpdates 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.
CartRemoveLinesmutationRemoves 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.
CartDiscountCodesUpdatemutationReplaces (NOT appends) the cart's discount codes with the given list. Pass `[]` to clear all codes. Each code is validated against the 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`. Single canonical replace-all entry point — prior append/single-remove variants were removed in favor of this explicit caller-controlled list semantics.
CartUpdateBuyerIdentitymutationSet the buyer's email and phone on the cart (guest checkout contact details). The cart is bound to a customer automatically from the authenticated session — there is no `customerId` input, so a guest cannot claim another shopper's account. Sign in and the cart attaches to that customer (re-binding overwrites, last-write-wins); the `customerId` is then readable on `cart.buyerIdentity`. Use during guest checkout to capture contact info and after login to attach the buyer. Does not trigger tax / shipping recalculation.
CartMergemutationMerge a guest cart into the signed-in customer's existing cart right after login. Pass the guest cart id; its secret travels in the cart credential header and the customer is taken from the authenticated session (never the client). Line quantities are summed per variant, the buyer's in-session checkout fields win, and the prior customer cart is discarded. The returned cart keeps the SAME id and secret as the guest cart, so the stored cart-id stays valid — no cookie re-issue. Requires authentication (returns `CART_MERGE_REQUIRES_AUTH` otherwise) and refuses carts held in different currencies (`CART_CURRENCY_MISMATCH`).
CartDowngradeOnLogoutmutationDowngrade a cart to guest on logout. Pass the cart id (its secret travels in the cart credential header). Clears the customer association, contact details, addresses and payment selection, but keeps line items, discount codes, the selected shipping method, currency and notes. The cart id and secret are unchanged (no rotation), so the stored cart-id stays valid and the buyer keeps their items as a guest. Call from the logout flow before tearing down the auth session so the next person on a shared device sees none of the previous buyer's data. Gated by the cart secret only (no auth) — a missing/wrong secret returns `CART_NOT_FOUND`.
CartRecoveryRedeemmutationRedeem a signed cart recovery link (from an abandoned-cart email). Pass the token taken from the link's query parameter. On success the cart is recovered (made active again) and its access secret is ROTATED — the NEW secret is returned once in `secret` (persist it immediately; the previous secret stops working), and the SDK sets the cart-id cookie to the recovered cart. Buyer self-service: the merchant only sends the link, never takes over the cart. A bad link returns `CART_RECOVERY_LINK_EXPIRED` or `CART_RECOVERY_LINK_INVALID` without exposing any cart content; `CART_NOT_FOUND` if the cart no longer exists.
CartUpdateNotemutationSets 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.

Customer Auth Mutations

OperationKindDescription
CustomerSignupmutationRegisters a new customer. On shops with the default flow the buyer is signed in immediately: `accountStatus` is `ACTIVE` and `customerAccessToken` is returned. On shops that verify new accounts manually (approval gate) `accountStatus` is `PENDING_APPROVAL`, `customerAccessToken` is null and login is refused until the store approves the account — show `pendingApprovalMessage` to the buyer. A welcome e-mail with an e-mail verification link (consumed by `customerVerifyEmail` or the built-in confirmation page) is sent asynchronously; verification is NOT required for login. Cookie: `customerAccessToken`, 30-day max-age, httpOnly. JWT TTL: 24h. Bot-protection guarded.
CustomerLoginmutationLogs 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).
CustomerLogoutmutationClears 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.
CustomerRefreshTokenmutationIssues 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.

Newsletter Mutations

OperationKindDescription
CustomerSubscribeToMarketingmutationSubscribes an e-mail address to the shop newsletter. Public — no customer session required, so it fits a footer widget. Double opt-in: the address is stored as pending and a confirmation e-mail is sent; it joins the list only after the recipient clicks the link. `accepted: true` means the request was taken, NOT that the address is on the list — an address that already exists or is already subscribed returns exactly the same response, so the endpoint cannot be used to probe the list. `accepted: false` means the submitted address was rejected: read `userErrors[].code` (`INVALID_EMAIL_FORMAT`, `TOO_LONG`) — one address can produce more than one entry, so scan the array instead of reading the first element. Rate limited to 10 requests per minute; requires a bot-protection token when the shop has a provider configured.
CustomerUnsubscribeFromMarketingmutationRemoves an e-mail address from the shop newsletter. Public — no customer session required. Same response contract as the subscribe mutation: `accepted: true` for every address that passed validation, whether or not it was ever subscribed; `accepted: false` only for a rejected address, with the reason in `userErrors[].code`. Marketing e-mails also carry their own one-click unsubscribe link, so you do not need to build an unsubscribe page to stay compliant. Rate limited to 10 requests per minute; requires a bot-protection token when the shop has a provider configured.

Customer Profile Mutations

OperationKindDescription
CustomerUpdatemutationUpdates 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.

Customer Address Mutations

OperationKindDescription
CustomerAddAddressmutationAdds 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.
CustomerUpdateAddressmutationUpdates 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.
CustomerRemoveAddressmutationHard-deletes an address row from `customer_addresses`. Historical orders that referenced this address are unaffected (address is snapshotted into the order at checkout completion).
CustomerSetDefaultAddressmutationMarks 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.

Customer Password Mutations

OperationKindDescription
CustomerRequestPasswordResetmutationSends 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.
CustomerActivatemutationActivates a newly-created account using the 64-hex activation token from the account-activation email (sent when the merchant creates the account) + 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.
CustomerResetPasswordmutationResets 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, marks the e-mail address as verified (completing the reset proves mailbox ownership) and returns a fresh JWT for auto-login (no second login step needed). Rate-limited.
CustomerVerifyEmailmutationConfirms the customer's e-mail address using the 64-hex verification token from the verification e-mail. Public (no auth needed — the customer may open the link on any device). Token TTL is 24h, single-use; calling again after a successful verification returns `success: true` (safe to show a success screen on refresh). Error codes: `TOKEN_INVALID`, `TOKEN_EXPIRED` (offer `customerResendVerificationEmail`), `TOKEN_USED`. Rate-limited.
CustomerResendVerificationEmailmutationQueues a fresh e-mail verification message for the currently authenticated customer. Takes no arguments — the recipient is derived from the session, so the mutation cannot probe whether an arbitrary address has an account. Error codes: `TOKEN_INVALID` (not signed in), `ALREADY_VERIFIED` (address already confirmed — no e-mail sent). The e-mail is sent asynchronously (small delay). Rate-limited.

Cart Completion Mutations

OperationKindDescription
CartSetShippingAddressmutationPhase 3 unify-cart-graphql-surface: wszystkie fulfillment + payment + completion operations teraz na Cart aggregate (zamiast Checkout dual-aggregate). Klient robi typowy checkout flow: cart create/add items → setShipping/Billing/Method → selectPayment → (optional) applyGiftCard → cartComplete → Order created. Sets the shipping address on the cart (full replace, not patch). Triggers cart re-pricing (tax recalculation per address country/region). Address format validated against `CartAddressInput` constraints (firstName/lastName/streetLine1/city/country/postalCode required). Errors: `INVALID_ADDRESS`, `CART_NOT_FOUND`.
CartSetBillingAddressmutationSets the billing address on the cart (full replace). Independent of shipping address — pass it explicitly even when "billing same as shipping". Errors: `INVALID_ADDRESS`, `CART_NOT_FOUND`.
CartSelectShippingMethodmutationSelects a shipping method by `shippingMethodId` (typed `ID!`, a stable shipping-method UUID — NOT a per-request token). The id comes from a list of methods available for the current address + cart subtotal (queryable separately). Errors: `SHIPPING_METHOD_REQUIRED`, `ZIP_CODE_NOT_SUPPORTED`, `CART_NOT_FOUND`.
CartSelectPaymentMethodmutationSelects a payment method on the cart by category (`methodType` — BLIK, CARD, BANK_TRANSFER, ...). Optional `preferredProvider` overrides the merchant priority when the buyer explicitly picks a gateway from `PaymentMethod.providersAvailable`. The backend resolves the gateway routing from `MerchantPaymentConfig` at `cartComplete`; the selection persisted here is the category. Errors: `PAYMENT_METHOD_REQUIRED`, `CART_NOT_FOUND`.
CartApplyGiftCardmutationApplies 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 `cartComplete`. Errors: `GIFT_CARD_NOT_FOUND`, `GIFT_CARD_DEPLETED`, `GIFT_CARD_UNUSABLE`.
CartRemoveGiftCardmutationRemoves a gift card from the applied list and recalculates FIFO `appliedAmount` for the remaining cards. Since gift card balances are only debited at `cartComplete`, removing before completion has no effect on the underlying gift card balance.
CartUpdateGiftCardRecipientmutationSets per-line-item recipient details (name, email, message) for digital gift card products in the cart (line items where the variant represents a gift-card SKU). Required before `cartComplete` for any line item with a gift-card variant. Recipient details propagated to the resulting order.
CartCompletemutationFinalizes the cart — creates the `Order`, deducts gift cards, sends order-created confirmation — all atomically. **Idempotent on `idempotencyKey`** (auto-generated from cartId + minute timestamp if caller omits it). Returns `order` field after completion. Note: `paymentUrl` is intentionally NOT in payload — for hosted gateways (online providers) the storefront calls a separate `paymentCreate` mutation after this returns (check `order.canCreatePayment` first). Errors: `EMAIL_REQUIRED`, `SHIPPING_ADDRESS_REQUIRED`, `SHIPPING_METHOD_REQUIRED`, `PAYMENT_METHOD_REQUIRED`, `INSUFFICIENT_STOCK`, `ALREADY_COMPLETED`.
PaymentCreatemutationInitiates a payment session for an order created by `cartComplete` — call this when `order.canCreatePayment` is `true` (orders with an offline payment method like cash-on-delivery skip this step). The session charges the order's **outstanding balance** (total minus settled captures plus settled refunds), not necessarily the full total — a gift-card-covered part or an earlier partial payment is never charged twice; pending (unfinished) sessions do not reduce the balance. `orderId` is required; `returnUrl` / `cancelUrl` are optional and, when supplied, must point to a verified domain of the shop (open-redirect protected). Branch on the returned `payment.flow`: `ONLINE_REDIRECT` → redirect to `payment.redirectUrl`, `ONLINE_EMBEDDED` → render a widget with `payment.clientSecret`, `INSTANT_DIRECT` → already settled, read `payment.status`. Public, but ownership-checked — an authenticated customer cannot pay for another customer's order. **Idempotent** — calling it again for the same order returns the existing still-valid session **as long as its amount still equals the outstanding balance**; when the balance changed in the meantime (e.g. the shop edited the order items), a fresh session on the current balance is created instead (safe to retry either way). Rate limit: 5 requests/minute. `userErrors[].code`: `ORDER_NOT_FOUND`, `ORDER_ALREADY_PAID`, `ORDER_NOT_PAYABLE`, `PAYMENT_PROVIDER_NOT_CONFIGURED`, `RETURN_URL_INVALID`, `INVALID_ID_FORMAT`, `PAYMENT_FAILED`, `INSTRUMENT_PRESELECTION_FAILED`. `warnings[]` array zawiera `INSTRUMENT_CLEARED_FOR_RETRY` post-auto-clear instrument-specific failure (storefront retry hint: next attempt uses gateway default landing).
CartClearPaymentSelectionmutationClears all payment selection state on the cart in a single atomic operation. Use for accordion "back to method picker" flows w storefront UI. Idempotent — calling twice yields the same end state. Cart MUSI być `ACTIVE` (CONVERTED carts reject z `ALREADY_COMPLETED`). Rate limit: 30 requests/minute per IP+shop.

Return Mutations

OperationKindDescription
ReturnCreatemutationCreates 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.
ReturnCancelmutationCancels 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.

Loyalty Program Mutations

OperationKindDescription
RedeemLoyaltyRewardmutationRedeems 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.
GenerateReferralCodemutationReturns 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.

Review Mutations

OperationKindDescription
ReviewCreatemutationSubmits 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).
ReviewUpvotemutationRecords 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).
ReviewDownvotemutationRecords a downvote (unhelpful) on a review. Same UPSERT semantics as `reviewUpvote` — one vote row per `(reviewId, customerId)`, replacing any prior vote. Increments `unhelpful_count`.

Wishlist Mutations

OperationKindDescription
WishlistCreatemutationCreates 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.
WishlistAddItemmutationAdds 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.
WishlistRemoveItemmutationHard-deletes a wishlist item by `itemId` (the `WishlistItem` row id, NOT the product id).
WishlistDeletemutationHard-deletes the wishlist row. All wishlist items are removed via cascade. No soft-delete; cannot be undone.

Cart Attributes

OperationKindDescription
CartUpdateAttributesmutationReplaces (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.

Treści cyfrowe

OperationKindDescription
DigitalDownloadLinkCreatemutationTworzy jednorazowy odnośnik do pobrania kupionej treści cyfrowej. Odnośnik działa dokładnie raz i wygasa po kilku minutach — nie zapisuj go w pamięci podręcznej, nie umieszczaj w adresie strony i nie wysyłaj mailem. Identyfikatory bierz z `OrderLineItem.digitalDownloads`. Wyczerpany limit pobrań i wygaśnięcie dostępu wracają jako `userErrors`, a nie jako błąd zapytania — pokaż je klientowi.
FormSubmitmutationSubmits a store-defined contact form. Values are strings (CHECKBOX "true"/"false", NUMBER a decimal string, MULTI_SELECT via `values`). Validation errors come back in `userErrors` with stable codes (UNKNOWN_FIELD, REQUIRED_FIELD_MISSING, INVALID_OPTION, INVALID_VALUE, FORM_NOT_FOUND, FORM_INACTIVE) and the offending field key in `field`. Bot-protection guarded and rate-limited (5/min per shop+IP).