> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abconvert.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless SDK reference

> Every entry point, function, option, and object of the ABConvert headless SDK, with its arguments and what it returns.

This page lists every export of `@abconvert/headless` by entry point. For what the SDK is for and how to set it up, read the [overview](/headless-sdk/overview) and your [stack guide](/headless-sdk/overview#pick-your-stack) first.

IDs are accepted as the Storefront API returns them, `gid://shopify/ProductVariant/47522361606401`, or as the number, `'47522361606401'`. Test IDs are the number the ABConvert admin shows, as a string or a number. Returned IDs, such as `Assignment.experimentId`, `Price.productId`, and `Price.variantId`, are always strings holding the number: `'47522361606401'`.

## Entry points

<Tabs>
  <Tab title="Any stack">
    ```ts Entry points theme={null}
    // The client: runs on the server, at the edge, or in the browser
    import { createABConvert, hydrateABConvert } from '@abconvert/headless';

    // The React bindings
    import { ABConvertProvider, useABConvert } from '@abconvert/headless/react';
    ```
  </Tab>

  <Tab title="Next.js">
    ```ts Entry points theme={null}
    // The middleware and the client for a page or server action
    import { abconvertMiddleware, planMiddleware, createABConvert } from '@abconvert/headless/next';

    // The React bindings
    import { ABConvertProvider, useABConvert } from '@abconvert/headless/react';
    ```
  </Tab>

  <Tab title="Hydrogen 2026.10 and later">
    ```ts Entry points theme={null}
    // The client for your loaders, the React bindings with Hydrogen's analytics and consent, and the full-page cache guard
    import { createABConvert, ABConvertProvider, useABConvert, keepPrivateWhenTested } from '@abconvert/headless/hydrogen';

    // The cart attributes, for a cart that changes in the browser
    import { ABConvertCartAttributes, useCartReadyForCheckout } from '@abconvert/headless/hydrogen';
    ```
  </Tab>

  <Tab title="Hydrogen before 2026.10">
    ```ts Entry points theme={null}
    // The client for your loaders and the full-page cache guard
    import { createABConvert, keepPrivateWhenTested } from '@abconvert/headless/hydrogen';

    // The React bindings, placed inside Analytics.Provider
    import { ABConvertProvider, useABConvert } from '@abconvert/headless/hydrogen/analytics-provider';
    ```
  </Tab>
</Tabs>

## createABConvert

Creates the client. What you pass decides which client you get:

| You pass | You get | Runs on |
| - | - | - |
| `request` | A server client for that visitor. Reads the visitor ID, URL, and country from the request. Adds `getPageState`, `isPageTested`, and `isTesting`. | Server |
| `pageKey` | A page client for the key's test groups. Reads no request and has no visitor. Adds `getPageState`. See [A client made with pageKey](#a-client-made-with-pagekey). | Server |
| Neither, in a browser | A browser client. Assigns the visitor after the tests load, writes the SDK's cookies, and starts tracking. Resolves once the visitor is assigned. | Browser |

```ts Signature theme={null}
createABConvert(options: ABConvertOptions & { request: Request | { headers, url } }): Promise<ServerABConvert>
createABConvert(options: ABConvertOptions & { pageKey: string }): Promise<PageABConvert>
createABConvert(options: ABConvertOptions): Promise<ABConvert>
```

`ServerABConvert` adds `getPageState`, `isPageTested`, and `isTesting`; `PageABConvert` adds `getPageState`. Both are exported, so TypeScript knows which methods your client has.

Never rejects, with one exception: a client made with `pageKey` rejects when the tests the key names cannot be loaded. When the tests cannot be loaded for a request, the client answers as the original store. See [Loading and failures](/headless-sdk/loading#if-loading-fails).

### Options

<ParamField path="shop" type="string" required>
  Your `myshopify.com` domain: `'your-store.myshopify.com'`.
</ParamField>

<ParamField path="storefrontKey" type="string" required>
  A storefront key from **Integrations > Headless Storefront** in ABConvert. It tells ABConvert which store's tests to load, and is safe to ship in storefront code.
</ParamField>

<ParamField path="request" type="Request | { headers, url }">
  The incoming request. Required on the server. Pass the Fetch `Request`, or `{ headers, url }` when your framework has none.
</ParamField>

<ParamField path="country" type="string">
  ISO 3166-1 alpha-2 code of the country your storefront shows prices for, the same value you pass to the Storefront API in `@inContext`. Defaults to your store's default country. See [Prices by country](/headless-sdk/prices#prices-by-country).
</ParamField>

<ParamField path="customer" type="Customer">
  The signed-in customer, for customer conditions. Every field is optional. See [Customer](/headless-sdk/targeting#customer).

  <Expandable title="fields">
    <ParamField path="loggedIn" type="boolean">
      Defaults to `true` when `customer` is passed.
    </ParamField>

    <ParamField path="tags" type="string[]">
      `tags` on the customer.
    </ParamField>

    <ParamField path="ordersCount" type="number">
      `numberOfOrders` on the customer.
    </ParamField>

    <ParamField path="totalSpent" type="number">
      In your store currency.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="timeout" type="number" default="1000">
  Milliseconds to wait for your tests when the server holds no copy yet. See [If loading is slow](/headless-sdk/loading#if-loading-is-slow).
</ParamField>

<ParamField path="cacheTtl" type="number" default="60">
  Seconds a loaded copy of your tests stays fresh. See [How the copy refreshes](/headless-sdk/loading#how-the-copy-refreshes).
</ParamField>

<ParamField path="onError" type="(error: Error) => void">
  Called when your tests cannot be loaded.
</ParamField>

<ParamField path="consent" type="{ analytics: boolean }">
  Whether the visitor allows analytics. Without it, the SDK reads Shopify's `_tracking_consent` cookie when the visitor has one, and otherwise treats analytics as allowed. See [Consent](/headless-sdk/tracking#consent).
</ParamField>

<ParamField path="expectCustomerPrivacy" type="boolean" default="false">
  `true` when your storefront loads Shopify's Customer Privacy API. The SDK then tracks nothing until the visitor answers. The Hydrogen entries set it for you.
</ParamField>

<ParamField path="pageKey" type="string">
  Builds the client from a page key instead of a request: `getPageKey()` from the server client, or the segment the Next.js middleware rewrote to. `''` is a page no test changes.
</ParamField>

<ParamField path="onlyOwnProductPrices" type="boolean" default="false">
  `true` when your product pages show no price but their own product's. A product page's key then carries only the price tests that include that product. Leave it unset when a related product's price appears on the page.
</ParamField>

### A client made with pageKey

A client made with `pageKey` renders a page a cache may serve to everyone in its test groups, so it has no visitor:

* `createABConvert` rejects when the tests the key names cannot be loaded. It waits up to 10 seconds, or `timeout` if that is longer. An empty key never rejects.
* Price and assignment reads answer for the key's test groups. Shipping rates and offers are not in a page key, so those reads return nothing.
* `getPageState()` replaces `getClientState()`, which throws.
* `getCartAttributes()`, `getCartLineAttributes()`, and `getCheckoutUrl()` throw. Create a client from the request for them.
* `getVisitorId()` returns `''`, `getRedirectUrl()` returns `null`, and `getSetCookieHeaders()` returns `[]`.

## Methods

The server client, the browser client, and `useABConvert()` in a component share one API. "Runs on" marks the methods that do something on one side only; elsewhere they return the empty value shown.

| Method | Returns | Runs on |
| - | - | - |
| [`getAssignment(experimentId)`](#getassignment) | `Assignment \| null` | Both |
| [`getAssignments()`](#getassignments) | `Assignment[]` | Both |
| [`getFeature(key)`](#getfeature) (coming soon) | `Assignment \| null` | Both |
| [`getPriceByVariantId(variantId, options?)`](#getpricebyvariantid) | `Price \| null` | Both |
| [`getPriceByProductId(productId, options?)`](#getpricebyproductid) | `Price \| null` | Both |
| [`formatPrice(amount, currency?)`](#formatprice) | `string` | Both |
| [`getShippingRates(options?)`](#getshippingrates) | `ShippingRate[] \| null` | Both |
| [`getFreeShippingThreshold(options?)`](#getfreeshippingthreshold) | `FreeShippingThreshold \| null` | Both |
| [`getOffers()`](#getoffers) | `Offer[]` | Both |
| [`getVisitorId()`](#getvisitorid) | `string` | Both |
| [`getCountry()`](#getcountry) | `string` | Both |
| [`forceTestGroup(experimentId, index)`](#forcetestgroup) | `void` | Both |
| [`clearForcedTestGroup(experimentId)`](#clearforcedtestgroup) | `void` | Both |
| [`getCartAttributes()`](#getcartattributes) | `CartAttribute[]` | Both |
| [`getCartLineAttributes()`](#getcartlineattributes) | `CartAttribute[]` | Both |
| [`getCheckoutUrl(options)`](#getcheckouturl) | `string` | Both |
| [`getPageKey()`](#getpagekey) | `string` | Both |
| [`getRedirectUrl(url)`](#getredirecturl) | `string \| null` | Server |
| [`getClientState()`](#getclientstate) | `ClientState` | Server |
| [`getPageState()`](#getpagestate) | `PageState` | Server |
| [`isPageTested()`](#ispagetested) | `boolean` | Server, from a request |
| [`isTesting()`](#istesting) | `boolean` | Server, from a request |
| [`getSetCookieHeaders()`](#getsetcookieheaders) | `string[]` | Server |
| [`refreshTests()`](#refreshtests) | `Promise<void>` | Server |
| [`track(name, payload?)`](#track) | `void` | Browser |
| [`trackExposure(experimentId)`](#trackexposure) | `void` | Browser |
| [`setConsent(consent)`](#setconsent) | `void` | Browser |

### getAssignment

Returns the visitor's [`Assignment`](/api-reference/browser-api-reference#assignment) for one test, or `null` when the visitor is not in it. See [Read an assignment](/headless-sdk/test-groups#read-an-assignment).

<ParamField path="experimentId" type="string | number" required>
  The test ID shown in the ABConvert admin: `'49603'`.
</ParamField>

### getAssignments

Returns one [`Assignment`](/api-reference/browser-api-reference#assignment) per test the visitor is in. `[]` when they are in none.

### getFeature

Coming soon. Returns the visitor's [`Assignment`](/api-reference/browser-api-reference#assignment) in the running [feature test](/headless-sdk/feature-tests) with this key, or `null`. Until feature tests ship it returns `null` for every key.

<ParamField path="key" type="string" required>
  The [feature key](/headless-sdk/feature-tests#feature-key) set on the test: `'home-hero'`.
</ParamField>

### getPriceByVariantId

Returns the visitor's [`Price`](/api-reference/browser-api-reference#price) for one product variant, or `null` when the visitor sees the original price: the product variant is in no price test, or the visitor is not in its test. A visitor in Control gets the original price as a `Price`. See [Show a test price](/headless-sdk/prices#show-a-test-price).

<ParamField path="variantId" type="string | number" required>
  The product variant ID.
</ParamField>

<ParamField path="options" type="PriceOptions">
  <Expandable title="fields">
    <ParamField path="country" type="string">
      ISO 3166-1 alpha-2 code. Reads that country's price instead of the client's `country`.
    </ParamField>
  </Expandable>
</ParamField>

### getPriceByProductId

Returns a [`Price`](/api-reference/browser-api-reference#price) for one product, from its product variants' prices, or `null` in the same cases as `getPriceByVariantId`.

<ParamField path="productId" type="string | number" required>
  The product ID.
</ParamField>

<ParamField path="options" type="PriceOptions">
  <Expandable title="fields">
    <ParamField path="country" type="string">
      ISO 3166-1 alpha-2 code. Reads that country's price instead of the client's `country`.
    </ParamField>

    <ParamField path="aggregate" type="'min' | 'max' | 'first'" default="'min'">
      Which product variant's price to return. `'min'` and `'max'` pick among the product variants the test prices for this visitor. `'first'` picks the product's first product variant, or the first one the test prices.
    </ParamField>
  </Expandable>
</ParamField>

### formatPrice

Returns `amount` formatted in `currency`, such as `'$62.70'`, in the locale the page rendered in, so the server and the browser format alike.

<ParamField path="amount" type="number" required />

<ParamField path="currency" type="string">
  ISO 4217 code: `'USD'`. Defaults to your store's currency.
</ParamField>

### getShippingRates

Returns every [`ShippingRate`](/api-reference/browser-api-reference#shippingrate) the visitor's test group offers, `null` when the visitor is in no shipping test, or `[]` when the test group has no rates or `zone` names a zone the test does not have. See [Show shipping rates and offers](/headless-sdk/prices#show-shipping-rates-and-offers).

<ParamField path="options" type="ShippingOptions">
  <Expandable title="fields">
    <ParamField path="zone" type="string">
      A shipping zone name as set in the test: `'United States'`. Defaults to every zone.
    </ParamField>
  </Expandable>
</ParamField>

### getFreeShippingThreshold

Returns the [`FreeShippingThreshold`](/api-reference/browser-api-reference#freeshippingthreshold) the visitor's test group gets, or `null` when the visitor is in no shipping test or no single threshold exists.

<ParamField path="options" type="ShippingOptions">
  <Expandable title="fields">
    <ParamField path="zone" type="string">
      A shipping zone name as set in the test.
    </ParamField>
  </Expandable>
</ParamField>

### getOffers

Returns every [`Offer`](/api-reference/browser-api-reference#offer) the visitor's test group gets. `[]` when the visitor is in no offer test, or their test group has no offer.

### getVisitorId

Returns the visitor ID the SDK keeps in the `abconvert-visitor` cookie. `''` on a client made with `pageKey`.

### getCountry

Returns the country the client prices for: the `country` you passed, or the store's default.

### forceTestGroup

Shows one test group to this visitor, for the rest of the visit. A forced visit is left out of results. On Next.js, a force on the server lasts only for that render. See [Preview a test group](/headless-sdk/preview#preview-a-test-group).

<ParamField path="experimentId" type="string | number" required>
  The test ID shown in the ABConvert admin.
</ParamField>

<ParamField path="index" type="number" required>
  The test group's position counting from 0: `0` is Control, `1` is Variant A.
</ParamField>

### clearForcedTestGroup

Undoes `forceTestGroup` for one test.

<ParamField path="experimentId" type="string | number" required>
  The test ID shown in the ABConvert admin.
</ParamField>

### getCartAttributes

Returns the [cart attributes](/headless-sdk/cart#write-the-attributes) that make Shopify checkout apply the visitor's tests: one [`CartAttribute`](#cartattribute) per key. Send them with the cart's own attributes on every `cartAttributesUpdate`. Throws on a client made with `pageKey`.

### getCartLineAttributes

Returns the same data as [`CartAttribute`](#cartattribute) entries for a cart line. Add them to each line you add. Throws on a client made with `pageKey`.

### getCheckoutUrl

Returns a Shopify cart permalink that lands in checkout with the visitor's test groups applied. Build it when you render the button; never share or cache it. Throws on a client made with `pageKey`. See [Checkout URLs](/headless-sdk/cart#checkout-urls).

<ParamField path="options" type="CheckoutUrlOptions" required>
  <Expandable title="fields">
    <ParamField path="lines" type="CheckoutLine[]" required>
      The lines to check out: `[{ variantId: '47522361606401', quantity: 1 }]`. Line attributes reach the first line only.
    </ParamField>

    <ParamField path="payment" type="'shop_pay'">
      Opens Shop Pay checkout.
    </ParamField>
  </Expandable>
</ParamField>

### getPageKey

Returns the page key: the test groups this page renders for, the same for every visitor in them. `''` when no test changes the page. In the browser, a client built from `getPageState()` returns the key the page was rendered for, and one built from `getClientState()` returns `''`.

### getRedirectUrl

Server only. Returns where a [URL redirect test](/headless-sdk/url-redirects) sends this visitor, as a same-origin path, or `null` when they stay: they are in Control or outside the test, the URL is not part of a test, or the visitor is a known crawler. `null` in the browser.

<ParamField path="url" type="string" required>
  The URL the visitor requested: `request.url`.
</ParamField>

### getClientState

Server only. Returns the [`ClientState`](#clientstate) that hands this visitor's page to the browser. It holds the visitor, so a page that carries it renders per visitor and must not be cached. Pass it to the provider's `state`. Throws on a client made with `pageKey`.

### getPageState

Server only. Returns the [`PageState`](#pagestate) that hands the page to the browser with nothing about the visitor, so a cached page can carry it. From a client made from a request, it carries the tests the client's reads touched, as the visitor saw them; from a client made with `pageKey`, the key's test groups. Pass it to the provider's `pageState`.

### isPageTested

Server only, on a client made from a request. Returns `true` when a test that runs or is in Preview can change this page for some visitor, whatever this visitor's test groups: a price test prices a product the URL shows, a URL redirect test starts or lands on the page, a read on this client returned a test's data, `getClientState()` was read, or the request is a preview session. A shared cache must then not keep one copy of the page for everyone.

### isTesting

Server only, on a client made from a request. Returns `true` while any test runs for your store, whatever the page, and in a preview session.

### getSetCookieHeaders

Server only. Returns the `Set-Cookie` values the SDK needs on the response, only for cookies that changed, such as on a first visit. `[]` in the browser and on a client made with `pageKey`. On Next.js, the middleware sets them for you.

### refreshTests

Server only. Fetches a fresh copy of your tests on this server now. If the fetch fails, the old copy is kept. See [How the copy refreshes](/headless-sdk/loading#how-the-copy-refreshes).

### track

Browser only. Records a storefront event for the visitor. Does nothing on the server. See [Page views and add to cart](/headless-sdk/tracking#page-views-and-add-to-cart).

<ParamField path="name" type="string" required>
  `'page_viewed'`, `'product_viewed'`, `'product_added_to_cart'`, or the name of a [custom event](/headless-sdk/tracking#custom-events) in ABConvert.
</ParamField>

<ParamField path="payload" type="object">
  `{ productId, variantId }` for `product_viewed`, `{ variantId, quantity }` for `product_added_to_cart`, none for `page_viewed`.
</ParamField>

### trackExposure

Browser only. Records that the visitor experienced a test, once per test per visit. Call it once a test price or an offer is on screen. See [Exposure](/headless-sdk/tracking#exposure).

<ParamField path="experimentId" type="string | number" required>
  `price.experimentId` or `offer.experimentId` from the object you rendered.
</ParamField>

### setConsent

Browser only. Updates the visitor's answer to your cookie banner. What the SDK held is sent when `analytics` becomes `true`, and dropped when it becomes `false`. See [Consent](/headless-sdk/tracking#consent).

<ParamField path="consent" type="{ analytics: boolean }" required />

## Browser client

### hydrateABConvert

Builds the browser client from what the server handed over, without React. `ABConvertProvider` does this for you. See [Other stacks](/headless-sdk/other-stacks#without-react).

```ts Signature theme={null}
hydrateABConvert(state: ClientState | PageState | null, options?: HydrateOptions): BrowserABConvert
```

<ParamField path="state" type="ClientState | PageState | null" required>
  `getClientState()` or `getPageState()` from the server, as the page carried it.
</ParamField>

<ParamField path="options" type="HydrateOptions">
  <Expandable title="fields">
    <ParamField path="onAssignment" type="(assignment: Assignment) => void">
      Runs once for each test the visitor is in, on every page load, once the visitor allows analytics. Not for forced or preview assignments.
    </ParamField>

    <ParamField path="expectCustomerPrivacy" type="boolean" default="false">
      Hold all tracking until Shopify's Customer Privacy API loads and allows analytics.
    </ParamField>
  </Expandable>
</ParamField>

The browser client has every [method](#methods), plus:

<ResponseField name="start()" type="void">
  Begins tracking: reports what is pending once consent allows. Call it once the page has loaded. Calling it again does nothing.
</ResponseField>

<ResponseField name="stop()" type="void">
  Stops waiting for the visitor's consent. What is held stays held.
</ResponseField>

<ResponseField name="subscribe(listener)" type="() => void">
  Calls `listener` after a forced test group changes what the reads return. Returns the function that removes the listener.
</ResponseField>

## React

From `@abconvert/headless/react`. On Hydrogen, import both from the [Hydrogen entry](#hydrogen) instead.

### ABConvertProvider

Shares the client with every component under it and records which test groups the visitor saw. Pass exactly one of `state`, `pageState`, or `options`. A server component can render it directly.

<ParamField path="state" type="ClientState">
  `getClientState()` from your server: a page rendered for its visitor.
</ParamField>

<ParamField path="pageState" type="PageState">
  `getPageState()` from your server: a page rendered for its test groups, which a cache may serve to everyone in them. The provider reads the visitor from their cookies once it mounts.
</ParamField>

<ParamField path="options" type="ABConvertOptions">
  Browser-only mode, with no server: the [`createABConvert` options](#options) without `request`. The tests load after the page does, and `isReady` is `false` until then. See [Browser only](/headless-sdk/other-stacks#browser-only).
</ParamField>

<ParamField path="onAssignment" type="(assignment: Assignment) => void">
  Runs once for each test the visitor is in, on every page load, once the visitor allows analytics. Not for forced or preview assignments. See [Send test groups to another tool](/headless-sdk/test-groups#send-test-groups-to-another-tool).
</ParamField>

<ParamField path="expectCustomerPrivacy" type="boolean" default="false">
  Hold all tracking until Shopify's Customer Privacy API loads and allows analytics. See [Consent](/headless-sdk/tracking#consent).
</ParamField>

### useABConvert

Returns the client for the nearest provider: every [method](#methods), plus `isReady`.

<ResponseField name="isReady" type="boolean">
  `false` only in browser-only mode, until the tests load and the visitor is assigned. Hide prices until it is `true`.
</ResponseField>

## Next.js

From `@abconvert/headless/next`. See the [Next.js guide](/headless-sdk/nextjs) and [Next.js caching and CDN](/headless-sdk/nextjs-caching).

### abconvertMiddleware

Sets the SDK's cookies, passes the visitor on to your pages, redirects for a URL redirect test, rewrites a tested page to its page key, and sets `cache-control` where a shared cache must not keep the response. Returns the response for Next.js to send. Call it from your `middleware` or `proxy` function.

```ts Signature theme={null}
abconvertMiddleware(request: NextRequest, options: NextOptions): Promise<NextResponse>
```

<ParamField path="request" type="NextRequest" required />

<ParamField path="options" type="NextOptions" required>
  The [`createABConvert` options](#options) except `request` and `pageKey`, plus:

  <Expandable title="fields">
    <ParamField path="rewritePrefix" type="string" default="'abconvert'">
      The path segment tested pages are rewritten under. Pick one that none of your routes uses, and name your `app/abconvert` folder after it.
    </ParamField>

    <ParamField path="cdnKeyedByCookie" type="boolean" default="false">
      `true` once your CDN includes the `abconvert-key` cookie in its cache key. A tested page then stays cacheable at the CDN. See [Cache tested pages at your CDN](/headless-sdk/nextjs-caching#cache-tested-pages-at-your-cdn).
    </ParamField>
  </Expandable>
</ParamField>

### planMiddleware

What `abconvertMiddleware` would do with the request, as data, for a middleware of your own that also rewrites. Apply every field to your response, as the [sample](/headless-sdk/nextjs-caching#if-you-already-have-middleware) does. Resolves to `null` for a request that is not a page, such as an image.

```ts Signature theme={null}
planMiddleware(request: Request, options: NextOptions): Promise<MiddlewarePlan | null>
```

Takes the same arguments as `abconvertMiddleware`. The plan has these fields:

<ResponseField name="redirect" type="string">
  The absolute URL to redirect to. Absent when the visitor stays.
</ResponseField>

<ResponseField name="redirectStatus" type="307 | 308">
  `307` for a URL redirect test, `308` for a direct visit to the rewrite prefix. Present with `redirect`.
</ResponseField>

<ResponseField name="rewritePath" type="string">
  The page key path to render instead, `/<prefix>/<page key><path><query>`. Present when a test changes the page, and on a server action from such a page.
</ResponseField>

<ResponseField name="pageKey" type="string">
  The page key, `''` when no test changes the page. Absent on a redirect, on a request left alone, and on a server action from a page no test changes.
</ResponseField>

<ResponseField name="requestHeaders" type="Headers">
  The request headers to render the page with. Absent on a redirect. For a server action, they keep the action on the same copy of your tests as the page the visitor saw.
</ResponseField>

<ResponseField name="setCookies" type="string[]">
  `Set-Cookie` values to append to the response.
</ResponseField>

<ResponseField name="responseHeaders" type="Record<string, string>">
  Headers to set on the response, such as `cache-control: private`.
</ResponseField>

### createABConvert on Next.js

The client a layout, page, or server action renders with. Never sets cookies; the middleware does.

```ts Signature theme={null}
createABConvert(options: NextOptions & { pageKey: string }): Promise<PageABConvert>
createABConvert(options: NextOptions): Promise<ServerABConvert>
```

<ParamField path="options" type="NextOptions" required>
  The [`createABConvert` options](#options) except `request`. With `pageKey`, the client for a cached page: it reads no request, so the page stays static. Without it, the client for the request the middleware prepared, which makes the page dynamic; use it in a server action or route handler.
</ParamField>

## Hydrogen

From `@abconvert/headless/hydrogen` on 2026.10 and later, or `@abconvert/headless/hydrogen/analytics-provider` before 2026.10. See the [Hydrogen guide](/headless-sdk/hydrogen).

### createABConvert on Hydrogen

The server client for a loader. The same as [`createABConvert`](#createabconvert) with `request`, except that it tracks nothing until Shopify's Customer Privacy API wrote the visitor's answer, and sets a cookie only when one changed, so a route that renders no test's data carries no `Set-Cookie` and can stay in the full-page cache. Exported by both Hydrogen entries.

```ts Signature theme={null}
createABConvert(options: ABConvertOptions & { request: Request }): Promise<ServerABConvert>
```

<ParamField path="options" type="ABConvertOptions" required>
  The [`createABConvert` options](#options). `request` is required; `expectCustomerPrivacy` is set for you.
</ParamField>

### ABConvertProvider on Hydrogen

The [React provider](#abconvertprovider), which also listens to Hydrogen's analytics for page views, product views, and add to cart, and waits for the visitor's consent. On 2026.10 and later, render it below Hydrogen's `ShopifyScripts`; before 2026.10, inside `Analytics.Provider`. Takes the provider's props except `expectCustomerPrivacy`, which is always on.

### keepPrivateWhenTested

Turns a `public` `Cache-Control` or `Oxygen-Cache-Control` header into `private` when a test can change the route, when the response sets a cookie, or when the client could not load your tests. Call it on the response headers of a route that opts into Oxygen's full-page cache, after the loader's reads. Returns `true` when it changed a header. See [Hydrogen full-page cache](/headless-sdk/hydrogen-caching).

```ts Signature theme={null}
keepPrivateWhenTested(headers: Headers, client: ServerABConvert): boolean
```

<ParamField path="headers" type="Headers" required>
  Your route's response headers, with its cache setting already on them.
</ParamField>

<ParamField path="client" type="ServerABConvert" required>
  The client from `createABConvert` for this request.
</ParamField>

### ABConvertCartAttributes

Writes ABConvert's cart attributes to the cart whenever it lacks one or holds an older value, and keeps the cart's other attributes. It waits until no cart change is in flight. When an update fails, it tries again after 1, 2, 4... seconds, up to 30. Render it once, inside both `CartProvider` and `ABConvertProvider`. Exported by `@abconvert/headless/hydrogen` only, for 2026.10 and later, where the cart changes in the browser. See [Write the cart attributes](/headless-sdk/hydrogen#step-4-write-the-cart-attributes).

```tsx Signature theme={null}
<ABConvertCartAttributes useCart={useCart} />
```

<ParamField path="useCart" type="UseCart" required>
  The `useCart` hook that `createCartComponents` returns in your app.
</ParamField>

### useCartReadyForCheckout

Returns `true` once the cart carries ABConvert's current attributes. Checkout charges what they say, so hold your checkout button until then. Exported by `@abconvert/headless/hydrogen` only, for 2026.10 and later.

```ts Signature theme={null}
useCartReadyForCheckout(useCart: UseCart): boolean
```

<ParamField path="useCart" type="UseCart" required>
  The `useCart` hook that `createCartComponents` returns in your app.
</ParamField>

## Objects

`Assignment`, `TestGroup`, `Price`, `ShippingRate`, `FreeShippingThreshold`, and `Offer` are the same objects as on the JavaScript API, described under its [Objects](/api-reference/browser-api-reference#objects). The headless SDK adds these.

### Feature test fields

For a [feature test](/headless-sdk/feature-tests), coming soon:

<ResponseField name="Assignment.featureKey" type="string">
  The [feature key](/headless-sdk/feature-tests#feature-key) set on the test. Absent for every other test type.
</ResponseField>

<ResponseField name="TestGroup.variables" type="Record<string, string | number | boolean>">
  The values set for that test group. `{}` for every other test type.
</ResponseField>

### CartAttribute

<ResponseField name="key" type="string" />

<ResponseField name="value" type="string" />

### CheckoutLine

<ResponseField name="variantId" type="string | number">
  The product variant ID.
</ResponseField>

<ResponseField name="quantity" type="number" />

### ClientState

What `getClientState()` hands the browser: the visitor, their test groups, and the tests cut to what they see. It identifies the visitor, so a page that carries it is that visitor's alone. Pass it to the provider's `state` or to `hydrateABConvert` as it is.

### PageState

What `getPageState()` hands the browser. It holds nothing about the visitor, so a cached page can carry it. Pass it to the provider's `pageState` or to `hydrateABConvert` as it is.

<ResponseField name="pageKey" type="string">
  The key the page was rendered for. `''` when no test changes the page.
</ResponseField>

<ResponseField name="country" type="string">
  The country the page was priced for.
</ResponseField>

<ResponseField name="assignments" type="Assignment[]">
  The key's test groups: what the page renders for.
</ResponseField>

## TypeScript

Every type on this page is exported. The options are `ABConvertOptions`, and `NextOptions` and `MiddlewarePlan` from `@abconvert/headless/next`. `UseCart`, from `@abconvert/headless/hydrogen`, is the type of the `useCart` argument. `createABConvert` returns an `ABConvert`: a `ServerABConvert` when made from a request, a `PageABConvert` when made with `pageKey`, and `hydrateABConvert` returns a `BrowserABConvert`. The objects are `Assignment`, `TestGroup`, `Price`, `ShippingRate`, `FreeShippingThreshold`, `Offer`, `CartAttribute`, `CheckoutLine`, `ClientState`, and `PageState`.

```ts theme={null}
import type { ABConvertOptions, Price, ServerABConvert } from '@abconvert/headless';
import type { MiddlewarePlan, NextOptions } from '@abconvert/headless/next';
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.