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.
Overview
Henry’s server SDK exposes three primitives -products.search, products.details, and the cart.* family - that compose into a handful of recurring app patterns. Pick the flow below that matches where your users start:
| If your users start from… | Use flow |
|---|---|
| A text query | Shopping agent / chatbot |
| A photo or screenshot | Visual / “shop the look” app |
| A merchant catalog feed | Product recommendation feed |
| A merchant-scoped storefront | Embedded merchant storefront |
| A known product URL | ”Buy with Henry” from any link |
checkoutUrl you can hand to your user, or a headless cart.checkout.purchase call that places the order from your server.
Building blocks
The four search modes
products.search is one endpoint with four useful shapes, switched by type and filters.type:
| Mode | Shape | When to reach for it |
|---|---|---|
| Global text | type: 'global', filters: { type: 'text', query } | Discovery across the open web |
| Global image | type: 'global', filters: { type: 'image', imageUrl } | Visual search from a photo / screenshot |
| Merchant text | type: 'merchant', filters: { merchant }, optional top-level query | Search within one merchant’s catalog |
| Merchant browse | type: 'merchant', filters: { merchant }, no query | Paginated browse of a merchant’s full catalog |
Variant resolution on details
products.details returns an options tree under result.options. Each option’s values[].nextOption is recursive - selecting one value reveals the next axis (e.g. size → colour → length). To walk it:
- Call
products.detailswithlink. - Read
result.optionsto discover the first axis (say, “fit”). - Re-call
products.detailswithselectedOptions: ['regular']to surface the next axis (say, “colour”). - Re-call with
selectedOptions: ['regular', 'black']for the next (size), etc.
selectedOptions array is positional - values are applied in order against the option chain. The same array drops straight into cart.item.{ ..., selectedOptions } when you’re ready to add the item.
Async vs sync
Every async operation (products.search, products.details, cart.checkout.details, cart.checkout.purchase) accepts mode: 'async' | 'sync'. Sync waits up to 30 seconds; async returns immediately with a refId you poll via the matching poll* helper. See Polling for the reusable helper - this guide assumes it’s imported as pollJob.
Flow 1 - Shopping agent / chatbot
Use this when your app takes a free-text user intent (“find me a quiet hairdryer under $200”) and returns purchasable products. Typical surfaces: AI assistants, conversational commerce, MCP tools.Search globally with filters
Use
type: 'global' with filters.type: 'text' and any of country, price, sortBy.Enrich the top candidate
Search results are catalog-grade. For prices, availability, and variants accurate enough to commit to, fetch details on the candidate you want.
Flow 2 - Visual / “shop the look” app
Use this when your users upload or screenshot something they want to buy. Typical surfaces: Lens-style apps, social shopping, photo-to-buy.filters.imageUrl accepts an HTTP(S) URL, a data: URL, or a raw base64 payload. Results route through shopping.google.com regardless of which merchant ends up matching.
Let the user pick, then enrich
Image search produces visually-similar candidates - present them as a grid, then fetch full details once the user commits.
Price filters (
filters.price.min/max) and sortBy are text-search only. Image search returns visually-similar matches, ordered by relevance.Flow 3 - Product recommendation feed
Use this when you’re powering a recommendation or deal feed by mirroring a merchant’s catalog. A periodic crawl plus change detection lets you surface fresh picks, price drops, or restocks to your users. Typical surfaces: recommendation feeds, deal sites, comparison engines, RSS-style product feeds, price trackers. The trick:products.search in merchant mode with no query returns the merchant’s broad catalog. Cache the cheap fields (link, price, availability), then only spend a products.details call on items that actually changed.
Spend details calls only on diffs
Compare each row against your cached snapshot. Re-fetch details when
price moves or availability flips.Flow 4 - Embedded merchant storefront
Use this when you’re building a branded buy-button or niche storefront scoped to a specific merchant - your user starts inside one brand and you want to walk them through size / colour / length variants. Typical surfaces: white-label merchant search, creator-led storefronts, brand-specific buy-buttons. This is the flow whereselectedOptions does the heaviest lifting.
Walk the option tree
Each
products.details call surfaces the next axis. Re-call as the user picks values, feeding the running array back in.When
result.options.status === 'unknown' Henry couldn’t determine variants for that product - send the user straight to cart with the link only and let the hosted checkout collect any remaining selections.Flow 5 - “Buy with Henry” from any link
Use this when the user (or your agent) already has a product URL and you want to skip search entirely. Typical surfaces: browser extensions, recipe sites, social-post buy-buttons, AI agents that receive a URL directly.(Optional) preview the product
Useful if you want to show price / availability / image before sending the user to checkout.
(Optional) take payment headlessly
If you’re collecting card details in your own UI via the Card Element, skip the hosted URL and call
cart.checkout.purchase directly. See Headless Checkout for the full pattern.Error handling & retries
Every flow above touches the same async machinery, so the same handling applies:| Symptom | Likely cause | What to do |
|---|---|---|
status: 'failed' with error.code | Merchant-side failure (e.g. captcha, listing pulled) | Log error, fall back to the next candidate or surface a “we couldn’t fetch this product” UI |
mode: 'sync' returns with pending / processing | Operation didn’t finish in the 30s window | Fall back to polling with pollJob instead of re-issuing the request |
result.options.status === 'unknown' | Variants couldn’t be inferred | Drop into cart without selectedOptions and let hosted checkout collect them |
404 Not Found on cart.fetch | cartId belongs to a different app, or doesn’t exist | Verify the cart was created with the same API key |
401 Unauthorized everywhere | HENRY_SDK_API_KEY missing or wrong | Check your env |
webhookUUID on the cart’s settings.events and Henry will push completion events to you.
Next steps
Product discovery
Full reference for
products.search and products.details parametersUniversal cart
Cart settings, items, tags, and lifecycle events
Checkout
Hosted iframe / redirect or headless
cart.checkout.purchasePolling
The reusable
pollJob helper used throughout this guideWebhooks
Skip polling entirely - have Henry push order events to you
Merchants
Look up which merchants are supported before scoping a flow