Response Format
All endpoints return a consistent JSON envelope:
{
"success": true,
"data": { ... },
"error": "...",
"timestamp": "2026-01-15T10:00:00.000Z"
}Authentication
The API supports two authentication methods depending on the endpoint:
Authorization: Bearer <key> header or x-api-key header. Used by the SDK for assignment and event endpoints.Limits & Quotas
Bandit doesn't enforce per-second rate limits today. Volume is metered by monthly event quotas with soft overages, and individual requests are bounded by payload-size and batch-size limits.
Request size
POST /assignmentsandPOST /events: max body size 100 KB. Over the limit returns 413.POST /events/batch: up to 1,000 events per request. Larger batches must be split client-side.
Monthly event quotas
- Starter — 10,000 events / month. Traffic pauses at the cap until the next cycle or an upgrade.
- Pro — 500,000 events / month. Soft cap; additional events billed at $0.10 / 1K.
- Scale — 5,000,000 events / month. Soft cap; additional events billed at $0.06 / 1K.
- Enterprise — uncapped, custom volume pricing.
Current quotas track the figures on the pricing page.
Recommended client practices
- Use
POST /events/batchfor high-volume tracking instead of one request per event. - The JavaScript SDK batches events automatically — prefer it over hand-rolled fetches.
- Retry on 500 with exponential backoff. Do not retry on 400 / 401 / 413 — those are caller errors.
Assignments
Get treatment assignments for users in experiments
/assignmentsCreate assignment
Request a treatment assignment for a user in an experiment. The bandit algorithm selects the optimal treatment based on historical performance. If the user already has a cached assignment, it is returned instead.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| experimentId | string | required | ID of the experiment to assign |
| userId | string | required | Unique identifier for the user |
| context | Record<string, string | number | boolean> | optional | Optional context for contextual bandit algorithms (e.g. device type, location) |
Responses
Example
Request
{
"experimentId": "exp_abc123",
"userId": "user_42",
"context": {
"deviceType": "mobile",
"country": "US"
}
}Response
{
"success": true,
"data": {
"assignmentId": "asgn_xyz789",
"treatmentId": "trt_def456",
"treatment": {
"name": "Headline B",
"config": {
"content": "Start your free trial today"
}
},
"confidence": 0.87
},
"timestamp": "2026-01-15T10:30:00.000Z"
}/assignments/:experimentIdGet assignment
Retrieve a treatment assignment for a user via query parameters. Functionally equivalent to the POST endpoint but useful for simple GET-based integrations.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experimentId | string | required | ID of the experiment |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| userId | string | required | Unique identifier for the user |
| context | string (JSON) | optional | JSON-encoded context object for contextual bandits |
Responses
Events
/api/eventsTrack event
Track a single named event with optional value and metadata. Events are free-form — which event names count as rewards and trigger bandit algorithm updates is configured per experiment in the dashboard. Requires API key authentication.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | required | Free-form event name (e.g. "purchase", "signup", "button_click"). Configure which names count as rewards in the dashboard. |
| category | string | optional | Optional category to group related events |
| sessionId | string | required | Session identifier for tracking event attribution |
| value | number | optional | Numeric value for the event (e.g. revenue amount) |
| metadata | Record<string, any> | optional | Arbitrary key-value metadata to attach to the event |
Responses
Example
Request
{
"name": "purchase",
"category": "conversion",
"sessionId": "sess_abc123",
"value": 29.99,
"metadata": {
"product": "premium-plan",
"coupon": "SAVE10"
}
}Response
{
"success": true,
"data": {
"tracked": 1
},
"timestamp": "2026-01-15T10:30:00.000Z"
}/api/events/batchTrack events (batch)
Track multiple events in a single request for better performance and reduced network overhead. Accepts 1-1000 events per batch. The SDK automatically batches events — prefer it over hand-rolled batch requests. Requires API key authentication.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| events | Array<Event> | required | Array of 1-1000 event objects. Each event has: name (required string), category (optional string), sessionId (required string), value (optional number), metadata (optional object). |
Responses
Example
Request
{
"events": [
{
"name": "page_view",
"category": "engagement",
"sessionId": "sess_abc123",
"metadata": {
"page": "/pricing"
}
},
{
"name": "button_click",
"category": "interaction",
"sessionId": "sess_abc123",
"metadata": {
"button": "cta-primary"
}
},
{
"name": "purchase",
"category": "conversion",
"sessionId": "sess_abc123",
"value": 49.99,
"metadata": {
"product": "pro-plan"
}
}
]
}Response
{
"success": true,
"data": {
"tracked": 3
},
"timestamp": "2026-01-15T10:30:00.000Z"
}/events/:experimentIdList events
Retrieve event history for a specific experiment. Results are paginated with limit and offset query parameters.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experimentId | string | required | ID of the experiment |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| limit | number | optional | Maximum number of events to return (default: 100) |
| offset | number | optional | Number of events to skip (default: 0) |
Responses
Experiments
Create and manage experiments
/experimentsList experiments
Retrieve all experiments belonging to the authenticated company. Returns experiments with their treatments.
Responses
/experimentsCreate experiment
Create a new experiment in DRAFT status. Optionally include initial treatments. The experiment must be activated separately via PUT after adding at least one treatment.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | required | Human-readable name for the experiment |
| description | string | optional | Description of the experiment goals |
| algorithm | BanditAlgorithm | required | Algorithm to use: THOMPSON_SAMPLING or CONTEXTUAL_LINEAR |
| forgetting | ForgettingConfig | optional | How the algorithm forgets old results. { mode: 'none' } (default) counts every result forever; { mode: 'discounted', halfLifeMs } halves a result's weight every half-life; { mode: 'sliding_window', windowMs } counts only results inside the window. |
| contentType | ContentType | optional | Content type of treatments (default: PLAIN_TEXT) |
| algorithmConfig | Record<string, any> | optional | Algorithm-specific configuration |
| assignmentTtl | number | optional | Assignment cache TTL in seconds (default: 86400 = 24 hours) |
| treatments | Treatment[] | optional | Optional initial treatments to create with the experiment |
Responses
Example
Request
{
"name": "Homepage Headline Test",
"description": "Test different headlines on the landing page",
"algorithm": "THOMPSON_SAMPLING",
"forgetting": {
"mode": "discounted",
"halfLifeMs": 604800000
},
"treatments": [
{
"name": "Control",
"config": {
"content": "Welcome to our platform"
}
},
{
"name": "Urgency",
"config": {
"content": "Limited time offer - start free today!"
}
}
]
}Response
{
"success": true,
"data": {
"id": "exp_abc123",
"name": "Homepage Headline Test",
"algorithm": "THOMPSON_SAMPLING",
"status": "draft",
"treatments": [
"..."
]
},
"timestamp": "2026-01-15T10:00:00.000Z"
}/experiments/:idUpdate experiment
Update experiment properties including name, description, status, and TTL. To activate an experiment, set status to ACTIVE. Activation requires at least one treatment.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the experiment to update |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | optional | Updated name |
| description | string | optional | Updated description |
| status | ExperimentStatus | optional | New status: DRAFT, ACTIVE, PAUSED, COMPLETED, or ARCHIVED |
| assignmentTtl | number | optional | Updated assignment TTL in seconds |
| endAt | string (ISO 8601) | optional | Scheduled end date for the experiment |
Responses
/experiments/:idGet experiment
Retrieve a single experiment by ID, including all associated treatments and their configurations.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the experiment |
Responses
/experiments/:id/generate-variantAI generate variant
Use AI to generate a new treatment variant based on the experiment's existing treatments and performance data. The AI analyzes what's working and generates a new variant using one of eight strategies (emotional appeal, urgency, social proof, etc.).
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the experiment |
Responses
/experiments/:id/refreshRefresh algorithm state
Rebuild the in-memory algorithm state from the event history in the database. Useful after manual data changes or if algorithm state has drifted.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the experiment |
Responses
/experiments/:id/treatmentsAdd treatment
Add a new treatment (variant) to an experiment. Each treatment has a name, content configuration, optional weight for traffic allocation, and an optional control flag.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the experiment |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | required | Name of the treatment variant |
| description | string | optional | Description of this variant |
| config | { content: string, metadata?: Record<string, any> } | required | Treatment configuration with content and optional metadata |
| weight | number | optional | Traffic weight (default: 1.0) |
Responses
/experiments/:id/treatments/:treatmentIdRemove treatment
Remove a treatment from an experiment. The treatment must belong to the specified experiment.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the experiment |
| treatmentId | string | required | ID of the treatment to remove |
Responses
Funnels
Create and manage multi-step experiment funnels
/funnelsList funnels
Retrieve all funnels belonging to the authenticated company. Each funnel links multiple experiments into a sequential conversion path.
Responses
/funnelsCreate funnel
Create a new funnel linking one or more experiments into a multi-step conversion path. Requires at least one experiment ID.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | required | Human-readable name for the funnel |
| description | string | optional | Description of the funnel goals |
| experimentIds | string[] | required | Ordered list of experiment IDs that form the funnel steps (minimum 1) |
Responses
Example
Request
{
"name": "Onboarding Flow",
"description": "Test headline → CTA → pricing across the onboarding funnel",
"experimentIds": [
"exp_headline",
"exp_cta",
"exp_pricing"
]
}Response
{
"success": true,
"data": {
"id": "fnl_abc123",
"name": "Onboarding Flow",
"description": "Test headline → CTA → pricing across the onboarding funnel",
"experimentIds": [
"exp_headline",
"exp_cta",
"exp_pricing"
],
"status": "draft"
},
"timestamp": "2026-01-15T12:00:00.000Z"
}/funnels/:idGet funnel
Retrieve a single funnel by ID, including details about its linked experiments and their current status.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the funnel |
Responses
/funnels/:idUpdate funnel
Update funnel properties such as name, description, or status.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the funnel to update |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | optional | Updated name |
| description | string | optional | Updated description |
| status | string | optional | New status: draft, active, paused, or completed |
Responses
/funnels/:idDelete funnel
Delete a funnel. This does not delete the linked experiments, only the funnel grouping.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the funnel to delete |
Responses
/funnels/:id/rewardRecord funnel reward
Record a reward (conversion value) for a user who completed the funnel. Updates the bandit algorithm state for all experiments in the funnel based on the treatments the user was assigned. Each user can only have one reward per funnel.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the funnel |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| userId | string | required | ID of the user who completed the funnel |
| value | number | required | Reward value (must be >= 0, e.g. revenue amount) |
Responses
Example
Request
{
"userId": "user_42",
"value": 49.99
}Response
{
"success": true,
"data": {
"funnelId": "fnl_abc123",
"userId": "user_42",
"value": 49.99
},
"timestamp": "2026-01-15T14:00:00.000Z"
}Bundles
Simultaneous multi-experiment assignments with joint optimization across all slots. The bundle bandit learns combinations of treatments that maximize the collective reward.
/bundles/:id/rewardRecord bundle reward
Record a reward (conversion value) for a user who completed a bundle conversion. Updates the bandit algorithm state for all experiments in the bundle based on the combination of treatments the user was served. Each user can only have one reward per bundle within the conversion window.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | required | ID of the bundle |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| userId | string | required | ID of the user who completed the bundle conversion |
| value | number | required | Reward value (must be >= 0, e.g., revenue amount in dollars) |
Responses
Example
Request
{
"userId": "user_42",
"value": 49.99
}Response
{
"success": true,
"data": {
"experimentsUpdated": 2
},
"timestamp": "2026-08-26T19:35:00.000Z"
}/bundles/assignmentGet bundle assignment
Request assignments for all experiments in a bundle. The bundle bandit algorithm selects the optimal combination of treatments across all slots based on joint performance. If the user already has a live bundle assignment, it is returned (stickiness).
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| bundleId | string | required | ID of the bundle to assign |
| userId | string | required | Unique identifier for the user |
| context | Record<string, string | number | boolean> | optional | Optional context shared across all slots in the bundle |
| sessionId | string | optional | Session ID for event attribution (auto-generated by SDK) |
Responses
Example
Request
{
"bundleId": "bnd_abc123",
"userId": "user_42",
"context": {
"deviceType": "mobile",
"country": "US"
}
}Response
{
"success": true,
"data": {
"bundleAssignmentId": "ba_xyz789",
"slots": {
"homepage-headline": {
"assignmentId": "asgn_h1",
"experimentId": "exp_headline",
"treatmentId": "trt_urgency",
"config": {
"content": "Limited time offer!"
}
},
"cta-button": {
"assignmentId": "asgn_cta",
"experimentId": "exp_cta",
"treatmentId": "trt_green",
"config": {
"content": "Start Free Trial",
"color": "#00FF00"
}
}
}
},
"timestamp": "2026-08-26T19:30:00.000Z"
}Identity
Link anonymous user IDs to identified users (email, account ID) with attribution
/identifyLink anonymous user to identified user
Link an anonymous user ID to a durable identity (email, account ID). All historical assignments and events for the anonymous ID are re-attributed to the identified user. Future calls to getAssignment() with either ID will resolve to the same assignment. Requires admin authentication with a personal access token (PAT), not an API key.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| anonymousId | string | required | Temporary or anonymous user ID (e.g., crypto.randomUUID()) |
| identifiedUserId | string | required | Permanent user ID (e.g., email, database ID) |
| mergeReason | string | optional | Why the identification is happening: user_signup, email_verification, social_login, account_recovery, or manual (default: manual) |
Responses
Example
Request
{
"anonymousId": "anon_550e8400-e29b-41d4-a716-446655440000",
"identifiedUserId": "user@example.com",
"mergeReason": "user_signup"
}Response
{
"success": true,
"data": {
"anonymousId": "anon_550e8400-e29b-41d4-a716-446655440000",
"identifiedUserId": "user@example.com",
"alreadyMapped": false
},
"timestamp": "2026-08-26T19:30:00.000Z"
}