Upload documents as files

Upload up to 200 PDF, Word, PowerPoint, text or Markdown files into a batch, parsed on the server, each with the hash that proves which bytes were checked.

Last updated 12 September 2026

Upload documents as files

POST https://api.quorumtech.ch/attest/v1/batches

Uploads up to 200 files (PDF, Word, PowerPoint, plain text, Markdown) and parses them on the server into a batch. Each document comes back with two hashes: fileHash is the SHA-256 of the bytes as uploaded, which you can recompute from the file you hold; contentHash is the hash of the normalised text actually searched. Only fileHash proves which document a verdict was produced against.

Files that could not be parsed are listed in failed; the batch is still created from the rest. A single file is capped at 25 MB.

This is the preferred way to load evidence. The server parses each file into citable spans in reading order, so a citation points at something the server actually read, and returns two hashes per document. fileHash is the SHA-256 of the bytes as uploaded, which you can recompute from the file you hold with shasum -a 256. contentHash is the hash of the normalised text searched, which depends on the parser's version and cannot be recomputed from the file. Only fileHash proves which document a verdict was produced against.

Files that could not be parsed are listed in failed with a reason, and the batch is created from the rest. When no file could be parsed the answer is 422 no_files_parsed. A single file is capped at 25 MB.

name labels the batch on listings. scope describes what the evidence covers, in a sentence, so the review can tell an out-of-scope claim from an unsupported one.

The batch lives in memory on one server and expires after six hours. Delete it when the report is written.

{
  "name": "Board pack, September 2026",
  "scope": "The September board pack: the CFO report, the minutes of the July meeting and the risk register."
}

Request

A multipart/form-data body with these parts:

| Part | Type | Required | Description | | --- | --- | --- | --- | | files | one or more files | yes | One or more documents. | | name | text | no | A name for the batch, for example "Board Pack FY2024". | | scope | text | no | What this evidence set covers. Describes the evidence, never the claims. |

Example request

curl -X POST "https://api.quorumtech.ch/attest/v1/batches" \
  -H "Authorization: Bearer $QUORUM_API_KEY" \
  -F "files=@board-pack.pdf" -F "files=@minutes.docx" \
  -F "name=Board pack, September 2026" \
  -F "scope=The September board pack: the CFO report, the minutes of the July meeting and the risk register."

Responses

201

The batch is loaded. Documents that failed to parse are named in failed.

| Field | Type | Description | | --- | --- | --- | | batchId | string | | | documents | UploadedBatchDocument[] | | | spans | integer | | | totalChars | integer | | | batchHash | string | | | createdAt | string (date-time) | | | failed | FailedFile[] | Files that could not be parsed. The batch was created from the rest. | | failed[].file | string | | | failed[].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 | | --- | --- | --- | | 400 | invalid_upload, invalid_request | The multipart body is malformed or uses an unexpected field (invalid_upload), or no file part was sent at all (invalid_request). On a draft upload, a file that could not be read is also invalid_upload. | | 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. | | 413 | | The batch exceeds the size the server holds in memory. | | 422 | | No file could be parsed. | | 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. |

Cost

Free. Uploading costs no credits; the claims checked against the batch are charged when they are checked.

Still need help?

Ask Quincy in the chat bubble below, or write to support@quorumtech.ch and we will help you directly.