GET /v1/decisions/{request_id}
Pull the full routing audit for a single chat-completion request, keyed by the x-request-id header that the gateway echoes on every response. Useful for correlating an entry in your application logs with the exact provider, model, candidates, and confidence that Floopy chose for it.
This endpoint never produces a decision_trace row of its own — it only reads.
Endpoint
Section titled “Endpoint”GET https://api.floopy.ai/v1/decisions/{request_id}Authentication
Section titled “Authentication”Authorization: Bearer <your-floopy-api-key>- Permission:
read_permissionon the API key (HTTP method maps toGET → read_permission). - Available on all plans.
- We are rolling this endpoint out gradually; an organization that has not been enabled yet receives
404 not_found, with bytes identical to a non-existent request. Contact support for early access.
Path parameters
Section titled “Path parameters”| Field | Type | Required | Constraints |
|---|---|---|---|
request_id | string (UUID v4) | Yes | Validated with strict UUID parse. Malformed input returns 400 invalid_request_id. |
Response (200)
Section titled “Response (200)”{ "request_id": "0c4a1d3f-7bea-4a1f-8b6e-2c0a8e4d3f10", "request_created_at": "2026-05-05T17:42:11Z", "session_id": "sess_demo_1", "routing_strategy": "feedback_driven", "phase": "auto", "weights": { "session": 0.5, "auto": 0.3, "manual": 0.1, "benchmark": 0.1 }, "candidates": [ { "provider": "openai", "model": "gpt-5.4-mini", "score": 0.81 }, { "provider": "anthropic", "model": "claude-sonnet-4", "score": 0.78 } ], "filtered": [ { "provider": "openai", "model": "gpt-5.4", "reason": "constraint_max_cost_increase", "score": 0.86 } ], "winner": { "provider": "openai", "model": "gpt-5.4-mini" }, "reason": "dispatched", "confidence": 0.78, "confidence_reason": "ok", "exploration_rate_effective": 0.05, "used_shared_pool_prior": false, "outcome": { "status": 200, "latency_ms": 412, "cost_micro_usd": 230, "cache_hit": false, "threat_blocked": false, "fallback_used": false }, "evidence": { "samples": 412, "top2_score_gap": 0.07, "outcome_variance": 0.04, "recent_regressions": { "kind": "exact", "exact": 1 }, "last_regression_at": "2026-05-04T18:25:00Z" }, "explanation": { "text": "Floopy routed this request to openai/gpt-5.4-mini based on 412 historical samples and a moderate confidence of 0.78. The next candidate scored within 0.07 points and outcome variance has been stable. One regression was recorded in the last 7 days.", "template_id": "feedback_driven_moderate_confidence" }}The response also carries a Content-Language header echoing the locale used to render explanation.text — see the Decision Explanation feature.
Field reference
Section titled “Field reference”| Field | Type | Description |
|---|---|---|
request_id | UUID | The id from the x-request-id response header. |
request_created_at | ISO8601 | Server-side request timestamp (UTC). |
session_id | string | null | The floopy-session-id header value, if any. |
routing_strategy | string | Name of the strategy that ran (feedback_driven, smart_cost, fallback, round_robin, weighted, latency_based, legacy_model). |
phase | string | null | Feedback-Driven phase (day0, auto, nps); null for non-Feedback-Driven runs. |
weights | object | null | Signal weights at decision time (Feedback-Driven only). |
candidates | array | Models considered, with the routing score the strategy assigned each. |
filtered | array | Candidates removed during filtering. reason is a closed enum (see below). |
winner | object | null | The dispatched (provider, model). null when no candidate could be selected. |
reason | string | dispatched, exhausted, or no_enabled_targets. |
confidence | number | null | Router’s self-reported confidence in the winner, in [0.0, 1.0]. null means no router was invoked (e.g. cache hit). See Confidence methodology. |
confidence_reason | string | null | Why this confidence value — ok, cap_day0, cap_shared, no_router_invoked, insufficient_samples, single_candidate. |
exploration_rate_effective | number | Effective exploration rate for this request. |
used_shared_pool_prior | boolean | Whether the routing decision drew on cross-tenant priors. |
outcome.status | integer | HTTP status of the underlying chat completion. |
outcome.latency_ms | integer | null | End-to-end latency in milliseconds. |
outcome.cost_micro_usd | integer | Provider cost for the request in micro-USD. |
outcome.cache_hit | boolean | Whether the response was served from cache. |
outcome.threat_blocked | boolean | null | Whether the LLM firewall blocked the request. |
outcome.fallback_used | boolean | Whether a fallback target was used. |
evidence | object | null | Inputs that drove the confidence number, surfaced for audit (Feedback-Driven and Smart-Cost only). null when no router was invoked. See Confidence methodology. |
evidence.samples | integer | Rolling sample count n_samples over the winner. |
evidence.top2_score_gap | number | Score gap between winner and runner-up. |
evidence.outcome_variance | number | Rolling outcome variance for the winner. |
evidence.recent_regressions | tagged union | Bucketed regression count over the winner’s (provider, model) in the last 7 days. Tagged union with kind: "exact" and field exact for n < 10, or kind: "at_least" and field at_least for 10 and 50 thresholds. |
evidence.last_regression_at | ISO8601 | null | Timestamp of the most recent regression in the 7-day window, rounded to the nearest 5-minute boundary. null when no regression exists. |
explanation | object | null | Human-readable explanation of the routing decision, rendered at read time in the request’s Accept-Language (falling back to en). null when no router was invoked. See Decision Explanation. |
explanation.text | string | Plain-prose paragraph (≤ 600 characters) describing the decision. Never references prompt content. |
explanation.template_id | string | Closed-enum template id that was rendered. See the template-id taxonomy. |
filtered[].reason values: circuit_breaker_open, rate_limited, constraint_max_cost_increase, constraint_max_regression, constraint_confidence_below_threshold, constraint_min_samples, constraint_high_variance, constraint_cost_drop_requires_validation, constraint_shadow_required, no_enabled_targets, firewall_blocked, other.
Response headers
Section titled “Response headers”In addition to the standard headers, the response carries:
Content-Language: enorContent-Language: pt— the locale used to renderexplanation.text. Resolved from the request’sAccept-Languageheader, falling back toenon absent, malformed, or unsupported input. See Decision Explanation.
Errors
Section titled “Errors”| Status | error code | When |
|---|---|---|
400 | invalid_request_id | request_id is not a valid UUID v4. |
403 | read_permission | Key lacks read_permission. |
403 | plan_required | The endpoint is not included on the caller’s plan. |
404 | not_found | No row exists in the caller’s organisation for that request_id. Returned with identical bytes for cross-tenant lookups so existence is not leaked. |
410 | retention_expired | Row exists but is older than the plan’s log_retention_days. Body carries retention_window_days. |
429 | rate_limited | Exceeded 600 req/min/org or 200 req/min/key. Carries Retry-After. |
5xx | internal | Upstream analytics failure. |
Curl example
Section titled “Curl example”curl -s -H "Authorization: Bearer $FLOOPY_API_KEY" \ "https://api.floopy.ai/v1/decisions/0c4a1d3f-7bea-4a1f-8b6e-2c0a8e4d3f10"Rate limits
Section titled “Rate limits”- 600 requests / minute / organization.
- 200 requests / minute / API key.
Both windows are evaluated atomically; if either denies, the request is rejected with 429.
See also
Section titled “See also”- GET /v1/decisions — paginated list with HMAC cursors.
- GET /v1/export/decisions — bulk JSONL export.
- Confidence methodology.