Skip to content

mutation

Terminal window
npx skills add https://github.com/crystallizeapi/ai --skill mutation

Crystallize 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:

  1. What are you trying to create or update? Products, documents, folders, customers, orders?
  2. Do you have the tenant identifier and access tokens? Mutations require authentication.
  3. Where in the flow are you? Content/catalog management → Core API. Cart/checkout/orders → Shop API.
  4. Do you need to update individual fields or create items from scratch? updateComponent for fields, create for new items.
  5. Should changes be published immediately? Creating an item doesn’t publish it — that’s a separate step.
  6. 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 format

API Selection

Use CaseAPIWhy
Creating/editing items, shapes, customersCore APIFull read/write, admin-level access
Cart management, checkoutShop API /cartEdge-distributed cart lifecycle
Order creation, payments, pipelinesShop API /orderFull order CRUD after checkout
Bulk shape + item creationCore API via mass operationsOrdered multi-step creation

API Endpoints & Authentication

APIEndpointAuth RequiredUse For
Corehttps://api.crystallize.com/@{tenant}YesItems, shapes, customers
Shop /carthttps://shop-api.crystallize.com/{tenant}/cartYes (JWT)Cart management, checkout
Shop /orderhttps://shop-api.crystallize.com/{tenant}/orderYes (JWT)Order CRUD, payments

Core API

POST https://api.crystallize.com/@{tenant-identifier}

Note the @ prefix before the tenant identifier.

Terminal window
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}/cart
Authorization: Bearer YOUR_JWT_TOKEN

No @ prefix for the Shop API endpoint.

Common Workflow Patterns

Create a product end-to-end

  1. Create the product with shape and parent folder
  2. Set variants with SKU, pricing, stock, and images
  3. Update components (description, specs, media)
  4. Publish the item

See Core API Reference for each mutation.

Update content on an existing item

  1. Query the item to confirm its ID and current state (use the query skill)
  2. Call item.updateComponent for each field you need to change
  3. 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)

  1. Hydrate a cart with product SKUs and quantities
  2. Add/remove items as the customer shops
  3. Set customer info and addresses
  4. Place the cart to lock it for payment
  5. 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:

  1. Pieces (dependencies first)
  2. Shapes
  3. Topic maps
  4. 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 mutations
const result = await api.pimApi(mutationString, variables);

Output Format

When generating mutations for the user, produce:

  1. GraphQL mutations with clear variable placeholders (e.g., "your-tenant-id", "item-id")
  2. Variable definitions when the mutation uses GraphQL variables
  3. 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


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 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:

ErrorCause
BasicErrorGeneral validation or input errors
UnauthorizedErrorMissing or insufficient access token permissions
ItemNotFoundErrorItem ID doesn’t exist or wrong tenant
OrderDoesNotBelongToTenantErrorOrder ID belongs to a different tenant

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
StateDescriptionMutable?
cartActive cart, items and prices can be changedYes
placedLocked for payment — no modifications allowedNo
orderedFulfilled — linked to an order via orderIdNo
abandonedExplicitly 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:

  1. Takes SKUs and external items as input
  2. Fetches product data from Catalogue API
  3. Calculates prices, taxes, and totals
  4. 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

FieldTypeDescription
idUUIDExisting cart ID (omit to create new cart)
items[CartSkuItemInput]SKU items to add to the cart
externalItems[CartItemInputType]External items (shipping, fees)
customerCustomerInputCustomer info (inline with hydration)
contextContextInputLanguage and pricing context
typeCartTypecart or wishlist
nameStringOptional cart name (mainly for wishlists)
meta[KeyValueInput]Arbitrary key-value metadata

CartSkuItemInput Fields

FieldTypeRequiredDescription
skuString!YesProduct variant SKU
quantityPositiveIntNoQuantity (default: 1)
typeCartItemTypeNostandard, subscription, shipping, fee, promotion, refund, service, digital, bonus, tax
groupStringNoFree-form group label (e.g., “Outdoor”, “Plants”)
taxRateRateNoOverride tax rate for this item
meta[KeyValueInput]NoItem-level metadata

