mutation
npx skills add https://github.com/crystallizeapi/ai --skill mutationCrystallize Mutation Skill
Create, update, and manage data in Crystallize using GraphQL mutations. This skill covers all write operations across the Core API (admin/content management) and Shop API (cart/checkout).
Consultation Approach
Before writing mutations, understand the context. Ask clarifying questions:
- What are you trying to create or update? Products, documents, folders, customers, orders?
- Do you have the tenant identifier and access tokens? Mutations require authentication.
- Where in the flow are you? Content/catalog management → Core API. Cart/checkout/orders → Shop API.
- Do you need to update individual fields or create items from scratch?
updateComponentfor fields,createfor new items. - Should changes be published immediately? Creating an item doesn’t publish it — that’s a separate step.
- Are you doing a one-off change or a bulk import? Single mutations vs mass operations.
Decision Tree
What do you need to do?│├─ Create/update catalogue items (products, documents, folders)│ ├─ Create new item → Core API: product/document/folder.create│ ├─ Update a field on an item → Core API: item.updateComponent│ ├─ Add/update product variants → Core API: product.setVariants│ ├─ Publish/unpublish → Core API: item.publish / item.unpublish│ └─ Delete an item → Core API: item.delete│├─ Manage customers│ ├─ Create individual → Core API: customer.createIndividual│ ├─ Create organization → Core API: customer.createOrganization│ └─ Update customer → Core API: customer.update│├─ Manage orders│ ├─ Create order from cart → Shop API /order: createFromCart│ ├─ Create order directly (POS, import) → Shop API /order: create│ ├─ Add/update payments → Shop API /order: addPayments / setPayments│ ├─ Track order through pipeline → Shop API /order: addToStage│ └─ Update order metadata → Shop API /order: setMeta (or Core API: order.update)│├─ Cart & checkout (storefront)│ ├─ Create/hydrate a cart → Shop API: hydrate│ ├─ Modify cart items → Shop API: addItems / removeItems / setCartItem│ ├─ Set customer & addresses → Shop API: setCustomer / setAddresses│ └─ Convert cart to order → Shop API: cartAsOrderIntent│└─ Bulk operations └─ Use mass operation JSON via the content-model skill's output formatAPI Selection
| Use Case | API | Why |
|---|---|---|
| Creating/editing items, shapes, customers | Core API | Full read/write, admin-level access |
| Cart management, checkout | Shop API /cart | Edge-distributed cart lifecycle |
| Order creation, payments, pipelines | Shop API /order | Full order CRUD after checkout |
| Bulk shape + item creation | Core API via mass operations | Ordered multi-step creation |
API Endpoints & Authentication
| API | Endpoint | Auth Required | Use For |
|---|---|---|---|
| Core | https://api.crystallize.com/@{tenant} | Yes | Items, shapes, customers |
| Shop /cart | https://shop-api.crystallize.com/{tenant}/cart | Yes (JWT) | Cart management, checkout |
| Shop /order | https://shop-api.crystallize.com/{tenant}/order | Yes (JWT) | Order CRUD, payments |
Core API
POST https://api.crystallize.com/@{tenant-identifier}Note the @ prefix before the tenant identifier.
curl -X POST 'https://api.crystallize.com/@your-tenant' \ -H 'Content-Type: application/json' \ -H 'X-Crystallize-Access-Token-Id: YOUR_TOKEN_ID' \ -H 'X-Crystallize-Access-Token-Secret: YOUR_TOKEN_SECRET' \ -d '{"query": "mutation { ... }"}'Generate access tokens in the Crystallize App under Settings > Access Tokens. See the permissions skill for scoping tokens.
Shop API
POST https://shop-api.crystallize.com/{tenant-identifier}/cartAuthorization: Bearer YOUR_JWT_TOKENNo @ prefix for the Shop API endpoint.
Common Workflow Patterns
Create a product end-to-end
- Create the product with shape and parent folder
- Set variants with SKU, pricing, stock, and images
- Update components (description, specs, media)
- Publish the item
See Core API Reference for each mutation.
Update content on an existing item
- Query the item to confirm its ID and current state (use the query skill)
- Call
item.updateComponentfor each field you need to change - Publish if the item should go live immediately
Each updateComponent call targets a single component by componentId. You can update multiple components by sending multiple mutations.
Checkout flow (storefront)
- Hydrate a cart with product SKUs and quantities
- Add/remove items as the customer shops
- Set customer info and addresses
- Place the cart to lock it for payment
- Create the order from the placed cart
See Shop API Cart Mutations for steps 1-4, and Shop API Order Mutations for step 5.
Bulk import / mass operations
For creating many items at once, use the mass operations JSON format produced by the content-model skill. Mass operations follow a 4-phase ordering:
- Pieces (dependencies first)
- Shapes
- Topic maps
- Items
Error Handling
The Core API uses union return types. Always include error fragments in your mutations:
mutation { product { create(input: { ... }) { ... on Product { id name } ... on BasicError { errorName message } } }}Common error types: BasicError, UnauthorizedError, ItemNotFoundError, OrderDoesNotBelongToTenantError.
Using the JS API Client
For JavaScript/TypeScript projects, use @crystallize/js-api-client instead of raw HTTP calls. It provides typed helpers for all mutations. See the js-api-client skill for setup and usage.
import { createClient } from "@crystallize/js-api-client";const api = createClient({ tenantIdentifier: "your-tenant", accessTokenId: "...", accessTokenSecret: "...",});
// Use pimApi for Core API mutationsconst result = await api.pimApi(mutationString, variables);Output Format
When generating mutations for the user, produce:
- GraphQL mutations with clear variable placeholders (e.g.,
"your-tenant-id","item-id") - Variable definitions when the mutation uses GraphQL variables
- Expected response shape so the user knows what to look for
If the user is working in a JS/TS project, prefer generating code using @crystallize/js-api-client helpers.
References
- Core API Mutations - Item CRUD, variants, components, customers, publish/unpublish, delete, media uploads
- Shop API Cart Mutations - Cart hydration, item management, checkout flow, cart lifecycle
- Shop API Order Mutations - Order creation (from cart or direct), payments, pipelines, metadata
Reference Details
Core API Mutations Reference
The Core API provides full read/write access to items, shapes, customers, orders, and configuration.
See SKILL.md for endpoint URLs and authentication headers.
Table of Contents
- Item Mutations - Create, update, publish, unpublish, delete
- Component Updates - Update individual fields on items
- Product Variants - SKUs, pricing, stock, images
- Customer Mutations - Individual, organization, update
- Order Mutations - Update order metadata
- Media & Images - Upload images for items and variants
- Flow Mutations - Manage item workflows
- Error Handling
Item Mutations
Create Product
mutation CreateProduct { product { create( input: { tenantId: "tenant-id" shapeIdentifier: "sneaker" name: "Air Max 2024" tree: { parentId: "folder-id" } } ) { ... on Product { id name path } ... on BasicError { errorName message } } }}Create Document
mutation CreateDocument { document { create( input: { tenantId: "tenant-id" shapeIdentifier: "blog-post" name: "Welcome to Our Store" tree: { parentId: "blog-folder-id" } } ) { ... on Document { id name } } }}Create Folder
mutation CreateFolder { folder { create( input: { tenantId: "tenant-id" shapeIdentifier: "category" name: "Summer Collection" tree: { parentId: "shop-folder-id" } } ) { ... on Folder { id name } } }}Publish Item
Publishing makes the item visible on the storefront. Creating an item does NOT publish it.
mutation PublishItem { item { publish(id: "item-id", language: "en", includeDescendants: false) { ... on Item { id publishedAt } } }}Set includeDescendants: true to publish all children (useful for folders).
Unpublish Item
mutation UnpublishItem { item { unpublish(id: "item-id", language: "en", includeDescendants: false) { ... on Item { id } } }}Delete Item
mutation DeleteItem { item { delete(id: "item-id") { ... on Item { id } ... on BasicError { errorName message } } }}Deleting a folder with children will fail unless children are moved or deleted first.
Move Item in Tree
mutation MoveItem { item { moveToTree(itemId: "item-id", input: { parentId: "new-parent-folder-id", position: 0 }) { ... on Item { id tree { parentId path } } } }}Component Updates
Use updateComponent to change individual fields on an item. Each call targets one component by its componentId (the identifier defined in the shape).
Rich Text
mutation UpdateRichText { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "description" richText: { html: "<p>New product description with <strong>rich text</strong></p>" } } ) { ... on Item { id updatedAt } ... on BasicError { message } } }}Single Line
mutation UpdateSingleLine { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "tagline", singleLine: { text: "Premium quality materials" } } ) { ... on Item { id } } }}Numeric
mutation UpdateNumeric { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "weight", numeric: { number: 1.5, unit: "kg" } } ) { ... on Item { id } } }}Boolean (Switch)
mutation UpdateSwitch { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "featured", boolean: { value: true } } ) { ... on Item { id } } }}Images Component
mutation UpdateImages { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "gallery" images: [{ key: "image-key-from-upload", altText: "Product front view" }] } ) { ... on Item { id } } }}Selection
mutation UpdateSelection { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "color", selection: { keys: ["red"] } } ) { ... on Item { id } } }}Item Relations
mutation UpdateItemRelations { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "related-products" itemRelations: { itemIds: ["related-item-id-1", "related-item-id-2"] } } ) { ... on Item { id } } }}Content Chunk (repeatable)
mutation UpdateChunk { item { updateComponent( itemId: "item-id" language: "en" component: { componentId: "specifications" contentChunk: { chunks: [ [ { componentId: "label", singleLine: { text: "Weight" } } { componentId: "value", singleLine: { text: "1.5 kg" } } ] [ { componentId: "label", singleLine: { text: "Dimensions" } } { componentId: "value", singleLine: { text: "30x20x10 cm" } } ] ] } } ) { ... on Item { id } } }}Product Variants
Variants represent purchasable SKUs on a product. Each variant has built-in fields: sku, name, price, stock, images, attributes.
Set Variants on a Product
This replaces all variants on the product. Include all variants you want to keep.
mutation SetVariants { product { setVariants( productId: "product-id" language: "en" variants: [ { sku: "sneaker-red-42" name: "Red - Size 42" isDefault: true price: 129.99 stock: 50 attributes: [{ attribute: "color", value: "Red" }, { attribute: "size", value: "42" }] images: [{ key: "uploaded-image-key", altText: "Red sneaker size 42" }] } { sku: "sneaker-blue-42" name: "Blue - Size 42" isDefault: false price: 129.99 stock: 30 attributes: [{ attribute: "color", value: "Blue" }, { attribute: "size", value: "42" }] } ] ) { ... on Product { id variants { sku name price stock } } ... on BasicError { errorName message } } }}Update Stock
Stock is managed per variant through setVariants. To update stock on a single variant without affecting others, query all current variants first, modify the stock value, and call setVariants with the full list.
Variant Attributes
Attributes define the variant matrix (e.g., color + size). They appear as filterable properties in the storefront. Use consistent attribute names across products for proper filtering.
Customer Mutations
Create Individual Customer
mutation CreateIndividual { customer { createIndividual( input: { tenantId: "tenant-id" firstName: "Jane" lastName: "Smith" email: "jane@example.com" phone: "+1234567890" addresses: [ { type: "delivery", street: "123 Main St", city: "New York", postalCode: "10001", country: "US" } ] } ) { ... on Customer { id identifier } } }}Create Organization
mutation CreateOrganization { customer { createOrganization( input: { tenantId: "tenant-id", name: "Acme Corp", email: "contact@acme.com", taxId: "XX123456789" } ) { ... on Customer { id identifier } } }}Update Customer
mutation UpdateCustomer { customer { update(id: "customer-id", input: { firstName: "Jane", lastName: "Doe" }) { ... on Customer { id } } }}Delete Customer
mutation DeleteCustomer { customer { delete(id: "customer-id") { ... on Customer { id } ... on BasicError { errorName message } } }}Order Mutations
Update Order
mutation UpdateOrder { order { update(id: "order-id", input: { meta: [{ key: "tracking_number", value: "1Z999AA10123456784" }] }) { ... on Order { id updatedAt } } }}Media & Images
Images in Crystallize are uploaded to the tenant’s media library, then referenced by key in components or variants.
Upload Image via URL
mutation UploadImageFromURL { fileUpload { uploadFromUrl( tenantId: "tenant-id" url: "https://example.com/product-photo.jpg" fileName: "product-photo.jpg" ) { ... on FileContent { key url } ... on BasicError { errorName message } } }}The returned key is used when assigning images to components or variants.
Flow Mutations
Flows model item workflows (e.g., Draft > Review > Published). Items can be moved between stages.
Set Item Flow Stage
mutation SetFlowStage { item { setFlowStage(itemId: "item-id", stageId: "stage-id") { ... on Item { id } ... on BasicError { errorName message } } }}Error Handling
The Core API uses union return types. Always handle potential errors:
mutation { item { updateComponent(...) { ... on Item { id } ... on ItemNotFoundError { errorName message } ... on UnauthorizedError { errorName message } ... on BasicError { errorName message } } }}Common error types:
| Error | Cause |
|---|---|
BasicError | General validation or input errors |
UnauthorizedError | Missing or insufficient access token permissions |
ItemNotFoundError | Item ID doesn’t exist or wrong tenant |
OrderDoesNotBelongToTenantError | Order ID belongs to a different tenant |
Related Links
Shop API Cart Mutations Reference
The Shop API /cart scope provides edge-distributed mutations for cart management and checkout flows.
See SKILL.md for endpoint URLs and authentication headers. This file covers the /cart endpoint only. For order creation and management, use the /order endpoint.
Cart State Machine
Carts follow a state machine that controls what operations are allowed:
cart → placed → ordered ↘ abandoned| State | Description | Mutable? |
|---|---|---|
cart | Active cart, items and prices can be changed | Yes |
placed | Locked for payment — no modifications allowed | No |
ordered | Fulfilled — linked to an order via orderId | No |
abandoned | Explicitly abandoned (e.g., user left checkout) | No |
Query the cart state with:
query { cart(id: "cart-id") { id state # cart | placed | ordered | abandoned isStale # true if prices may have changed since last hydration isExpired # true if cart has expired orderId # set after fulfill, links to the order }}Cart Hydration
The primary mutation for creating and updating carts. Hydration:
- Takes SKUs and external items as input
- Fetches product data from Catalogue API
- Calculates prices, taxes, and totals
- Returns a fully constructed cart
Basic Hydration
mutation { hydrate( input: { items: [{ sku: "robot-pink-standard", quantity: 1 }, { sku: "robot-red-standard", quantity: 3 }] } ) { id state isStale isExpired items { sku name quantity price { gross net } } total { gross net } }}Hydration with Customer and Context
The hydrate input accepts customer info inline — this is the recommended way to associate a customer during checkout. It also accepts item type and group for categorizing line items.
mutation { hydrate( input: { customer: { identifier: "john@example.com", isGuest: false, firstName: "John", lastName: "Doe" } context: { language: "en" price: { pricesHaveTaxesIncludedInCrystallize: true decimals: 4 currency: "EUR" selectedVariantIdentifier: "sales" compareAtVariantIdentifier: "default" fallbackVariantIdentifiers: ["default"] } } items: [ { sku: "palissade-lounge-sofa-iron-red", quantity: 1, type: standard, group: "Outdoor" } { sku: "palissade-bar-stool-sky-grey", quantity: 1, type: standard, group: "Outdoor" } { sku: "monstera-deliciosa-medium", quantity: 2, type: standard, group: "Plants" } ] } ) { id state isStale customer { identifier firstName lastName } appliedPromotions { identifier name mechanism { type value } } items { name variant { sku price { gross net taxAmount taxPercent } compareAtPrice { gross net } } price { net gross taxAmount discounts { percent amount } } } total { net gross discounts { percent amount } } }}CartInput Fields
| Field | Type | Description |
|---|---|---|
id | UUID | Existing cart ID (omit to create new cart) |
items | [CartSkuItemInput] | SKU items to add to the cart |
externalItems | [CartItemInputType] | External items (shipping, fees) |
customer | CustomerInput | Customer info (inline with hydration) |
context | ContextInput | Language and pricing context |
type | CartType | cart or wishlist |
name | String | Optional cart name (mainly for wishlists) |
meta | [KeyValueInput] | Arbitrary key-value metadata |
CartSkuItemInput Fields
| Field | Type | Required | Description |
|---|---|---|---|
sku | String! | Yes | Product variant SKU |
quantity | PositiveInt | No | Quantity (default: 1) |
type | CartItemType | No | standard, subscription, shipping, fee, promotion, refund, service, digital, bonus, tax |
group | String | No | Free-form group label (e.g., “Outdoor”, “Plants”) |
taxRate | Rate | No | Override tax rate for this item |
meta | [KeyValueInput] | No | Item-level metadata |
Context Options
| Field | Description |
|---|---|
language | Locale for product names (default: “en”) |
selectedVariantIdentifier | Price variant to use as active price |
compareAtVariantIdentifier | Price variant for discount comparison |
decimals | Decimal precision (default: 0, recommended: 4) |
pricesHaveTaxesIncludedInCrystallize | Tax handling: true for B2C, false for B2B |
taxRate | Override tax rate |
markets | Array of markets for price lists |
currency | Display currency (must match price variant config) |
customerGroup | Customer group for pricing |
fallbackVariantIdentifiers | Fallback price variants if selected is missing |
voucherCode | Apply voucher code |
Hydration with External Items
For items not in Crystallize (shipping, fees):
mutation { hydrate( input: { items: [{ sku: "product-sku", quantity: 1 }] externalItems: [ { sku: "shipping-fedex" quantity: 1 name: "FedEx Ground Shipping" images: [] variant: { price: { gross: 12.99, net: 10.39 } product: { id: "shipping-product", path: "/shipping" } } } ] } ) { id items { sku name origin } }}Cart Item Management
Add SKU Item
Add a single SKU item to an existing cart. If the item already exists and is managed, only the quantity is updated.
mutation { addSkuItem(id: "cart-id", input: { sku: "new-product-sku", quantity: 2, type: standard, group: "Furniture" }) { id items { name variant { sku } quantity } }}Add External Item
Add a fully custom item not in Crystallize (e.g., shipping, service fees).
mutation { addExternalItem( id: "cart-id" input: { sku: "shipping-standard" name: "Standard Shipping" quantity: 1 price: { gross: 9.99, net: 7.99 } type: shipping } ) { id items { name variant { sku } price { gross net } } }}Remove Items
mutation { removeCartItem(id: "cart-id", sku: "product-to-remove") { id items { variant { sku } quantity } }}Update Item Quantity
mutation { setCartItem(id: "cart-id", sku: "product-sku", quantity: 5) { id items { sku quantity } }}Change Item Pricing
Override managed pricing (makes item unmanaged):
mutation { changeCartItemPricing(id: "cart-id", sku: "product-sku", price: { gross: 49.99, net: 39.99 }) { id items { sku managed price { gross net } } }}Customer Information
Set Customer on Cart
mutation { setCustomer( id: "cart-id" customer: { firstName: "John", lastName: "Doe", email: "john@example.com", phone: "+1234567890" } ) { id customer { firstName lastName email } }}Set Addresses
mutation { setAddresses( id: "cart-id" billing: { street: "123 Main St", city: "New York", postalCode: "10001", country: "US" } delivery: { street: "456 Oak Ave", city: "Brooklyn", postalCode: "11201", country: "US" } ) { id }}Cart to Order Intent
Get cart formatted for order creation:
mutation { cartAsOrderIntent(id: "cart-id") { customer { firstName lastName email } cart { sku name quantity price { gross net } } total { gross net } }}Cart Lifecycle
Place Cart
Placing a cart locks it and makes it immutable. This should happen when the user enters checkout (e.g., before collecting payment). After placing, items, quantities, and prices cannot be changed.
mutation { place(id: "cart-id") { id state # now "placed" items { name quantity } total { gross net currency } }}Back navigation: If the user navigates back after the cart is placed, create a new cart by calling
hydratewithout anid. The placed cart cannot be modified.
Fulfill Cart
After creating an order (via createFromCart on the /order endpoint), mark the cart as fulfilled by linking it to the order ID:
mutation { fulfill(id: "cart-id", orderId: "order-uuid") { id state # now "ordered" orderId # the linked order ID }}Abandon Cart
Explicitly mark a cart as abandoned (e.g., user left checkout, session timeout):
mutation { abandon(id: "cart-id") { id state # now "abandoned" }}Mark as Wishlist
Convert a cart to a non-expiring wishlist:
mutation { markAsWishlist(id: "cart-id", name: "My Favorites") { id isWishlist name }}Set Cart Expiration
mutation { setCartExpiration(id: "cart-id", expiresAt: "2024-12-31T23:59:59Z") { id expiresAt }}Remove Cart
mutation { remove(id: "cart-id") { id }}Complete Checkout Flow
The full checkout flow spans the Core API, /cart endpoint, and /order endpoint:
0. (Optional) Create Customer → Core API — createIndividual or createOrganization1. Hydrate Cart → POST /cart — Create cart with items, customer, context2. (Optional edits) → POST /cart — addSkuItem, setCustomer, setAddresses, etc.3. Place Cart → POST /cart — Lock cart for payment4. Create Order → POST /order — createFromCart (⚠ different endpoint!)5. (Optional) Fulfill → POST /cart — Link cart to order IDCustomer Creation
Customers can be created before checkout via the Core API or inline during hydration:
- Core API (persistent customer records): Use
customer.createIndividualorcustomer.createOrganizationto create a customer in the PIM. The customer’sidentifier(typically email) can then be passed tohydrate. - Inline during hydrate (recommended for checkout): Pass
customer: { identifier, firstName, lastName, isGuest }directly in the hydrate input. This associates the customer with the cart without requiring a separate API call.
For guest checkout, set isGuest: true in the hydrate customer input.
Minimal checkout example:
# Step 1: Create and hydrate cart (POST /cart)mutation { hydrate( input: { customer: { identifier: "john@example.com", isGuest: false } context: { language: "en" price: { pricesHaveTaxesIncludedInCrystallize: true decimals: 4 currency: "EUR" selectedVariantIdentifier: "default" } } items: [ { sku: "product-sku-1", quantity: 2, type: standard } { sku: "product-sku-2", quantity: 1, type: standard } ] } ) { id state total { gross net currency } }}
# Step 2: Place cart — locks it for payment (POST /cart)mutation { place(id: "cart-uuid-from-step-1") { id state # "placed" }}
# Step 3: Create order from placed cart (POST /order — DIFFERENT ENDPOINT!)mutation { createFromCart(id: "cart-uuid-from-step-1", input: { type: standard, paymentStatus: paid }) { id coreId type total { gross net currency } }}Best Practices
- Reuse cart IDs — Pass returned ID to subsequent requests
- Set customer and context in hydrate — Set them inline during hydration rather than separate calls
- Place before payment — Always place the cart before initiating payment to prevent modifications
- Use
/orderfor order creation —createFromCartis on the/orderendpoint, not/cart - Handle back navigation — If user goes back after placing, create a new cart via
hydratewithout anid - Use item groups — Group items logically (“Outdoor”, “Plants”) for organized order views
- Handle managed state — Know when items become unmanaged after price overrides
- External items for non-catalog items — Shipping, fees, discounts via
addExternalItem - Auto-cleanup — Carts expire after 3 months of inactivity
Related Links
- Shop API Order Mutations - Order creation, payments, pipelines (
/orderendpoint) - Core API Mutations - Customer creation (
createIndividual,createOrganization), order updates - Crystallize Shop API Documentation
- Checkout Flow Tutorial
Shop API Order Mutations Reference
The Shop API /order scope provides mutations for creating and managing orders. This is a separate endpoint from the /cart scope.
See SKILL.md for endpoint URLs and authentication headers. This file covers the /order endpoint. Order mutations use /order, NOT /cart — the createFromCart mutation and all order management mutations must be sent to this endpoint.
Create Order from Cart
Convert a placed cart into an order. The cart must be in “placed” state first (via place mutation on the /cart endpoint).
mutation CreateOrderFromCart($id: UUID!, $input: OrderFromCartInput) { createFromCart(id: $id, input: $input) { id coreId type paymentStatus total { gross net taxAmount currency } items { name sku quantity price { gross net } } customer { firstName lastName email } createdAt }}Variables:
{ "id": "cart-uuid-here", "input": { "type": "standard", "paymentStatus": "paid" }}OrderFromCartInput
| Field | Type | Description |
|---|---|---|
type | OrderType enum | Order type (default: standard) |
paymentStatus | OrderPaymentStatus | Payment status |
payments | [OrderPaymentInput] | Payment records |
pipelines | [OrderPipelineInput] | Pipeline stage assignments |
stockLocationIdentifier | String | Stock location for inventory |
relatedOrderIds | [String] | Related order IDs |
additionalInformation | String | Free-text additional info |
Create Order Directly
Create an order without a cart (e.g., for POS, imports, or manual order creation).
mutation CreateOrder($input: OrderInput!) { create(input: $input) { id coreId type paymentStatus total { gross net taxAmount currency } items { name sku quantity price { gross net } } createdAt }}Variables:
{ "input": { "customer": { "isGuest": false, "firstName": "John", "lastName": "Doe", "email": "john@example.com", "identifier": "john@example.com", "addresses": [ { "type": "billing", "street": "123 Main St", "city": "New York", "postalCode": "10001", "country": "US" }, { "type": "delivery", "street": "456 Oak Ave", "city": "Brooklyn", "postalCode": "11201", "country": "US" } ] }, "items": [ { "name": "Robot Action Figure", "sku": "robot-pink-standard", "quantity": 2, "productId": "product-id", "imageUrl": "https://example.com/robot.jpg", "price": { "gross": 49.99, "net": 39.99 } } ], "type": "standard", "paymentStatus": "paid", "payments": [ { "provider": "stripe", "transactionId": "pi_abc123", "amount": 99.98, "method": "card", "createdAt": "2025-01-15T10:30:00Z" } ], "meta": [ { "key": "source", "value": "web" }, { "key": "campaign", "value": "summer-sale" } ] }}OrderInput
| Field | Type | Required | Description |
|---|---|---|---|
items | [OrderItemInput] | No | Order line items |
customer | CustomerInput | No | Customer information |
context | OrderContextInput | No | Price/language context |
type | OrderType | No | Order type (default: standard) |
paymentStatus | OrderPaymentStatus | No | Payment status |
payments | [OrderPaymentInput] | No | Payment records |
pipelines | [OrderPipelineInput] | No | Pipeline stage assignments |
stockLocationIdentifier | String | No | Stock location for inventory deduction |
relatedOrderIds | [String] | No | Related order IDs (returns, replacements) |
additionalInformation | String | No | Free-text additional info |
meta | [KeyValueInput] | No | Arbitrary key-value metadata |
createdAt | DateTime | No | Override creation timestamp |
OrderItemInput
| Field | Type | Required | Description |
|---|---|---|---|
name | String! | Yes | Item display name |
sku | String! | Yes | Item SKU |
quantity | PositiveInt! | Yes | Quantity ordered |
price | PriceInput! | Yes | Unit price (gross + net) |
productId | String | No | Crystallize product ID |
imageUrl | String | No | Item image URL |
promotions | [Float] | No | Discount amounts on item total |
subscriptionContractId | String | No | Subscription contract ID |
subscription | OrderItemSubscriptionInput | No | Subscription details |
meta | [KeyValueInput] | No | Item-level metadata |
PriceInput
| Field | Type | Required | Description |
|---|---|---|---|
gross | Float! | Yes | Price including tax |
net | Float! | Yes | Price excluding tax |
discounts | [DiscountInput] | No | Applied discounts |
CustomerInput
| Field | Type | Required | Description |
|---|---|---|---|
isGuest | Boolean! | Yes | Whether customer is guest |
identifier | String | No | Unique customer ID |
firstName | String | No | First name |
lastName | String | No | Last name |
middleName | String | No | Middle name |
email | String | No | Email address |
phone | String | No | Phone number |
birthDate | DateTime | No | Date of birth |
companyName | String | No | Company name |
taxNumber | String | No | Tax/VAT number |
type | CustomerType | No | individual or organization |
externalReference | String | No | External system reference |
externalReferences | HashMap | No | Multiple external refs |
addresses | [AddressInput] | No | Customer addresses |
meta | [KeyValueInput] | No | Customer metadata |
AddressInput
| Field | Type | Required | Description |
|---|---|---|---|
type | AddressType | No | delivery, billing, or other |
firstName | String | No | First name |
middleName | String | No | Middle name |
lastName | String | No | Last name |
street | String | No | Street address |
street2 | String | No | Additional street info |
streetNumber | String | No | Street number |
postalCode | String | No | Postal/ZIP code |
city | String | No | City |
state | String | No | State/province |
country | String | No | Country code |
phone | String | No | Phone number |
email | String | No | Email address |
meta | [KeyValueInput] | No | Address metadata |
OrderPaymentInput
| Field | Type | Required | Description |
|---|---|---|---|
provider | String | No | Payment provider name |
transactionId | String | No | Transaction reference |
amount | Float | No | Payment amount |
method | String | No | Payment method |
createdAt | DateTime | No | Payment timestamp |
meta | [KeyValueInput] | No | Payment metadata |
OrderPipelineInput
| Field | Type | Required | Description |
|---|---|---|---|
identifier | String! | Yes | Pipeline identifier |
stage | String | No | Stage within pipeline |
Add Payments to Order
Add payment records to an existing order.
mutation AddPayments($id: UUID!, $payments: [OrderPaymentInput!]!) { addPayments(id: $id, payments: $payments) { id paymentStatus payments { provider transactionId amount method createdAt } }}Variables:
{ "id": "order-uuid", "payments": [ { "provider": "stripe", "transactionId": "pi_xyz789", "amount": 99.98, "method": "card", "createdAt": "2025-01-15T10:30:00Z" } ]}Replace All Payments
Replace all payment records on an order.
mutation SetPayments($id: UUID!, $payments: [OrderPaymentInput!]!) { setPayments(id: $id, payments: $payments) { id paymentStatus payments { provider transactionId amount } }}Set Order Metadata
Set or merge metadata on an order.
mutation SetOrderMeta($id: UUID, $meta: [KeyValueInput], $merge: Boolean) { setMeta(id: $id, meta: $meta, merge: $merge) { id meta }}Variables:
{ "id": "order-uuid", "meta": [ { "key": "fulfillment_status", "value": "shipped" }, { "key": "tracking_number", "value": "1Z999AA10123456784" } ], "merge": true}When merge is true, new keys are added and existing keys are updated. When false (default), all existing metadata is replaced.
Set Order Customer
Update the customer information on an existing order.
mutation SetOrderCustomer($id: UUID!, $customer: CustomerInput!) { setCustomer(id: $id, customer: $customer) { id customer { firstName lastName email identifier } }}Create Order from Subscription Contract
Create a new order from an existing subscription contract, using its current phase and period.
mutation CreateFromSubscription($subscriptionContractId: UUID!) { createFromSubscriptionContract(subscriptionContractId: $subscriptionContractId) { id type items { name sku quantity subscription { name start end } } }}Pipeline Management
Add Order to Pipeline Stage
Add an order to a pipeline stage while keeping it in any existing stages.
mutation AddToStage($id: UUID!, $pipeline: String!, $stage: String!) { addToStage(id: $id, pipeline: $pipeline, stage: $stage) { id pipelines { identifier stage } }}Variables:
{ "id": "order-uuid", "pipeline": "fulfillment", "stage": "shipped"}Remove Order from Pipeline
mutation RemoveFromPipeline($id: UUID!, $pipeline: String!) { removeFromPipeline(id: $id, pipeline: $pipeline) { id pipelines { identifier stage } }}Enums
OrderType
| Value | Description |
|---|---|
standard | Regular order |
draft | Draft order (not finalized) |
creditNote | Credit note / refund |
replacement | Replacement order |
backorder | Backorder (out-of-stock fulfillment) |
preOrder | Pre-order |
quote | Price quote |
recurring | Recurring/subscription order |
split | Split order (partial shipment) |
test | Test order |
OrderPaymentStatus
| Value | Description |
|---|---|
paid | Fully paid |
partiallyPaid | Partially paid |
partiallyRefunded | Partially refunded |
refunded | Fully refunded |
unpaid | Not yet paid |
CustomerType
| Value | Description |
|---|---|
individual | Individual person |
organization | Company/org |
AddressType
| Value | Description |
|---|---|
delivery | Shipping address |
billing | Billing address |
other | Other address |
CartItemType (on Order items)
| Value | Description |
|---|---|
standard | Regular product item |
subscription | Subscription item |
shipping | Shipping fee |
fee | Additional fee |
promotion | Promotional item |
refund | Refund line |
service | Service item |
digital | Digital product |
bonus | Bonus/gift item |
tax | Tax line item |
Order Response Type
The Order type returned by all mutations:
| Field | Type | Description |
|---|---|---|
id | UUID! | Shop API order ID |
coreId | String | Core API order ID |
reference | String | Order reference |
type | OrderType | Order type enum |
additionalInformation | String | Additional info text |
stockLocationIdentifier | String | Stock location |
relatedOrderIds | [String] | Related order IDs |
createdAt | DateTime | Creation timestamp |
updatedAt | DateTime | Last update timestamp |
context | HashMap | Order context (pricing, language) |
customer | Customer | Customer details with addresses |
items | [Item] | Order line items |
total | TotalPrice! | Order totals (gross, net, tax, currency) |
paymentStatus | OrderPaymentStatus | Payment status |
payments | [Payment] | Payment records |
pipelines | [Pipeline] | Pipeline stage assignments |
appliedPromotions | [PromotionSlim!] | Applied promotions |
meta | HashMap | All metadata as key-value map |
metaProperty | String | Single metadata property by key |
Complete Checkout Flow (Cart → Order)
The full checkout flow spans both /cart and /order endpoints:
1. Hydrate Cart → POST /cart (hydrate mutation)2. Set Customer → POST /cart (setCustomer mutation)3. Place Cart → POST /cart (place mutation)4. Create Order → POST /order (createFromCart mutation)# Step 1: Hydrate cart (on /cart endpoint)mutation { hydrate(input: { items: [{ sku: "product-sku", quantity: 1 }] }) { id }}
# Step 2: Set customer info (on /cart endpoint)mutation { setCustomer(id: "cart-id", customer: { firstName: "John", lastName: "Doe", email: "john@example.com" }) { id }}
# Step 3: Place the cart (on /cart endpoint)mutation { place(id: "cart-id") { id }}
# Step 4: Create order from cart (on /order endpoint — DIFFERENT ENDPOINT!)mutation { createFromCart(id: "cart-id", input: { type: standard, paymentStatus: paid }) { id coreId total { gross net currency } }}Best Practices
- Use the correct endpoint — Cart operations on
/cart, order operations on/order - Include
orderscope in JWT — Token must haveorderscope for/orderendpoint - Place cart before creating order —
createFromCartrequires the cart to be in “placed” state - Always check for errors — Validate
result?.errorsin responses - Use
metafor custom data — Store fulfillment status, tracking numbers, external references - Use pipelines for workflow — Track orders through fulfillment stages
- Use
coreId— When you need to reference the order in Core API mutations
Related
- Shop API Cart Mutations - Cart hydration and management (
/cartendpoint) - Shop API Order Queries - Querying orders (
/orderendpoint)
Crystallize AI