Skip to main content

Recipes

Practical, copy-paste-ready code examples for the most common commerce workflows. Every recipe uses the TypeScript SDK.
All examples assume you have already installed and configured the SDK. See the Introduction if you haven’t.

Recipe 1: Customer Authentication

Register a new customer, log in, and use the customer token to access protected routes like order history.
import { createStorefrontClient } from '@headless-commerce/sdk';

const client = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_HC_API_KEY!,
});

// 1. Register a new customer
const customer = await client.customers.register({
  email: 'jane@example.com',
  password: 'SecureP@ss123!',
  first_name: 'Jane',
  last_name: 'Doe',
});

// 2. Log in to get a JWT token
const { token } = await client.customers.login({
  email: 'jane@example.com',
  password: 'SecureP@ss123!',
});

// 3. Set the customer token for subsequent requests
client.setCustomerToken(token);

// 4. Access protected routes
const { data: orders } = await client.customers.orders.list({ limit: 10 });
const profile = await client.customers.me();
console.log(profile.email); // "jane@example.com"

Recipe 2: Cart Merging After Login

When an anonymous visitor adds items to a cart and then logs in, merge the guest cart into their existing customer cart.
import { createStorefrontClient } from '@headless-commerce/sdk';

const client = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_HC_API_KEY!,
});

// 1. Guest browses and adds items
const guestCart = await client.carts.create({ session_id: 'anon-session-456' });
await client.carts.addItem(guestCart.id, {
  variant_id: 'var_hoodie_l_navy',
  quantity: 1,
});

// 2. Guest decides to log in
const { token } = await client.customers.login({
  email: 'jane@example.com',
  password: 'SecureP@ss123!',
});
client.setCustomerToken(token);

// 3. Merge the anonymous cart into the customer's cart
// Items from the guest cart are added to the customer's existing cart.
// Duplicate variants have their quantities summed.
const mergedCart = await client.carts.merge({
  source_cart_id: guestCart.id,
});

console.log(mergedCart.items.length); // Combined items from both carts

Recipe 3: Discount Code Workflow

Admin creates a discount code, the storefront applies it to a cart, and the customer checks out with the discount.
import { createAdminClient, createStorefrontClient } from '@headless-commerce/sdk';

// --- Admin side: create the discount ---
const admin = createAdminClient({ apiKey: 'sk_live_xxx' });

const discount = await admin.discounts.create({
  code: 'SUMMER20',
  type: 'percentage',
  value: 20, // 20% off
  starts_at: '2025-06-01T00:00:00Z',
  ends_at: '2025-08-31T23:59:59Z',
  usage_limit: 500,
  min_order_amount: 30000, // KRW
});

// --- Storefront side: apply to cart ---
const client = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_HC_API_KEY!,
});

const cart = await client.carts.create({ session_id: 'session-789' });
await client.carts.addItem(cart.id, {
  variant_id: 'var_dress_s_red',
  quantity: 1,
});

// Apply discount code
const updated = await client.carts.applyDiscount(cart.id, { code: 'SUMMER20' });
console.log(updated.discount_total); // -8000 (20% of 40000)

// Checkout proceeds with discount applied
const order = await client.carts.checkout(cart.id, {
  email: 'shopper@example.com',
  shipping_address: { line1: '456 Oak Ave', city: 'Busan', country: 'KR' },
  payment_method: 'stripe',
});

Recipe 4: Inventory Management

List products with low stock, adjust inventory quantities, and configure safety stock thresholds.
import { createAdminClient } from '@headless-commerce/sdk';

const admin = createAdminClient({ apiKey: 'sk_live_xxx' });

// 1. List variants with low stock
const { data: lowStock } = await admin.inventory.list({
  status: 'low_stock',
  limit: 20,
});
console.log(`${lowStock.length} items below safety stock`);

// 2. Adjust inventory for a specific variant (e.g., restock)
await admin.inventory.adjust({
  variant_id: 'var_tshirt_m_black',
  quantity: 50, // positive = add, negative = subtract
  reason: 'Restock from supplier PO-2025-0042',
});

// 3. Set safety stock threshold (triggers inventory.low webhook)
await admin.inventory.update('var_tshirt_m_black', {
  safety_stock: 10,
  track_inventory: true,
});

// 4. Get current inventory for a variant
const inv = await admin.inventory.get('var_tshirt_m_black');
console.log(inv.available);    // 50
console.log(inv.safety_stock); // 10

Recipe 5: Return & Refund Flow

A customer requests a return, the admin reviews and processes it through approval, receiving, completion, and finally issues a refund.
import { createAdminClient, createStorefrontClient } from '@headless-commerce/sdk';

const client = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_HC_API_KEY!,
});
client.setCustomerToken('cust_token_xxx');

// 1. Customer requests a return
const returnReq = await client.returns.create({
  order_id: 'order_abc123',
  items: [
    { order_item_id: 'oi_item1', quantity: 1, reason: 'Wrong size' },
  ],
  note: 'I ordered M but need L instead',
});

