Cart & Checkout
Create carts, manage items, and process payments through the QUANTM7 API.
Cart and checkout endpoints require a secret key (q7_sk_). These operations must run on your server, never in client-side code.
Create a cart
Creates a new empty cart.
POST /v1/cartAuth: Secret key required
Request body
| Field | Type | Required | Description |
|---|---|---|---|
currency | string | No | Three-letter ISO 4217 currency code (default: GBP) |
{
"currency": "GBP"
}Response
{
"data": {
"id": 5,
"public_id": "cart_01J8ABC...",
"currency": "GBP",
"status": "active",
"created_at": "2026-08-01T10:00:00.000Z"
}
}Errors
| Status | Code | Message |
|---|---|---|
| 400 | bad_request | currency must be a 3-letter ISO code |
Get a cart
Returns a cart with all its items, quantities, and totals.
GET /v1/cart/{publicId}Auth: Secret key required
Path parameters
| Parameter | Type | Description |
|---|---|---|
publicId | string | Cart public ID |
Response
{
"data": {
"id": 5,
"public_id": "cart_01J8ABC...",
"currency": "GBP",
"status": "active",
"discount_code": null,
"notes": null,
"created_at": "2026-08-01T10:00:00.000Z",
"updated_at": "2026-08-01T10:05:00.000Z",
"items": [
{
"id": 1,
"product_id": 42,
"variant_id": 101,
"quantity": 2,
"title": "Classic Tee",
"variant_title": "Small / Black",
"sku": "CT-SM-BLK",
"price": 29.99,
"compare_at_price": 39.99,
"image_url": "https://cdn.quantm7.com/store/products/abc123.webp",
"requires_shipping": true,
"weight": 0.2,
"weight_unit": "kg"
}
],
"item_count": 2,
"subtotal": 59.98
}
}Cart fields
| Field | Type | Description |
|---|---|---|
id | integer | Internal cart ID |
public_id | string | Public cart identifier (use this in URLs) |
currency | string | Cart currency (ISO 4217) |
status | string | Cart status (active, converted) |
discount_code | string or null | Applied discount code |
notes | string or null | Order notes from the customer |
items | array | Cart line items |
item_count | integer | Total quantity across all items |
subtotal | number | Sum of (price x quantity) for all items |
Cart item fields
| Field | Type | Description |
|---|---|---|
id | integer | Line item ID |
product_id | integer | Product ID |
variant_id | integer | Variant ID |
quantity | integer | Quantity |
title | string | Product title |
variant_title | string | Variant title |
sku | string or null | SKU |
price | number | Unit price |
compare_at_price | number or null | Original price (for showing savings) |
image_url | string or null | Product image |
requires_shipping | boolean or null | Whether the item needs shipping |
weight | number or null | Item weight (snapshot from variant) |
weight_unit | string or null | Weight unit (g, kg, lb, oz) |
Errors
| Status | Code | Message |
|---|---|---|
| 404 | not_found | Cart not found |
Add item to cart
Adds a product variant to the cart. If the variant is already in the cart, the quantity is increased.
POST /v1/cart/{publicId}/itemsAuth: Secret key required
Request body
| Field | Type | Required | Description |
|---|---|---|---|
variant_id | integer | Yes | The variant to add |
quantity | integer | No | Quantity to add (default: 1, max: 999) |
{
"variant_id": 101,
"quantity": 2
}Response
Returns the full updated cart (same shape as GET /v1/cart/{publicId}).
Errors
| Status | Code | Message |
|---|---|---|
| 400 | bad_request | variant_id is required |
| 400 | bad_request | quantity must be between 1 and 999 |
| 400 | bad_request | Variant not found |
| 400 | bad_request | Product is not available |
| 400 | bad_request | Insufficient stock |
Update item quantity
Changes the quantity of a cart item. Set quantity to 0 to remove the item.
PUT /v1/cart/{publicId}/items/{itemId}Auth: Secret key required
Path parameters
| Parameter | Type | Description |
|---|---|---|
publicId | string | Cart public ID |
itemId | string | Cart item ID |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
quantity | integer | Yes | New quantity (0 removes the item, max: 999) |
{
"quantity": 3
}Response
Returns the full updated cart.
Errors
| Status | Code | Message |
|---|---|---|
| 400 | bad_request | quantity must be between 0 and 999 |
| 400 | bad_request | Insufficient stock |
Remove item from cart
Removes an item from the cart entirely.
DELETE /v1/cart/{publicId}/items/{itemId}Auth: Secret key required
Path parameters
| Parameter | Type | Description |
|---|---|---|
publicId | string | Cart public ID |
itemId | string | Cart item ID |
Response
Returns the full updated cart.
Validate cart
Checks that all cart items are still available, in stock, and at the correct price. Call this before checkout to catch issues early.
POST /v1/cart/{publicId}/validateAuth: Secret key required
Path parameters
| Parameter | Type | Description |
|---|---|---|
publicId | string | Cart public ID |
Response
{
"data": {
"valid": true,
"issues": []
}
}If there are problems, valid is false and issues lists each one:
{
"data": {
"valid": false,
"issues": [
{
"type": "insufficient_stock",
"item_id": 1,
"title": "Classic Tee",
"detail": "Only 1 available"
},
{
"type": "price_changed",
"item_id": 2,
"title": "Leather Belt",
"detail": "Price changed from 45 to 49.99"
}
]
}
}Issue types
| Type | Description |
|---|---|
cart_not_found | Cart does not exist or is not active |
cart_empty | Cart has no items |
variant_removed | A variant has been deleted from the store |
product_unavailable | A product is no longer active |
insufficient_stock | Not enough inventory for the requested quantity |
price_changed | The variant price has changed since it was added to the cart |
Create checkout
Creates a Stripe PaymentIntent for the cart. Returns a client_secret that you pass to Stripe's Payment Element on your frontend.
POST /v1/checkoutAuth: Secret key required
Request body
| Field | Type | Required | Description |
|---|---|---|---|
cart_id | string | Yes | Cart public ID |
email | string | Yes | Customer email address |
shipping | object | Yes | Shipping address |
Shipping address fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Recipient name |
address1 | string | Yes | Street address line 1 |
address2 | string | No | Street address line 2 |
city | string | Yes | City |
province | string | No | State, province, or region |
zip | string | Yes | Postal or ZIP code |
country_code | string | Yes | Two-letter country code (ISO 3166-1 alpha-2) |
phone | string | No | Phone number |
{
"cart_id": "cart_01J8ABC...",
"email": "customer@example.com",
"shipping": {
"name": "Jane Smith",
"address1": "123 Main Street",
"address2": "Flat 4",
"city": "London",
"province": "Greater London",
"zip": "SW1A 1AA",
"country_code": "GB",
"phone": "+447700900000"
}
}Response
{
"data": {
"client_secret": "pi_3ABC...secret_XYZ",
"publishable_key": "pk_live_...",
"connected_account_id": "acct_...",
"subtotal": 59.98,
"shipping": 0,
"tax": 0,
"total": 59.98
}
}Response fields
| Field | Type | Description |
|---|---|---|
client_secret | string | Stripe PaymentIntent client secret |
publishable_key | string | Stripe publishable key for the connected account |
connected_account_id | string | Stripe Connect account ID |
subtotal | number | Sum of item prices |
shipping | number | Shipping cost |
tax | number | Tax amount |
total | number | Total charge amount |
Using the client secret
Pass the client_secret to Stripe's Payment Element on your frontend:
import { loadStripe } from '@stripe/stripe-js'
// Use the publishable_key and connected_account_id from the checkout response
const stripe = await loadStripe(data.publishable_key, {
stripeAccount: data.connected_account_id
})
const elements = stripe.elements({
clientSecret: data.client_secret
})
const paymentElement = elements.create('payment')
paymentElement.mount('#payment-element')
// When the customer submits the form
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: 'https://mystore.com/order/confirmation'
}
})Errors
| Status | Code | Message |
|---|---|---|
| 400 | bad_request | cart_id is required |
| 400 | bad_request | email is required |
| 400 | bad_request | Invalid email format |
| 400 | bad_request | shipping is required |
| 400 | bad_request | name is required in shipping address |
| 400 | bad_request | address1 is required in shipping address |
| 400 | bad_request | city is required in shipping address |
| 400 | bad_request | zip is required in shipping address |
| 400 | bad_request | country_code is required in shipping address |
| 400 | bad_request | Cart not found or already converted |
| 400 | bad_request | Cart is empty |
| 400 | bad_request | Store has not enabled payments |
| 400 | bad_request | Store has no connected Stripe account |
| 400 | bad_request | Store payment account is not yet active |
| 400 | bad_request | Insufficient stock |
| 400 | bad_request | Order total must be at least 0.50 |
Payment flow overview
1. Customer adds items to cart → POST /v1/cart/{id}/items
2. Customer enters address → Your frontend form
3. Your server creates checkout → POST /v1/checkout
4. Customer enters payment → Stripe Payment Element (client-side)
5. Stripe processes payment → Stripe handles this
6. Stripe webhook confirms order → QUANTM7 handles this automatically
7. Customer sees confirmation → Your return_url pageYou do not need to handle webhooks. QUANTM7 receives the Stripe webhook, confirms the order, decrements inventory, and creates the order record.