---
title: "Check many claims"
summary: "Check up to 100 claims in one call. Results come back in the same order, one slot per claim, so a failed check never shifts the verdicts after it."
category: "developer"
tags: ["attest", "reference", "bulk", "claims"]
updated: "2026-09-14T04:25:10.696Z"
url: "https://support.quorumtech.ch/articles/attest-check-claims"
markdown: "https://support.quorumtech.ch/articles/attest-check-claims.md"
---

# Check many claims

`POST https://api.quorumtech.ch/attest/v1/attest/bulk`

Check up to 100 claims in one call.

Checks a list of claims. The `results` array is always the same length as `claims`, and
position *i* always describes claim *i*, so a failed check never shifts the verdicts after it.
Each claim may carry its own evidence or name a batch. A claim naming a batch that has aged
out fails in its own slot with `batch_not_found` and is not checked; the rest still run.

Spends as many claims of the daily budget as the array holds.

Use this when you already have the claims, for example one per sentence of a draft you split yourself, and want them checked in one round trip. Each entry is a full claim request: its own evidence, or a `batchId`, and its own options.

The `results` array is always the same length as `claims`, and position `i` describes claim `i`. A claim that fails fills its own slot with `ok: false` and an `error`, rather than being dropped, because callers map results back onto sentences by position and a dropped element would send them to edit the wrong sentence. For the same reason the call returns 200 whenever the request was well formed, even if every claim failed. Read `ok` before reading a verdict. `INDETERMINATE` lands as `ok: false`, so a failure to measure is never actioned as a finding.

If you have the document rather than a list of claims, prefer the draft review: extraction on the server finds the claims a writer does not perceive as claims, which is where the errors are.

```json
{
  "claims": [
    {
      "claim": "FY2025 revenue was CHF 94.0m.",
      "evidence": [{ "id": "ev_1", "content": "Group revenue for FY2025 was CHF 94.0m, up from CHF 82.0m in the prior year." }]
    },
    {
      "claim": "Headcount grew in FY2025.",
      "evidence": [{ "id": "ev_1", "content": "Group revenue for FY2025 was CHF 94.0m, up from CHF 82.0m in the prior year." }]
    }
  ]
}
```

The second claim returns `INSUFFICIENT_EVIDENCE`: the evidence is about revenue and says nothing about headcount.

## Request

A JSON body with these fields:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `claims` | AttestRequest[] | yes | 1 to 100 items. |
| `claims[].claim` | string | yes | The statement to check. One assertion reads best; split compound sentences. 1 to 4,000 characters. |
| `claims[].evidence` | Evidence[] | no | The documents to check against, inline. Ids must be unique. At most 50 items. |
| `claims[].evidence[].id` | string | no | Your identifier for this item, unique within the request. Spans are cited as `<id>:<ordinal>`, for example `ev_1:0`. Assigned as `ev_<n>` when omitted. 1 to 64 characters. Pattern `^[A-Za-z0-9_.-]+$`. |
| `claims[].evidence[].content` | string | yes | The evidence text. Total across all items is capped at 200,000 characters. |
| `claims[].evidence[].source` | EvidenceSource | no | Where a piece of evidence came from. Shown in citations; never used to retrieve anything. |
| `claims[].batchId` | string | no | Check against a batch loaded earlier instead of inline evidence. |
| `claims[].kind` | `assertion` or `synthesis` or `judgement` | no | What kind of statement this is, which decides how it is checked. An assertion is checked against the evidence; a synthesis combines several facts; a judgement is an opinion, which evidence can inform but not settle. Default `"assertion"`. |
| `claims[].glossary` | object of string | no | What the draft means by particular words. Definitions only; a glossary cannot assert that anything is true. |
| `claims[].reviewDate` | string | no | The date the review is taken as of, so a claim about elapsed time is reproducible. At most 40 characters. |
| `claims[].config` | object | no |  |
| `claims[].config.explain` | boolean | no | Include the span-cited explanation and the audit block. Off returns the verdict alone; the spans come from the reading, so they are empty too. Default `true`. |

## Example request

```bash
curl -X POST "https://api.quorumtech.ch/attest/v1/attest/bulk" \
  -H "Authorization: Bearer $QUORUM_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "claims": [
    {
      "claim": "FY2025 revenue was CHF 94.0m.",
      "evidence": [
        {
          "id": "ev_1",
          "content": "Group revenue for FY2025 was CHF 94.0m, up from CHF 82.0m in the prior year."
        }
      ]
    },
    {
      "claim": "Headcount grew in FY2025.",
      "evidence": [
        {
          "id": "ev_1",
          "content": "Group revenue for FY2025 was CHF 94.0m, up from CHF 82.0m in the prior year."
        }
      ]
    }
  ]
}'
```

## Responses

### 200

Every claim was attempted. Check each result's `ok` before reading its verdict.

| Field | Type | Description |
| --- | --- | --- |
| `results` | BulkClaimResult[] | Same length and order as the request's `claims`. |
| `results[].index` | integer | Position in the request's `claims`. |
| `results[].claim` | string |  |
| `results[].ok` | boolean |  |
| `results[].result` | EvidenceModeResult or BatchModeResult |  |
| `results[].error` | object | Present when `ok` is false. |
| `results[].error.code` | string |  |
| `results[].error.message` | string |  |
| `summary` | object |  |
| `summary.total` | integer |  |
| `summary.supported` | integer |  |
| `summary.needsAttention` | integer | Every checked claim whose verdict is not SUPPORTED. |
| `summary.failed` | integer | Claims that could not be checked at all. |

## 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 |
| --- | --- | --- |
| 400 |  | The request does not match the schema. `error.issues` names each failing field. |
| 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. |
| 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. |
| 500 |  | Something failed on the server. |

## Cost

Per claim in the array, one credit ($0.01) with its own evidence and two ($0.02) when it names a batch. Charged for the claims attempted; a request refused as a whole charges nothing.
