> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnia-voice.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GRPO runs API

> Start, monitor, stop, and download online-RL runs programmatically — with validated rewards and ledger-true budgets.

Online-RL runs are fully scriptable: key-authed, workspace-scoped, owner/admin
only, at `https://gateway.omnia-voice.com/v1`. Request bodies on this surface
are **camelCase** (`promptCount`, `rewardBudgetUsd`); responses are snake\_case
like the rest of the management API.

## Start a run

```bash theme={null}
curl -X POST https://gateway.omnia-voice.com/v1/grpo/runs \
  -H "Authorization: Bearer $OMNIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reward": { "mode": "single", "criterionId": "crit_..." },
    "model": "Qwen/Qwen3-32B",
    "promptCount": 50,
    "rewardBudgetUsd": 5,
    "gpuHourBudget": 4,
    "useCandidateQueue": true
  }'
```

Returns `201` with `{"trigger_run_id": "..."}`; the run itself appears at the
top of `GET /v1/grpo/runs` immediately.

The `reward` is validated before anything spends: it must reference a
**calibrated, non-drift-flagged** judge, and an unvalidated judge is refused
with `400`. A reward you can't trust trains a model you can't trust, so there
is no override. `mode: "compositional"` combines several judges
(`criterionIds`) and optional deterministic assertions into one reward.

Budgets are hard, server-side, and ledger-true:

* `rewardBudgetUsd` caps judge/reward spend. The reward server refuses the
  run's next scoring call once the ledger crosses the ceiling; the run ends
  `OVERBUDGET`. The trainer never self-reports spend.
* `gpuHourBudget` caps wall clock on the training side.

Useful optional fields (see the API reference for the full schema):

| Field                                            | What it does                                                                                                                                                                                            |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `promptTag`                                      | Sample training prompts from traffic with this tag instead of the queue.                                                                                                                                |
| `useCandidateQueue`                              | Train on the prompts offline self-training gave up on.                                                                                                                                                  |
| `platformGpu` / `autoProvision`                  | The platform provisions the GPU box; the rate is frozen at start and billed hourly while the run is `ACTIVE`, under its own meter. Omit both to bring your own GPU (no GPU billing).                    |
| `groupSize`, `maxSteps`, `maxCompletionTokens`   | The usual GRPO knobs; bounds live server-side, shared with the dashboard.                                                                                                                               |
| `holdoutCount`                                   | Held-out prompts for the bake-off. Use 50+ if you want the verdict to mean something.                                                                                                                   |
| `environment`, `allowSideEffects`, `tasksInline` | Agentic mode: episodes run against your [declared tools](#agent-tools-the-consent-surface), and the reward judge must be trace-unit calibrated. Side effects stay off unless you explicitly allow them. |
| `autoAdopt`                                      | Opt-in per run: an "improved" bake-off verdict triggers deploy, then a canary on the named alias under the online quality gate.                                                                         |

## List runs

```bash theme={null}
curl https://gateway.omnia-voice.com/v1/grpo/runs \
  -H "Authorization: Bearer $OMNIA_API_KEY"
```

Returns `{"runs": [...], "candidates_waiting": n, "auto_provision_available": bool}`,
newest first (max 50). `candidates_waiting` is the depth of the candidate
queue, the strongest signal for when a run is worth its cost.

Each run carries status (`ACTIVE`, `COMPLETED`, `OVERBUDGET`, `STOPPED`, or
`FAILED`: the trainer gave up, as opposed to `STOPPED`, an external
termination), plus three separate ledger-true meters: `spent_usd` (reward,
counted against the budget), `gpu_spent_usd`, and `env_spent_usd` (sandbox
execution). While a platform GPU is attached, `gpu_rate_usd_per_hour` is the
live burn rate.

Terminal runs include an `outcome` when the training box produced its
artifact: `steps`, the reward trend (`first_half_mean_reward` vs
`second_half_mean_reward`), `stopped_by_tripwire` (the reward-hacking
tripwire), and a `bakeoff` card with the held-out verdict, `delta`, and
`delta_ci95`. A missing outcome on a `FAILED` run is honest, not a bug.

## Get, stop

```bash theme={null}
curl https://gateway.omnia-voice.com/v1/grpo/runs/{id} \
  -H "Authorization: Bearer $OMNIA_API_KEY"

curl -X POST https://gateway.omnia-voice.com/v1/grpo/runs/{id}/stop \
  -H "Authorization: Bearer $OMNIA_API_KEY"
```

Stopping flips the status; that flip is the whole mechanism. The reward server
refuses the run's next scoring call and the orchestrator exits on its next
sweep. Only `ACTIVE` runs can be stopped (anything else is `404`).

## Download the trained adapter

```bash theme={null}
curl https://gateway.omnia-voice.com/v1/grpo/runs/{id}/weights \
  -H "Authorization: Bearer $OMNIA_API_KEY"
```

Presigned download links for a terminal run's adapter files
(`{"files": [{"name", "size_bytes", "url", "expires_at"}], "partial": ...}`).
`partial: true` means the run didn't complete cleanly and the files are a
checkpoint, not the finished adapter. An empty list comes with an
`empty_reason`; links expire, so re-request fresh ones. This is the same
own-your-weights story as [SFT](/reference/own-your-model).

## Agent tools: the consent surface

Agentic runs may only call tools you've declared:

```bash theme={null}
curl -X POST https://gateway.omnia-voice.com/v1/env/tools \
  -H "Authorization: Bearer $OMNIA_API_KEY" \
  -d '{"name": "search", "endpointUrl": "https://...", "authHeader": "Bearer ...", "readOnly": true}'
```

Endpoints are https-only; credentials are encrypted at rest and never
returned (reads show a redacted prefix). `DELETE /v1/env/tools/{id}` is
consent withdrawal. The list response also carries
`egress_verification_secret`, an HMAC key your endpoint can use to verify
that a call really came from a training run (signature over
`"<timestamp>.<raw body>"` in the `X-Omnia-Signature` header).

## RAFT rounds, read-only

`GET /v1/raft/rounds` lists the offline self-training rounds that feed the
candidate queue: per round, the judge used, winners kept, give-ups, the SFT
job a completed round produced (`produced_job_id`), and ledger-true spend
against its budget. Rounds start from the dashboard today; the API surface is
observability parity.

<Note>
  Run creation is rate-limited per workspace (default 20/min) as an abuse
  guard; a `429` carries `Retry-After`. All endpoints here require an
  owner/admin key, reads included.
</Note>
