stubkit docs

A/B testing paywalls

stubkit lets you run paywall experiments — pick two (or more) offerings, split traffic, and watch conversion rate per variant. Users are bucketed consistently: the same user always sees the same variant.

How it works

  1. You create an experiment in the dashboard under /apps/{id}/experiments with a slug and 2+ variants. Each variant points to an existing offering.
  2. On the device, the SDK calls GET /v1/experiments/{app_id}/{slug}?user_id={uuid}. The server hashes user_id + experiment_id with SHA-256 and picks a variant weighted by the experiment's weights.
  3. The SDK renders the returned offering and reports events (view, purchase, dismiss) to POST /v1/paywall-events.
  4. The dashboard shows conversion rate, unique viewers, and revenue per variant, with a "leading" badge on the top performer (only when signal is sufficient).

Bucketing guarantees

Because bucketing is a deterministic hash of the user id, results are stable across devices, app restarts, and cache clears. Adding a new variant reshuffles users — plan experiments accordingly.

Example — pricing test

POST /v1/admin/experiments
{
  "app_id": "notesam",
  "slug": "pricing-q2-2026",
  "description": "Does a 7-day trial convert better than no trial?",
  "variants": [
    { "variant_key": "control", "offering_slug": "default", "weight": 50 },
    { "variant_key": "trial", "offering_slug": "with-trial", "weight": 50 }
  ]
}

Reading results

The results endpoint returns per-variant views, purchases, unique viewers, revenue, and conversion rate (as a percentage). For statistical significance, aim for at least 500–1000 views per variant before declaring a winner — the dashboard shows an "early" hint below that threshold.

GET /v1/admin/experiments/{id}/results

{
  "experiment": { "id": "...", "slug": "pricing-q2-2026", "active": 1 },
  "variants": [
    {
      "id": "...",
      "variant_key": "control",
      "views": 842, "purchases": 34,
      "unique_viewers": 820,
      "conversion_rate": 4.04,
      "revenue_cents": 33966
    },
    {
      "id": "...",
      "variant_key": "trial",
      "views": 861, "purchases": 51,
      "unique_viewers": 839,
      "conversion_rate": 5.92,
      "revenue_cents": 50949
    }
  ]
}

Ending an experiment

When you're confident in a winner, set the losing variant's offering to inactive (so new users flow into the winner) and pause the experiment. Existing subscribers aren't affected either way.