Skip to content

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_permission for GET, write_permission for POST.
  • Plan: Pro only (has_test_ab).
  • Behind the per-org canary kill switch.

List experiments for the caller’s organisation.

GET https://api.floopy.ai/v1/experiments
[
{
"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
}
]
FieldTypeDescription
idUUIDExperiment id.
typestringcanary or shadow.
statusstringdraft, active, completed, or rolled_back.
traffic_pctnumberPercentage of eligible traffic on the candidate (0..=100).
baseline.provider, baseline.modelstringThe control side.
candidate.provider, candidate.modelstringThe variant side.
started_atISO8601 | nullWhen the experiment was activated.
ended_atISO8601 | nullWhen the experiment ended (or was rolled back).
summaryobject | nullAggregate quality and cost summary, populated after the experiment ends.
StatusCodeWhen
403read_permission / plan_requiredPermission or plan flag missing.
5xxinternalUpstream failure.
Terminal window
curl -s -H "Authorization: Bearer $FLOOPY_API_KEY" \
"https://api.floopy.ai/v1/experiments" | jq .

Create a new canary or shadow experiment. Body cap: 8 KiB.

POST https://api.floopy.ai/v1/experiments
Authorization: Bearer <your-floopy-api-key>
Content-Type: application/json
X-Floopy-Confirm: experiments
{
"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 }
}
FieldTypeRequiredConstraints
typestringYescanary or shadow.
baseline.providerstringYesExisting provider id.
baseline.modelstringYesExisting model id.
candidate.providerstringYesExisting provider id. Must differ from baseline pair.
candidate.modelstringYesExisting model id.
traffic_pctnumberYes0..=100.
stop_rule.max_regressionnumberNoComposite-quality drop ceiling, [0, 0.5].
stop_rule.min_nintegerNoMinimum sample count before the experiment can complete.

The body is deny_unknown_fields — unknown keys return 400 invalid_body.

Returns the created row, identical shape to a GET item.

StatusCodeWhen
400missing_confirmationX-Floopy-Confirm: experiments missing or wrong value.
400invalid_bodySchema validation failed, or unknown keys.
400invalid_traffic_pcttraffic_pct outside [0, 100].
403write_permission / plan_requiredPermission or plan flag missing.
409active_already_existsAn active experiment already exists for the same (baseline, candidate) pair.
429rate_limitedOver per-org budget.
5xxinternalUpstream failure.
Terminal window
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"

Force a running experiment back to its baseline immediately.

POST https://api.floopy.ai/v1/experiments/{id}/rollback
Authorization: Bearer <your-floopy-api-key>
X-Floopy-Confirm: experiments

No request body.

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.

StatusCodeWhen
400missing_confirmationConfirmation header missing or wrong.
403write_permission / plan_requiredPermission or plan flag missing.
404not_foundNo experiment with that id in the caller’s organisation. Identical bytes for cross-tenant lookups.
409completed_cannot_rollbackThe experiment is already completed and cannot be rolled back.
5xxinternalUpstream failure.
Terminal window
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 .

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.