---
title: "Save a row's verdict"
method: PUT
path: "/agent-training/sessions/{session_id}/rows/{row_id}/feedback/"
tags: ["agent-training"]
---

# Save a row's verdict

`PUT /agent-training/sessions/{session_id}/rows/{row_id}/feedback/`

## Request

- Base URL: `https://api.getoneprofile.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getoneprofile.ai/agent-training/sessions/{session_id}/rows/{row_id}/feedback/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Record this cycle's good-or-bad verdict on one row's output. A bad verdict must carry a note saying what to change; a good one carries none. A null verdict withdraws whatever was recorded.

## Path parameters

- `session_id` string, uuid, required
- `row_id` string, uuid, required

## Request body

- TrainingFeedbackUpdateSchema: One row's verdict, and the note a bad verdict has to carry.
  - `feedback` string: What was wrong and how to change it. Required when the verdict is bad.
  - `verdict` 'good' | 'bad': The reviewer's call on one row's output. Kept as its own enum rather than reusing the cells' ``up``/``down`` thumbs: a training verdict is a judgement on a worked example, and the two domains are deliberately independent of each other. ``BAD`` always carries a note, because a complaint with no explanation gives the prompt rewrite nothing to act on. ``GOOD`` never does: it says the output was right, and the rewrite is told to preserve whatever produced it.

## Response `200`

Successful Response

- TrainingSessionReadSchema: A whole training session, as the reviewer's page polls it. Everything the page needs in one payload: the phase (``status``), the cycle, the rows with their results and critiques, and the pending proposal when there is one.
  - `created_at` string, date-time, required
  - `cycle` integer, required
  - `error` string, nullable, required
  - `id` string, uuid, required
  - `progress` TrainingProgressSchema, required: How far the current cycle's run has got.
    - `done` integer, required
    - `failed` integer, required
    - `total` integer, required
  - `proposal` TrainingProposalSchema, required: A pending prompt revision, alongside what it would replace.
    - `changed` boolean, required: False when the model proposed no change at all.
    - `current_prompt` string, required
    - `proposed_prompt` string, required
    - `review` ProposalReviewDTO: Readable explanation and an explicitly illustrative outcome, never a scored run.
      - `changes` string[]
      - `example_after` string, nullable
      - `example_before` string, nullable
      - `example_label` string, nullable
      - `summary` string, required
    - `stale` boolean: True when the agent's prompt changed after this proposal was written, so approving it would be refused. The only signal to warn on: current_prompt is always the live prompt.
  - `rows` TrainingRowReadSchema[], required
    - `changed_fields` string[]: Names of the output fields whose answer meaningfully differs from `previous_value`. Compared by what each field holds, so an agent rewording the same paragraph does not count. Empty when nothing moved, and on a row with nothing to compare against.
    - `confidence` 'high' | 'medium' | 'low', required: The agent's own read on how solid its final answer is.
    - `credits_charged` number, required
    - `cycle` integer, required
    - `error` string, nullable, required
    - `error_class` 'timeout' | 'rate_limited' | 'quota_exhausted' | 'provider_error' | 'network' | 'schema_violation' | 'refused' | 'invalid_input' | 'no_data' | 'enrichment_failed' | 'unexpected', required: Machine-readable classification of a failed enrichment cell. The class decides everything downstream: whether the executor retries (transient classes) or gives up (terminal ones), what the cell tooltip names, and whether the failure is free. Errors never charge, but only a classified error can prove which kind it was. A clean no-answer (no_data) is free for a lookup; an AI column's is charged what it used.
    - `feedback` string, required
    - `id` string, uuid, required
    - `inputs` object, required
    - `outcome` 'succeeded' | 'failed' | 'skipped', required: How one cell's enrichment execution ended. ``SKIPPED`` is a first-class third outcome: the action was deliberately not run (a required input was empty), no work was done, and nothing is charged.
    - `position` integer, required
    - `previous_value` unknown
    - `previous_verdict` 'good' | 'bad': The reviewer's call on one row's output. Kept as its own enum rather than reusing the cells' ``up``/``down`` thumbs: a training verdict is a judgement on a worked example, and the two domains are deliberately independent of each other. ``BAD`` always carries a note, because a complaint with no explanation gives the prompt rewrite nothing to act on. ``GOOD`` never does: it says the output was right, and the rewrite is told to preserve whatever produced it.
    - `prior_feedback` TrainingFeedbackHistorySchema[], required
      - `cycle` integer, required
      - `feedback` string, required
      - `verdict` 'good' | 'bad', required: The reviewer's call on one row's output. Kept as its own enum rather than reusing the cells' ``up``/``down`` thumbs: a training verdict is a judgement on a worked example, and the two domains are deliberately independent of each other. ``BAD`` always carries a note, because a complaint with no explanation gives the prompt rewrite nothing to act on. ``GOOD`` never does: it says the output was right, and the rewrite is told to preserve whatever produced it.
    - `reasoning` string, nullable, required
    - `skipped_reason` string, nullable, required
    - `sources` string[], required
    - `status` 'pending' | 'running' | 'done', required: How far one training row has got in the CURRENT cycle's run. ``DONE`` means the run settled, not that it succeeded. A row whose agent call failed is still done, and its ``outcome`` says what happened. The reviewer critiques failures too.
    - `steps` TrainingStepSchema[], required
      - `action` 'search' | 'fetch_page' | 'map' | 'scrape' | 'crawl' | 'mcp_tool' | 'workspace_action' | 'find_work_email' | 'read_linkedin_profile' | 'find_people_at_company' | 'finish', required: What one agent step did. ``FETCH_PAGE`` is historical: the loop emits ``SCRAPE`` for the same act today, but stored step traces still carry the old value and must keep deserializing, so the member stays.
      - `detail` string, required
      - `duration_ms` integer, nullable: How long the step took; null on records that predate timing.
      - `observation` string, required
      - `outcome` 'completed' | 'failed' | 'refused': How one agent step ended: the structured half of its observation.
      - `thought` string, nullable: The model's stated reasoning for this step; null when not recorded.
    - `value` unknown
    - `verdict` 'good' | 'bad', required: The reviewer's call on one row's output. Kept as its own enum rather than reusing the cells' ``up``/``down`` thumbs: a training verdict is a judgement on a worked example, and the two domains are deliberately independent of each other. ``BAD`` always carries a note, because a complaint with no explanation gives the prompt rewrite nothing to act on. ``GOOD`` never does: it says the output was right, and the rewrite is told to preserve whatever produced it.
  - `saved_agent_id` string, uuid, required
  - `source_input_mappings` object, required
  - `source_kind` 'table' | 'manual', required: Where a session's training inputs came from.
  - `source_table_id` string, uuid, nullable, required
  - `status` 'running' | 'reviewing' | 'proposed' | 'completed' | 'abandoned', required: Where a training session stands: the state the reviewer's screen shows. One enum rather than a separate lifecycle flag and phase, because the phase IS the state: a session is either waiting on the machine (``RUNNING``), waiting on the reviewer (``REVIEWING``, ``PROPOSED``), or finished. Callers never derive the screen from row counts.
  - `updated_at` string, date-time, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Session or row not found
- `409`: The session is not in a state for this
- `422`: Invalid request made to the API.
- `500`: Internal server error.
- `502`: Bad gateway error.
- `503`: Service unavailable.

---

[API](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile.md) · [All operations](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/llms.txt) · [OpenAPI document](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/revisions/4a6bd71e3f02?raw)
