---
title: "Review a draft"
method: POST
path: "/api/v1/review"
tags: ["Review"]
---

# Review a draft

`POST /api/v1/review`

## Request

- Base URL: `https://lenz.io`
- URL: `https://lenz.io/api/v1/review`
- Auth: HTTP bearer

Read a draft, quick-check every claim, deep-check the ones that look
wrong or uncertain, and hand back the issues with the fixes.

Returns 202 with a `review_id`; read the review at the `Location`
(`GET /reviews/{review_id}`), or receive `review.completed` /
`review.failed` at your webhook. A review takes two to four minutes.

It spends the credits the primitives spend: 1 per claim assessed, and 10
(5 at `depth: low`) per deep check it runs. At most
`min(claims, max_assessments) × 1 + max_verifications × depth cost`;
the review reports what it charged.

Send `escalate.max_citations: N` to also check the draft's first N
citations (markdown links, bare URLs, DOIs, `[n]` markers with a reference
list): for each, whether the source says what the draft attributes to it.
Off by default; 1 credit per checked citation, and a source that could not
be checked is not charged. `POST /citecheck` is the same check alone.
The rows are in `citations[]`, the ones that do not hold up in
`citation_issues[]`, and `outcome` reads `issues_found` for them.
`escalate.max_assessments: 0` runs no quick check (a review of the
citations alone).

Only claims traced directly back to the draft are checked: a claim found
nowhere in it is left out (as `/extract` does with `locate: true`). Each
claim row's `positions` says where the draft makes it, and
`more_claim_locations` gives `{claim, positions}` for each of
`more_claims`. Claim and citation positions share one shape, `{start, end,
text}`: `start` / `end` in Unicode code points of `text` as you sent it,
half-open, and `text` the passage (`null` on a citation, whose row carries
the statement). For a URL draft `start` / `end` are `null` and `text`
carries the passage.

A `text` that is one URL is read like `/extract` reads it and counts as
one `/extract` call: past the daily `/extract` limit it answers 429
`extract_daily_limit`. The other 429 is `review_in_flight` (three reviews
running on the account).

**Versions.** 402: `2026-10-11` receives `ReviewErrorOut`, `2026-05-13` receives `ReviewErrorOutLegacy`; 409: `2026-10-11` receives `ReviewErrorOut`, `2026-05-13` receives `ReviewErrorOutLegacy`; 429: `2026-10-11` receives `ReviewErrorOut`, `2026-05-13` receives `ReviewErrorOutLegacy`; 503: `2026-10-11` receives `ReviewErrorOut`, `2026-05-13` receives `ReviewErrorOutLegacy`; default: `2026-10-11` receives `ErrorOut`, `2026-05-13` receives `ErrorOutLegacy`.

## Headers

- `X-Lenz-API-Version` string

## Request body

- ReviewIn: A draft to review: up to 50,000 characters, or one public http(s) URL.
  - `text` string, required: The draft, up to 50,000 characters (longer text is cut and reported as input_truncated), or one public http(s) URL, read like /extract reads one.
  - `language` string: ISO 639-1 code for the output language of every claim and rewrite. Omitted: English. `auto`: detected once from the draft (the same rule as lenz.io: a supported language, or English when the draft is short, English or in a language we do not answer in; a draft that is one URL is read on its page). A code always wins. The review carries the code that was used; for a one-URL draft it reads `auto` until the page is read.
  - `webhook_url` string, nullable: Omitted or null: the credential's default webhook URL. "": no webhook for this review. A URL: review.completed / review.failed go there.
  - `visibility` 'private' | 'unlisted': Applied to every deep check.
  - `escalate` EscalationPolicyIn: Which quick-checked claims get a deep check. A claim escalates when its quick label is in ``verdicts`` OR its confidence band is in ``confidence``. Both may be empty; ``max_verifications: 0`` makes an assess-only review.
    - `verdicts` string[], nullable: Quick-check labels that get a deep check. Default ["False", "Mostly False", "Mixed", "No Public Record"].
    - `confidence` string[], nullable: Quick-check confidence bands that get a deep check (low, medium, high). Default ["low"].
    - `max_verifications` integer, nullable: The deep-check cap. Default 5.
    - `max_assessments` integer, nullable: How many of the draft's claims, most check-worthy first, get a quick verdict. Default 20. 0: no quick checks (a review that checks only the citations).
    - `depth` 'standard' | 'low', nullable: Depth of every deep check. Default standard.
    - `max_citations` integer, nullable: How many of the draft's citations (links, DOIs, [n] markers with a reference list), in its order, are checked: does each source say what the draft attributes to it? 1 credit per checked citation. Default 0: no citation check. Guide: https://lenz.io/docs/citations
    - `suggest_edits` boolean, nullable: For each claim with a suggested rewrite, from its deep check or, when it has none, from its quick check (found False or Mostly False with high confidence), the smallest edits to the draft that make it say what the rewrite says, written in the draft's language (claims[].suggested_edits). No extra credits. The review completes once they are settled. Default false. Guide: https://lenz.io/docs/citations

## Response `202`

Accepted

- ReviewAcceptedOut
  - `review_id` string, required
  - `status` 'queued', required: The acceptance receipt, not the current state.

## Other responses

- `402`: Payment Required
- `409`: Conflict
- `422`: The body names a field or a value the endpoint does not take.
- `429`: Too Many Requests
- `503`: Service Unavailable
- `default`: An error

---

[API](https://skmtc.dev/lenz/apis/lenz-ai-fact-checking-api.md) · [All operations](https://skmtc.dev/lenz/apis/lenz-ai-fact-checking-api/llms.txt) · [OpenAPI document](https://skmtc.dev/lenz/apis/lenz-ai-fact-checking-api/revisions/6150947324bf?raw)
