Manage your tests through the API.
The management API, the MCP server for assistants, Slack alerts and signed webhooks, and the server helper package.
Manage Probant from a server or an assistant
The management API does from a server what the interface does on screen: list, create, and edit drafts, launch, pause, resume, stop with a decision, roll out a variant, manage metrics, and read results. Same rules, same messages: a change the editor rejects is rejected by the API too, and a test plan is recalculated at launch, just as on the “Plan and launch” screen.
The same operations are available as tools on an MCP server, so an assistant can manage your tests.
Authentication and roles
Create a management key in Settings, “Integrations” tab (workspace owner). Send it in the Authorization: Bearer sk_… header, from a server or an assistant, never from a web page: a request that carries an origin is rejected.
- Read: list tests and metrics, read a test and its results.
- Read and write: also create, edit, launch, pause, stop, and roll out.
A key never does more than the person who created it: if their role is lowered, so is the key’s, and the key stops working if they leave the workspace. Back-office secret keys only work with the orders API.
Limit: 120 requests per minute per key, 240 per IP address; beyond that, a 429 response with Retry-After.
OpenAPI 3.1 description: GET /api/v1/openapi.json, for a client generator or an API testing tool.
Reference
GET /api/v1/experiments List tests
The workspace’s tests, newest first, with their status and exposed visitors. Key: any management key (read access is enough).
?status=: Only return tests with this status.
POST /api/v1/experiments Create a draft
Same rules as the editor: two to five variants, the original “a” with no changes, and sanitized changes (one rejected change rejects the whole draft). Key: “read and write”, or a custom role that grants the “Prepare tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
name | string | Test name. |
hypothesis · optional | string | “If…, then…, because…”. |
zone · optional | string | Site zone: in exclusive mode (the default), two tests in the same zone don’t see the same visitor. |
urlPattern · optional | string | Targeted pages: “/products/*”, or a full URL. |
devices · optional | "mobile" | "tablet" | "desktop"[] | Targeted devices; empty or omitted: all. |
previewUrl · optional | string | Public page the variants were built on. |
variants | object[] |
GET /api/v1/experiments/{key} Get a test
Hypothesis, targeting, variants and their changes, primary metric, the test plan locked at launch, and the latest decision. Key: any management key (read access is enough).
PATCH /api/v1/experiments/{key} Update a draft
Omitted fields keep their value. A launched test can no longer be changed (409). Key: “read and write”, or a custom role that grants the “Prepare tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
name · optional | string | Test name. |
hypothesis · optional | string | “If…, then…, because…”. |
zone · optional | string | Site zone: in exclusive mode (the default), two tests in the same zone don’t see the same visitor. |
urlPattern · optional | string | Targeted pages: “/products/*”, or a full URL. |
devices · optional | "mobile" | "tablet" | "desktop"[] | Targeted devices; empty or omitted: all. |
previewUrl · optional | string | Public page the variants were built on. |
variants · optional | object[] |
POST /api/v1/experiments/{key}/launch Launch a draft
The engine recalculates the test plan (visitors per variant, duration, checkpoints) from the declared traffic, as on the “Plan and duration” screen. Rejected when the plan’s quota is reached (402) or when the zone has no room left (409). An optimization test draft launches with POST /experiments/{key}/bandit/launch (409 here). guardrails: keys of the guardrail metrics, locked with the test plan, following the screen’s rules; a switchback or geo test rejects speed and retention metrics and, for geo tests, amount metrics (422). Such a test also launches here: looks sets its checkpoints (one reading, at the end, by default); dailyVisitors is still required but isn’t used (nothing is computed in visitors). Key: “read and write”, or a custom role that grants the “Launch and stop tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
metric · optional | string | Key of the primary metric; if omitted, “commande” (order), created if needed, as in the interface. |
baselineRate · optional | number | The metric’s baseline rate (0.032 for 3.2%), for a rate metric. |
baselineMean · optional | number | Baseline mean per visitor, for a mean metric. |
baselineSd · optional | number | Baseline standard deviation per visitor, for a mean metric. |
dailyVisitors | number | Measurable visitors per day on the targeted pages. |
mde · optional | 0.02 | 0.05 | 0.08 | 0.1 | 0.15 | 0.2 | Relative effect to detect (0.05 = 5%); required for the “superiority” objective (the default). |
looks · optional | 1 | 2 | 3 | 4 | 5 | Checkpoints; 3 by default (1, at the end, for a switchback or geo test). An objective with a margin always has a single look, at the end. |
objective · optional | "superiority" | "non-inferiority" | "equivalence" | Decision objective: “superiority” (the default) looks for a better variant; “non-inferiority” shows it isn’t worse than the original by more than the margin; “equivalence” shows the difference stays within ± the margin (TOST). Never a “winner” with a margin. |
margin · optional | 0.01 | 0.02 | 0.03 | 0.05 | 0.1 | Relative margin m (0.02 = 2%), required with “non-inferiority” or “equivalence”; none is set by default. Sealed with the test plan. |
share · optional | 1 | 0.75 | 0.5 | 0.25 | 0.1 | Share of the zone’s traffic; 1 by default. |
overlap · optional | "exclusive" | "parallel" | Concurrent tests: “exclusive” (the default) shares the zone without overlap; “parallel” gives the test its own layer, on all the traffic of its pages, and its interactions with other tests are checked. |
after · optional | string[] | Keys of launched tests this one lets go first: their visitors don’t see it, and the test plan counts the share left for it. Required (all launched tests) if the workspace allows one test per visitor. |
group · optional | string | Named exclusion group, in exclusive mode: the test shares this group’s traffic (tests from different zones can join it); if omitted, its zone’s traffic. |
guardrails · optional | string[] | Keys of guardrail metrics, each with its direction and limit (see /metrics): a proven degradation beyond the limit stops the test on its own, with the reason. The primary metric can’t also be a guardrail. Switchback or geo test: no speed or retention metrics, and only rates for a geo test. |
interactions · optional | boolean | Multivariate test: announce second-order interactions in the test plan (counted in the Holm correction; requires many more visitors). Ignored for an A/B test. |
POST /api/v1/experiments/{key}/pause Pause
Visitors see the original; the test resumes on the same traffic slice with the same assignments. Key: “read and write”, or a custom role that grants the “Launch and stop tests” permission; never more than the permissions of the person who created the key.
POST /api/v1/experiments/{key}/resume Resume
Resumes a paused test. Key: “read and write”, or a custom role that grants the “Launch and stop tests” permission; never more than the permissions of the person who created the key.
POST /api/v1/experiments/{key}/stop Stop with a decision
The decision and the stop are recorded together. A stopped test can’t restart. Key: “read and write”, or a custom role that grants the “Launch and stop tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
decision | "ship" | "keep-control" | "abandon" | Roll out a variant, keep the original, or abandon. |
variant · optional | string | Variant to roll out, with “ship”. |
note · optional | string | Why: recorded with the decision. |
POST /api/v1/experiments/{key}/rollout Roll out a variant
Gradual rollout in steps, with the workspace’s guardrails monitored continuously and automatic rollback. Records the decision to roll out. Key: “read and write”, or a custom role that grants the “Roll out the winning variant” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
variant | string | Variant to roll out (not the original). |
steps · optional | number[] | Increasing steps as fractions, the last one at 1; 0.25, 0.5, 1 by default. |
GET /api/v1/experiments/{key}/results Get results
Per metric (primary, then secondary metrics and workspace guardrails) and per variant: visitors, converters, and sums, then the relative difference, range, p-value, and verdict computed by the engine. decisionAuthorized says whether the test plan allows a conclusion (sample reached or boundary crossed). When the workspace has turned on the Bayesian reading, bayesian adds it for the primary metric (BayesianReading schema): probability that the variant does better, credible interval, and expected loss, with a zero-centered prior drawn from the workspace’s stopped tests; it doesn’t decide anything. A windowed metric (retention, or a CRM conversion: window.kind “deferred”) only counts visitors whose window has closed; window.pendingVisitors says how many are still waiting. When the traffic split is abnormal (srm.isMismatch), results are masked as in the app: effect and value are null, the verdict is blocked, masked gives the reason, and split includes the diagnosis (visitors per variant, browser and device, Holm-corrected checks, and the segment at fault when the imbalance is concentrated there). Counts are kept. Key: any management key (read access is enough).
?format=: “csv”: the same report as CSV (“;” separator, decimal comma).
POST /api/v1/bandits Create an optimization test
Optimization test (multi-armed bandit) draft, with the same fields as a draft plus bandit (minimum share, learning, per-device shares). It maximizes conversions, or an amount per visitor, during the test; it doesn’t prove that one variant is better. Launch it with POST /experiments/{key}/bandit/launch, under the same rules as an A/B test. Key: “read and write”, or a custom role that grants the “Prepare tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
name | string | Test name. |
hypothesis · optional | string | “If…, then…, because…”. |
zone · optional | string | Site zone: in exclusive mode (the default), two tests in the same zone don’t see the same visitor. |
urlPattern · optional | string | Targeted pages: “/products/*”, or a full URL. |
devices · optional | "mobile" | "tablet" | "desktop"[] | Targeted devices; empty or omitted: all. |
previewUrl · optional | string | Public page the variants were built on. |
variants | object[] | |
bandit · optional | object |
POST /api/v1/experiments/{key}/bandit Turn a draft into an optimization test
A draft’s type and settings; a launched test keeps its type (409). Key: “read and write”, or a custom role that grants the “Prepare tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
floor · optional | number | Minimum share of each variant (0.1 = 10%, 10% by default), so learning continues on all of them; at most 1/K with K variants. |
minVisitors · optional | integer | Visitors per variant, across all epochs, before the first reallocation (learning with equal shares); 1,000 by default. |
notifyFloor · optional | boolean | Alert when a variant stays at its floor for seven days; true by default. |
context · optional | object | Contextual bandit: “device” gives shares per device (mobile, tablet, desktop), each with its own learning and floor; null, the same shares for everyone (the default). The device is read in the browser: the server helper and feature flags only assign visitors from the first epoch. |
POST /api/v1/experiments/{key}/bandit/launch Launch an optimization test
Launches an optimization draft: the metric to maximize (a rate, or an amount per visitor such as revenue, capped at the percentile set on the metric), the share of the zone, concurrent tests, and, if needed, its settings (otherwise the draft’s). Same rules as an A/B test launched through the API: rejected when the workspace requires another member to approve each launch (409; the request is made in Probant), when the plan’s quota is reached (402), when the zone has no room left, or for an A/B test (409). The first epoch, with equal shares, is sealed with the test plan. It maximizes conversions during the test; it doesn’t prove that one variant is better. Key: “read and write”, or a custom role that grants the “Launch and stop tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
metric · optional | string | Key of the metric to maximize: a conversion rate, or an amount per visitor (revenue), capped at the percentile set on the metric; if omitted, “commande” (order), created if needed, as in the interface. |
share · optional | 1 | 0.75 | 0.5 | 0.25 | 0.1 | Share of the zone’s traffic; 1 by default. |
overlap · optional | "exclusive" | "parallel" | Concurrent tests: “exclusive” (the default) shares the zone without overlap; “parallel” gives the test its own layer. |
floor · optional | number | Minimum share of each variant (0.1 = 10%, 10% by default), so learning continues on all of them; at most 1/K with K variants. |
minVisitors · optional | integer | Visitors per variant, across all epochs, before the first reallocation (learning with equal shares); 1,000 by default. |
notifyFloor · optional | boolean | Alert when a variant stays at its floor for seven days; true by default. |
context · optional | object | Contextual bandit: “device” gives shares per device (mobile, tablet, desktop), each with its own learning and floor; null, the same shares for everyone (the default). The device is read in the browser: the server helper and feature flags only assign visitors from the first epoch. |
GET /api/v1/experiments/{key}/bandit Get an optimization test
Dated epochs and their shares, the current share, each variant’s difference estimated epoch by epoch and then combined, an always-valid range, conversions gained compared with equal shares, and the traffic split checked per epoch. For a contextual bandit, the same reading per device (reading.byDevice), and the overall reading by epoch × device strata. Never a winner. A/B results (/results) for an optimization test return 409: their naive comparison would be biased. Key: any management key (read access is enough).
POST /api/v1/experiments/{key}/unit Choose what a draft randomizes
Visitors (A/B test), time slots (switchback: a price, stock) or geographic zones (French departments, regions, or countries: shipping fees). Departments require department-level geolocation, which isn’t always available; without it, 422 with the explanation. Exactly two versions for time slots and zones; a launched test keeps its unit (409). Launch in Probant, under the same rules as an A/B test. Key: “read and write”, or a custom role that grants the “Prepare tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
unit | "visitor" | "switchback" | "zones" | What gets randomized: `visitor` (A/B test), `switchback` (alternating time slots), or `zones` (geographic zones). |
windowHours · optional | number | Switchback: length of a time slot, in hours (24 by default). |
days · optional | number | Switchback: test duration, in days (28 by default). |
burnInMinutes · optional | number | Switchback: start of each time slot left out of the reading after a switch, in minutes (30 by default). |
level · optional | "department" | "region" | "country" | Geographic zones: French departments (“FR-75”, only if department-level geolocation is available; otherwise 422 with the explanation), ISO 3166-2 regions (the default), or countries. |
weeks · optional | number | Geographic zones: test duration, in weeks (4 by default). |
GET /api/v1/experiments/{key}/unit Get a switchback or geo test
The schedule of time slots or the randomized zones, locked at launch (a department-level test also says where the department comes from, departmentSource); the state (draft, waiting, collecting, read); once there are enough pairs, the traffic-weighted paired difference, ranges (Student, Fieller for the relative effect), a permutation test, and the signature. No effect figure until the state is read. A/B results (/results) for such a test return 409: read per visitor, they would give ranges that are too narrow. guardrails: guardrails read at the latest checkpoint reached, on the same pairs (relative range at the checkpoint’s level, breached when the whole range exceeds the limit: the test stops on its own); decision: the latest recorded decision, automatic for a guardrail stop, with its reason in note. target: the target relative effect, the plan’s minimum detectable effect, locked at launch (null without history); reading.futility: at a checkpoint before the last, the chance of reaching a conclusion by the end if the true difference were this effect, and futile below the threshold. This is a recommendation: nothing stops on its own for futility. Key: any management key (read access is enough).
GET /api/v1/metrics List metrics
The workspace’s catalog: type, event or goal, role, and guardrail rule. Key: any management key (read access is enough).
POST /api/v1/metrics Create a metric
An event sent by probant.track(…), or a no-code goal (click, form submission, page visit). Key: “read and write”, or a custom role that grants the “Create metrics” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
name | string | |
key · optional | string | Derived from the name if omitted. |
kind · optional | "proportion" | "mean" | Rate (proportion) or mean per visitor; a no-code goal is a rate. |
eventName · optional | string | Name passed to probant.track(…); defaults to the key. |
valueField · optional | string | Field summed for a mean; “value” by default. |
goal · optional | object | No-code goal: click, form submission, or page visit. |
role · optional | "primary" | "secondary" | "guardrail" | |
description · optional | string | |
direction · optional | "increase-is-bad" | "decrease-is-bad" | Direction that counts as a degradation, for a guardrail. |
guardrailLimit · optional | number | Worst tolerated degradation, as a fraction (0.05 = 5%). |
retention · optional | object | Retention metric: share of exposed visitors who come back, or send the event, between day N and day M after their first exposure. A visitor exposed less than M days ago is left out of the calculation. null: a regular metric. |
deferred · optional | object | CRM conversion (POST /api/v1/conversions, “eventName” key): qualified lead, closed deal. Rate, or amount per visitor (kind “mean”). A visitor exposed less than N days ago is left out of the calculation. null: a site metric. |
GET /api/v1/metrics/{key} Get a metric
One metric from the catalog. Key: any management key (read access is enough).
PATCH /api/v1/metrics/{key} Update a metric
What a metric measures (type, event, goal) can no longer change once a launched test decides on it (409). Key: “read and write”, or a custom role that grants the “Edit and delete metrics” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
name · optional | string | |
key · optional | string | Derived from the name if omitted. |
kind · optional | "proportion" | "mean" | Rate (proportion) or mean per visitor; a no-code goal is a rate. |
eventName · optional | string | Name passed to probant.track(…); defaults to the key. |
valueField · optional | string | Field summed for a mean; “value” by default. |
goal · optional | object | No-code goal: click, form submission, or page visit. |
role · optional | "primary" | "secondary" | "guardrail" | |
description · optional | string | |
direction · optional | "increase-is-bad" | "decrease-is-bad" | Direction that counts as a degradation, for a guardrail. |
guardrailLimit · optional | number | Worst tolerated degradation, as a fraction (0.05 = 5%). |
retention · optional | object | Retention metric: share of exposed visitors who come back, or send the event, between day N and day M after their first exposure. A visitor exposed less than M days ago is left out of the calculation. null: a regular metric. |
deferred · optional | object | CRM conversion (POST /api/v1/conversions, “eventName” key): qualified lead, closed deal. Rate, or amount per visitor (kind “mean”). A visitor exposed less than N days ago is left out of the calculation. null: a site metric. |
DELETE /api/v1/metrics/{key} Delete a metric
Rejected while a test uses it as its primary metric (409). Key: “read and write”, or a custom role that grants the “Edit and delete metrics” permission; never more than the permissions of the person who created the key.
POST /api/v1/multivariate-experiments Create a multivariate test
Multivariate test draft: 2 to 4 sections, each with 2 to 4 levels (the first is the original), and up to 16 combinations. Probant generates the full factorial design; each combination is a regular variant whose changes combine those of its levels. Same checks and sanitizing as the editor. Launch it with POST /experiments/{key}/launch: the factorial plan is computed from the workspace’s measured traffic (declared traffic otherwise), with a single look at the end, and interactions announces second-order interactions. Key: “read and write”, or a custom role that grants the “Prepare tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
name | string | Test name. |
hypothesis · optional | string | “If…, then…, because…”. |
zone · optional | string | Site zone: in exclusive mode (the default), two tests in the same zone don’t see the same visitor. |
urlPattern · optional | string | Targeted pages: “/products/*”, or a full URL. |
sections | object[] | 2 to 4 sections; up to 16 combinations, generated by Probant, each served as a variant. |
GET /api/v1/experiments/{key}/sections Get a multivariate test by section
Main effects of each section (marginal mean of each level, difference from the original, range), second-order interactions (difference of differences), Holm-adjusted p-values on the comparisons announced in the test plan, and a recommendation. Without a conclusive interaction, the recommendation follows the main effects. 409 for an A/B test. Key: any management key (read access is enough).
GET /api/v1/experiments/{key}/integration Build a variant into the site
A variant’s changes rewritten as code the site keeps, without the Probant script: a CSS stylesheet (styles, states, hidden elements, variant CSS) and a standalone, idempotent script (text, sanitized content, blocks, attributes, images, moves, and variant JavaScript as is). The script reproduces the test’s targeting (pages, devices, audience); when the test only targeted some visitors, it also carries the stylesheet and applies it to them only (css is then null), and warnings lists the audience criteria it can’t check (country, attributes). Also includes the files to put in assets/ and the layout/theme.liquid snippet for Shopify, instructions, and what would be better rewritten in the theme (fragile selectors included). Without variant, the one from the latest “roll out” decision. Once the code is live, stop the test or roll its rollout back to the original. Key: “read and write”, or a custom role that grants the “Roll out the winning variant” permission; never more than the permissions of the person who created the key.
?variant=: Key of the variant to build in (“b”); defaults to the one from the “roll out” decision.?format=: json (default), or a single file to save as is.
GET /api/v1/widgets List widget templates
Banner, countdown, pop-up, badge, reassurance, action bar, delivery date, social proof, promo code, free shipping: name, description, behavior loaded on demand (countdown, pop-up, delivery date, social proof, promo code, free shipping; an action bar that clicks a button on the page), parameters (JSON schema), and a complete example. Key: any management key (read access is enough).
POST /api/v1/widgets Generate a widget’s changes
A widget’s regular changes (an insert with sanitized HTML, then style changes applied through the CSSOM), with its warnings (contrast, past deadline) and its readable description. Nothing is saved: add them to a variant’s changes. Key: any management key (read access is enough).
| Field | Type | Description |
|---|---|---|
widget | object | Template (“banner”, “countdown”, “popin”, “badge”, “reassurance”, “actionbar”, “delivery”, “proof”, “promo”, “freeshipping”, “survey”) and its parameters; see GET /api/v1/widgets. |
target · optional | object | Element near which to insert a widget in the page (“inline” placement, badge, reassurance, social proof): a stable CSS selector and a position. |
POST /api/v1/experiments/{key}/widgets Add a widget to a draft
Generates the widget and adds it to the end of the variant’s changes, under a draft’s rules (sanitization, up to 50 changes per variant, audit log). A launched test can no longer be changed (409). Key: “read and write”, or a custom role that grants the “Prepare tests” permission; never more than the permissions of the person who created the key.
| Field | Type | Description |
|---|---|---|
widget | object | Template (“banner”, “countdown”, “popin”, “badge”, “reassurance”, “actionbar”, “delivery”, “proof”, “promo”, “freeshipping”, “survey”) and its parameters; see GET /api/v1/widgets. |
target · optional | object | Element near which to insert a widget in the page (“inline” placement, badge, reassurance, social proof): a stable CSS selector and a position. |
variant | string | Variant that receives the widget (not the original, “a”). |
Examples
Create a draft
curl -X POST https://getprobant.com/api/v1/experiments \
-H "Authorization: Bearer sk_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Estimated delivery date",
"zone": "Product page",
"urlPattern": "/products/*",
"variants": [
{ "key": "a", "name": "Original", "changes": [] },
{ "key": "b", "name": "Estimated date", "changes": [
{ "kind": "insert", "selector": "[data-add-to-cart]", "position": "afterend",
"html": "<p>Delivered within 2 business days</p>" }
] }
]
}'The first variant is the original, a, with no changes. Changes (insert, text, style, attribute, hide) go through the same sanitization as the editor’s.
Launch
curl -X POST https://getprobant.com/api/v1/experiments/estimated-delivery-date/launch \
-H "Authorization: Bearer sk_…" \
-H "Content-Type: application/json" \
-d '{ "metric": "commande", "baselineRate": 0.032, "dailyVisitors": 4000, "mde": 0.05 }'The declared baseline rate and traffic are used to calculate the test plan (visitors per variant, duration, checkpoints); a ready-made plan sent with the request isn’t used.
Read results
curl https://getprobant.com/api/v1/experiments/estimated-delivery-date/results \ -H "Authorization: Bearer sk_…" # The same report as CSV (“;” separator, decimal comma) curl "https://getprobant.com/api/v1/experiments/estimated-delivery-date/results?format=csv" \ -H "Authorization: Bearer sk_…" -o report.csv
decisionAuthorized is sample when the planned sample size is reached, look when a checkpoint boundary is crossed, and null otherwise: before that, a confidence interval that excludes 0 doesn’t allow a conclusion.
Responses and errors
| Code | Meaning |
|---|---|
200 | OK. |
201 | Created. |
202 | Accepted: feature flag change submitted as a request, or exposures received. |
400 | Unreadable body. |
401 | Missing, unknown, or revoked key. |
402 | Plan quota reached: no new tests start this month. |
403 | The key lacks this permission (read-only, orders key, or its creator left the workspace), or the call came from a web page. |
404 | Unknown test, metric, or feature flag in this workspace. |
409 | Conflict with the current state (test already launched or stopped, zone full, key taken…). |
413 | Body too large (512 KB max). |
422 | Input rejected by the interface’s rules; the message says which one. |
429 | Rate limit: 120 requests per minute per key. |
503 | Service temporarily unavailable. |
MCP server
Address: /api/v1/mcp, HTTP transport (“Streamable HTTP”), stateless. There are two ways to connect.
Without a key, through OAuth: in Claude, ChatGPT, or any assistant that connects this way, just add the address. The assistant opens Probant, where you sign in and choose the workspace and the access level (read, or read and write), never more than your own role. The assistant then appears in Settings → Integrations, where you can disconnect it. With Claude Code:
claude mcp add --transport http probant https://getprobant.com/api/v1/mcp
With a management key, for a script or an assistant that doesn’t support OAuth:
claude mcp add --transport http probant https://getprobant.com/api/v1/mcp \ --header "Authorization: Bearer sk_…"
For a client configured with a file:
{
"mcpServers": {
"probant": {
"type": "http",
"url": "https://getprobant.com/api/v1/mcp",
"headers": { "Authorization": "Bearer sk_…" }
}
}
}Launching, stopping, and rolling out can’t be undone. Called without confirm: true, these tools do nothing and return a preview (plan and duration, decision, steps and guardrails) to show the person; each one asks the assistant to call it again with confirm: true only after their explicit approval.
| Tool | What it does |
|---|---|
list_experiments | The workspace’s tests, newest first, with their status and exposed visitors. |
get_experiment | One test: hypothesis, targeting, variants and their changes, metric, test plan, and latest decision. |
get_results | A test’s results, computed by the statistics engine: visitors and conversions per variant, relative difference and range, verdict, traffic split check (SRM), and checkpoints. decisionAuthorized says whether the test plan allows a conclusion. If the workspace has turned it on, bayesian adds a Bayesian reading (probability of doing better, credible interval, expected loss) that doesn’t decide anything. A windowed metric (retention, or a CRM conversion: window.kind “deferred”) only counts visitors whose window has closed; window.pendingVisitors says how many are still waiting. Abnormal traffic split (srm.isMismatch): differences are masked (masked), and split gives the diagnosis by browser and device; don’t conclude anything until the cause is found. |
get_cohort | After a test stops, its exposed visitors are followed at day 30, 90, 180, and 365: net revenue collected, MRR, and still subscribed, with each variant’s difference and its simultaneous range. verdict says whether the decision made is reversed (reversed), holds (confirmed), is neither confirmed nor contradicted (consistent), or is waiting for a reading to close (pending). This is a descriptive estimate, not a new decision: never present it as a new winner. |
get_integration_code | Code that reproduces a variant without the Probant script: a CSS stylesheet, a standalone and idempotent script, assets/ files and a layout/theme.liquid snippet for Shopify, instructions, and changes that would be better rewritten in the theme. The script reproduces the test’s targeting (pages, devices, audience); warnings name the audience criteria it can’t check (country, attributes): tell the person. Without variant, the one from the “roll out” decision. Hand-written JavaScript is copied as is: tell the person. Remind them to stop the test (or roll its rollout back to the original) once the code is live. |
create_draft | Creates a draft test, as from the editor: variants (“a” = the original with no changes), sanitized changes, and targeting. A variant can carry a CSS stylesheet and a JavaScript script written by the person (kind “css” or “js”): don’t write JavaScript yourself; reuse the code they wrote and reviewed. Nothing is shown to visitors before launch. |
update_draft | Updates a draft; omitted fields keep their value. A launched test can no longer be changed. |
list_widget_templates | Banner, countdown, pop-up, badge, reassurance, action bar, delivery date, social proof, promo code, free shipping, and survey: expected parameters (JSON schema), a full example, and whether the template has a behavior (countdown, pop-up, delivery date, social proof, promo code, free shipping, and survey), loaded on demand on the site. |
render_widget | A widget’s changes (an insert with sanitized HTML, then its styles), with its warnings (contrast, past deadline) and a readable description. Saves nothing: add them to a variant’s changes with update_draft, or use add_widget_to_draft. |
add_widget_to_draft | Adds a widget at the end of the changes of a draft variant (not the original), with the editor’s rules. A launched test can no longer be changed. Show the widget’s description to the person. |
launch_experiment | Launches a draft: the test plan (visitors, duration) is recalculated from the declared traffic, and visitors start seeing the variants (an optimization test launches with launch_bandit). Use the “non-inferiority” or “equivalence” objective with a margin (margin) to show that a variant isn’t worse, or is equivalent: a single look, never a winner. Irreversible: without confirm: true, it only returns the test plan. Requires confirmation. |
pause_experiment | Pauses a running test: visitors see the original. It resumes on the same traffic slice with the same assignments. |
resume_experiment | Resumes a paused test. |
stop_experiment | Stops a test and records the decision: roll out a variant (ship), keep the original (keep-control), or abandon. A stopped test can’t restart. Irreversible: without confirm: true, it does nothing. Requires confirmation. |
deploy_variant | Gradually rolls out a variant of a running or stopped test (steps of 25, 50, and 100% by default), with guardrails monitored and automatic rollback. Records the decision to roll out. Irreversible: without confirm: true, it only returns the steps and guardrails. Requires confirmation. |
create_bandit | Creates a draft optimization test (multi-armed bandit): the same fields as a draft, plus the minimum share per variant, the equal-share learning phase, and, if needed, per-device shares (context: "device"). It maximizes conversions (or an amount per visitor) during the test (short promotion, headline); it doesn’t prove that one variant is better. Launch it with launch_bandit. |
launch_bandit | Launches an optimization draft: the metric to maximize (a rate, or an amount per visitor such as revenue), the share of the zone, and, if needed, its settings (otherwise the draft’s). Same rules as an A/B test: approval by another member when the workspace requires it, quota, and room in the zone. Irreversible: without confirm: true, it only returns what would be launched. Requires confirmation. |
get_bandit | Dated epochs and their shares, each variant’s current share, each variant’s difference from the original estimated epoch by epoch and then combined (the naive comparison is biased when shares change), an always-valid range, conversions (or amount) gained compared with equal shares, and the traffic split checked per epoch. Never declare a winner: conclusion is “better” or “worse” only if the range excludes zero, and it remains an indication to confirm with an A/B test. |
get_unit_test | A test whose randomized unit is the time slot (switchback: a price, stock) or the geographic zone (French departments, regions, countries): the schedule or zones locked at launch, the state, then the paired difference, ranges, and a permutation test. Until the state is “read”, there’s no effect figure: don’t conclude anything. “winner” or “loser” only if the signature says so. A department-level test carries `departmentSource`: if `available` is false, no one is assigned, and `explanation` says why. Guardrails (`guardrails`) are read at the latest checkpoint reached: “breached” stops the test on its own, and `decision.automatic` says so, with the reason in `decision.note`. |
list_metrics | The workspace’s metrics: type, event, role (primary, secondary, guardrail), and guardrail rule. |
create_metric | Creates a metric: an event sent by probant.track(…), a no-code goal (click, form, page), or a CRM conversion (deferred.windowDays: qualified lead, closed deal). |
update_metric | Updates a metric, identified by metricKey. What it measures can no longer change once a launched test decides on it. |
list_flags | The workspace’s feature flags: state (in preparation, on, off), type, last change, and pending request. |
get_flag | One feature flag: variations, “off” value, specific IDs, rules, default rule, and pending request. |
create_flag | Creates a feature flag in preparation, served to no one until it’s turned on. |
update_flag | Updates the name, description, or full definition. A change to what’s served is applied, or submitted as a request if the workspace requires approval. A rollout percentage can only go up. |
activate_flag | Turns on a feature flag: its rules decide what visitors see. Without confirm: true, it only returns what would be served. Requires confirmation. |
kill_flag | Emergency kill switch: the “off” value is served everywhere within 60 s. Without confirm: true, it does nothing. Requires confirmation. |
list_audiences | Audiences pushed by the workspace’s CDP: key (the one used by the “imported audience” criterion), name, source, member count, and last update received. Never the members themselves. |
get_audience | An imported audience’s name, source, size (member count), and the date of the last update received. |
create_audience | Creates an empty audience that waits for members sent by the CDP (API or Segment). Its key is the CDP’s key. |
rename_audience | Changes the name shown in Probant; the key, which tests target, doesn’t change. |
delete_audience | Deletes the audience and all its members; rejected while a test that hasn’t stopped or a personalization that hasn’t ended targets it. Irreversible: without confirm: true, it does nothing. Requires confirmation. |
create_multivariate_test | Creates a draft multivariate test: 2 to 4 sections (headline, photo, badge…), each with 2 to 4 levels where the first is the original, and up to 16 combinations. Probant generates the combinations, served as variants. Two levels from different sections must not change the same element: launch rejects it. Launch it with launch_experiment (interactions: true to announce interactions). |
get_sections | Main effects of each section, second-order interactions, and the recommendation for a multivariate test, Holm-corrected on the announced comparisons. Don’t declare a best combination beyond the recommendation: without a conclusive interaction, it follows the main effects. |
list_automations | The workspace’s automations (conversion drop, post-launch check, overdue test, weekly report): on or off, settings, and the latest run and its findings. |
run_automation | Runs an automation now, like the daily morning task, and returns its findings (figures computed by the engine). Without send, nothing is sent; with send: true, justified alerts go to the workspace’s destinations, once per finding. |
configure_automation | Turns an automation on or off, or changes its settings. When it’s turned on, if no destination receives its alerts yet, all of the workspace’s destinations subscribe to it. |
analyze_page | Reads a public page like the editor does (no cookies, public hosts only) and returns its summary: title, headings, buttons, forms, collapsed sections, and markers present or missing (shipping, reviews, payment…), along with the workspace’s zones, themes, and ideas already noted. The basis for add_ideas. |
add_ideas | Saves ideas as “To evaluate”: title, zone, themes, effort (S, M, L), observation (what the page shows, quoted in “”), hypothesis (“If…, then…, because…”). Don’t include any gain or duration figures: an idea that states one is rejected, and Probant returns, for each idea, the expected effect and the test duration computed by its engine. |
Alerts: Slack and signed webhooks
In Settings, “Integrations” tab, the owner adds a Slack incoming webhook or an HTTPS address to receive alerts, and chooses which ones. Each alert is sent only once.
experiment.decision: The test plan allows a conclusion: planned sample size reached or a checkpoint boundary crossed.experiment.srm: Visitors aren’t split between variants as planned (SRM). Results are on hold.rollout.rolled_back: A rollout went back to the original on its own because a guardrail exceeded its limit.rollout.advanced: A rollout set to advance automatically moved to the next step: minimum duration elapsed with no guardrail crossed and, for 100%, every range within its limit.quota.reached: This month’s tested visitors reached the plan quota. No new test can start.experiment.interaction: Two overlapping tests interact: the effect of one changes with the variant of the other.experiment.fading: The lift of a running test weakens clearly after the first days. A novelty effect may be inflating the result.flag.killed: A feature flag was turned off in an emergency. Its “off” value is served everywhere at the next SDK refresh.bandit.floor: In an optimization test, a variant has received only its minimum share for seven days. Consider stopping it or switching to a rollout.experiment.display: A change in a running test isn’t applied for part of the exposed visitors, from day one (wrong selector, different page) or after a site change (theme update, edited page).automation.drop: Automation: yesterday, a conversion rate or tracked traffic fell more than the previous week’s variation explains.automation.launch-check: Automation: one day after a test launches, it checks exposures, traffic split, how each variant displays, and conversions.automation.stale: Automation: a running test has passed the duration in its test plan without reaching its planned sample size.automation.report: Automation: every week, your running tests, their progress, and the decisions of the past seven days.automation.checkpoint: Automation: a running test reaches a checkpoint in its test plan. The alert says whether the boundary is crossed (you can conclude) or the test should continue.automation.guardrails: Automation: every morning, the guardrails of running rollouts are checked at the current step. It alerts you when they’re shown to be within limits, or when one leans the wrong way.automation.segment-srm: Automation: the visitor split between variants departs from the plan for a device, new or returning visitors, or a traffic source, after correcting for multiple comparisons.automation.quota: Automation: this month’s tested visitors pass the share of the plan you chose, before the quota blocks new tests.automation.flag-debt: Automation: once a week, the feature flags your code can drop (same value for everyone, turned off, test ended, no longer evaluated).automation.survey: Automation: every morning, the number of open-ended answers your test surveys received the day before, to read in Probant (the alert contains no answer text).
A webhook receives a JSON POST, signed with the secret shown once when you add the destination:
POST https://example.com/webhooks/probant
Content-Type: application/json
Probant-Event: experiment.decision
Probant-Delivery: 0b6f…
Probant-Signature: t=1794299400,v1=5d1c…
{
"id": "0b6f…",
"type": "experiment.decision",
"occurredAt": "2026-11-10T08:30:00.000Z",
"workspace": { "slug": "maison-lune", "name": "Maison Lune" },
"title": "“Estimated delivery date”: B beats the original.",
"text": "Estimated lift +6.1% on “Order”…",
"url": "https://getprobant.com/experiences/estimated-delivery-date",
"data": { "experiment": { "key": "…", "name": "…" }, "verdict": "winner", "effect": { "relative": 0.061, "lower": 0.012, "upper": 0.11 } }
}The signature is an HMAC-SHA256 of “timestamp.raw body.” Verify it in constant time, and reject a timestamp older than 5 minutes:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyProbant(secret: string, header: string, rawBody: string): boolean {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // too old: replayed?
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
const given = Buffer.from(v1 ?? "", "hex");
return given.length === expected.length && timingSafeEqual(expected, given);
}Probant sends only to public HTTPS addresses, doesn’t follow redirects, and waits at most 5 seconds for a response. A failed delivery isn’t retried: the “Integrations” tab shows the error, and “Send a test” checks the destination.
Server helper package
Server-side assignment (assignRequest) is available as a dependency-free package, @probant/edge, for Next.js, Cloudflare Workers, or any server that speaks fetch. Neither this package nor its PHP, Python, Ruby, and Go equivalents are published on their registries yet: copy the server helper into your project, as described in the installation documentation.
// proxy.ts (Next.js 16), at the root of your site
import { NextResponse, type NextRequest } from "next/server";
import { assignRequest, assignedVariants, createConfigLoader } from "@probant/edge";
const loadConfig = createConfigLoader({ key: process.env.NEXT_PUBLIC_PROBANT_KEY!, endpoint: "https://getprobant.com" });
export async function proxy(request: NextRequest) {
const config = await loadConfig();
if (!config) return NextResponse.next();
const { setCookie, assignments } = assignRequest({
cookieHeader: request.headers.get("cookie"),
url: request.url,
userAgent: request.headers.get("user-agent"),
config,
consent: request.cookies.get("consent")?.value === "yes",
});
const headers = new Headers(request.headers);
headers.set("x-probant-variants", JSON.stringify(assignedVariants(assignments)));
const response = NextResponse.next({ request: { headers } });
if (setCookie) response.headers.append("set-cookie", setCookie);
return response;
}createConfigLoader keeps the configuration in memory for one minute and serves the previous one if Probant doesn’t respond, so your site doesn’t wait for Probant.
The same helper exists in PHP, Python, Ruby, and Go, with no dependencies and the same configuration cache. Each package is checked against the reference implementation on thousands of shared cases: same visitor, same variant as the script.
Conclusive Or not yet…
Create your workspace, or explore the demo first with data from a fictional store.
- Open demo, no sign-up
- Free to start, no overage charges
- Database and compute in Amsterdam, with Scaleway