Context Options

FieldDescription
languageLocale for product names (default: “en”)
selectedVariantIdentifierPrice variant to use as active price
compareAtVariantIdentifierPrice variant for discount comparison
decimalsDecimal precision (default: 0, recommended: 4)
pricesHaveTaxesIncludedInCrystallizeTax handling: true for B2C, false for B2B
taxRateOverride tax rate
marketsArray of markets for price lists
currencyDisplay currency (must match price variant config)
customerGroupCustomer group for pricing
fallbackVariantIdentifiersFallback price variants if selected is missing
voucherCodeApply 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 hydrate without an id. 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 createOrganization
1. Hydrate Cart → POST /cart — Create cart with items, customer, context
2. (Optional edits) → POST /cart — addSkuItem, setCustomer, setAddresses, etc.
3. Place Cart → POST /cart — Lock cart for payment
4. Create Order → POST /order — createFromCart (⚠ different endpoint!)
5. (Optional) Fulfill → POST /cart — Link cart to order ID

Customer Creation

Customers can be created before checkout via the Core API or inline during hydration:

  • Core API (persistent customer records): Use customer.createIndividual or customer.createOrganization to create a customer in the PIM. The customer’s identifier (typically email) can then be passed to hydrate.
  • 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

  1. Reuse cart IDs — Pass returned ID to subsequent requests
  2. Set customer and context in hydrate — Set them inline during hydration rather than separate calls
  3. Place before payment — Always place the cart before initiating payment to prevent modifications
  4. Use /order for order creationcreateFromCart is on the /order endpoint, not /cart
  5. Handle back navigation — If user goes back after placing, create a new cart via hydrate without an id
  6. Use item groups — Group items logically (“Outdoor”, “Plants”) for organized order views
  7. Handle managed state — Know when items become unmanaged after price overrides
  8. External items for non-catalog items — Shipping, fees, discounts via addExternalItem
  9. Auto-cleanup — Carts expire after 3 months of inactivity

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

FieldTypeDescription
typeOrderType enumOrder type (default: standard)
paymentStatusOrderPaymentStatusPayment status
payments[OrderPaymentInput]Payment records
pipelines[OrderPipelineInput]Pipeline stage assignments
stockLocationIdentifierStringStock location for inventory
relatedOrderIds[String]Related order IDs
additionalInformationStringFree-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

FieldTypeRequiredDescription
items[OrderItemInput]NoOrder line items
customerCustomerInputNoCustomer information
contextOrderContextInputNoPrice/language context
typeOrderTypeNoOrder type (default: standard)
paymentStatusOrderPaymentStatusNoPayment status
payments[OrderPaymentInput]NoPayment records
pipelines[OrderPipelineInput]NoPipeline stage assignments
stockLocationIdentifierStringNoStock location for inventory deduction
relatedOrderIds[String]NoRelated order IDs (returns, replacements)
additionalInformationStringNoFree-text additional info
meta[KeyValueInput]NoArbitrary key-value metadata
createdAtDateTimeNoOverride creation timestamp

OrderItemInput

FieldTypeRequiredDescription
nameString!YesItem display name
skuString!YesItem SKU
quantityPositiveInt!YesQuantity ordered
pricePriceInput!YesUnit price (gross + net)
productIdStringNoCrystallize product ID
imageUrlStringNoItem image URL
promotions[Float]NoDiscount amounts on item total
subscriptionContractIdStringNoSubscription contract ID
subscriptionOrderItemSubscriptionInputNoSubscription details
meta[KeyValueInput]NoItem-level metadata

PriceInput

FieldTypeRequiredDescription
grossFloat!YesPrice including tax
netFloat!YesPrice excluding tax
discounts[DiscountInput]NoApplied discounts

