Skip to main content

Overview

Henry’s Headless Checkout gives you complete control over the commerce experience through our Advanced API. Build custom checkout flows while we handle payment infrastructure.
Headless Checkout requires Pro tier access. Contact us to upgrade.
Perfect for: Teams requiring custom flows and complete control over UI/UX.

Why Choose Headless Checkout?

Complete Control

Build exactly the experience your users need

White Label

Your brand, your design, no compromises

Advanced Features

Card tokenization and custom flows

How It Works

Headless Checkout follows a simple three-step flow:
Use the same x-user-id header across all API calls to maintain user context.

Quick Start

import HenrySDK from "@henrylabs/sdk";

const client = new HenrySDK({
  apiKey: process.env.HENRY_API_KEY!,
});
1

Get Pro Access

Contact us for Pro tier access.
2

Collect Card Details

Use our hosted modal to securely collect payment cards:
const cardCollect = await client.wallet.createCardCollection({
  'x-user-id': 'user_123',
  auth: false,
});

// Redirect user to modal
window.location.href = cardCollect.data.modal_url;
Headless Checkout calls to client.wallet.createCardCollection must include auth: false.
3

Search Products

Find products from supported merchants:
const { data } = await client.products.search({
  query: 'Nike',
});
4

Add to Cart & Get Quote

Add products to a cart and retrieve a quote (with tax, fees, and a session-token):
// 1. Add items to cart
await client.cart.items.add({
  'x-user-id': 'user_123',
  productsDetails: [
    {
      name: "Men's Trail Runners",
      price: '100',
      quantity: 1,
      productLink:
        'https://www.on.com/en-us/products/cloud-6-versa-m-3mf1004/mens/black-eclipse-shoes-3MF10040106',
      productId: 'P01145AC2',
      metadata: {
        color: 'Black',
        size: '9',
      },
      productImageLink:
        'https://images.ctfassets.net/hnk2vsx53n6l/2xi62H2BswFpVK0SjUmhXM/0d4a4bb14915c9a5d3228df45c774629/c36d3fd00cf91ec9fb5ff4bc4d4a0093cccbe8cd.png?w=192&h=192&fm=avif&f=center&fit=fill&q=80',
    },
  ],
});

// 2. Get cart quote (session token + totals)
const quote = await client.checkout.session.createQuote({
  'x-user-id': 'user_123',
  shippingDetails: {
    fullName: 'John Doe',
    email: '[email protected]',
    phoneNumber: '+1234567890',
    addressLine1: '350 5th Ave',
    countryCode: 'US',
    city: 'New York',
    stateOrProvince: 'New York',
    postalCode: '10001',
  },
});

const sessionToken = quote.data.session_token;
console.log(sessionToken); // Use this in checkout
5

Complete Checkout

Process payment with the stored card:
const confirmation = await client.checkout.session.confirmCheckout({
  'x-session-token': sessionToken,
  'x-user-id': 'user_123',
});

const orderData = confirmation.data;
console.log(orderData.id, orderData.status);

Complete Example

Here’s a full implementation:
async function checkout(userId) {
  // Step 1: Collect card via modal
  const cardCollect = await client.wallet.createCardCollection({
    "x-user-id": userId,
    auth: false,
  });

  window.location.href = cardCollect.data.modal_url;

  // Step 2: Search products (after user returns)
  const products = await client.products.search({
    query: "Nike",
  });

  const selected = products.data[0];

  // Step 3a: Add to cart
  await client.cart.items.add({
    "x-user-id": userId,
    productsDetails: [
      {
        name: selected.name,
        price: String(selected.price),
        quantity: 1,
        productLink: selected.productLink,
        productId: selected.id,
        productImageLink: selected.imageUrl,
      },
    ],
  });

  // Step 3b: Get cart quote (returns session_token)
  const quote = await client.checkout.session.createQuote({
    "x-user-id": userId,
    shippingDetails: {
      fullName: "John Doe",
      email: "[email protected]",
      phoneNumber: "+1234567890",
      addressLine1: "350 5th Ave",
      addressLine2: "Suite 21",
      countryCode: "US",
      city: "New York",
      stateOrProvince: "New York",
      postalCode: "10001",
    },
  });

  const sessionToken = quote.data.session_token;

  // Step 4: Confirm checkout with session_token
  const order = await client.checkout.session.confirmCheckout({
    "x-session-token": sessionToken,
    "x-user-id": userId,
  });

  console.log("Order:", order.data.id);
}

