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-modal',
  {
    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

Complete Checkout

Process payment with the stored card:
const order = await fetch(
  'https://api.sandbox.henrylabs.ai/v0/checkout/single',
  {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_API_KEY',
      'x-user-id': 'user_123', // Same user ID
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      productDetails: {
        productId: 'product_123',
        name: 'Nike Air Max',
        price: 129.99,
        quantity: 1,
        productLink: 'https://store.com/product',
        productImageLink: 'https://image.url'
      },
      shippingDetails: {
        fullName: 'John Doe',
        email: 'john@example.com',
        addressLine1: '123 Main St',
        city: 'New York',
        stateOrProvince: 'NY',
        countryCode: 'US',
        postalCode: '10001'
      }
    })
  }
);

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-modal",
    {
      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 3: Checkout with stored card
  const orderResponse = await fetch(
    "https://api.sandbox.henrylabs.ai/v0/checkout/single",
    {
      method: "POST",
      headers: {
        "x-api-key": API_KEY,
        "x-user-id": userId,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        productDetails: {
          productId: selected.id,
          name: selected.name,
          price: selected.price,
          quantity: 1,
          productLink: selected.productLink,
          productImageLink: selected.imageUrl,
        },
        shippingDetails: {
          fullName: "John Doe",
          email: "john@example.com",
          addressLine1: "123 Main St",
          city: "New York",
          stateOrProvince: "NY",
          countryCode: "US",
          postalCode: "10001",
        },
      }),
    },
  );

  const order = await orderResponse.json();
  console.log("Order:", order.data.id);
}

Integration Methods

Method 1: Embedded 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-modal",
    {
      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-modal", {
    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-modal",
    {
      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-modal", {
      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 Embedded Checkout if you want to launch quickly.