Experiments API
The Experiments API exposes the same canary and shadow experiments managed in the Zeus dashboard, behind a stable contract. Three endpoints: list, create, and rollback.
All write endpoints require the safety header X-Floopy-Confirm: experiments. This is a deliberate, low-cost gate (no new auth scope) that blocks accidental and drive-by abuse from a leaked key.
Authentication and gating (all three endpoints)
Section titled “Authentication and gating (all three endpoints)”Authorization: Bearer <your-floopy-api-key>- Permission:
read_permissionforGET,write_permissionforPOST. - Plan: Pro only (
has_test_ab). - Behind the per-org canary kill switch.
GET /v1/experiments
Section titled “GET /v1/experiments”List experiments for the caller’s organisation.
Endpoint
Section titled “Endpoint”GET https://api.floopy.ai/v1/experimentsResponse (200)
Section titled “Response (200)”[ { "id": "5b3f9c1e-7a2b-4c3d-9e1f-0a1b2c3d4e5f", "type": "canary", "status": "active", "traffic_pct": 5, "baseline": { "provider": "openai", "model": "gpt-5.4" }, "candidate": { "provider": "openai", "model": "gpt-5.4-mini" }, "started_at": "2026-05-04T09:00:00Z", "ended_at": null, "summary": null }]| Field | Type | Description |
|---|---|---|
id | UUID | Experiment id. |
type | string | canary or shadow. |
status | string | draft, active, completed, or rolled_back. |
traffic_pct | number | Percentage of eligible traffic on the candidate (0..=100). |
baseline.provider, baseline.model | string | The control side. |
candidate.provider, candidate.model | string | The variant side. |
started_at | ISO8601 | null | When the experiment was activated. |
ended_at | ISO8601 | null | When the experiment ended (or was rolled back). |
summary | object | null | Aggregate quality and cost summary, populated after the experiment ends. |
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
403 | read_permission / plan_required | Permission or plan flag missing. |
5xx | internal | Upstream failure. |
Curl example
Section titled “Curl example”curl -s -H "Authorization: Bearer $FLOOPY_API_KEY" \ "https://api.floopy.ai/v1/experiments" | jq .POST /v1/experiments
Section titled “POST /v1/experiments”Create a new canary or shadow experiment. Body cap: 8 KiB.
Endpoint
Section titled “Endpoint”POST https://api.floopy.ai/v1/experimentsRequired headers
Section titled “Required headers”Authorization: Bearer <your-floopy-api-key>Content-Type: application/jsonX-Floopy-Confirm: experimentsRequest body
Section titled “Request body”{ "type": "canary", "baseline": { "provider": "openai", "model": "gpt-5.4" }, "candidate": { "provider": "openai", "model": "gpt-5.4-mini" }, "traffic_pct": 5, "stop_rule": { "max_regression": 0.02, "min_n": 200 }}| Field | Type | Required | Constraints |
|---|---|---|---|
type | string | Yes | canary or shadow. |
baseline.provider | string | Yes | Existing provider id. |
baseline.model | string | Yes | Existing model id. |
candidate.provider | string | Yes | Existing provider id. Must differ from baseline pair. |
candidate.model | string | Yes | Existing model id. |
traffic_pct | number | Yes | 0..=100. |
stop_rule.max_regression | number | No | Composite-quality drop ceiling, [0, 0.5]. |
stop_rule.min_n | integer | No | Minimum sample count before the experiment can complete. |
The body is deny_unknown_fields — unknown keys return 400 invalid_body.
Response (201)
Section titled “Response (201)”Returns the created row, identical shape to a GET item.
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
400 | missing_confirmation | X-Floopy-Confirm: experiments missing or wrong value. |
400 | invalid_body | Schema validation failed, or unknown keys. |
400 | invalid_traffic_pct | traffic_pct outside [0, 100]. |
403 | write_permission / plan_required | Permission or plan flag missing. |
409 | active_already_exists | An active experiment already exists for the same (baseline, candidate) pair. |
429 | rate_limited | Over per-org budget. |
5xx | internal | Upstream failure. |
Curl example
Section titled “Curl example”curl -s -X POST \ -H "Authorization: Bearer $FLOOPY_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Floopy-Confirm: experiments" \ -d '{ "type": "canary", "baseline": {"provider":"openai","model":"gpt-5.4"}, "candidate": {"provider":"openai","model":"gpt-5.4-mini"}, "traffic_pct": 5, "stop_rule": {"max_regression": 0.02, "min_n": 200} }' \ "https://api.floopy.ai/v1/experiments"POST /v1/experiments/{id}/rollback
Section titled “POST /v1/experiments/{id}/rollback”Force a running experiment back to its baseline immediately.
Endpoint
Section titled “Endpoint”POST https://api.floopy.ai/v1/experiments/{id}/rollbackRequired headers
Section titled “Required headers”Authorization: Bearer <your-floopy-api-key>X-Floopy-Confirm: experimentsNo request body.
Response (200)
Section titled “Response (200)”Returns the experiment row in rolled_back state. Idempotent — calling rollback on an already-rolled-back experiment returns 200 with the existing row, not 409.
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
400 | missing_confirmation | Confirmation header missing or wrong. |
403 | write_permission / plan_required | Permission or plan flag missing. |
404 | not_found | No experiment with that id in the caller’s organisation. Identical bytes for cross-tenant lookups. |
409 | completed_cannot_rollback | The experiment is already completed and cannot be rolled back. |
5xx | internal | Upstream failure. |
Curl example
Section titled “Curl example”EXP_ID="5b3f9c1e-7a2b-4c3d-9e1f-0a1b2c3d4e5f"curl -s -X POST \ -H "Authorization: Bearer $FLOOPY_API_KEY" \ -H "X-Floopy-Confirm: experiments" \ "https://api.floopy.ai/v1/experiments/$EXP_ID/rollback" | jq .Audit trail
Section titled “Audit trail”Every experiment lifecycle event (created, started, rolled_back, completed, error) is persisted to routing_experiment_events with actor_api_key_id. These events surface in the decisions export sidecar and the Zeus Experiments page. The experiment_rolled_back audit event is never throttled — every rollback produces a row.
See also
Section titled “See also”- GET /v1/constraints — set the safety envelope the experiments must respect.
- GET /v1/decisions — query individual decisions tagged by experiment id.