Building a Modern Checkout Flow with Crystallize APIs
A typical ecommerce experience needs a reliable checkout flow that moves a customer from browsing products to a complete order. With Crystallize, you can build such flows using a combination of the Discovery API and the Shop API. In this post, we walk through the core steps involved in a modern checkout flow: fetching products, hydrating a cart, placing a cart, and creating an order. The examples are language-agnostic GraphQL operations you can adapt to your frontend or backend stack.

Fetching a Product
The first step in any checkout flow is to retrieve product information. With Crystallize, product data is fetched from the Discovery API, which is optimized for read performance and flexible querying. You can access it without authentication unless you have tightened security for your storefront.
API endpoint:
https://api.crystallize.com/your-tenant-identifier/discoveryUse a GraphQL query like this to fetch the minimal product data needed for checkout:
query GetProduct($language: String!, $path: String!) {
browse {
product(language: $language, path: $path) {
hits {
name
path
variants {
name
sku
retailPrice: defaultPrice
salesPrice
}
}
}
}
}You specify the product by path and language. The response includes each variant’s SKU and price data, which you will use when adding items to the cart.
If you need additional product fields such as images, stock, or custom attributes, you can expand the query accordingly.
Adding to Cart
Once you have product data, the next step is to add items to a cart. Crystallize’s Shop API handles cart management, pricing, totals, taxes, and promotions for you. It requires authentication.
API endpoint:
https://shop-api.crystallize.com/your-tenant-identifier/cartUse a mutation like this to create or update a cart with items and context:
mutation HydrateCart($input: HydrateCartInput!) {
hydrate(input: $input) {
id
state
isStale
isExpired
appliedPromotions {
identifier
name
mechanism {
type
value
}
}
items {
name
variant {
sku
yourPrice: price {
gross
net
taxAmount
taxPercent
}
compareAtPrice {
gross
net
}
product {
name
}
}
price {
net
gross
taxAmount
discounts {
percent
amount
}
}
}
total {
net
gross
discounts {
percent
amount
}
}
}
}
The hydrate operation takes:
- customer details, for pricing rules
- context, such as language, currency, price variant, and tax settings
- items, each defined by SKU and quantity
The Shop API calculates pricing, taxes, and promotions automatically. You simply send the item SKUs and quantity.
Cart context
Make sure you define cart context correctly:
- language must match a published language in your catalogue
- currency and selected price variant determine pricing
- tax settings drive B2B versus B2C behaviour
- Optional fallback prices help when specific variants are missing
This context is essential for consistency across locales and pricing scenarios.
💡Note.
I recommend going to the endpoint (https://shop-api.crystallize.com/your-tenant/cart) https://shop-api.crystallize.com/your-tenant/cart and playing around with the queries and mutations. The docs within the playground are easy to comprehend, and if you have any questions, you can always contact us on Slack.
Place the cart
Before finalizing checkout, place the cart. This locks the cart so its content, quantities, and pricing cannot change during payment processing.
mutation PlaceCart($cartId: ID!) {
place(id: $cartId) {
id
items {
name
quantity
}
}
}Once placed, the cart becomes immutable, and you can safely use it as the basis for order creation. If a user navigates back and edits items after placement, create a new cart by running the hydrate mutation without a cart ID.
Create an order
With a placed cart, you can create an order via the Shop API. This step finalizes the transaction in Crystallize and allows you to specify order type and payment status.
API endpoint:
https://shop-api.crystallize.com/your-tenant-identifier/orderThe mutation to create order from a placed cart.
mutation CreateOrderFromCart($id: ID!, $input: CreateOrderFromCartInput!) {
createFromCart(id: $id, input: $input) {
id
type
}
}
In this mutation:
idis the cart IDinputcan includetype, such as standard, draft, quote, or subscription- you can also set
paymentStatusto reflect payment outcomes
Crystallize supports multiple order types, helping you model various business cases from normal orders to quotes and recurring orders.
💡Note.
You, as a user, have full control over the information provided to the Shop API. The data from your cart will be automatically used, but you can add additional and override data when creating the order.
Summary
A basic checkout flow in Crystallize consists of:
- Querying the product with the Discovery API
- Hydrating a cart with items and context using the Shop API
- Placing the cart to lock it
- Creating an order from the placed cart
Most pricing logic, totals, taxes, and promotions are handled by the Shop API. You only need to send the relevant SKUs, context, and identifiers. But as usual with Crystallize you can still override and add to make it fit to your use case.
If you use the official Crystallize JS API Client, it simplifies authentication and request handling, letting you focus on your business logic.
