paperless-gpt now lets different documents be processed differently. An invoice can get a title prompt built around its invoice number, a contract its own custom fields, private mail only a title, each with its own prompts and steps, set up and tested in the UI.
Around that: searchable PDFs that become a new version of the same document instead of a new document, no more OCR for pages that already have clean text, and a long list of fixes, many of them for documents that ended up with the wrong tags, values or a fail tag without anyone noticing.
The theme is control: over what happens to which document, and over what you can see before it happens.
Upgrading: nothing to do for most setups. Please read Before you upgrade at the bottom if you mount your volumes differently from the examples, used AI workflows from @MrMoep's fork, or customised the custom field prompt.
🧭 AI Workflows: different prompts for different documents
Until now there was one automatic path: every document tagged paperless-gpt-auto got the same prompts and the same steps. That stops working once an archive holds very different things.
An AI workflow is another processing path with its own trigger tag. Tag a document with it (by hand, or with a paperless-ngx workflow or mail rule) and paperless-gpt processes it with that workflow's prompts and steps, then swaps the trigger for the workflow's completion tag.
- Own prompts, only where they differ. Each prompt is either the shared global one or the workflow's own version; the editor makes clear which one you are editing. Only the steps a workflow actually runs show a prompt.
- Steps per workflow. Title, tags, correspondent, document type, created date and custom fields are each Default, On or Off, and the editor shows what Default currently means. Optional OCR first, with its own page limit and OCR prompt.
- Test on a document before you save. The editor runs the workflow, unsaved changes included, on a real document and shows the result next to the current values. Nothing is written to paperless-ngx.
- Workflows are files. Each one is a folder in
prompts/workflows/, next to the global prompts, so thepromptsvolume you already have keeps them. You can also write them by hand or keep them in git; changes are picked up without a restart. - No loops. A completion tag can't be a trigger, workflows can't be chained, and paperless-gpt's own tags can't be triggers. The editor flags this while you type; files written by hand follow the same rules.
- Plays well with the default path. A document carrying both a workflow trigger and
paperless-gpt-autois processed once, by the workflow.
Thanks @MrMoep for building this and bringing a working implementation in the feature request. (#1108, #1110, #1115, #1116, closes #1096)
📄 Searchable PDFs as a new version (paperless-ngx 3.0)
With PDF_UPLOAD_MODE=version, the searchable PDF is added as a new version of the same document. It keeps its ID, tags, custom fields, notes and storage path, and the original stays available as the previous version. Nothing is deleted. On paperless-ngx 3.0 this is the safe replacement for PDF_REPLACE.
new stays the default; older paperless-ngx versions get a clear error instead of a silent failure, and PDF_REPLACE is ignored in version mode.
⚡ No OCR for pages that already have text
Born-digital PDFs (invoices, statements, letters generated as PDF) already contain exact text. OCR on them costs time and tokens and can only make the text worse. With OCR_SKIP_DIGITAL_PAGES=true, paperless-gpt checks each page of the original file and keeps the text of pages that have a clean, visible text layer. Scans, pages with another tool's OCR layer, and pages with broken text, including umlauts mangled into f¸r or für, still go to OCR. In a real archive we checked, about half of all pages needed no OCR at all.
Off by default; image mode only, and off whenever a searchable PDF or hOCR is produced. Inspired by doc-router. (#1148)
🛡️ Fixes for wrong or lost data
- Tag suggestions no longer remove paperless-gpt's own tags. Applying suggestions replaced a document's tags, which silently dropped
PDF_OCR_COMPLETE_TAG,AUTO_TAG_COMPLETE,FAIL_TAGand workflow completion tags. They are now kept. Thanks @nirvdrum for the report. (#1167, closes #1156) - Correspondents are matched regardless of case. When the model answered
MONTANA Energieversorgungfor an existingMontana Energieversorgung, paperless-gpt tried to create a duplicate, paperless-ngx refused, and the whole update failed with the fail tag. Document types had the same problem. And if a correspondent still can't be used, the other suggestions are applied and the document is flagged for review instead of losing everything. Thanks @MichiFr for the report. (#1147, from #1146) - No more "Unknown" correspondent. When nothing fits, the prompt answers
Unknown; paperless-gpt created a correspondent with exactly that name. It now leaves the correspondent unchanged. Thanks @MaxRink! (#1169, from #1162) - No more invented amounts in custom fields. The default custom field prompt used
1.664,58 €as its formatting example, and models sometimes returned it when a document had no amount. The prompt now only contains placeholders. Thanks @MichiFr for the report. (#1168, from #1163) PDF_REPLACEonly deletes after a confirmed import. On paperless-ngx 3.0 the import status wasn't read correctly, so the original could be deleted while its replacement failed to import. Beyond that, the original is now only deleted once paperless-ngx reports the import as done; if that can't be confirmed, the original is kept and the run says so. Thanks @kzaoaai! (#1112, #1136)- An invalid date no longer fails the whole update. One impossible date in a custom field (say
2026-09-31) made paperless-ngx reject everything; it is now skipped and the rest applied. Thanks @tdabasinskas! (#1150, closes #1149) - Custom field answers in other shapes are understood. Replies as a single object or a map of field names, or indented with non-breaking spaces, used to be dropped silently. Thanks @shiaho777! (#1132 from #1129)
- Year-first dates are normalised.
2023.01.01or2023-01-01T15:04:05Zwere dropped on apply. Thanks @shiaho777! (#1130) - Partial updates are reported, not hidden. In manual review a partially applied document used to show "nothing was applied"; it now names the fields that were left out and flags the document like the background processing does.
🧩 Settings, prompts and setup
- Warnings when your data isn't persisted. In a container, settings, prompts, workflows and the undo history are lost on every update unless
config/,prompts/anddb/are volumes. paperless-gpt now warns at startup and on the Settings and AI Workflows pages, with the volume line to add. It also spots the/root/promptsmount from setups older than October 2024, which has silently had no effect since. Thanks @MichiFr for the report that led here. (#1120, from #1118) - Improved default prompts reach existing installations. A prompt in
prompts/that you never edited is updated to the current default on startup. Prompts you changed are never touched. (#1168) - Review decisions survive a reload. Thanks @shiaho777! (#1138)
- Configurable Docling output format (
DOCLING_OUTPUT_FORMAT). Thanks @mw88-personal! (#1152) LOG_LEVELnow applies to OCR logging too. Thanks @mw88-personal! (#1153)
🔌 Providers
OPENAI_HEADERSadds HTTP headers to OpenAI-compatible requests, e.g. for API gateways. The headers stay on those requests only, are also used for LLM OCR, and are not sent along when a redirect points to another host. Thanks @zhzy0077! (#1122 from #1077)- Retry-After is honoured. When a provider answers 429 with a cooldown, paperless-gpt waits for it instead of retrying straight into the limit. Thanks @MaxRink! (#1170 from #1161)
- Google AI is rate-limited like every other provider, so
LLM_REQUESTS_PER_MINUTEandLLM_MAX_RETRIESapply. Thanks @shiaho777! (#1128) - An empty model reply counts as an empty suggestion instead of failing and retrying. Thanks @titulartarantula! (#1157, #1158)
🔧 Under the hood
- Startup calls to paperless-ngx are bounded, so the web UI comes up even when paperless-ngx is slow; tags are fetched 1000 per page. Thanks @MaxRink! (#1169)
- Dependency updates close a batch of known vulnerabilities in indirect dependencies. Thanks @Svector-anu! (#1151)
- AI workflows are covered by an end-to-end test suite that runs on every pull request. (#1166)
go testno longer overwrites a developer's local prompts, workflows or settings. (#1134)
⚠️ Before you upgrade
- Volumes. If the startup log or the Settings page warns that a directory is not on a volume, add the line it shows. The example compose file now mounts all three:
./prompts:/app/prompts,./config:/app/config,./db:/app/db. - AI workflows from @MrMoep's fork are moved from
config/settings.jsontoprompts/workflows/on the first start (one log line per workflow). Back up both first. One deliberate difference: a workflow's completion tag now replacesAUTO_TAG_COMPLETEinstead of being added next to it. Workflows that break the loop rules are skipped with an error in the log. - Custom field prompt. If you customised
prompts/custom_field_prompt.tmpl, remove or neutralise the1.664,58 €example there. Untouched prompts are updated automatically.
🙏 Credits
- @MrMoep: AI workflows (#1096)
- @kzaoaai: searchable PDFs as a new version, and the paperless-ngx 3.0 replace fix (#1125, #1112)
- @MaxRink: Retry-After, no "Unknown" correspondents, bounded startup (#1161, #1162, #1160)
- @shiaho777: custom field replies, date normalisation, Google AI rate limit, review reload (#1129, #1130, #1128, #1138)
- @tdabasinskas: invalid dates in custom fields (#1150)
- @zhzy0077:
OPENAI_HEADERS(#1077) - @mw88-personal: Docling output format, OCR log level (#1152, #1153)
- @titulartarantula: empty model replies and tests (#1157, #1158)
- @Svector-anu: dependency security updates (#1151)
- Reports and discussions that shaped this release: @MichiFr (#1118, #1146, #1163), @nirvdrum (#1156)