Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.henrylabs.ai/llms.txt

Use this file to discover all available pages before exploring further.

Want to skip the reading and run it live? Check out our demo app or clone the quickstart repo.
This guide shows the full E2E flow - search → cart → checkout → order tracking. You don’t have to use all of it. Jump to whichever feature you need: you can call cart.checkout.purchase directly with a product link, or use products.details standalone without ever touching the cart.

Prerequisites

  • Node.js 18+ (or Bun/Deno)
  • An API key from the Henry Dashboard
  • A server-side environment (API key must stay private)

Setup

1

Install the SDK

npm i @henrylabs/sdk
2

Set your environment variable

#.env.local
HENRY_SDK_API_KEY=your_api_key_here
3

Initialize the client

import HenrySDK from '@henrylabs/sdk';

const henry = new HenrySDK({
  apiKey: process.env.HENRY_SDK_API_KEY!,
});

End-to-end walkthrough

Step 1 - Search for products

Product search is async. The initial call returns a refId so in this example we’ll poll until results arrive.
// Kick off the search
const search = await henry.products.search({
	type: 'global',
	filters: { type: 'text', query: 'Nike Air Max' },
	limit: 10,
});

// Poll until complete
async function pollUntilDone<T extends { status: string; refId: string }>(initial: T, poll: (args: { refId: string }) => Promise<T>, intervalMs = 1000): Promise<T> {
	let current = initial;
	while (current.status === 'pending' || current.status === 'processing') {
		await new Promise((r) => setTimeout(r, intervalMs));
		current = await poll({ refId: initial.refId });
	}
	return current;
}

const searchResult = await pollUntilDone(search, (args) => henry.products.pollSearch(args));

const products = searchResult.result?.products ?? [];
const first = products[0];
console.log(first.name, first.price.value, first.price.currency);
// → "Nike Air Max 270" "150.00" "USD"
Cache the refId - you can re-poll any time to retrieve results without re-running the search.

Step 2 - Fetch product details

Use the product link from the search result to pull full details: rich images, all variants, and live availability.
const detailResult = await henry.products.details({
	link: first.link,
});

const detail = await pollUntilDone(detailResult, (args) => henry.products.pollDetails(args));

const product = detail.result;
console.log(product?.name, product?.variants);

Step 3 - Create a cart

Pass one or more product links to cart.create. The response includes a cartId and a ready-to-use checkoutUrl for hosted checkout.
const cart = await henry.cart.create({
	items: [
		{
			link: first.link,
			quantity: 1,
			selectedOptions: ['regular', 'black', '10-w'],
		},
	],
});

const { cartId, checkoutUrl } = cart.data;
console.log('Cart:', cartId);
console.log('Checkout URL:', checkoutUrl);
// → https://checkout.henrylabs.ai/cart/<card_id>

Step 4 - Add more items (optional)

You can add products to an existing cart at any time.
await henry.cart.item.add(cartId, {
	item: {
		link: 'https://www.nike.com/t/air-max-270-shoes/AH8050-002',
		quantity: 2,
	},
});

Step 5 - Launch hosted checkout

The checkoutUrl returned by cart.create points to Henry’s hosted checkout modal. Redirect your user, open it in an iframe, or pop it in a modal - Henry handles payment, address, and tax collection.
// Option A: Full page redirect (server-side)
res.redirect(checkoutUrl);

// Option B: Embed in an iframe (client-side)
const iframe = document.createElement('iframe');
iframe.src = checkoutUrl;
iframe.style.cssText = 'width:100%;height:700px;border:none;border-radius:12px';
document.getElementById('checkout-container')!.appendChild(iframe);

// - [Optional]: Listen for completion events
window.addEventListener('message', (event) => {
	if (!event.origin.endsWith('.henrylabs.ai')) return;

	const { action, orderId } = event.data ?? {};
	if (action === 'orderCompleted') {
		console.log('Order placed:', orderId);
	}
});
Need full control over the checkout UI? See Headless Checkout

Step 6 - Track the order

The recommended approach is webhooks - Henry will POST to your endpoint when the order status changes, so you don’t have to poll. Register a webhook endpoint in the Henry Dashboard, then handle the events:
app.post('/webhooks/henry', express.raw({ type: 'application/json' }), async (req, res) => {
	if (!verifyHenryWebhook(req.body.toString(), req.headers['x-henry-signature'], req.headers['x-henry-timestamp'])) {
		return res.sendStatus(401);
	}

	const { event, data } = JSON.parse(req.body.toString());

	if (event === 'order.purchase.full.complete') {
		console.log('Order complete:', data.refId, data.result?.costs);
	} else if (event === 'order.purchase.full.cancelled') {
		console.log('Order cancelled:', data.refId);
	}

	res.sendStatus(200);
});
See the Webhooks guide for signature verification and the full list of events. If you need to check order status on demand, use orders.list({cartId}).

Next steps

Product Discovery

Deep dive: search filters, async polling, and rich product details

Universal Cart

Create, manage, and configure multi-merchant carts

Checkout

Hosted checkout with postMessage events, and Pro headless checkout

Order Management

List, filter, and poll orders across all your users