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: Platforms requiring custom flows and complete control over 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

1

Get Pro Access

Contact us for Pro tier access and your Advanced API key.
2

Collect Card Details

Use our hosted modal to securely collect payment cards:
// Get card collection modal URL
const response = await fetch(
  'https://api.sandbox.henrylabs.ai/v0/wallet/card-collect',
  {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'x-user-id': 'user_123'
    }
  }
);

const { data } = await response.json();
// Redirect user to modal
window.location.href = data.modal_url;
Your servers never touch card data - achieving PCI SAQ-A compliance.
3

Search Products

Find products from supported merchants:
const products = await fetch(
  'https://api.sandbox.henrylabs.ai/v0/products/search',
  {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      query: 'Nike',
      limit: 20
    })
  }
);

const { data } = await products.json();
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 fetch('https://api.sandbox.henrylabs.ai/v0/cart/items', {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'x-user-id': 'user_123',
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    },
    body: JSON.stringify({
      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
  const quoteResponse = await fetch(
    'https://api.sandbox.henrylabs.ai/v0/cart/quote',
    {
      method: 'POST',
      headers: {
        'x-api-key': 'YOUR_API_KEY',
        'x-user-id': 'user_123',
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify({
        shippingDetails: {
          fullName: "John Doe",
          email: "john.doe@example.com",
          phoneNumber: "+1234567890",
          addressLine1: "350 5th Ave",
          countryCode: "US",
          city: "New York",
          stateOrProvince: "New York",
          postalCode: "10001",
          addressLine2: "string"
        }
      })
    }
  );

  const { data } = await quoteResponse.json();
  console.log(data.session_token); // Use this in checkout
5

Complete Checkout

Process payment with the stored card:
  const orderResponse = await fetch(
  'https://api.sandbox.henrylabs.ai/v0/checkout/session/confirm',
  {
    method: 'POST',
    headers: {
      'x-api-key': '<api-key>',
      'x-user-id': 'user_123', // Same user ID
      'x-session-token': data.session_token, // From /cart/quote
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    },
  }
  );

  const { data: orderData } = await orderResponse.json();
  console.log(orderData);

Complete Example

Here’s a full implementation:
async function checkout(userId) {
  // Step 1: Collect card via modal
  const modalResponse = await fetch(
    "https://api.sandbox.henrylabs.ai/v0/wallet/card-collect",
    {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "x-user-id": userId,
      },
    },
  );

  const modal = await modalResponse.json();
  window.location.href = modal.data.modal_url;

  // Step 2: Search products (after user returns)
  const searchResponse = await fetch(
    "https://api.sandbox.henrylabs.ai/v0/products/search",
    {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        query: "Nike",
        greaterThanPrice: 50,
        lowerThanPrice: 200,
      }),
    },
  );

  const products = await searchResponse.json();
  const selected = products.data[0];

  // Step 3a: Add to cart
  await fetch("https://api.sandbox.henrylabs.ai/v0/cart/items", {
    method: "POST",
    headers: {
      "x-api-key": API_KEY,
      "x-user-id": userId,
      "Content-Type": "application/json",
      Accept: "application/json",
    },
    body: JSON.stringify({
      productsDetails: [
        {
          name: selected.name,
          price: selected.price,
          quantity: 1,
          productLink: selected.productLink,
          productId: selected.id,
          productImageLink: selected.imageUrl,
        },
      ],
    }),
  });

  // Step 3b: Get cart quote (returns session_token)
  const quoteResponse = await fetch(
    "https://api.sandbox.henrylabs.ai/v0/cart/quote",
    {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "x-user-id": userId,
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify({
        shippingDetails: {
          fullName: "John Doe",
          email: "john@example.com",
          phoneNumber: "+1234567890",
          addressLine1: "350 5th Ave",
          countryCode: "US",
          city: "New York",
          stateOrProvince: "New York",
          postalCode: "10001",
          addressLine2: "string",
        },
      }),
    },
  );

  const quote = await quoteResponse.json();
  const sessionToken = quote.data.session_token;

  // Step 4: Confirm checkout with session_token
  const orderResponse = await fetch(
    "https://api.sandbox.henrylabs.ai/v0/checkout/session/confirm",
    {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "x-user-id": userId,
        "x-session-token": sessionToken,
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify({
        shippingDetails: {
          fullName: "John Doe",
          email: "john@example.com",
          phoneNumber: "+1234567890",
          addressLine1: "350 5th Ave",
          countryCode: "US",
          city: "New York",
          stateOrProvince: "New York",
          postalCode: "10001",
          addressLine2: "string",
        },
      }),
    },
  );

  const order = await orderResponse.json();
  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) {
  // Get modal URL
  const response = await fetch(
    "https://api.sandbox.henrylabs.ai/v0/wallet/card-collect",
    {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "x-user-id": userId,
      },
    },
  );

  const { data } = await response.json();

  // 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:
function openCardModal(userId) {
  // Get modal URL
  fetch("https://api.sandbox.henrylabs.ai/v0/wallet/card-collect", {
    method: "POST",
    headers: {
      "x-api-key": API_KEY,
      "x-user-id": userId,
    },
  })
    .then((res) => res.json())
    .then(({ data }) => {
      const modal = window.open(
        data.modal_url,
        "henry-card-collection",
        "width=500,height=700,left=200,top=100",
      );

      // Listen for completion
      window.addEventListener("message", (event) => {
        if (event.origin === "https://checkout.henrylabs.ai") {
          if (event.data.status === "complete") {
            modal.close();
            handleCardSaved(event.data);
          }
        }
      });
    });
}

Method 3: Redirect

Simplest integration - redirect users to Henry’s hosted page:
async function redirectToCardCollection(userId) {
  const response = await fetch(
    "https://api.sandbox.henrylabs.ai/v0/wallet/card-collect",
    {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "x-user-id": userId,
      },
    },
  );

  const { data } = await response.json();
  window.location.href = data.modal_url;
}

Handling Results

Track order status after checkout:
// Check order status
function getOrderStatus(orderId) {
  return fetch(`https://api.sandbox.henrylabs.ai/v0/orders/${orderId}`, {
    headers: { "x-api-key": API_KEY },
  }).then((res) => res.json());
}

// Poll for updates
function pollOrderStatus(orderId, callback) {
  const interval = setInterval(async () => {
    const order = await getOrderStatus(orderId);
    callback(order.data);

    if (["delivered", "cancelled", "failed"].includes(order.data.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 { WebView } from "react-native-webview";

function CardCollectionScreen({ userId }) {
  const [modalUrl, setModalUrl] = useState(null);

  useEffect(() => {
    fetch("https://api.sandbox.henrylabs.ai/v0/wallet/card-collect", {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "x-user-id": userId,
      },
    })
      .then((res) => res.json())
      .then((data) => setModalUrl(data.data.modal_url));
  }, []);

  return modalUrl ? (
    <WebView
      source={{ uri: modalUrl }}
      onMessage={(event) => {
        const data = JSON.parse(event.nativeEvent.data);
        if (data.status === "complete") {
          navigation.navigate("Products");
        }
      }}
    />
  ) : null;
}

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.