Integration Methods

Method 1: Hosted Iframe

Keep users on your site with an embedded card collection:
async function collectCard(userId: string) {
  const { data } = await client.wallet.createCardCollection({
    "x-user-id": userId,
    auth: false,
  });

  // Embed in iframe
  const iframe = document.createElement("iframe");
  iframe.src = data.modal_url;
  iframe.style.width = "100%";
  iframe.style.height = "600px";
  iframe.style.border = "none";
  document.getElementById("card-container")?.appendChild(iframe);
}

Method 2: Modal/Popup

Balance between redirect and embedded:
async function openCardModal(userId: string) {
  const { data } = await client.wallet.createCardCollection({
    "x-user-id": userId,
    auth: false,
  });

  const modal = window.open(
    data.modal_url,
    "henry-card-collection",
    "width=500,height=700,left=200,top=100",
  );

  window.addEventListener("message", (event) => {
    if (event.origin !== "https://checkout.henrylabs.ai") return;

    const payload = event.data as { status?: string } | null;
    if (payload?.status === "complete") {
      modal?.close();
      handleCardSaved(payload);
    }
  });
}

Method 3: Redirect

Simplest integration - redirect users to Henry’s hosted page:
async function redirectToCardCollection(userId: string) {
  const { data } = await client.wallet.createCardCollection({
    "x-user-id": userId,
    auth: false,
  });

  window.location.href = data.modal_url;
}

Handling Results

Track order status after checkout:
// Check order status
async function getOrderStatus(orderId: string) {
  const response = await client.orders.retrieveStatus(orderId);
  return response.data;
}

// Poll for updates
function pollOrderStatus(
  orderId: string,
  callback: (order: Awaited<ReturnType<typeof getOrderStatus>>) => void,
) {
  const interval = setInterval(async () => {
    const order = await getOrderStatus(orderId);
    callback(order);

    if (["delivered", "cancelled", "failed"].includes(order.status)) {
      clearInterval(interval);
    }
  }, 5000);

  return () => clearInterval(interval);
}
Webhook support for real-time updates is coming soon.

Mobile Integration

Swift

import WebKit

class CardCollectionViewController: UIViewController {
    @IBOutlet weak var webView: WKWebView!

    func loadCardCollection(modalUrl: String) {
        if let url = URL(string: modalUrl) {
            webView.load(URLRequest(url: url))
        }
    }
}

Kotlin

class CardCollectionActivity : AppCompatActivity() {
    private lateinit var webView: WebView

    fun loadCardCollection(modalUrl: String) {
        webView.loadUrl(modalUrl)
        webView.settings.javaScriptEnabled = true
    }
}

React Native

import { useEffect, useState } from "react";
import { WebView } from "react-native-webview";

type Props = {
  userId: string;
  onComplete(orderId: string): void;
};

export function CardCollectionScreen({ userId, onComplete }: Props) {
  const [modalUrl, setModalUrl] = useState<string | null>(null);

  useEffect(() => {
    client.wallet
      .createCardCollection({ "x-user-id": userId, auth: false })
      .then(({ data }) => setModalUrl(data.modal_url));
  }, [userId]);

  if (!modalUrl) {
    return null;
  }

  return (
    <WebView
      source={{ uri: modalUrl }}
      onMessage={(event) => {
        try {
          const payload = JSON.parse(event.nativeEvent.data) as {
            status?: string;
            orderId?: string;
          };
          if (payload.status === "complete" && payload.orderId) {
            onComplete(payload.orderId);
          }
        } catch (error) {
          console.warn("Ignoring malformed checkout message", error);
        }
      }}
    />
  );
}

Next Steps

Ready to implement Headless Checkout?
1

Get Pro Access

Contact us to upgrade to Pro tier
2

Review API Docs

Explore the API Reference for detailed endpoints
3

Test Integration

Use sandbox mode to validate your implementation
4

Go Live

Switch to production API key when ready
Headless Checkout requires Pro tier access. Start with Hosted Checkout if you want to launch quickly.