Changelog
A public log of API changes. Additive, backward-compatible changes (new optional fields); breaking changes are flagged explicitly — existing integrations keep working unchanged.
2026-07-28 — Renamed to ChunkChef
The product is now ChunkChef. Three changes are visible on the wire; the rest is internal.
- Breaking — webhook signature headers renamed.
X-Hotdoc-TimestampandX-Hotdoc-Signatureare nowX-Chunkchef-TimestampandX-Chunkchef-Signature. The signing scheme is unchanged; only the header names differ. Update your verifier to read the new names. - Breaking — API key prefix changed. New keys are issued as
chunkchef_<id>.<secret>instead ofhotdoc_<id>.<secret>. Existing keys stop authenticating; mint a replacement in the dashboard. - Breaking — the typed error discriminator changed. Inside
Status.details,@typenow readstype.googleapis.com/chunkchef.v1.billing.QuotaExceededDetail. If you match on that string, update it.
Endpoints, request and response shapes, and the domains hotdoc.io / api.hotdoc.io are unchanged.
2026-07-28 — OpenAPI specification
- Machine-readable spec published at
/openapi.yaml— OpenAPI 3.0.3, generated from the service’s protobuf definitions, covering the five public Jobs operations (POST /v1/jobs/upload,POST /v1/jobs,GET /v1/jobs/{id},GET /v1/jobs/{id}/result,GET /v1/jobs). Point any OpenAPI-compatible generator at it to build a client; this hand-written reference stays as the prose documentation. - Behaviour change —
GET /v1/jobspage size is now bounded.pageSizedefaults to50and is capped at200. A call that omittedpageSizepreviously returned the entire job history in one response. If you relied on that, page through withpageToken. - Behaviour change — a
pageTokenthat no longer resolves returns400. Such a token previously restarted the listing from the first page with no signal, which could hand you rows you had already seen. Treat the400as “the cursor no longer applies, restart withpageTokenunset”. - Fixed — paging through
GET /v1/jobsno longer skips or repeats jobs. The cursor anchored on a different column than the one the list was ordered by, so a multi-page listing could drop some jobs and return others twice.
2026-07-24 — K-consensus confidence
- New
consensusfield (optional) onPOST /v1/jobs— opt-in, requiresresponseSchema(rejected with400otherwise).consensus.modepicks the run count:CONSENSUS_MODE_TWO_RUNS(2, an instability probe),CONSENSUS_MODE_THREE_RUNS(3, recommended), orCONSENSUS_MODE_FIVE_RUNS(5, maximum scrutiny). hotdoc runs each chunkktimes on your key and votes field-by-field (plurality) on the answer. - New
llm[].consensusresult field —k,runsVotable,minAgreement,incomplete, anddisagreements[](competingvariants[]with run counts and whether eachincludedthe vote’s winner). Lets you see which fields the model was stable on and which it wavered on, atk× the token cost on your key. See “Consensus runs” in the Job API docs.
2026-07-15 — Chunk merge
- New
mergefield (optional) onPOST /v1/jobs— opt-in, server-side merging of per-chunk model answers into one whole-document result.merge.scopepicks per-file (MERGE_SCOPE_FILE, default) or per-job (MERGE_SCOPE_JOB) merging;merge.conflictPolicypicks how disagreeing field values are resolved (MERGE_CONFLICT_POLICY_FIRST_NON_NULL, default, orMERGE_CONFLICT_POLICY_MAJORITY);merge.dedupeByopts into cross-file content dedup by JSON field names (JSON +responseSchemaonly; empty keeps the safe technical-only default). - New
merged[]result field — one entry per file (or per job atscope=job) with the combinedcontent,schemaValid(whenresponseSchemais set),conflicts[],dedupeRemovedTechnical/dedupeRemovedContentcounters, andmergeIncomplete/incompleteChunkswhen a chunk couldn’t be safely merged. The existingllm[]per-chunk rows are unchanged and remain available. This closes the standing “the service still does not merge chunks” note from 2026-06-24 — merging is now available, opt-in, and off by default. See “Merging chunked results” in the Job API docs.
2026-07-14 — Structured output (JSON Schema)
- New
responseSchemafield (optional) onPOST /v1/jobs— a self-contained JSON Schema (raw string; internal#/$defsonly, ≤128 KiB, depth ≤64, ≤10000 nodes) that hotdoc validates the model’s JSON answer against. For OpenAI/Grok it must be strict-subset-compatible (every property inrequired, optionality viatype: [..., "null"],additionalProperties: false); a schema written to that subset works unchanged across all providers. An invalid schema is rejected with400at job creation. - New
llm[].schemaValid/llm[].schemaErrorsresult fields — reported only for single-chunk files. A non-conforming answer is still returned incontent, but the job finishes asJOB_STATUS_PARTIALinstead ofJOB_STATUS_COMPLETE. Providers hotdoc emulates structured outputs for (DeepSeek, Xiaomi) get one automatic repair retry before scoring.
2026-07-09 — Extraction mode
- New
extractionModefield (optional) onPOST /v1/jobs—EXTRACTION_MODE_HYBRID(Optimized Hybrid Extraction, the default) lets the converter decide text-extract vs OCR per file;EXTRACTION_MODE_OCR_ALWAYSforces neural OCR on every file. Plus a new account-level default, configurable in the dashboard under Settings, used whenever a job omitsextractionMode.
2026-07-08 — Public playground
/playgroundon the site — try hotdoc without an account: upload a file, run a preset prompt, see the result in the browser.- New anonymous demo endpoints,
/v1/demo/*: upload a file, create a demo job, poll its result, and claim it onto a real account after signup. Sessions are tracked by a cookie (no login) and capped at 3 runs per session plus a shared daily budget across all anonymous visitors; results are truncated. This is additive — the authenticated/v1/jobsAPI is unchanged.
2026-06-30 — Multi-provider OCR (BYOK)
Breaking. The OCR stage now takes ocr.{provider, model, providerKey} (was
ocr.mistralApiKey). Choose any supported provider — Mistral is the default
(mistral-ocr-latest). model is required. New OCR error markers
(provider_auth_failed, provider_key_required, rate_limited, ocr_timeout,
ocr_backend_*, too_many_pages) replace mistral_key_required.
2026-06-26 — Idempotency keys
POST /v1/jobs accepts idempotencyKey. Retrying with the same key and identical
parameters returns the original job; the same key with different parameters returns
400.
2026-06-24 — job completion webhooks
- New
webhookUrlandwebhookSecretfields (both optional) on job creation.webhookUrlis an absolutehttp/httpsendpoint; the service POSTs it once with a JSON body (job_id,account_id,status,finished_at) when the job reaches a terminal status.webhookSecretis an HMAC secret: when set, each request includesX-Hotdoc-TimestampandX-Hotdoc-Signatureheaders (sha256=hex(hmac_sha256(secret, "<ts>.<body>"))). Both fields are accepted as input only and never appear in responses. Retry policy: up to 6 attempts with delays 1 m / 5 m / 15 m / 30 m / 30 m. See “Webhooks” for details.
2026-06-24 — structure-aware chunking and configurable chunk size
- Structure-aware text chunking. Long recognized text is now cut along the boundaries of its format’s structure (Markdown / HTML / XML / plain): tables are not torn mid-row (a table’s header is repeated in each chunk), and section-heading context is preserved. The previous behavior (chunks as separate
llm[]rows keyed bychunkIndex/chunkTotal) is unchanged — the service still does not merge chunks. - New
neural.chunkBudgetTokensfield (optional) on job creation — per-chunk token budget for your model’s context window; range16000–2000000, defaults to the service’s conservative value. The response echoes the actual effective budget. - New
ocr[].outputFormatfield in the job result — the format of the recognized text (markdown/html/xml/plain).