Collect a draft review
Poll a review that answered 202. Returns progress while it runs and the full result once it is done.
Last updated 12 September 2026
Collect a draft review
GET https://api.quorumtech.ch/attest/v1/attest/draft/jobs/{jobId}
Collect a draft review that is still running.
Returns the review's progress while it runs and the full result once it is done. Collecting
the result is also what settles the bill, so a caller that only wants the counts should read
.../progress or .../events instead. A finished result stays for five minutes after it is
first collected, so a dropped poll can be repeated, and is then forgotten. Jobs are held in
memory and are not retrievable after the process restarts; a 404 means start the review
again.
While the review runs, progress.extracted grows as each passage is read, with
passagesDone of passagesTotal beside it, and updatedAt says when those counts last
changed.
While the review runs the answer carries state: "running" and progress with the claims extracted and checked so far and the phase (extracting, checking, finished). Once done it carries state: "done" and the full review, the same shape as a review that finished inside the request.
Poll every few seconds. Polls are free and do not count as claims. A finished result stays for five minutes after it is first collected, so a dropped poll can be repeated, and is then forgotten. Jobs are held in memory and are not retrievable after the service restarts: a 404 means start the review again. A 410 means the draft or the batch was deleted after the review started.
Request
Path parameters:
| Parameter | Description |
| --- | --- |
| jobId | The jobId from a 202 response to POST /attest/v1/attest/draft. |
Example request
curl -X GET "https://api.quorumtech.ch/attest/v1/attest/draft/jobs/<jobId>" \
-H "Authorization: Bearer $QUORUM_API_KEY"
Responses
200
Either progress (state is running) or the finished review (state is done).
Every shape below carries these fields:
| Field | Type | Description |
| --- | --- | --- |
| jobId | string | |
| draft | DraftSummary | Which bytes were reviewed. |
| draft.id | string | The draft id to review by. inline when the text came in the request. |
| draft.filename | string | May be null. |
| draft.fileHash | string | SHA-256 of the draft as uploaded, or of the text as sent. |
| draft.chars | integer | |
| draft.createdAt | string (date-time) | |
| draft.source | upload or inline | |
Draft job running
| Field | Type | Description |
| --- | --- | --- |
| state | running | |
| progress | JobProgress | |
| progress.extracted | integer | Claims found so far. It grows as each passage is read, and is final once phase leaves extracting. |
| progress.checked | integer | Claims checked so far |
| progress.passagesDone | integer | Passages read so far |
| progress.passagesTotal | integer | |
| progress.phase | extracting or checking or finished | |
| progress.failedPassages | FailedPassage[] | |
| progress.failedPassages[].chunk | integer | |
| progress.failedPassages[].excerpt | string | |
| progress.failedPassages[].reason | string | |
| updatedAt | string (date-time) | When the counts last changed. |
Draft job done
| Field | Type | Description |
| --- | --- | --- |
| state | done | |
| claims | DraftClaimCheck[] | |
| claims[].index | integer | |
| claims[].claim | string | The claim as extracted from the draft. |
| claims[].source | string | The passage it was extracted from. |
| claims[].ok | boolean | |
| claims[].duplicateOf | integer | Set when an earlier claim in this review has the same text. The result is that claim's, checked and charged once. |
| claims[].kind | assertion or synthesis or judgement | |
| claims[].coverage | in_scope or out_of_scope | Whether the batch could speak to this claim at all. Out-of-scope attention is not a finding against the draft. |
| claims[].missingTerms | string[] | Terms from the claim that appear nowhere in the batch, when out of scope. |
| claims[].result | EvidenceModeResult or BatchModeResult | |
| claims[].error | object | |
| claims[].error.code | string | |
| claims[].error.message | string | |
| summary | object | |
| summary.total | integer | |
| summary.supported | integer | |
| summary.needsAttention | integer | |
| summary.failed | integer | Claims whose check could not run. Not the same as failedChunks |
| summary.extracted | integer | Claims found in the draft. |
| summary.chunks | integer | Passages the draft was read in |
| summary.extractedChunks | integer | Passages actually read. Fewer than chunks when the claim ceiling stopped extraction early. |
| summary.failedChunks | integer | Passages that could not be read. |
| summary.skippedChunks | integer | Passages left unchecked because the claim ceiling was reached. |
| summary.truncated | boolean | True when the claim ceiling was reached and part of the draft went unchecked. |
| summary.nextCursor | string | Where to continue when truncated: pass it back as config.cursor with the same draft and batch. Null when the whole draft was read. May be null. |
| summary.nextOffset | integer | The character offset in the draft where the next review starts. Null when the whole draft was read. May be null. |
| summary.duplicates | integer | Claims whose text repeated an earlier claim |
| summary.cached | integer | Claims answered from a measurement already made |
| summary.usage | Usage | What this response cost at the model provider. Zeros for a replay. |
| summary.usage.calls | integer | Completions made. |
| summary.usage.promptTokens | integer | Tokens sent. |
| summary.usage.completionTokens | integer | Tokens returned. |
| summary.usage.cachedTokens | integer | Prompt tokens served from the provider's cache. |
| summary.usage.latencyMs | integer | Wall time waiting on the provider. |
| summary.usage.fellBack | integer | Calls the preferred tier shed to the fallback. |
| summary.attentionInScope | integer | Flagged claims the batch could speak to. |
| summary.attentionOutOfScope | integer | Flagged claims the batch could not speak to. |
| summary.attentionByKind | object | |
| summary.attentionByKind.assertion | integer | |
| summary.attentionByKind.synthesis | integer | |
| summary.attentionByKind.judgement | integer | |
| failedPassages | FailedPassage[] | |
| failedPassages[].chunk | integer | |
| failedPassages[].excerpt | string | |
| failedPassages[].reason | string | |
| skippedPassages | FailedPassage[] | The passages the claim ceiling left unchecked. Nothing went wrong with them; they are where the next review starts. |
| skippedPassages[].chunk | integer | |
| skippedPassages[].excerpt | string | |
| skippedPassages[].reason | string | |
Errors
The body is { "error": { "code", "message" } }; the full catalogue with what to do about each code is on the Errors page.
| Status | Codes | When |
| --- | --- | --- |
| 401 | | The key is missing, not recognised, or revoked. |
| 402 | payment_required, credits_exhausted, remaining, needed | The workspace has no active API subscription (payment_required), or is prepaid and has no credits left for this request (credits_exhausted, with remaining and needed). Manage billing and add credits at https://developer.quorumtech.ch/billing. |
| 403 | | The key is not scoped to Attest. |
| 404 | | No such job. |
| 410 | inputs_deleted | The draft or the batch was deleted after the review started (inputs_deleted). Kept for a few minutes, then a 404. Upload again and start a new review. |
| 429 | rate_limited, claims_rate_limited, claim_budget_exhausted, monthly_cap_reached, cap | Over the key's requests-per-minute limit (rate_limited), its claims-per-minute ceiling (claims_rate_limited), its claims-per-day budget (claim_budget_exhausted), or, for a postpaid workspace, its monthly credit cap (monthly_cap_reached, with cap and resetsAt). Retry-After says when to try again; the daily budget resets at midnight UTC and the cap on the first of the month. |
| 502 | | The review failed at the model provider. |
| 503 | | The review failed because no part of the draft could be read. |
Cost
Free to poll. The review's credits settle on the poll that collects the finished result, and are reported in that response's X-Credits-Charged header.
Related articles
Fetch a page in one call
The same work as the extract call, with the request held open until the page is done and the Markdown returned directly.
Watch an extraction
Server-sent events for one job: its state now, every change after it, and the finished page. Closes when the job does.
Read an extraction's progress
One frame of a job's state without its Markdown, for a progress bar that should not carry a page's text with every poll.
Collect an extraction
Poll a job that answered 202. Returns its state while it runs and the Markdown once it is done.
Still need help?
Ask Quincy in the chat bubble below, or write to support@quorumtech.ch and we will help you directly.