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

# A/B tests

> Start price, headline, cover and checkout-layout tests, read live results and keep the winner — from the API, the SDK or an AI agent.

Starting a test needs the **Pro** plan: below it, `POST /ab-tests` answers
`403` with `code: FEATURE_LOCKED` and `details.feature: "abTests"`. Reading,
stopping, declaring a winner and deleting are never gated. Every route works
in the API key's mode (live or test).

## Start a test

`variants` are the challengers (B, C, D). Variant A is the product as it is
now and is added for you.

```bash theme={"system"}
curl -X POST https://api.pocketsflow.com/ab-tests \
  -H "Authorization: Bearer pk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "665f1c2a9b3e4a0012ab34cd",
    "type": "price",
    "variants": [{ "label": "Higher price", "price": 59 }]
  }'
```

| `type` | Field on each variant |
| - | - |
| `price` | `price` — list price in your account currency (1–1,000,000) |
| `headline` | `headline`, optional `description` (HTML) |
| `cover` | `coverImage` — an https URL (upload with `POST /images`) |
| `checkout_layout` | `checkoutLayout` — `split`, `flipped`, `focus`, `banner`, `card`, `hero`, `sidebar`, `gallery`, `sheet` or `frame` |

Optional: `name`, and `weights` — the traffic split in whole percents, A
first, each 5–95, adding up to 100 (default: equal).

| Status | `code` | When |
| - | - | - |
| `201` | — | The running test |
| `400` | — | Invalid body, a variant equal to A, a pay-what-you-want or variant product in a price test |
| `403` | `FEATURE_LOCKED` | Your plan is below Pro |
| `409` | `AB_TEST_RUNNING` | The product already has a running test |

## Routes

| Method | Path | What it does |
| - | - | - |
| `GET` | `/ab-tests` | Your tests, newest first, with `summary: { visitors, conversions }` (`status`, `productId` filters) |
| `POST` | `/ab-tests` | Start a test |
| `GET` | `/ab-tests/{id}` | The test with its live `results` (`timezoneOffsetMinutes` for local days) |
| `GET` | `/ab-tests/{id}/results` | The `results` alone |
| `POST` | `/ab-tests/{id}/stop` | End it without a winner (`409 AB_TEST_NOT_RUNNING` when already ended) |
| `POST` | `/ab-tests/{id}/winner` | `{ "variantId" }` — end it and apply the winner to the product |
| `DELETE` | `/ab-tests/{id}` | Delete an ended test (`409 AB_TEST_RUNNING` while running) |
| `POST` | `/ab-tests/public/assign` | PUBLIC — the storefront's lookup of one visitor's variant |

## Results

```json theme={"system"}
{
  "currency": "USD",
  "metric": "revenue_per_visitor",
  "totals": { "visitors": 6872, "conversions": 225, "revenue": 11742 },
  "variants": [
    {
      "key": "A", "control": true, "visitors": 3412, "conversions": 116,
      "conversionRate": 3.4, "revenuePerVisitor": 1.67, "verdict": "control"
    },
    {
      "key": "B", "control": false, "visitors": 3388, "conversions": 109,
      "conversionRate": 3.22, "revenuePerVisitor": 1.9,
      "lift": -5.3, "pValue": 0.6744, "probabilityToBeat": 0.3372,
      "revenueProbabilityToBeat": 0.9712, "probabilityToBeBest": 0.97,
      "significant": true, "verdict": "winning"
    }
  ],
  "daily": [{ "date": "2026-10-03", "variants": { "<variantId>": { "visitors": 210, "conversions": 6 } } }],
  "recommendation": { "variantId": "<B's id>", "reason": "winner" },
  "thresholds": { "minVisitors": 100, "minConversions": 10 }
}
```

* `pValue` — two-proportion z-test of the conversion rate against A
  (two-sided). Under 0.05 = 95% confident they differ.
* `probabilityToBeat` — Bayesian chance (flat prior, exact) the variant's
  conversion rate is higher than A's.
* `revenueProbabilityToBeat` — price tests: chance the variant earns more per
  visitor. Price tests are judged on this (`metric: "revenue_per_visitor"`).
* `verdict` — `collecting` until both arms have 100+ visitors and 10+
  conversions between them; then `winning`, `losing` or `no_difference`.
* Conversions are paid, unrefunded sales credited to the variant the buyer
  saw; revenue is in your account currency.

## Visitor assignment

`POST /ab-tests/public/assign` with `{ productId, visitorId, currency? }`
returns `{ test: { testId, variantId, type, basePrice, overrides } }` or
`{ test: null }`. `visitorId` is the visitor's own random id (16–64
characters of `A-Z a-z 0-9 _ -`); the same id always gets the same variant,
and the first call logs the visit. Bots get `null`. With `currency`, a price
variant also comes back as `displayPrice`, converted exactly as checkout will
charge it.

The hosted product page and checkout send the same id as `abVisitor`, and
the variant they actually rendered as `abVariant`, when they create the
checkout session. The server charges the variant only when `abVariant`
matches its own record of the visitor; otherwise (no claim, `""`, or another
id — the page could not load the variant) it charges the product's own price
and the sale is not credited to the test. A price never comes from the page.
`GET /pricing/quote/{productId}` and `GET /personal-deadlines/public/offer`
take the same pair as `vid` + `abv`.

## Webhook

`ab_test.completed` fires when a test ends — a winner was declared, it was
stopped, or a conflicting product edit stopped it — with every variant's
results. See [Webhook events](/api-webhooks/events#ab-test-completed).

## MCP tools

`list_ab_tests`, `create_ab_test`, `get_ab_test`, `get_ab_test_results`,
`stop_ab_test`, `declare_ab_test_winner`, `delete_ab_test`.


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