/v1/contextual-bandit-queries— the assignment SQL plus the targeting (context) columns the bandit splits on. This is the bandit-specific replacement for the experiment assignment query./v1/contextual-bandits— the bandit itself, which references a query by id.
1. Create a Contextual Bandit Query
A Contextual Bandit needs a query that defines the assignment SQL and the targeting-attribute columns it splits on.targetingAttributeColumns is required and must be non-empty — a bandit with no context to split on is not a contextual bandit. Each column name must be a valid SQL identifier and match a non-archived targeting attribute in your organization (Settings → Attributes), otherwise the request fails with a 400. Each column must also be selected by the query; this is not validated on create, but the snapshot will fail at run time if it is missing. The query should also select the leaf_id, bandit_version, and variation_weights columns logged by your tracking callback — without them the SRM health check is skipped.
Create the query with POST /v1/contextual-bandit-queries:
cbq_…). You can manage queries with these endpoints:
GET /v1/contextual-bandit-queries— list queries. Pass?datasourceId=ds_abcto scope by datasource.GET /v1/contextual-bandit-queries/:id— get one query.PUT /v1/contextual-bandit-queries/:id— update a query.DELETE /v1/contextual-bandit-queries/:id— delete a query.
2. Create the Contextual Bandit
Reference the query from the previous step viacontextualBanditQueryId. The full request schema is in the create Contextual Bandit reference.
name, trackingKey, datasource, contextualBanditQueryId, variations, decisionMetric, and contextualAttributes. Like targetingAttributeColumns, every contextualAttributes entry must match an organization targeting attribute; entries not present in the query’s targetingAttributeColumns are dropped at run time rather than rejected on create. Another crucial field is hashAttribute, which is the attribute used to randomize users into variations, which should map to the user identifier in your assignment query. For short-lived bandits, we suggest a session_id or device_id that will re-generate on future visits to ensure you get the most power while managing when a session ends yourself.
The response includes the new Contextual Bandit id (cb_…). The CB is provisioned in status: "draft" with an empty currentLeafWeights array at the document root; it stays empty until the first successful refresh.
3. Link a Feature Flag
A Contextual Bandit serves its variations through acontextual-bandit-ref rule on a Feature Flag, and it needs at least one linked Feature Flag before it can start. Create the flag first with POST /v2/features, then link it to the contextual bandit:
variations must cover every Contextual Bandit variation exactly once — variationId is the id of each entry in the CB’s variations array, not its key. Targeting (condition, saved groups, prerequisites, coverage) lives on the Contextual Bandit and is inherited by the rule, so those fields are not accepted here.
The rule is appended to the bottom of the flag’s rule list in a new draft revision, which auto-publishes when you start the Contextual Bandit. Pass "autoPublish": true to publish it right away, or "draftVersion": 7 to add the rule to an existing draft instead of starting a new one. By default the rule applies to every environment; pass "allEnvironments": false with "environments": ["production"] to narrow it.
Response:
GET /v1/contextual-bandits/:id/linked-features— the linked flags enriched with live/draft state, per-environment rule state, and variation values (the same payload the CB detail page renders).DELETE /v1/contextual-bandits/:id/linked-feature/:featureId— removes everycontextual-bandit-refrule pointing at this bandit from the flag and drops the linkage. Like the POST, the removal lands in a draft unless you pass?autoPublish=true. When the flag has no such rule left, only the linkage is cleared.
4. Start the Contextual Bandit
Move the CB out ofdraft (to status: "running") so it is eligible for refresh with POST /v1/contextual-bandits/:id/start:
{ "contextualBandit": { … } }.
5. Refresh — run a snapshot
POST /v1/contextual-bandits/:id/refresh triggers the contextual bandit update. Each update runs the warehouse query and the stats engine and updates weights. Each successful update that changes weights increments the CB’s banditVersion.
6. Read the latest results
GET /v1/contextual-bandits/:id/results returns the same payload the GrowthBook UI uses to render the CB results table — the latest stats engine output plus a snapshot-status summary.
contextualBanditSnapshot and latest are each null until a run exists):
latest.status is one of running, success, or error, and latest.runStarted may be null if the run hasn’t started.
Use the companion endpoints for finer-grained inspection:
GET /v1/contextual-bandits/:id/snapshots— list recent snapshot runs (optional?limit=, max 100).GET /v1/contextual-bandits/:id/snapshots/:snapshotId— single snapshot status.GET /v1/contextual-bandits/:id/events— list bandit event outputs, one per successful snapshot (optional?limit=, max 100).GET /v1/contextual-bandits/:id/events/:eventId— single contextual bandit event.GET /v1/contextual-bandits/:id/current— current root-levelcurrentLeafWeightsplus the latest event object (latestEvent, ornull).
/current response looks like:
7. Stop the Contextual Bandit
When you’re done, stop the CB withPOST /v1/contextual-bandits/:id/stop:
start, this returns the updated CB under contextualBandit.
Standard CRUD
Both resources also expose the default CRUD endpoints:GET /v1/contextual-bandits— filter with?projectId=,?datasourceId=, or?trackingKey=.GET /v1/contextual-bandits/:idPOST /v1/contextual-banditsPUT /v1/contextual-bandits/:idDELETE /v1/contextual-bandits/:idGET /v1/contextual-bandit-queries— filter with?datasourceId=.GET /v1/contextual-bandit-queries/:idPOST /v1/contextual-bandit-queriesPUT /v1/contextual-bandit-queries/:idDELETE /v1/contextual-bandit-queries/:id
Permissions cheat sheet
Permissions are checked directly against the Contextual Bandit doc (CBs no longer delegate RBAC to a paired experiment):- Read (
GET results,GET current,GET snapshot(s),GET event(s),GET /:id,GET /) —canReadSingleProjectResource(cb.project). - Create (
POST /) —canCreateContextualBandit(cb). - Update (
PUT /:id) —canUpdateContextualBandit(existing, updated). - Run (
POST refresh,POST start,POST stop) —canRunContextualBandit(cb, environments). - Delete (
DELETE /:id) —canDeleteContextualBandit(cb). - Link / unlink a Feature Flag (
POST/DELETE /:id/linked-feature/:featureId) —canUpdateContextualBanditon the bandit, pluscanEditFeatureDraftson the Feature Flag. WhenautoPublishis set,canPublishFeature(scoped to the rule’s environments) is also required.
canUpdateDataSourceSettings on the query’s datasource.
Plan gate (hasPremiumFeature("contextual-bandits")) is checked first on every endpoint and short-circuits with 402 before any data access.
