---
title: "Put Call Review"
method: PUT
path: "/api/v1/voice-calls/{voice_call_id}/review"
tags: ["call-reviews"]
---

# Put Call Review

`PUT /api/v1/voice-calls/{voice_call_id}/review`

Create-or-replace the current user's review for this call.

Single transaction for the review + annotations + outcome write; the
finding-sync runs **after** commit so the user sees durable state
even if sync throws. Retry is a re-PUT.

## Path parameters

- `voice_call_id` string, uuid, required

## Request body

- VoiceCallReviewWrite — Full create-or-replace payload for PUT /voice-calls/{id}/review. PUT over PATCH per ``fastapi_backend/CLAUDE.md``. The server replaces the reviewer's whole annotation set on every PUT; clients preserve annotation identity by re-sending the same ``id`` where it exists.
  - `verdict` 'good' | 'par' | 'bad', required — Reviewer's coarse verdict on the whole call (V0.5). The three values cover the spectrum without overclassifying: the agent nailed it, met the bar, or fell short. Annotations carry the signal — the verdict is a coarse summary only. See ``sync_review_to_findings.py`` for the uniform "verdict + 0 annotations → 0 findings" rule (intentional extension beyond the briefing's §9, which only guaranteed it for good/par; V0.5 extends it to bad so the sync function has one consistent rule).
  - `outcome` string, required
  - `annotations` VoiceCallReviewAnnotationWrite[]
    - `id` string, uuid, nullable
    - `message_id` string, uuid, required
    - `char_start` integer, nullable
    - `char_end` integer, nullable
    - `quoted_text` string, nullable
    - `suggested_correction` string, required
    - `finding_category` 'wrong_response' | 'missed_info' | 'wrong_tone' | 'hallucination' | 'wrong_tool_call' | 'should_have_escalated' | 'other', required — Category of issue a reviewer is pointing at on a specific turn (V0.5). Kept in sync with the CHECK constraint on ``voice_call_turn_annotations.finding_category``. Values are snake_case. V1 can extend the enum; it never renames — same contract as ``EvaluationFindingSource``.

## Response `200`

Successful Response

- VoiceCallReviewRead
  - `id` string, uuid, required
  - `voice_call_id` string, uuid, required
  - `reviewer_user_id` string, uuid, required
  - `verdict` 'good' | 'par' | 'bad', required — Reviewer's coarse verdict on the whole call (V0.5). The three values cover the spectrum without overclassifying: the agent nailed it, met the bar, or fell short. Annotations carry the signal — the verdict is a coarse summary only. See ``sync_review_to_findings.py`` for the uniform "verdict + 0 annotations → 0 findings" rule (intentional extension beyond the briefing's §9, which only guaranteed it for good/par; V0.5 extends it to bad so the sync function has one consistent rule).
  - `outcome` string, required
  - `annotations` VoiceCallReviewAnnotationRead[]
    - `id` string, uuid, required
    - `voice_call_review_id` string, uuid, required
    - `voice_call_id` string, uuid, required
    - `message_id` string, uuid, required
    - `char_start` integer, nullable
    - `char_end` integer, nullable
    - `quoted_text` string, nullable
    - `suggested_correction` string, required
    - `finding_category` 'wrong_response' | 'missed_info' | 'wrong_tone' | 'hallucination' | 'wrong_tool_call' | 'should_have_escalated' | 'other', required — Category of issue a reviewer is pointing at on a specific turn (V0.5). Kept in sync with the CHECK constraint on ``voice_call_turn_annotations.finding_category``. Values are snake_case. V1 can extend the enum; it never renames — same contract as ``EvaluationFindingSource``.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `401` — Not authenticated (missing user or tenant context)
- `409` — Conflict: duplicate annotation IDs for this call
- `422` — Validation error: invalid path UUID, request body, or review validation (e.g. outcome taxonomy, message_id)
- `500` — Server error (e.g. finding-sync or delete failed)

---

[API](https://skmtc.dev/getanana/apis/cleon-api.md) · [All operations](https://skmtc.dev/getanana/apis/cleon-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getanana/apis/cleon-api/revisions/ae7627cd46da?raw)