CustomerInput

FieldTypeRequiredDescription
isGuestBoolean!YesWhether customer is guest
identifierStringNoUnique customer ID
firstNameStringNoFirst name
lastNameStringNoLast name
middleNameStringNoMiddle name
emailStringNoEmail address
phoneStringNoPhone number
birthDateDateTimeNoDate of birth
companyNameStringNoCompany name
taxNumberStringNoTax/VAT number
typeCustomerTypeNoindividual or organization
externalReferenceStringNoExternal system reference
externalReferencesHashMapNoMultiple external refs
addresses[AddressInput]NoCustomer addresses
meta[KeyValueInput]NoCustomer metadata

AddressInput

FieldTypeRequiredDescription
typeAddressTypeNodelivery, billing, or other
firstNameStringNoFirst name
middleNameStringNoMiddle name
lastNameStringNoLast name
streetStringNoStreet address
street2StringNoAdditional street info
streetNumberStringNoStreet number
postalCodeStringNoPostal/ZIP code
cityStringNoCity
stateStringNoState/province
countryStringNoCountry code
phoneStringNoPhone number
emailStringNoEmail address
meta[KeyValueInput]NoAddress metadata

OrderPaymentInput

FieldTypeRequiredDescription
providerStringNoPayment provider name
transactionIdStringNoTransaction reference
amountFloatNoPayment amount
methodStringNoPayment method
createdAtDateTimeNoPayment timestamp
meta[KeyValueInput]NoPayment metadata

OrderPipelineInput

FieldTypeRequiredDescription
identifierString!YesPipeline identifier
stageStringNoStage 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

ValueDescription
standardRegular order
draftDraft order (not finalized)
creditNoteCredit note / refund
replacementReplacement order
backorderBackorder (out-of-stock fulfillment)
preOrderPre-order
quotePrice quote
recurringRecurring/subscription order
splitSplit order (partial shipment)
testTest order

OrderPaymentStatus

ValueDescription
paidFully paid
partiallyPaidPartially paid
partiallyRefundedPartially refunded
refundedFully refunded
unpaidNot yet paid

CustomerType

ValueDescription
individualIndividual person
organizationCompany/org

AddressType

ValueDescription
deliveryShipping address
billingBilling address
otherOther address

CartItemType (on Order items)

ValueDescription
standardRegular product item
subscriptionSubscription item
shippingShipping fee
feeAdditional fee
promotionPromotional item
refundRefund line
serviceService item
digitalDigital product
bonusBonus/gift item
taxTax line item

Order Response Type

The Order type returned by all mutations:

FieldTypeDescription
idUUID!Shop API order ID
coreIdStringCore API order ID
referenceStringOrder reference
typeOrderTypeOrder type enum
additionalInformationStringAdditional info text
stockLocationIdentifierStringStock location
relatedOrderIds[String]Related order IDs
createdAtDateTimeCreation timestamp
updatedAtDateTimeLast update timestamp
contextHashMapOrder context (pricing, language)
customerCustomerCustomer details with addresses
items[Item]Order line items
totalTotalPrice!Order totals (gross, net, tax, currency)
paymentStatusOrderPaymentStatusPayment status
payments[Payment]Payment records
pipelines[Pipeline]Pipeline stage assignments
appliedPromotions[PromotionSlim!]Applied promotions
metaHashMapAll metadata as key-value map
metaPropertyStringSingle 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

  1. Use the correct endpoint — Cart operations on /cart, order operations on /order
  2. Include order scope in JWT — Token must have order scope for /order endpoint
  3. Place cart before creating ordercreateFromCart requires the cart to be in “placed” state
  4. Always check for errors — Validate result?.errors in responses
  5. Use meta for custom data — Store fulfillment status, tracking numbers, external references
  6. Use pipelines for workflow — Track orders through fulfillment stages
  7. Use coreId — When you need to reference the order in Core API mutations


Crystallize AI