---
title: "Submit environment episode results"
method: POST
path: "/v1/validator/episode-results"
tags: ["validator"]
---

# Submit environment episode results

`POST /v1/validator/episode-results`

Submit up to 500 episode results for evaluations owned by the caller. HTTP 200 is a batch acknowledgement: inspect each item's status for acceptance (201), an already-recorded task (409), or rejection (404/422).

## Request body

- SubmitEpisodeResultsRequest — Submit between 1 and 500 episode results in a single request.
  - `results` EpisodeResultEntry[], required — Non-empty list of episode outcomes. Split more than 500 results into multiple requests.
    - `eval_run_id` string, uuid, required — Evaluation run this episode belongs to.
    - `env_pack_sha256` string, required — Pack the task was compiled from.
    - `task_id` string, required — Task identifier within the bound pack.
    - `family` string, required — Task family name (retrieval_recall, intent_decomposition, ...).
    - `outcome` 'completed' | 'partial' | 'agent_error' | 'environment_error' | 'verifier_error' | 'leakage' | 'exploit', required — Terminal task outcome.
    - `verdict_correct` boolean — Whether the deterministic hard gate passed.
    - `verdict_checks` object — Per-check tri-state results (final_in_gold, within_budget, ...).
    - `reward_components` object — Bounded per-family reward gradients — only paid when verdict_correct.
    - `aggregate_reward` union — Terminal reward paid for this episode. MUST be null when verdict_correct is false.
      - number
      - string
    - `wall_seconds` union — Validator-measured monotonic wall time from episode session creation through terminal result finalization.
      - number
      - string
    - `terminal_state_hash` string, nullable — sha256 of the ledger's final state — cross-validator parity anchor. Required for completed / partial / leakage / exploit; MUST be null for the *_error outcomes (no state to compare).
    - `ledger_uri` string, nullable — S3 URI of the complete episode artifact, including its ledger.
    - `step_count` integer — Number of agent steps taken before termination.

## Response `200`

Successful Response

- SubmitEpisodeResultsResponse — Acknowledgement for a processed batch; inspect every per-item status. HTTP 200 does not mean all items were accepted. Request-level validation and authorization failures may instead return a non-200 response.
  - `results` EpisodeResultSubmission[], required
    - `eval_run_id` string, uuid, required
    - `task_id` string, required
    - `status` 201 | 409 | 404 | 422, required
    - `episode_result_id` string, uuid, nullable — Populated when status=201 (row was written).
    - `error` string, nullable — Populated when status != 201; short human-readable reason.
  - `counts` object, required — Per-status count summary — quick health check for callers batching hundreds at a time.

## Other responses

- `401` — Missing or invalid authentication.
- `403` — Caller is not authorized.
- `422` — Validation Error
- `429` — Request rate limited; honor Retry-After before retrying.
- `500` — The request could not be completed.
- `503` — A service is temporarily unavailable.

## Changes

- **2026-09-15** `3d112a2abf4e` — 1 info
  - added the new optional request property `results/items/wall_seconds`
- **2026-09-14** `b53d2cf1bdf6` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/oroagents/apis/oro-api/changes/v1/validator/episode-results/post.md)

---

[API](https://skmtc.dev/oroagents/apis/oro-api.md) · [All operations](https://skmtc.dev/oroagents/apis/oro-api/llms.txt) · [OpenAPI document](https://skmtc.dev/oroagents/apis/oro-api/revisions/3e921f170eed?raw)