// --- Admin side ---
const admin = createAdminClient({ apiKey: 'sk_live_xxx' });

// 2. Admin approves the return
await admin.returns.approve(returnReq.id);

// 3. Admin marks items as received
await admin.returns.receive(returnReq.id, {
  items: [{ order_item_id: 'oi_item1', quantity: 1, condition: 'good' }],
});

// 4. Admin completes the return
await admin.returns.complete(returnReq.id);

// 5. Create a refund for the returned items
const refund = await admin.refunds.create({
  order_id: 'order_abc123',
  amount: 29000,
  reason: 'Return - wrong size',
  items: [{ order_item_id: 'oi_item1', quantity: 1 }],
});
console.log(refund.status); // "pending" → processed by payment provider

Recipe 6: Fulfillment Lifecycle

Create a fulfillment for an order, add tracking info, mark as shipped, and then mark as delivered.
import { createAdminClient } from '@headless-commerce/sdk';

const admin = createAdminClient({ apiKey: 'sk_live_xxx' });

// 1. Create a fulfillment for specific order items
const fulfillment = await admin.fulfillments.create({
  order_id: 'order_abc123',
  items: [
    { order_item_id: 'oi_item1', quantity: 2 },
    { order_item_id: 'oi_item2', quantity: 1 },
  ],
  tracking_number: '1234567890',
  tracking_company: 'CJ Logistics',
  tracking_url: 'https://trace.cjlogistics.com/tracking/1234567890',
});

console.log(fulfillment.status); // "created"

// 2. Mark as shipped (triggers fulfillment.shipped webhook)
await admin.fulfillments.ship(fulfillment.id, {
  shipped_at: new Date().toISOString(),
  notify_customer: true, // sends shipping notification email
});

// 3. Mark as delivered (triggers fulfillment.delivered webhook)
await admin.fulfillments.deliver(fulfillment.id, {
  delivered_at: new Date().toISOString(),
  notify_customer: true,
});

Recipe 7: Multi-Region Pricing

Create regions with different currencies and set region-specific prices for products.
import { createAdminClient } from '@headless-commerce/sdk';

const admin = createAdminClient({ apiKey: 'sk_live_xxx' });

// 1. Create regions
const krRegion = await admin.regions.create({
  name: 'South Korea',
  currency: 'KRW',
  countries: ['KR'],
  tax_rate: 10,
});

const usRegion = await admin.regions.create({
  name: 'United States',
  currency: 'USD',
  countries: ['US'],
  tax_rate: 0, // varies by state
});

// 2. Set region-specific prices for a variant
await admin.variants.setRegionPrices('var_tshirt_m_black', [
  { region_id: krRegion.id, price: 29000 },  // 29,000 KRW
  { region_id: usRegion.id, price: 2200 },   // $22.00 USD
]);

// 3. Storefront: fetch prices for a specific region
import { createStorefrontClient } from '@headless-commerce/sdk';

const storefront = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_HC_API_KEY!,
});

const product = await storefront.products.get('prod_abc123', {
  region_id: krRegion.id,
});
// product.variants[0].price → 29000 (KRW)

Recipe 8: CSV Bulk Import

Import products from a CSV spreadsheet, handling validation errors gracefully.
import { createAdminClient } from '@headless-commerce/sdk';
import fs from 'node:fs';

const admin = createAdminClient({ apiKey: 'sk_live_xxx' });

// 1. Read the CSV file
const csvBuffer = fs.readFileSync('./products.csv');

// 2. Start the import job
const job = await admin.imports.create({
  type: 'products',
  file: new Blob([csvBuffer], { type: 'text/csv' }),
  options: {
    on_duplicate: 'update',   // "skip" | "update" | "error"
    notify_on_complete: true,
  },
});

console.log(job.id);     // "import_xyz789"
console.log(job.status); // "processing"

// 3. Poll for completion
let result = await admin.imports.get(job.id);
while (result.status === 'processing') {
  await new Promise((r) => setTimeout(r, 2000));
  result = await admin.imports.get(job.id);
}

// 4. Check results
console.log(`Imported: ${result.summary.created} created, ${result.summary.updated} updated`);
if (result.summary.errors > 0) {
  console.log('Errors:');
  for (const err of result.errors) {
    console.log(`  Row ${err.row}: ${err.message}`);
  }
}
Expected CSV format:
name,sku,price,currency,status,variant_name,inventory_quantity
"Classic T-Shirt","TSH-001",29000,"KRW","active","M / Black",50
"Classic T-Shirt","TSH-002",29000,"KRW","active","L / Black",30
"Summer Dress","DRS-001",59000,"KRW","draft","S / Red",20

Next Steps

Webhooks

Webhook setup, signature verification, and production-grade processing patterns.

API Reference

Explore all 100+ REST endpoints.

SDKs

TypeScript and Python SDK reference with every resource and method.

Stripe Integration

Set up Stripe payments for your storefront.