errorbar
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| OMNIA_API_KEY | Yes | A workspace API key (sk_…) from Settings → API keys. Give the key only the scopes the agent should have. | |
| OMNIA_BASE_URL | No | API root, default https://gateway.errorbar.ai | https://gateway.errorbar.ai |
| OMNIA_MCP_NO_SPEND | No | Set to 1 to hide tools that start billable work (eval runs, training, dedicated capacity, judge assists). | |
| OMNIA_MCP_READ_ONLY | No | Set to 1 to expose only GET tools. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": true
} |
| resources | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| screen_my_trafficA | Would a cheaper (or newer) model hold on this workspace's own traffic? Starts a zero-config screening — the dominant logged model is the incumbent, its STORED answers the baseline, the cheaper model of each family (or the candidates you pass) the challengers — waits for it, and returns each candidate's verdict from the win-rate interval plus a switch/keep recommendation. SPENDS MONEY: judging and candidate generations bill the workspace wallet (402 when the wallet cannot cover the funds gate). Needs request logging on and logged traffic. Prefer this over create_eval for the 'is X better/cheaper' question. |
| is_my_judge_trustworthyA | Can this workspace's judges be believed? For one criterion (pass criterion_id) or all of them: the trust verdict read from the TPR/TNR intervals (trustworthy / under-measured / borderline / misaligned / unmeasured), how often it catches real failures and passes clean ones with 95% intervals, κ, how many grades it was measured on and when, drift status, and the one action that changes its state. Read-only. |
| can_i_shipA | Does the evidence let this change through? Reads the gate on a finished eval run (the latest DONE run when eval_id is omitted) with your thresholds — or sensible defaults: the certified-switch test (noninferiority_margin 0.05) for a criterion run against stored answers, min_win_rate 0.5 for a comparison — and returns ship/hold with every check's required vs actual, where 'actual' is the interval's LOWER bound. Read-only; the same call is the CI step. |
| list_alertsA | List every alert this workspace has fired, newest first, with the payload the notification carried, so a pipeline can react to quality, cost, or drift events without reading a mailbox. GET /v1/alerts (API-key scope: read). Returns: { alerts: [{ id, kind, fired_at (ISO), payload (JSON object: criterion, model, rates, reason as applicable) }], next_cursor: string|null } Notes: Keyset pagination: pass next_cursor back as cursor until it is null. 400 when since is not an ISO date or limit is outside 1..200. Cache-Control: no-store. |
| list_aliasesA | List this workspace's model aliases (stable names your code calls) with their current target, canary split, quality-gate config, evidence policy and the eval run that authorized the current routing. GET /v1/aliases (API-key scope: read). Returns: { object: "list", data: [{ id, name, target_model, canary_model, canary_percent, description, gate_criterion_id, gate_mode ("recommend"|"auto"), gate_min_samples, gate_rollback_threshold, gate_window_hours, gate_verdict ({decision, reason, canary, incumbent, acted}|null), gate_verdict_at, model_version_id, require_evidence, last_evidence_run_id, created_at, updated_at }] } Notes: Sorted by name ascending. last_evidence_run_id is null when the routing predates the evidence policy or went through as an audited override. |
| upsert_aliasA | Create or repoint a model alias by name (idempotent upsert) so production traffic moves to a new model without a redeploy; optionally attach a canary split, a quality gate, or an evidence-required policy. PUT /v1/aliases (API-key scope: aliases:write). Returns: 200 with the alias object: { id, name, target_model, canary_model, canary_percent, description, gate_criterion_id, gate_mode, gate_min_samples, gate_rollback_threshold, gate_window_hours, gate_verdict, gate_verdict_at, model_version_id, require_evidence, last_evidence_run_id, created_at, updated_at } Notes: MOVES PRODUCTION TRAFFIC: the gateway resolves aliases within ~10s. Requires the key's minting user to be workspace OWNER/ADMIN (403 otherwise). Same status 200 whether created or updated. 412 Precondition Failed (code precondition_failed) when the evidence policy refuses the repoint; a brand-new alias is never blocked by the policy. 400 for schema failures, canary_percent > 0 without canary_model, canary equal to target, unavailable model, gate criterion never aligned, or auto-mode eligibility refusals (judge not trustworthy, drift-flagged, trace-unit, or judge trained the destination). 404 "Gate criterion not found". Billing always follows the model that actually ran; an alias is routing only. |
| delete_aliasA | Remove a model alias; requests still using that name will fail afterwards, so this is a cutover step, not cleanup. DELETE /v1/aliases/{id} (API-key scope: aliases:write). Returns: 200 { ok: true } Notes: OWNER/ADMIN only (403). 404 "Alias not found" when the id is not in this workspace. Audited with the model it pointed at. |
| export_audit_logA | Export this workspace's audit rows with their hash-chain fields (seq, prev_hash, row_hash) as CSV or JSON, so a recipient can verify a later export reproduces the same hashes. GET /v1/audit/export (API-key scope: read). Returns: format=json: { rows: [{ id, timestamp, event_type, category, status, actor_id, actor_email, actor_role, target_type, target_id, description, seq, prev_hash, row_hash }], truncated: boolean }. format=csv: text/csv attachment (Content-Disposition audit-.csv) with header row seq,timestamp,event_type,category,status,actor_id,actor_email,actor_role,target_type,target_id,description,prev_hash,row_hash,id; header X-Truncated: true when the limit cut the result. Notes: Rows ordered by timestamp then seq ascending. Returns an empty set (not an error) if the log store is unavailable. Cache-Control: no-store. |
| list_audit_tombstonesA | List acknowledged audit-chain gaps (tombstones) with the recorded reason for each lost slot, so a known loss can be distinguished from tampering. GET /v1/audit/tombstones (API-key scope: read). Returns: { tombstones: [{ seq (integer), reason, created_at (ISO) }] } sorted by seq ascending Notes: The audit chain is platform-global, so this list is the same for every workspace. Cache-Control: no-store. |
| create_audit_tombstoneA | Acknowledge a verified audit-chain gap with a written reason so the integrity check stops reporting it as unexplained; a platform-admin repair action, never a way to hide a gap. POST /v1/audit/tombstones (API-key scope: platform:write). Returns: 201 { seq, reason, created_at } Notes: 403 unless the key's minting user has the platform-level admin role (workspace OWNER/ADMIN is not enough). 400 for invalid JSON, wrong types, seq <= 0, reason under 10 chars, or a seq that is not a gap in the latest verification (message names when that verification ran, or that none has run yet). |
| get_audit_verificationA | Read the latest nightly whole-chain integrity verification of the audit log (ok flag, rows checked, head seq, problems found, acknowledged gaps) plus the tombstone list, as the platform's integrity statement. GET /v1/audit/verify (API-key scope: read). Returns: { verification: { ran_at, ok, checked_rows, head_seq, problems: [{ seq, kind, detail }], acknowledged (count) } | null, tombstones: [{ seq, reason, created_at }], statement: string describing the hashing scheme } Notes: verification is null until the first nightly run has stored a result. The chain is platform-global; your own rows' hashes come from GET /v1/audit/export. Cache-Control: no-store. |
| list_batchesA | List this workspace's batch inference jobs, newest first, with status, request counts and billed cost. GET /v1/batches (API-key scope: read). Returns: A bare JSON array (no envelope) of up to 100 batch objects: { id, nebius_batch_id (upstream batch id), endpoint, status (VALIDATING|IN_PROGRESS|FINALIZING|COMPLETED|FAILED|EXPIRED|CANCELLING|CANCELLED), request_total, request_completed, request_failed, completion_window, billed_cost_usd (number|null), created_at, output_file_id, error_file_id, error } Notes: Feature-flagged: every /v1/batches route returns 404 { error: "Batch inference is not enabled" } while the batch flag is off (code default is off). Statuses here are the stored values; GET /v1/batches/{id} refreshes them live. |
| create_batchA | Submit an asynchronous, discounted batch of inference requests from a previously uploaded JSONL file, for workloads that can wait up to the completion window. POST /v1/batches (API-key scope: platform:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: 201 with the batch object: { id, nebius_batch_id, endpoint, status, request_total, request_completed, request_failed, completion_window, billed_cost_usd, created_at, output_file_id, error_file_id, error } Notes: MONEY: the wallet must hold at least $0.10 of available runway to submit (402 otherwise); the batch discount and markup are frozen at submit time and the job is billed on completion. OWNER/ADMIN only (403). 400 when input_file_id/endpoint/model is missing or the body is not JSON. 503 when batch creation is temporarily unavailable upstream (the input file stays uploaded; retry later). 404 while the batch feature flag is off. |
| get_batchA | Fetch one batch job with its status, request counts and output/error file ids refreshed live from the processing backend, to poll for completion. GET /v1/batches/{id} (API-key scope: read). Returns: { id, nebius_batch_id, endpoint, status, request_total, request_completed, request_failed, completion_window, billed_cost_usd, created_at, output_file_id, error_file_id, error } Notes: Best-effort live reconciliation: if the upstream status lookup fails the stored row is returned unchanged. Billing still happens in the background reconciler, not on this read. 404 "Batch not found" outside the workspace; 404 while the batch flag is off. |
| cancel_batchA | Request cancellation of an in-flight batch job. POST /v1/batches/{id}/cancel (API-key scope: platform:write). Returns: 200 with the updated batch object (status typically CANCELLING or CANCELLED) Notes: OWNER/ADMIN only (403). 404 "Batch not found". Audited. 404 while the batch feature flag is off. Work already completed before cancellation may still be billed. |
| list_criteriaA | List this workspace's judge criteria with their calibration metrics (TPR/TNR/kappa with intervals), trust verdict, drift status and online-monitoring config, to see which judges are proven enough to gate on. GET /v1/criteria (API-key scope: read). Returns: { object: "list", data: [{ id, name, description, judge_prompt, judge_model, status, source, unit ("request"|"trace"), population (tag), population_family, online_enabled, online_percent, online_cap_usd, online_spent_usd, tier (aligned|weak|misaligned|unmeasured), trust (trustworthy|misaligned|under-measured|borderline|unmeasured), fail_grades_needed, pass_grades_needed, tpr_ci ([lo,hi]|null), tnr_ci, drift_status (ok|flagged), drift_signal (stale|quality_drop|suspicious_rise|evidence_revised|null), drift_reason, drift_checked_at, tpr, tnr, kappa, alignment_n, aligned_at, created_at }] } Notes: trust is what every gate reads; tier is the legacy point-estimate badge. drift_status is derived (a fresh calibration supersedes a cached flag). drift_signal names the check that raised it: stale (calibration older than 30 days), quality_drop (live corrected rate fell well below what the judge validated at), suspicious_rise (traffic from a model TRAINED AGAINST this judge scores above what it validated at — an unvalidated gain), evidence_revised (grades the calibration was measured on were edited or deleted; clears on re-calibration or on reverting the edits). |
| create_criterionA | Create a judge criterion (a rubric prompt run by a judge model) that can score traffic online and be calibrated against human labels. POST /v1/criteria (API-key scope: evals:write). Returns: 201 with the criterion object (same shape as list items): id, name, description, judge_prompt, judge_model, status, source, unit, population, population_family, online_* fields, tier, trust, ci, drift, tpr, tnr, kappa, alignment_n, aligned_at, created_at Notes: OWNER/ADMIN only (403). Creating does not spend; judging (align, online monitoring) does. Body keys are snake_case exactly as listed; other criterion knobs (coverage, pre-checks, contract rules) are not settable through this endpoint. |
| suggest_criteriaA | Clusters the workspace's written failure critiques into up to 5 DRAFT judge criteria, one per failure mode — use it after grading a batch of fails with reasons to bootstrap criteria you then review and align. POST /v1/criteria/suggest (API-key scope: evals:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: { created: [<criterion objects, same snake_case shape as GET /v1/criteria: id, name, description, judge_prompt, judge_model, status ('draft'), source ('assist_suggested'), unit, population, population_family, online_*, tier, trust, fail_grades_needed, pass_grades_needed, tpr_ci, tnr_ci, drift_*, tpr, tnr, kappa, alignment_n, aligned_at, created_at>], critiques_used: , skipped_duplicates: <int, proposed drafts whose name already existed> }. Notes: A body-less POST (or invalid JSON) is valid and uses the default judge — there is no 400 for a missing body. Requires an OWNER/ADMIN minting user (403). 400 when fewer than 10 FAIL grades carry a critique (message includes the current count); only the 200 most recent critiques are considered. 400 if the model returns no parseable JSON array ('try again'). SPENDS THE WALLET: one metered clustering call (billed under assist:suggest). Drafts are never trusted by any gate until a human reviews them and runs an alignment; an existing criterion with the same name is skipped, never overwritten. Function maxDuration is 300s. |
| list_criterion_templatesA | Lists the shipped judge-criterion templates (starting-point judge prompts grouped by use case) so a caller can instantiate one via POST /v1/criteria with an edited judge_prompt. GET /v1/criteria/templates (API-key scope: read). Returns: { object: 'list', data: [{ id: <slug e.g. 'no-fabrication', 'grounded-in-context', 'right-next-action', 'tool-use-sound', 'finishes-what-it-starts'>, use_case: <'Support & assistants'|'RAG & knowledge'|'Extraction & structured output'|'Data processing'|'Agents & tools'|'Any traffic'>, name, description, judge_prompt, unit: 'request'|'trace', universal: <bool, true = meaningful on any traffic, safe to leave unscoped> }] }. 11 templates as of this build. Notes: Static and free; only authentication is required. A template is a starting point, not a truth — it still has to be aligned against the workspace's own labels. Task-specific (non-universal) templates should be scoped to the tag of the traffic they judge; unit 'trace' templates judge whole agent runs and need trace-scoped labels. |
| get_criterionA | Fetch one criterion with its current calibration metrics, trust verdict and the intervals it was derived from. GET /v1/criteria/{id} (API-key scope: read). Returns: The criterion object: id, name, description, judge_prompt, judge_model, status, source, unit, population, population_family, online_enabled, online_percent, online_cap_usd, online_spent_usd, tier, trust, fail_grades_needed, pass_grades_needed, tpr_ci, tnr_ci, drift_status, drift_signal, drift_reason, drift_checked_at, tpr, tnr, kappa, alignment_n, aligned_at, created_at Notes: 404 "Criterion not found" when the id is not in this workspace. Derivations (trust, drift) match GET /v1/criteria exactly. |
| update_criterionA | Update a criterion's prompt, judge model, population scope, online-monitoring settings or lifecycle status; instrument changes void its calibration. PATCH /v1/criteria/{id} (API-key scope: evals:write). Returns: 200 with the updated criterion object (same shape as GET /v1/criteria/{id}) Notes: OWNER/ADMIN only (403). 404 "Criterion not found". 400 for schema failures, a unit change, or an invalid status. MONEY: enabling online monitoring spends the wallet on judge calls, capped weekly by online_cap_usd. Voided calibration means trust becomes "unmeasured" until POST /align is run again. |
| delete_criterionA | Permanently delete a criterion and its calibration history. DELETE /v1/criteria/{id} (API-key scope: evals:write). Returns: 200 { id, deleted: true } Notes: OWNER/ADMIN only (403). 404 "Criterion not found" when not in this workspace. Aliases gated on this criterion lose their gate. |
| run_criterion_alignmentA | Calibrate a criterion by re-judging every in-scope human-labeled trace and measuring agreement (TPR/TNR with Wilson intervals, Cohen's kappa), which is what earns a judge the trust needed to gate on it. POST /v1/criteria/{id}/align (API-key scope: evals:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: Small sets (<= 50 labels) run synchronously and return the report: { scope_tag, scope_family, tag_breakdown: [{ tag, n }], mixed_population, excluded_other_cause, unattributed_fails, metrics: { n, tpr, tpr_ci, tnr, tnr_ci, kappa }, tier, thin_alignment_set, skipped, holdout: { tune_n, report_n }|null, one_class_note, disagreements: [{ request_id, judge_verdict, human_verdict }] }. Larger sets return { queued: true, total_labels } and the report is built in the background over the following minutes (poll GET /v1/criteria/{id}/alignment). Notes: MONEY: spends the wallet like any judging (one judge call per label; a new run is new spend). OWNER/ADMIN only (403). 400 when fewer than 30 in-scope labels exist (message says how many you have and how to label more), when a run is already in progress ("An alignment run is already in progress for this criterion."), or when fewer than 30 labels could actually be judged. 404 "Criterion not found". Route maxDuration is 300s. |
| get_criterion_alignmentA | Read the persistent report from the criterion's last calibration run: metrics with intervals, population breakdown, threshold sweep, and every judge/human disagreement with the human's critique and a response excerpt. Free; it never re-judges. GET /v1/criteria/{id}/alignment (API-key scope: read). Returns: { aligned_at, scope_tag, tag_breakdown: [{ tag, n }], mixed_population, tier, thin_alignment_set, metrics: { n, tpr, tpr_ci, tnr, tnr_ci, kappa }, threshold_analysis: { half ("tune"|"all"), sweep: { n, ungraded, argmax: metrics, best: { threshold, metrics, youden_j }|null, curve: [{ threshold, metrics, youden_j }], note }, report_check: { threshold, n, metrics, youden_j, argmax }|null }|null, agreements (count), disagreements: [{ request_id, judge_verdict, human_verdict, critique, tag, response_excerpt }] } Notes: 404 "Criterion not found". 400 "This criterion has no alignment run yet" when never calibrated or after a voiding edit; 400 "Alignment run in progress — N labels judged so far" while a background run is in flight (use it to poll). threshold_analysis is null for runs recorded before logprob grades were stored. All nested keys are snake_case. |
| auto_improve_criterionA | Runs one auto-improvement round on a judge criterion: mines the tune-half disagreements from its last alignment, rewrites the judge prompt coherently, and creates a successor DRAFT criterion with its alignment queued — use it when a calibrated judge still disagrees with your grades and you want a better candidate without hand-editing the prompt. POST /v1/criteria/{id}/auto_improve (API-key scope: evals:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: 201 with { criterion: <full criterion object, snake_case: id, name, description, judge_prompt, judge_model, status, source, unit, population, population_family, online_enabled, online_percent, online_cap_usd, online_spent_usd, tier, trust, fail_grades_needed, pass_grades_needed, tpr_ci, tnr_ci, drift_status, drift_signal, drift_reason, drift_checked_at, tpr, tnr, kappa, alignment_n, aligned_at, created_at>, tune_disagreements: <int, tune-half rows where judge and human disagreed>, alignment_queued: }. The returned criterion is the NEW successor (draft, metrics void), named ' (auto r2)' (round suffix increments), inheriting the parent's unit, judge model, tag, segment, deterministic pre-stage, coverage, contract rules and golden set — a round varies the PROMPT only. Notes: No request body is read. Requires an OWNER/ADMIN minting user (403 otherwise). 404 if the criterion is not in the workspace. 400 when: an alignment run is in progress; the last alignment has fewer than 80 judged rows (needs a holdout-scale run so the report half stays untouched); there are zero tune-half disagreements; or the rewriter returned an unusable prompt (nothing created, only the single rewriter call was spent). SPENDS THE WALLET: one metered rewriter call (billed under assist:iterate) plus the queued alignment run, which bills like any alignment. Deliberately single-round: loop it yourself once the successor's alignment lands; adoption (repoint monitoring, retire the parent) stays a human act. Function maxDuration is 300s. |
| get_criterion_certificateA | Returns the signed judge certificate for a criterion — what was proven (confusion matrix, TPR/TNR/kappa with intervals, trust verdict), on which population, what voids it, and what enforcement refused involving it — for audits, evidence bundles, or proving a judge's calibration to a third party. GET /v1/criteria/{id}/certificate (API-key scope: read). Returns: A JSON document whose keys are camelCase (NOT snake_case — it is emitted verbatim so its signature can be re-derived): signature: {alg:'HS256', key_id, value} | null (with unsigned: true when no signing secret is configured), criterionId, name, question (the judge prompt), unit ('request'|'trace'), judgeModel, issuedAt, calibration: {measured, matrix: {tp,fp,tn,fn}|null, metrics: {n, tpr, tprCi, tnr, tnrCi, kappa}|null, labels, holdoutActive (labels >= 80), alignedAt, goldenSet: {id, name, size, membershipHash, frozenAt, humanKappa, humanAgreement, humanKappaN, raterCount}|null}, trust: {trust: 'trustworthy'|'misaligned'|'under-measured'|'borderline'|'unmeasured', failGradesNeeded, passGradesNeeded, tprCi, tnrCi} (or just {trust:'unmeasured'}), population: {tag, segment, unit, statement}, validity: {driftStatus: 'ok'|'flagged', driftSignal: 'stale'|'quality_drop'|'suspicious_rise'|'evidence_revised'|null, driftReason, driftCheckedAt, voidedBy: string[]}, enforcement: {windowDays: 90, refusalsInvolvingJudge, lastReason}. Notes: Free (no judging). 404 if the criterion is not in the workspace. Response is Cache-Control: no-store. Hand the WHOLE JSON object to POST /v1/verify to check the signature later. An uncalibrated judge still returns a certificate that honestly says nothing is measured (calibration.measured=false, trust.trust='unmeasured'). enforcement counts refusal-ledger rows from the last 90 days whose subject is this criterion or whose reason names it. |
| scan_criterion_suspectsA | Judges a bounded batch of recent, not-yet-labeled traffic with this criterion and queues every FAIL as a pending suspect for human review in the dashboard's Review queue — the fastest way to grow a judge's failure-label set from live traffic. POST /v1/criteria/{id}/scan (API-key scope: evals:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: { scanned: <int, items actually judged>, flagged: <int, FAIL verdicts queued as pending suspects> }. Both are 0 when no unlabeled, unscanned candidates exist in scope. Notes: No request body is read. Requires an OWNER/ADMIN minting user (403). 404 if the criterion is not in the workspace. Request-unit criteria: takes the 100 most recent logged exchanges (scoped to the criterion's population segment when it has one), drops already-labeled and already-scanned rows (dismissed suspects never resurface), and judges at most 30. Trace-unit criteria: scans at most 10 COMPLETED agent runs from the last 7 days (quiet for 10 minutes), scoped to the criterion's tag and segment; requires a completed calibration (400 'Calibrate this judge first' otherwise) and has a pre-flight wallet gate of ~$0.10 per run (402 'Insufficient balance' before any spend). SPENDS THE WALLET: every judge call is metered as usage (billing prefix scan:). Suspects are adjudicated in the dashboard (accept = a real FAIL label; dismiss = never resurfaces). Function maxDuration is 300s. |
| decontaminate_textsA | Checks a batch of texts against the public-benchmark contamination index (13-word shingles of well-known test splits) and reports which inputs share material with which benchmark — use it before training so later benchmark scores measure capability, not memorised answer keys. POST /v1/datasets/decontaminate (API-key scope: evals:write). Returns: { checked: <int, texts actually checked; 0 when the index was unavailable>, index: { version, generatedAt (camelCase — passed through verbatim), benchmarks: [{id, name, rows}] } | null, hits: [{ index: , benchmark: , benchmark_id, matches: }], contaminated: <sorted unique int[] of input positions with any hit> }. Notes: 400 on invalid JSON, when |
| create_dataset_from_logsA | Curates logged gateway traffic into a managed training dataset (optionally with a disjoint eval holdout split), auto-dropping errored/truncated/empty/duplicate/human-failed/benchmark-contaminated exchanges and applying a chosen quality gate — use it to turn production logs into fine-tuning or eval data. POST /v1/datasets/from_logs (API-key scope: evals:write). Returns: 201 with snake_case: { summary: { total: , kept, dropped: { unparseable, errored, truncated, empty, duplicate, human_failed, contaminated }, folded: { folded_turns, conversations } }, quality: { mode, criterion_id?, criterion_name?, kappa?, human_pass_kept?, ungraded_excluded?, judged?, judge_passed?, judge_failed?, judge_unparsed?, judge_spend_usd? }, training_name, training_count, eval_name?: '-eval', eval_count? }. Notes: Requires an OWNER/ADMIN minting user for dataset creation (403, enforced in the dataset service). 400 (invalid_json) on unparseable JSON. Other 400s: name missing/over 80 chars; unknown quality mode; judge mode without criterion_id; judge criterion not found / trace-unit / misaligned / borderline / unmeasured / drift-flagged / segment-bound but build not scoped to that segment (each eligibility refusal is also written to the refusal ledger, kind dataset_judge_trust); no usable exchanges after curation (nothing created). Fetch is capped at 50,000 most recent matching rows. MONEY: judge mode is gated up front at ~$0.02 per conversation to judge (402 'Insufficient balance for judge gating' before any spend) and every judge call is then metered as usage; a scoring failure mid-run FAILS THE WHOLE BUILD (no dataset created) but rows already judged were billed (idempotent ids — retry does not re-bill). Human FAIL grades (or rows sharing an agent run with a trace-scoped FAIL) never enter a dataset in any mode. Multi-turn chats are folded into one weighted line per conversation. If the training set is created but the eval split fails, the response is a 400 that says the training dataset already exists. The eval split shares no example with the training set, so it is valid as an eval source (POST /v1/evals with sample_filters.dataset_id). |
| list_dedicated_endpointsA | Lists the workspace's dedicated (reserved-GPU) inference endpoints with live-reconciled status, frozen hourly price and unbilled cost accrued since the last meter — use it to monitor what is running and what it is costing. GET /v1/dedicated (API-key scope: read). Returns: A bare JSON array (no {object:'list'} envelope) of endpoint objects, snake_case: { id, name, description, model_name, flavor_name, gpu_type, gpu_count, region, min_replicas, max_replicas, status (e.g. PENDING/STARTING/RUNNING/UPDATING/STOPPING/STOPPED/FAILED), enabled, hourly_rate_usd (customer sell price per GPU-hour, frozen at deploy), pending_cost_usd (GPU-hours accrued since last_metered_at while RUNNING, not yet billed), routing_key (the model name to send to the inference API to hit this endpoint), base_url, last_metered_at, created_at }. Internal margin fields are never returned. Notes: Scope via requiredScopeFor is 'read' for GET; NOTE the dedicated routes use their own local apiKeyActor (app/api/v1/dedicated/_helpers.ts) which authenticates the key but does NOT enforce key scopes — any valid, unrevoked key passes. Deleted endpoints are excluded. Status/enabled/region are reconciled live from the control plane on every call (DB state served if reconcile fails). 400 on catalog/provider failure. Money: a RUNNING endpoint bills per GPU-hour (gpu_count x replicas x hourly_rate_usd) continuously; pending_cost_usd is what the next meter will charge. |
| create_dedicated_endpointA | Provisions a new dedicated inference endpoint (a model served on reserved GPUs at a frozen per-GPU-hour price) — use it for guaranteed capacity, custom fine-tuned weights, or predictable latency; billing starts as soon as it is running. POST /v1/dedicated (API-key scope: platform:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: 201 with { id: }. Poll GET /v1/dedicated/{id} for status and routing_key. Notes: 400 'Invalid JSON body' or 'Missing required field(s): ...' when any of name, model_name, flavor_name, gpu_type, gpu_count, region, min_replicas, max_replicas is absent/null (checked after alias lifting). Requires an OWNER/ADMIN minting user (403). 400 when the model/flavor/GPU/region/count combo is not in the catalog, replica range invalid, or no price is configured for the GPU/region. MONEY: 402 'Insufficient balance' unless the wallet covers at least DEDICATED_PREPAY_HOURS (default 1 hour) of runway at min_replicas x gpu_count x sell rate; the per-GPU-hour price is FROZEN on the endpoint at create time; GPU-hours are metered continuously while the endpoint is enabled and RUNNING — stop (PATCH enabled=false) or DELETE to stop billing. Scope note: the dedicated routes' local apiKeyActor does not enforce key scopes on this branch. |
| list_dedicated_templatesA | Returns the deployable model catalog for dedicated endpoints (model -> flavor -> GPU type -> allowed regions/counts/replica limits) plus this workspace's sell price per GPU-hour for every GPU/region combo — read it to build a valid POST /v1/dedicated request and estimate cost. GET /v1/dedicated/templates (API-key scope: read). Returns: { templates: [{ name (use as model_name), type ('text2text'|'embedding'|'image2text'|...), metadata?: { huggingface_url?, vendor?, context_window_k?, size_b?, license?: {url?, name?} }, flavors?: { : { quantization?, use_cases?, tags?, base_model_slug?, available_configurations?: { gpu_configurations?: { : { allowed_regions: string[], allowed_gpu_counts: int[], max_replicas_allowed: int } } } } } }], prices: [{ gpu_type, region, price_per_gpu_hour_usd: number|null }] }. Template contents are the upstream catalog shape, already snake_case. Notes: Scope via requiredScopeFor is 'read'; the local dedicated apiKeyActor does not enforce scopes. price_per_gpu_hour_usd is the customer price (base cost and margin are never returned); null means no price is configured for that combo yet and a deploy on it will be refused. 400 'Dedicated endpoints are not configured' or 'Failed to load dedicated endpoint catalog' on provider/config failure. Prices are quoted at request time; the price frozen on an endpoint is the one in effect when it is created. Free to call. |
| get_dedicated_endpointA | Returns one dedicated endpoint's current view (live status, frozen hourly price, unbilled accrued cost, routing key) — use it to poll a deploy until RUNNING or to check spend. GET /v1/dedicated/{id} (API-key scope: read). Returns: A single endpoint object, snake_case: { id, name, description, model_name, flavor_name, gpu_type, gpu_count, region, min_replicas, max_replicas, status, enabled, hourly_rate_usd, pending_cost_usd, routing_key, base_url, last_metered_at, created_at }. Notes: Scope via requiredScopeFor is 'read'; the local dedicated apiKeyActor does not enforce scopes. Implemented by listing the workspace's endpoints (live-reconciled) and picking the id, so it costs a full list call. 404 'Endpoint not found'. |
| update_dedicated_endpointA | Scales, starts/stops, renames, or changes the GPU configuration of a dedicated endpoint — use enabled=false to stop billing without deleting, or gpu_type/gpu_count to re-size (which re-freezes the price). PATCH /v1/dedicated/{id} (API-key scope: platform:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: { ok: true } on success (no body data). Notes: 400 'Invalid JSON body'. Requires an OWNER/ADMIN minting user (403). 404 'Endpoint not found'. 400 on invalid replica range (max must be >= min >= 1), GPU not available for the model/region, disallowed GPU count, max replicas above the configuration limit, or no price configured. MONEY: a GPU change re-prices the endpoint at today's rate (new frozen hourly_rate_usd) and is gated at 402 unless the wallet covers 1 prepay hour at the new configuration; stopping (enabled=false) or any GPU change immediately meters and bills the GPU-hours accrued so far. Status becomes UPDATING (GPU change), STOPPING (enabled=false) or STARTING (enabled=true). Scope note: local dedicated apiKeyActor does not enforce key scopes. |
| delete_dedicated_endpointA | Tears down a dedicated endpoint: meters and bills the GPU-hours accrued since the last meter, frees the reserved GPUs, and marks it DELETED — the way to permanently stop paying for an endpoint. DELETE /v1/dedicated/{id} (API-key scope: platform:write). Returns: { ok: true } on success. Notes: Requires an OWNER/ADMIN minting user (403). 404 'Endpoint not found' (already-deleted endpoints also 404). MONEY: runs a final meter first (bills accrued GPU-hours), then releases the endpoint; soft-deleted (status DELETED, enabled=false) and excluded from later lists. Not reversible. Scope note: local dedicated apiKeyActor does not enforce key scopes. |
| list_refusalsA | Pages through the workspace's refusal ledger — every time enforcement stopped something (an alias repoint without evidence, an auto-gate or dataset build refusing an untrusted judge, a reward refusing a drift-flagged criterion, a round gate holding, an eval refused off its calibrated population) — newest first, for audit and compliance reporting. GET /v1/enforcement/refusals (API-key scope: read). Returns: { refusals: [{ id, kind, subject, reason (verbatim refusal message, truncated to 500 chars), created_at (ISO) }], next_cursor: <string|null — null on the last page> }. Notes: Ordered by created_at desc, id desc. The ledger is append-only: a refusal later overridden is still listed. Cache-Control: no-store. Free. |
| list_env_toolsA | Lists the agent tools this workspace has declared for training environments (the egress allowlist) together with the per-workspace secret used to verify signed environment calls — use it to audit which endpoints and credentials environments may call. GET /v1/env/tools (API-key scope: read). Returns: snake_case: { tools: [{ id, name, endpoint_url, auth_prefix (first 10 chars of the stored header + ellipsis, '(configured)' if undecryptable, or null), read_only, max_calls_per_episode, enabled, created_at }], egress_verification_secret: <hex HMAC secret; environment calls carry X-Omnia-Environment / X-Omnia-Timestamp / X-Omnia-Signature = HMAC_SHA256(secret, |
| register_env_toolA | Registers (or updates, by name) an agent tool that training environments for this workspace are allowed to call — the explicit consent grant naming the https endpoint, its credential, whether it is read-only, and a per-episode call cap. POST /v1/env/tools (API-key scope: platform:write). Returns: 201 with the tool view, snake_case: { id, name, endpoint_url, auth_prefix, read_only, max_calls_per_episode, enabled, created_at }. Notes: Request body keys are camelCase (endpointUrl, authHeader, readOnly, maxCallsPerEpisode) while the response is snake_case — the route lifts no aliases. 400 'Invalid JSON body' or 'Missing required field(s): name, endpointUrl' when either is missing/empty. Gated behind the fineTuning feature flag (404 when off). Requires an OWNER/ADMIN minting user (403). Registration-time host validation only; DNS rebinding is not defended here. Writes an audit event. No money implication by itself. |
| delete_env_toolA | Revokes a declared agent tool registration (consent withdrawal) so training environments can no longer call that endpoint. DELETE /v1/env/tools/{id} (API-key scope: platform:write). Returns: { ok: true } on success. Notes: Gated behind the fineTuning feature flag (404 'Fine-tuning is not enabled' when off). Requires an OWNER/ADMIN minting user (403). 404 'Tool not found' when the id is not in the workspace. Hard delete; writes an audit event. |
| list_evalsA | List this workspace's eval runs (newest first, most recent 50) with status, progress and stored results, so a customer can see every comparison, criterion run and screening they have queued or finished. GET /v1/evals (API-key scope: read). Returns: {object:"list", data:[run]} where run = {id, name, rubric, rubric_type, eval_kind ("comparison"|"criterion"), criterion_snapshot (frozen judge instrument on criterion runs, else null), baseline_model (a catalog id or "stored"), candidate_models (arm keys), arms:[{key, model, label|null, system:bool, tools:bool, n}], judge_model, sample_count, sample_filters:{model?, tag?, segment?, dataset_id?, trace_replay?, screening?}, status (PENDING|RUNNING|DONE|ERROR|CANCELLED), error|null, results (null until DONE; comparison: {sample_count, clipped_samples, baseline:{model, stored_answers, truncated, avg_latency_ms, eval_cost_micros}, per_candidate:[{model, arm, wins, losses, ties, failed, attempted, judged_share, unreportable, win_rate, ci95, inconclusive, truncated, avg_latency_ms, eval_cost_micros, savings_pct, replay?}], judge_cost_micros, screening?:{incumbent, token_shape, per_candidate:[{model, similarity:{matched, differed, unparsed, judged, match_rate, ci95}, est_usd_per_request, est_savings_pct, projected_monthly_usd, projected_monthly_savings_usd, ...placement}], recommendation}}; criterion: {eval_kind:"criterion", sample_count, clipped_samples, criterion, per_model:[{model, judged_pass, judged_fail, unparsed, truncated, observed_pass_rate, observed_ci, corrected_pass_rate|null, corrected_ci|null, avg_latency_ms, eval_cost_micros}], judge_youden, judge_cost_micros}), assertions|null, created_at (ISO), progress_ratio (0..1)}. Notes: No pagination or filtering: always the 50 newest runs. All keys are snake_cased at the door (camelCase internally); model ids used as map keys pass through untouched. Read-only, no wallet spend. |
| create_evalA | Queue an eval run — a pairwise model comparison, an absolute criterion (calibrated judge) run, or a one-click screening of cheaper models against your own logged traffic — so a customer can measure a model, prompt, tool or index change before shipping it. POST /v1/evals (API-key scope: evals:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: 201 with the queued run in the same shape as GET /v1/evals/{id} (status PENDING, results null, progress_ratio 0, arms[] describing each candidate key). Poll GET /v1/evals/{id} until status is DONE|ERROR|CANCELLED, or gate a pipeline directly with GET /v1/evals/{id}/gate. Notes: MONEY: a run spends wallet credit (every generation for baseline + each arm, plus judge calls; best-of-N arms pay N×). Plain runs disclose cost and are gated lazily per tick by the runner; screening runs enforce a creation-time funds gate → 402 {error:{type:"insufficient_quota", code:"insufficient_balance"}}. The key's minting user must be workspace OWNER/ADMIN → otherwise 403. 400 on: invalid JSON, schema violations (name length, rubric length, sample_count range, >6 candidates, duplicates, candidate == baseline, bad assertion, bad arm label/override), unknown model, missing criterion_id, population-binding refusal, trace_replay without stored baseline, or too little population ("Not enough logged traffic for this filter (need at least 5 distinct prompts…)"). 404 for a dataset/criterion not in this workspace. Screening-specific: 422 {code:"unprocessable"} when there is nothing to screen (no/too little logged traffic, or nothing cheaper than the incumbent — the message carries the import hint), 400 for an unusable candidate_models list, 402 for funds. All errors are {error:{message, type, code}}. Input keys are snake_case; internally converted to camelCase (candidate_models→candidateModels, candidates[]→candidateModels keys + armOverrides map keyed by label, sample_filters.dataset_id→datasetId, trace_replay→traceReplay, max_output_tokens→genMaxOutputTokens). |
| compare_evalsA | Compare two finished eval runs arm-by-arm (before vs after a prompt, tool or index change) and get each arm's delta with a 95% interval and a significance flag, instead of eyeballing two reports. GET /v1/evals/compare (API-key scope: read). Returns: {a:{id, name, created_at, judge_model, eval_kind}, b:{…same}, rows:[{arm (candidate key), metric ("win_rate" for comparison runs; "observed_pass_rate" and, when both runs carry one, "corrected_pass_rate" for criterion runs), a:{rate, n, ci:[lo,hi]}, b:{rate, n, ci}, delta (b.rate − a.rate), delta_ci:[lo,hi] (Newcombe 95%), significant (interval excludes zero)}], unmatched:{a:[arm keys only in a], b:[arm keys only in b]}}. Cache-Control: no-store. Notes: 400 when a or b is missing. 404 when either run is not in this workspace. 412 {code:"precondition_failed"} when both runs are not DONE, when they are different eval kinds, or when they used different judge models (a delta between judges measures the judges, not your change — re-run one with the other's judge). Best used with identical sample_filters on both runs. Read-only, no spend. |
| get_failure_clustersA | See live production failures grouped into systemic causes per criterion (judge FAIL rationales plus pending scan suspects, clustered by a model) so a customer can find what to fix first rather than reading failures one by one. GET /v1/evals/failure_clusters (API-key scope: read). Returns: {window_days, generated_at, cached (true when served from the hourly cache), criteria:[{criterion_id, criterion_name, failures (online FAILs + pending suspects, deduped), without_reason (failures with no stored rationale — counted, never clustered), clusters:[{name, count, share (of this criterion's clustered failures), request_ids, example (one representative rationale verbatim)}]}]}. Cache-Control: no-store. Notes: 400 "window_days must be an integer 1..90" for an out-of-range value. MONEY: a fresh clustering (cache miss or force=true) makes one small metered model call per criterion that has ≥4 failure reasons (at most 40 reasons per criterion) — billed to the wallet like other assists; cached responses cost nothing. Criteria with fewer than 4 reasons are listed with no clusters. |
| get_evalA | Fetch one eval run's status, progress and — once DONE — its full results (per-arm win rate with CI, W/T/L, latency, eval cost, savings, or corrected pass rates for criterion runs); poll this after creating a run. GET /v1/evals/{id} (API-key scope: read). Returns: The run object: {id, name, rubric, rubric_type, eval_kind, criterion_snapshot, baseline_model, candidate_models, arms:[{key, model, label, system, tools, n}], judge_model, sample_count, sample_filters:{model?, tag?, segment?, dataset_id?, trace_replay?, screening?}, status (PENDING|RUNNING|DONE|ERROR|CANCELLED), error (null, a failure reason, or "Cancelled by "), results (see list_evals for the comparison / criterion / screening shapes), assertions, created_at, progress_ratio (0..1; completed inference units over total — feed a progress bar)}. Notes: 404 {code:"not_found"} when the run is not in this key's workspace. results is null until DONE. A DONE screening's results.screening carries the similarity lens (match rate — never part of win/loss), quality-vs-cost placement per candidate and a recommendation (a "keep" is a first-class good outcome). Read-only, no spend. |
| delete_evalA | Permanently delete a finished (DONE|ERROR|CANCELLED) eval run together with its samples, generated answers and judge verdicts — for cleaning up runs a customer no longer needs as evidence. DELETE /v1/evals/{id} (API-key scope: evals:write). Returns: 200 {id} of the deleted run. Notes: Requires an OWNER/ADMIN minting user (403 otherwise). A PENDING|RUNNING run is refused with 400 "Cancel it first — a live run can't be deleted." — call POST /v1/evals/{id}/cancel first. 404 if not found in this workspace. Deletion is irreversible and cascades to samples/outputs/verdicts; the models, judges and criteria the run referenced are untouched. After deletion GET /v1/evals/{id}/gate and /evidence return 404. No spend. |
| cancel_evalA | Stop a PENDING or RUNNING eval run so no further generations or judge calls are billed — use it when a run was misconfigured or is no longer needed. POST /v1/evals/{id}/cancel (API-key scope: evals:write). Returns: 200 with the run object (same shape as GET /v1/evals/{id}) with status "CANCELLED" and error set to "Cancelled by <email of the key's minting user>"; progress_ratio reflects work completed so far. Notes: Requires OWNER/ADMIN minting user (403) — same gate as create, because it controls wallet spend. A run already DONE|ERROR|CANCELLED is refused 400 ("This run is already done — only a pending or running eval can be cancelled."); a terminal result is never overwritten. 404 if not in this workspace. Cancelled runs are inert: no future tick claims them, so nothing more is billed; a slice already in flight finishes its bounded batch (already-paid work), and in the rare race where that was the last slice the run may still land DONE with real results. No request body is read. |
| get_eval_evidenceA | Download the forwardable proof bundle for a DONE eval run — verdict, frozen judge calibration and certificate, per-sample verdict lineage, refusal ledger events and an audit hash-chain attestation, optionally with the embedded CI-gate decision — so a customer can hand a reviewer or auditor one signed JSON that links claim to instrument to data. GET /v1/evals/{id}/evidence (API-key scope: read). Returns: 200 bundle (snake_cased): {signature|null, unsigned?:true (when no signing secret is configured), bundle_v:1, generated_at, run:{id, workspace_id, name, eval_kind, status:"DONE", created_at, sample_count, baseline_model, candidate_models, judge_model, rubric_type, sample_filters, assertions}, results (verdict verbatim), gate:{params, verdict}|null (only when at least one gate param was given), instrument:{judge_model, judge_prompt, criterion_snapshot, certificate|null, note|null}, samples:{count, note, lineage:[{sample id, every verdict with the ordering that measured it ("ab"/"ba" pairwise halves, "abs" absolute, "sim" screening similarity), prompt/answers only with with_content}]}, content:{included, reason}, refusals:{window_days, scope, count, complete, events:[{kind, subject, reason, created_at}]}, attestation:{ok, checked_rows, head_seq, problems:[{seq, kind, detail}], acknowledged, window:{since, until, from_seq}|null, chain_head:{seq, last_hash}|null, statement}}. Notes: 404 for a run outside this key's workspace. 412 {code:"precondition_failed"} for any run that is not DONE (PENDING/RUNNING/ERROR/CANCELLED) — fail closed like the gate; poll until DONE. with_content silently degrades (never errors) when request logging is off. Read-only, no spend. |
| get_eval_gateA | Turn a finished eval run into a CI deploy decision with one call — 200 when every requested threshold passes, 412 otherwise — so a pipeline can |
| get_eval_samplesA | Inspect the test cases behind a run's score — each sampled prompt, the answer every arm produced (reasoning traces stripped, as the judge saw them) and the per-sample verdict — the audit trail that makes a win rate trustworthy. GET /v1/evals/{id}/samples (API-key scope: read). Returns: A bare JSON array (no list envelope), one item per sample in order: {prompt (messages rendered as "ROLE: content" lines, clipped to 2000 chars), baseline_answer (the baseline's fresh answer, or the stored logged reply when baseline is "stored"; empty string on criterion runs), candidates:[{model (arm key), answer (clipped to 2000 chars), outcome}]}. outcome is "win"|"loss"|"tie"|"failed" (judge gave no reading) on comparison runs and "pass"|"fail"|"unparsed" on criterion runs; criterion runs list the baseline among candidates. Notes: 404 when the run is not in this workspace. Works on any status (partial data while RUNNING; empty array before sampling). Texts are clipped server-side at 2000 chars with a "…[clipped]" marker — use GET /v1/evals/{id}/evidence?with_content=true for full transcripts. Comparison items also carry human_verdict ("candidate"|"baseline"|"tie"|null) per candidate once pairs are labelled (label_eval_pair). Read-only, no spend. |
| label_eval_pairA | Record a HUMAN's verdict on one candidate-vs-baseline pair of a comparison run — the calibration evidence behind the corrected win rate. A pairwise judge's printed win rate is compressed toward 50/50 (a true 80/20 prints ~70/30 even for a judge at the human ceiling); from 30 labels the run reports a corrected rate with an interval that carries the calibration uncertainty. POST /v1/evals/{id}/pair_labels (API-key scope: evals:write). Returns: {sample_index, candidate, verdict, critique, pairwise} — pairwise is the run's refreshed calibration block (same shape as get_eval_pairwise), so one call shows what the label bought. One label per (sample, candidate); posting again overwrites. DELETE /v1/evals/{id}/pair_labels?sample_index=…&candidate=… removes one; GET lists them. Notes: Labelling is a human's job: only relay verdicts the user actually gave — never invent preferences to reach 30. 400 with the offender named on a bad sample_index/candidate/verdict; 404 when the run is not in this workspace. No spend. |
| get_eval_pairwiseA | The pairwise judge's spec sheet and calibration for a comparison run — position bias measured on THIS run's pairs (it ranges from +0 to +45 points for the same judge on different traffic), tie behaviour, swap consistency, and the corrected win rate once pairs are labelled. GET /v1/evals/{id}/pairwise (API-key scope: read). Returns: {v, computed_at, labelled_pairs, calibration: null until pairs are labelled, else {n, agreement, kappa, usable, reason (why not, when unusable), wins/losses/decided: per-side Se/Sp with CIs}, candidates: {: {spec_sheet: {pairs, failed_pairs, swap_consistency (+CI), picks_first/second/tie, first_minus_second (+CI — the position bias in points; humans measure ~0), tie_rate, disagreement_ties (ties that are the two orderings disagreeing = the judge preferring whichever answer it saw first)}, labelled_pairs, corrected: null until usable, else {win_rate (½ + (W−L)/2, tie = half a win, parity 50%), win_rate_ci (Lang–Reiczigel — includes calibration uncertainty), floor_half_width (the width no amount of judged pairs can beat at this label count), calibration_variance_share, decided: the ties-dropped variant}}}}. Notes: Recomputed on read from the run's persisted verdicts and labels — never stale. The same block is stored on the run's results as results.pairwise at finalize and on every label write, so gate/evidence read identical numbers. Report the corrected rate WITH its interval and the printed rate alongside; the floor says when to ask for more labels instead of more samples. 404 when the run is not in this workspace; 400 for criterion runs. Read-only, no spend. |
| list_training_filesA | List the training files this workspace has uploaded for fine-tuning, newest first, so a customer can find the file id to start a job with. GET /v1/fine_tuning/files (API-key scope: read). Returns: A bare JSON array (no list envelope): [{id (local record id), provider_file_id (the opaque upstream file id — THIS is the value to pass as training.file_id / training_file_id when creating a job), filename, bytes, purpose ("fine-tune"), created_at}]. Notes: Feature-flag gated: when the |
| upload_training_fileA | Upload a JSONL training (or validation) file for fine-tuning; the returned provider_file_id is what a job creation references. POST /v1/fine_tuning/files (API-key scope: platform:write). Returns: 201 {id, provider_file_id, filename, bytes, purpose:"fine-tune", created_at}. Use provider_file_id (not id) as training.file_id / training_file_id / validation_file_id in POST /v1/fine_tuning/jobs. Notes: Request must be multipart/form-data with a 'file' field — 400 "Expected multipart/form-data with a 'file' field" / "Missing 'file' field" otherwise. Requires OWNER/ADMIN minting user (403). Rate limited per workspace: 20 uploads per 60s → 429 with Retry-After. Feature-flag gated (404 when fineTuning is off). Storage is billable; the upload itself does not charge the wallet. Scopes are not enforced on this route today (see list_training_files). |
| list_fine_tuning_jobsA | List this workspace's fine-tuning jobs (newest first) with live status, progress, output model and price — for monitoring training from CI or a script. GET /v1/fine_tuning/jobs (API-key scope: read). Returns: A bare JSON array: [{id, provider_job_id, name|null, base_model, method ("supervised"|"spec-draft"), status (VALIDATING_FILES|QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED), fine_tuned_model|null, deployed_model_name|null (servable name after deployment), deploy_status|null ("queued"|"staging"|"relaying"|"converting"|"provisioning"|"serving"|"failed"), deploy_error|null, trained_tokens (string)|null, trained_steps|null, total_steps|null, rate_per_m_token_usd (customer price per 1M trained tokens), billed_cost_usd|null (set on completion), error|null, created_at}]. Notes: Statuses are reconciled live against the training backend on each call (best effort; DB state served on backend error); a locally terminal status is never resurrected. Internal margin (markup) is stripped from the wire shape. Feature-flag gated (404 when fineTuning is off). Read-only, no spend. Scopes not enforced on this route today. |
| create_fine_tuning_jobA | Start a supervised (SFT/LoRA) or spec-draft fine-tune of a catalog base model on an uploaded file or a workspace dataset, with bounds-checked hyperparameters — the way a customer trains a custom model from their own data. POST /v1/fine_tuning/jobs (API-key scope: platform:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: 201 {id (job id for all other /v1/fine_tuning/jobs/{id} calls), provider_job_id}. Poll GET /v1/fine_tuning/jobs/{id} for status and fine_tuned_model. Notes: MONEY: the wallet must hold a prepay runway of (1,000,000 estimated trained tokens × the model's per-token rate incl. markup) or the call fails 402 "Insufficient balance: starting a fine-tune requires at least $X of runway. Top up and try again."; the final charge is metered from real trained tokens on completion (billed_cost_usd). Requires OWNER/ADMIN minting user (403). Rate limited per workspace: 20 creates per 60s → 429 with Retry-After. 400 "Invalid JSON body" or "Invalid body: — " (e.g. missing training data: pass training or training_file_id). 400 when no price is configured for the model ("No fine-tuning price is set for this model yet."). Top-level keys accept both snake_case and camelCase; nested hyperparameter/integration/mapping keys are snake_case only. Feature-flag gated (404 when fineTuning is off). Scopes not enforced on this route today. |
| get_fine_tuning_jobA | Get one fine-tuning job's live status, step progress, trained tokens, output model name, deployment state and billed cost — poll this after creating a job. GET /v1/fine_tuning/jobs/{id} (API-key scope: read). Returns: {id, provider_job_id, name, base_model, method, status (VALIDATING_FILES|QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED), fine_tuned_model|null, deployed_model_name|null, deploy_status|null, deploy_error|null, trained_tokens (string)|null, trained_steps|null, total_steps|null, rate_per_m_token_usd, billed_cost_usd|null, error|null, created_at}. Notes: 404 "Job not found" when the job is not in this workspace. Live progress (trained_steps/total_steps) is fetched from the training backend best-effort; DB state is served if that fails. A SUCCEEDED job is trainable-not-servable until deployed (deployed_model_name stays null). Feature-flag gated (404 when fineTuning is off). Read-only, no spend. Scopes not enforced on this route today. |
| cancel_fine_tuning_jobA | Cancel a queued or running fine-tuning job so no more training is metered — the customer's cancel is authoritative even if the backend lags. DELETE /v1/fine_tuning/jobs/{id} (API-key scope: platform:write). Returns: 200 {ok:true}. The job's status becomes CANCELLED (idempotent: cancelling an already-CANCELLED job also returns 200 {ok:true}). Notes: This is a CANCEL, not a delete — the job record remains listed. Requires OWNER/ADMIN minting user (403). 404 when not in this workspace. 400 "This run already finished — there is nothing to cancel." for SUCCEEDED or FAILED jobs. The backend cancel is attempted but a backend error does not block the local cancel; metering treats local CANCELLED as final and finalizes at $0 further spend. Feature-flag gated (404 when fineTuning is off). Scopes not enforced on this route today. |
| get_fine_tuning_bakeoffA | Read a fine-tune's bake-off state, verdict (improved / regressed / inconclusive with NLL, perplexity and optional judged pass rates) and ledger-true spend — to decide whether the tuned model is worth deploying. GET /v1/fine_tuning/jobs/{id}/bakeoff (API-key scope: read). Returns: {status ("none"|"queued"|"running"|"done"|"failed"), error|null (customer-safe reason when failed), holdout_present (false = no validation split, so a comparison cannot be offered), available (platform compute configured), estimated_max_usd|null (the consent ceiling a start would hold; null when no GPU rate is configured), verdict|null: {verdict ("improved"|"regressed"|"inconclusive", sign-test backed), nll_base, nll_tuned, win_count, total, ppl_base, ppl_tuned, judged?: {criterion_id, criterion_name, base_pass_rate, tuned_pass_rate, scored}|null}, spent_usd}. Notes: Unlike other GETs this one requires an OWNER/ADMIN minting user (403 otherwise) because it reads spend. 404 "Run not found" when the job is not in this workspace. status "none" with holdout_present=false means the run can never be compared (no held-out split). Feature-flag gated (404 when fineTuning is off). Read-only, no spend. |
| start_fine_tuning_bakeoffA | Start a held-out bake-off that proves a succeeded supervised fine-tune against its base model (teacher-forced NLL/perplexity wins, optionally a judged win-rate) on an ephemeral GPU box — quality proof without deploying the model. POST /v1/fine_tuning/jobs/{id}/bakeoff (API-key scope: platform:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: 202 {trigger_run_id} — the comparison is queued; poll GET /v1/fine_tuning/jobs/{id}/bakeoff for status and verdict. Notes: MONEY: metered GPU-box minutes (plus judge calls) bill to the wallet under the bakeoff:: ledger prefix; a wallet HOLD for the whole cost ceiling (max 3 hours × up to 2 GPUs at the reference GPU rate, with markup) is placed before starting — 402 ("…Top up and try again.") if the wallet cannot hold it; only metered minutes are actually billed and the hold is released at the end. Requires OWNER/ADMIN minting user (403). Rate limited per workspace: 20 starts per 60s → 429. 404 "Run not found". 400 for: a spec-draft job, a job not SUCCEEDED, no held-out split ("A comparison needs a held-out split the model didn't train on — this run has none."), a comparison already queued/running, comparison compute or orchestration not configured on the platform, a base model whose parameter count can't be parsed from its name, or a base model over 75B parameters. Invalid JSON body → 400. Feature-flag gated (404 when fineTuning is off). Scopes not enforced on this route today. |
| list_grpo_runsA | List the workspace's online-RL (GRPO) training runs with ledger-true spend and outcomes, plus how many self-improvement candidates are waiting in the queue — use it to monitor training and decide whether to start another run. GET /v1/grpo/runs (API-key scope: read). Returns: JSON object: { candidates_waiting: integer, auto_provision_available: boolean, runs: [ { id, status (ACTIVE|STOPPED|COMPLETED|FAILED|OVERBUDGET), model, budget_usd, spent_usd (reward/judge spend), gpu_spent_usd, env_spent_usd, gpu_hour_budget: number|null, gpu_rate_usd_per_hour: number|null, created_at (ISO), outcome: null | { steps?, first_half_mean_reward?, second_half_mean_reward?, stopped_by_tripwire?, bakeoff?: { verdict, delta, delta_ci95: [lo, hi], prompts, k, mean_sim_fraction, mean_tool_steps } } } ] }. Newest first, at most 50 runs; outcome is only fetched for the 10 newest non-ACTIVE runs (older ones return outcome null). Notes: Feature-flag gated: the entire training API (fineTuning flag) returns 404 'Fine-tuning is not enabled' when the flag is off. The key's minting user must be workspace OWNER/ADMIN or the call is 403. Spend figures come from the billing ledger, never self-reported. No pagination parameters. |
| start_grpo_runA | Start an online-RL (GRPO) training run that improves a fine-tunable base model against a calibrated judge as the reward, with hard reward and GPU-hour budgets — use it to turn logged traffic or the candidate queue into a trained adapter. POST /v1/grpo/runs (API-key scope: platform:write). SPENDS MONEY: this starts billable work on the workspace wallet. Returns: 201 with { trigger_run_id: string } — the orchestration handle for the run (the GrpoRun id shows up in GET /v1/grpo/runs once registered). Notes: SPENDS MONEY: the whole commitment (rewardBudgetUsd + GPU hours at the frozen marked-up rate, or a conservative ceiling for autoProvision) is atomically HELD on the wallet at start; 402 when the wallet can't hold it; 400 'No wallet for this workspace' when there is no payment method. Body keys are camelCase only (except environment.proxy_base_url / max_steps and verifier[].timeout_sec, which are snake_case); unknown keys pass through. Zod validation failure returns 400 { error: 'Invalid body: — ' } (flat error shape). Rate limited to 20 starts/min per workspace (429). Feature-flag gated (fineTuning flag off → 404). Requires OWNER/ADMIN (403). Other 400 refusals: unaligned/wrong-unit/drift-flagged judge, iterated-RL round gate (fresh grades needed on a self-trained policy), model not fine-tunable, promptCount < 10, not enough logged prompts or queued candidates, autoAdopt alias missing, no GPU price set, orchestration not configured. |
| get_grpo_runA | Fetch one online-RL (GRPO) run's status, ledger-true spend, and training outcome (reward trend, bake-off verdict) — use it to poll a run you started. GET /v1/grpo/runs/{id} (API-key scope: read). Returns: JSON object: { id, status, model, budget_usd, spent_usd, gpu_spent_usd, env_spent_usd, gpu_hour_budget, gpu_rate_usd_per_hour, created_at, outcome: null | { steps, first_half_mean_reward, second_half_mean_reward, stopped_by_tripwire, bakeoff?: { verdict, delta, delta_ci95, prompts, k, mean_sim_fraction, mean_tool_steps } } }. Outcome is always attempted for this single run (null while ACTIVE or when no artifact exists). Notes: Feature-flag gated (fineTuning flag off → 404). OWNER/ADMIN key required (403). 404 'Run not found' for foreign ids. |
| stop_grpo_runA | Stop an ACTIVE online-RL (GRPO) run and release its wallet hold immediately — use it to cut a run short when spend or results aren't what you expected. POST /v1/grpo/runs/{id}/stop (API-key scope: platform:write). Returns: 200 { ok: true }. The run flips to STOPPED; the reward server refuses further scoring and the orchestrator exits on its next sweep. Notes: Idempotency: a run that is not ACTIVE (already stopped/completed) or not in the workspace returns 404 'Run not found or not active'. Money: the commitment hold is released right away; already-metered spend stays billed. Feature-flag gated (fineTuning flag off → 404). OWNER/ADMIN key required (403). No body is read. |
| get_grpo_run_weightsA | Get short-lived presigned download links for a finished online-RL (GRPO) run's trained adapter files so you can self-host the weights — use it after a run completes (or stops with a partial checkpoint). GET /v1/grpo/runs/{id}/weights (API-key scope: read). Returns: JSON object: { run_id, status, partial: boolean (true for STOPPED/FAILED — files are a partial checkpoint, not the finished adapter), files: [ { name (e.g. adapter_model.safetensors), size_bytes: integer|null, url (presigned GET, valid 15 minutes), expires_at (ISO) } ], empty_reason?: string (present when files is empty), storage_unavailable?: true (weight storage not configured — try later) }. Files sorted safetensors first, then adapter files, then config/tokenizer. Notes: 400 while the run is ACTIVE ('Weights are available once the run finishes.') and for OVERBUDGET runs ('This run has no trained adapter to download.'). 404 for foreign/unknown runs. Links expire after 15 minutes — re-call to refresh. Every call is audit-logged as a weight export. Feature-flag gated (fineTuning flag off → 404). OWNER/ADMIN key required (403). |
| list_label_setsA | List the workspace's golden sets — named collections of human-graded requests with their measured label quality (inter-rater kappa) — use it to see which sets exist, which are frozen, and which judges calibrate on them. GET /v1/label_sets (API-key scope: read). Returns: JSON array (bare array, newest first) of { id, name, description, size: integer, membership_hash: string|null, frozen_at: ISO|null, kappa: number|null, agreement: number|null, kappa_n: integer|null, rater_count: integer|null, attached_to: [ { id, name } ] (criteria calibrating on this set), created_at }. Notes: Returns a bare JSON array, not a { object: 'list' } envelope. kappa is null until the set is frozen, and stays null after freezing when no blind re-grades by a second rater exist inside the set. |
| create_label_setA | Create a golden set from human-graded requests — either explicit request ids or the newest N grades — as the first step toward a frozen, kappa-measured calibration set for a judge. POST /v1/label_sets (API-key scope: evals:write). Returns: 201 with the set object: { id, name, description, size, membership_hash: null, frozen_at: null, kappa: null, agreement: null, kappa_n: null, rater_count: null, attached_to: [], created_at }. The set is NOT frozen yet. Notes: A set needs at least 20 distinct graded requests (400 'A golden set needs at least 20 graded requests (have N)') and at most 5000. Verifier-sourced labels never count as members. Freeze the set (POST /v1/label_sets/{id}/freeze) before attaching it to a criterion. |
| attach_label_setA | Attach a frozen golden set to a judge criterion so its calibration runs on that set and its certificate carries the set's kappa — or detach it. POST /v1/label_sets/{id}/attach (API-key scope: evals:write). Returns: 200 { criterion_id: string, label_set_id: string|null } — the criterion's new golden-set binding. Notes: 400 'Freeze the golden set first — an unfrozen set can change under the calibration.' when the set has no frozen_at. 404 'Golden set not found' for foreign/unknown set ids (when attaching). A criterion holds at most one golden set; attaching replaces the previous one. |
| freeze_label_setA | Freeze a golden set: seal its membership with a hash and measure inter-rater agreement (kappa) on its members — required before a judge can calibrate on it. POST /v1/label_sets/{id}/freeze (API-key scope: evals:write). Returns: 200 with the updated set object: { id, name, description, size, membership_hash (sha256 of sorted member ids), frozen_at (ISO), kappa: number|null, agreement: number|null, kappa_n: integer|null, rater_count: integer, attached_to: [ { id, name } ], created_at }. Notes: No body. Re-freezing an already-frozen set re-measures kappa/agreement/rater_count but keeps the original frozen_at and membership. kappa is null (and reported as null, never as good) when fewer than two raters' blind re-grades exist inside the set. 400 if inter-rater stats are unavailable. |
| list_labelsA | List the workspace's human pass/fail grades (ground-truth labels) on logged requests, newest first — use it to audit or export the verdicts every judge is measured against. GET /v1/labels (API-key scope: read). Returns: JSON { object: 'list', total: integer (matching rows across all pages), data: [ { id, request_id, verdict ('pass'|'fail'), critique: string|null, source ('human'|'assist_accepted'|'verifier'), scope ('request'|'trace'), created_at (ISO) } ] }. Notes: Offset pagination: page through with offset += limit until offset >= total. Rows include labels of every source (human, assist_accepted, verifier). |
| create_labelA | Record a human or downstream-system pass/fail verdict on a logged request (or on the whole agent run it belongs to) — this is the ground truth judge calibration, corrected pass rates, and training rewards are measured against. POST /v1/labels (API-key scope: evals:write). Returns: 201 with the stored label: { id, request_id, verdict, critique, source ('human'), scope, fail_causes: string[], created_at }. Notes: Upsert by request_id: re-labeling the same request replaces the verdict/critique/scope (newest judgment wins); a 'pass' clears any prior failure attributions. Requires the key's minting user to be workspace OWNER/ADMIN (403) — labels define quality. Side effects: settles pending judge suspicions on the trace, fulfils pending recalibration-slice requests, and flags affected judges' calibrations for revision. Validation errors (e.g. bad scope, over-long critique) return 400 with the schema message. |
| list_logsA | Browse the workspace's logged chat exchanges (request messages + assistant reply, secret-scrubbed) with filters for model, tag, auto-detected traffic segment, finish reason, cache hit and time range — use it to inspect real traffic before grading, building datasets, or running evals. GET /v1/logs (API-key scope: read). Returns: JSON { object: 'list', total: integer, limit, offset, data: [ { request_id, created_at (Unix seconds), model, tag, segment (prompt family id), segment_exact (exact system-prompt hash), trace_id, finish_reason, streamed: boolean, cache_hit: boolean, fallback_from: string|null, prompt_tokens, completion_tokens, messages: parsed JSON array of request messages (null if unparseable), response: parsed assistant message object (null if unparseable) } ] }. Newest first, successful (non-aborted) exchanges only. Notes: Request logging is opt-in per workspace: returns 409 { error: { message, type: 'invalid_request_error', code: 'logging_disabled' } } when it is off — an empty list would otherwise read as 'no traffic'. Offset pagination: repeat with offset += limit until offset >= total. Pass a row's |
| export_logsA | Export the filtered logged exchanges as JSONL in chat format — one {"messages":[...]} line per exchange with the assistant reply appended — ready to pipe into your own training or eval tooling. GET /v1/logs/export (API-key scope: read). Returns: 200 with Content-Type application/jsonl; charset=utf-8. Body: newline-terminated lines, each {"messages": [ ...request messages, assistantReplyMessage ]}, newest first. Response headers: X-Omnia-Export-Count (lines written) and X-Omnia-Export-Capped ('true' when the 10,000-row cap was hit — narrow the filter, e.g. a time range, to get the rest). Notes: No limit/offset — the export is capped at 10,000 rows; use X-Omnia-Export-Capped to detect truncation. Rows whose stored JSON doesn't parse are skipped, never fail the export. 409 { error: string } (flat shape) when request logging is disabled for the workspace. Only successful (non-aborted) exchanges are exported. |
| list_model_versionsA | List the workspace's model-version chain — one immutable record per completed training round, pinning the judge, curriculum and holdout its verdict depended on — use it to review improvement history and pick a version to adopt or roll back to. GET /v1/model_versions (API-key scope: read). Returns: JSON { object: 'list', data: [ { id, parent_id: string|null, base_model, artifact_ref, served_model: string|null (null = not deployed/servable yet), source_run_id, source_kind ('grpo'|'finetune'), verdict: any (bake-off verdict JSON), judge_criterion_id, curriculum_hash, holdout_hash, comparable_to_parent: boolean (true only when parent's holdout hash matches — otherwise treat the delta as a discontinuity), adopted_at: ISO|null, created_at: ISO } ] }. Newest first, at most 200. Notes: NOT feature-flag gated (deliberately readable even when training is paused, so the audit trail stays visible). The key's minting user must be workspace OWNER/ADMIN (403). No pagination beyond the 200 cap. |
| get_model_versionA | Fetch one model version's record (lineage, artifact, served model name, verdict, pinned hashes, adoption time) — use it to inspect a specific training round before adopting it. GET /v1/model_versions/{id} (API-key scope: read). Returns: JSON object { id, parent_id, base_model, artifact_ref, served_model, source_run_id, source_kind, verdict, judge_criterion_id, curriculum_hash, holdout_hash, comparable_to_parent, adopted_at, created_at } — same shape as the list rows. Notes: Not feature-flag gated. OWNER/ADMIN key required (403). |
| adopt_model_versionA | Point a model alias at a deployed model version — adoption and rollback are the same audited repoint on different rows of the chain — use it to promote a trained round into production or roll back to an earlier one. POST /v1/model_versions/{id}/adopt (API-key scope: platform:write). Returns: 200 { served_model: string } — the model name the alias now resolves to. Notes: MOVES PRODUCTION TRAFFIC: the alias's target is replaced and any live canary split on it is cleared (canary_model null, canary_percent 0). 400 'This version is not deployed yet — deploy its weights before routing traffic to it.' when served_model is null. 404 for a foreign version id or unknown alias. Feature-flag gated (fineTuning flag off → 404) unlike the GET routes. OWNER/ADMIN key required (403). Zod failure returns 400 { error: 'Invalid body: aliasName — ...' } (flat shape). Adoption stamps adopted_at on first adoption only; it is audit-logged. |
| list_raft_roundsA | List the workspace's self-improvement (rejection-sampling fine-tuning) rounds, newest first, with winners/give-ups, budget and ledger-true spend — read-only observability for rounds started from the dashboard. GET /v1/raft/rounds (API-key scope: read). Returns: JSON { rounds: [ { id, status, criterion_name, policy_model, prompt_count, candidates_per_prompt, winners_count, giveups_count, produced_job_id: string|null (the fine-tuning job a COMPLETED round produced), skip_reason: string|null (SKIPPED rounds), budget_usd: number|null, spent_usd: number, created_at: ISO, completed_at: ISO|null } ] }. At most 200 rounds. Notes: List-only: there is no public start endpoint for rounds. Feature-flag gated (fineTuning flag off → 404). OWNER/ADMIN key required (403). Returns { rounds: [] } (not an error) when the round history table hasn't been provisioned yet. spent_usd is 0 for rounds created before per-round budgets existed. |
| get_judge_settingsA | Read the workspace's default judge model (used for eval runs that don't name their own judge) and the platform's house default. GET /v1/settings/judge (API-key scope: read). Returns: JSON { default_judge_model: string|null (null = house default / auto), house_default: string }. Sent with Cache-Control: no-store. Notes: Precedence at run time: a run's own judge_model > this workspace default > house_default. Screening still swaps a default that would judge its own sibling model. |
| set_judge_settingsA | Set (or clear) the workspace's default judge model for eval runs that don't specify one — must be a chat model from the platform catalog offered to this workspace. PUT /v1/settings/judge (API-key scope: platform:write). Returns: 200 { default_judge_model: string|null, house_default: string } — the settings after the update. Notes: Judges run on the platform's key and are metered to the wallet, so only platform-catalog models qualify (a workspace's own provider keys are for inference, not judging). Needs platform:write (it is a workspace setting). |
| get_setup_statusA | Answer 'where am I and what should I do next?' in one call — workspace identity, logging state, traffic and grade counts, judge calibration progress, and the single dependency-ordered next step; also the cheapest way to check that an API key is live and which workspace it belongs to. GET /v1/setup/status (API-key scope: read). Returns: JSON (camelCase keys — this route does NOT snake-case): { workspace: { slug, name }, logging: { enabled: boolean, retentionDays: integer }, traffic: { loggedConversations: integer|null (null = log store unreachable, NOT zero traffic) }, grades: { total, neededToCalibrate (grades still short of the 30 required) }, judges: { total, calibrated, trustworthy, failGradesNeeded: integer|null }, next: { action: 'enable_logging'|'send_traffic'|'grade'|'create_judge'|'calibrate'|'grade_failures'|'recalibrate'|'compare', detail: string, href: string (dashboard path) } }. With request header Accept: text/plain the same data is returned as flat snake_case key=value lines (e.g. grades_total=12, next_action=grade), one per line. Notes: Never cached (Cache-Control: no-store). A 200 proves the key is valid; 401 otherwise. Field casing differs from every other /v1 route (camelCase in JSON, snake_case only in the text/plain form). |
| get_traceA | Fetch every logged step of one agent run or conversation (grouped by the X-Omnia-Trace-Id you sent), oldest-first in execution order and including aborted partials — use it for error analysis of a multi-step run. GET /v1/traces/{traceId} (API-key scope: read). Returns: JSON { object: 'list', trace_id, data: [ { request_id, created_at (Unix seconds), model, alias: string|null, tag, status ('SUCCESS'|'ABORTED'), finish_reason, streamed, cache_hit, fallback_from, prompt_tokens, completion_tokens, messages: parsed request messages (null if unparseable), response: parsed assistant message (null if unparseable) } ] } ordered oldest first. Notes: Requires request logging to be enabled — 409 { error: string } (flat shape) otherwise. Unlike /v1/logs this includes ABORTED partial rows (a run that died at step 4 is the finding). No pagination or filters. |
| verify_documentA | Verify that a downloaded certificate or evidence bundle was issued by the platform and has not been altered, by re-deriving its HMAC signature — use it when a third party hands you a document and you need to trust its numbers. POST /v1/verify (API-key scope: read). Returns: Always 200 for a well-formed body: { ok: true, key_id: string } when the bytes are ours and unaltered; otherwise { ok: false, reason: 'unsigned' (no signature field) | 'malformed' (not an object or signature shape wrong) | 'unknown_key' (signed by a key this platform doesn't hold, e.g. after rotation) | 'mismatch' (any field was edited) | 'no_secret' (verification not configured on the platform) }. Notes: Read scope suffices (POST that writes nothing); nothing is stored. Verification canonicalises the document (keys sorted recursively, undefined dropped) before hashing, so key order does not matter but any value change does. Sent with Cache-Control: no-store. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| set-up-errorbar | Fetches the canonical setup instructions and follows them: one integration path, verified with a receipt, then the after-traffic-flows steps. |
| after-traffic-flows | The five steps once traffic is being captured, each with the tool that performs it. |
| release-check | Gate a finished eval run on the interval and report ship/hold with every check. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| agent-setup | The canonical instruction set: pick ONE integration path (gateway, your own provider key, OpenTelemetry, tracing SDK), verify with the setup check, then the after-traffic-flows steps. Same file as https://www.errorbar.ai/agent-setup.md. |
| llms-txt | The product described for a model: what it does in the order it runs, the doctrine for reporting numbers, how to connect, where the API is. Same as https://www.errorbar.ai/llms.txt. |
| after-traffic-flows | Logging → screening → one judge → calibrate at ~30 grades → gate before shipping. Each step names the MCP tool that performs it. |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/omnia-v/errorbar-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server