How to redact PDFs, Word and Excel files before uploading them to AI
To redact documents before uploading them to an AI model, mask the file’s text in transit rather than editing copies by hand. Shadow does this for uploads to OpenAI, Anthropic and Gemini APIs: DOCX, XLSX and PPTX are masked in place and stay valid documents, text formats are masked line by line, and PDF text is extracted, masked and sent as text. The answer comes back with real values restored.
Why are documents riskier than prompts?
Section titled “Why are documents riskier than prompts?”A prompt is a sentence or two. An attached contract, export or slide deck carries pages of names, addresses, account numbers and internal codenames, and nobody reads every cell before pressing upload. Manual redaction is slow, inconsistent between people, and usually destroys the structure the model needed.
Which upload paths does Shadow mask?
Section titled “Which upload paths does Shadow mask?”| Transport | Examples |
|---|---|
multipart/form-data uploads | OpenAI POST /v1/files, POST /v1/uploads parts |
| Base64 files inside JSON | OpenAI input_file.file_data, Anthropic document blocks with a base64 source, Gemini inline_data |
data: URIs | data:text/csv;base64,... anywhere in a JSON string |
The format is recognised from the declared MIME type and the file name.
Which formats are supported?
Section titled “Which formats are supported?”| Format | Treatment | Result |
|---|---|---|
| Plain text, Markdown | Masked as text | Same file, masked |
| CSV | Masked cell by cell | Same columns and rows |
| JSON | String values masked | Valid JSON |
| HTML, XML | Text masked | Markup intact |
| DOCX | Text runs masked in place | A valid Word document |
| XLSX | Shared strings masked in place | A valid workbook; numbers and formulas untouched |
| PPTX | Slide text runs masked in place | A valid presentation |
Text extracted, masked, sent as text (pdf = "text") | The model receives masked text, not the PDF | |
| Images | Passed through (images = "pass") or refused ("block") | No OCR |
| Anything else | Passed through |
Files use the same detectors, persona, vault and policy as prompts. A customer named in the prompt and in the spreadsheet gets the same fake in both, and a customer list attached to a “build a report” request gets typed tokens such as [PERSON_0001].
How to mask documents before they reach the model
Section titled “How to mask documents before they reach the model”1. Install Shadow and start it
Section titled “1. Install Shadow and start it”curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | shshadow init --org "Acme GmbH"shadow run2. Set file handling in shadow.toml
Section titled “2. Set file handling in shadow.toml”[files]enabled = true # defaultpdf = "text" # text | block | passimages = "block" # no OCR: refuse screenshots and scans instead of sending them unmaskedmax_bytes = 52428800 # 50 MiB; larger files are not maskedblock_oversize = true # refuse files above max_bytes instead of forwarding them unmaskedIf your users need the model to reason about PDF layout (forms, charts), pdf = "text" will give worse answers. Prefer pdf = "block" for those workflows and send the source DOCX or XLSX instead, which is masked in place.
3. Teach Shadow your vocabulary
Section titled “3. Teach Shadow your vocabulary”Customer names, matter or project codenames and colleagues are not something a regex can know:
shadow terms add "org:Globex" "org:Initech" "Project Falcon" "person:Jane Doe"For better recall on free-text names in long documents, enable the open-weight NER sidecar or Presidio.
4. Test on a representative document
Section titled “4. Test on a representative document”shadow scan reads text files. Export the document’s text (for example Save as plain text in Word, or CSV from Excel) and scan it:
shadow scan contract.txt # redacted previewsshadow scan contract.txt --reveal # full values, on your screen onlyshadow scan customers.csv --json # kind, line, column, detector, score, valueAnything missing from the output will reach the provider as is. Add terms until the scan covers what matters.
5. Upload through Shadow
Section titled “5. Upload through Shadow”Point the SDK at the gateway, or use proxy mode for tools you don’t control:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8877/gw/openai/v1")with open("q3-renewals.xlsx", "rb") as f: file = client.files.create(file=f, purpose="user_data") # masked in transitExample: a contract review
Section titled “Example: a contract review”Clause in the DOCX the lawyer attaches (fictional):
This Agreement is made between Acme GmbH and Globex Ltd. Notices to Jane Doe,jane.doe@acme.example, Hauptstraße 5, 10115 Berlin. Payments to DE89 3704 0044 0532 0130 00.With organization = ["Acme GmbH"] and the term org:Globex Ltd, the provider receives this inside the still-valid DOCX (output from shadow mask on one machine; your persona’s fakes will differ):
This Agreement is made between Lingxi Technology Co., Ltd. and Talari Technology Co., Ltd. Notices to Yong Sun,xiu.wang@lingxi.cn, 53 Binjiang Avenue, 10115 Berlin. Payments to DE46 0256 5725 3509 8212 53.The street address takes the persona’s style while the postcode and city stay, and the IBAN is a different German IBAN that still passes mod-97. Without the org:Globex Ltd term, Globex would have been sent as is: add counterparties before you rely on it.
The model’s markup of risky clauses comes back about Acme GmbH, Globex Ltd and Jane Doe, so the lawyer works on the real contract. Shadow restores whole fakes, and a fake person’s first or last name on its own; if the model shortens a company (“Talari” for Talari Technology Co., Ltd.), that short form stays fake, so ask it to quote party names in full.
Limitations
Section titled “Limitations”Related
Section titled “Related”- File masking and detection
- Protect customer data in RAG pipelines
- Industries with document-heavy work: legal, healthcare, financial services
- Benchmarks and how Shadow compares
Frequently asked questions
Can Shadow redact a PDF before it is sent to ChatGPT or Claude?
Through the API, yes for text. With the default pdf = "text", Shadow extracts the PDF's text, masks it and sends it as a text document. Layout and images are lost, and scanned PDFs are not OCR'd. Consumer web apps such as chatgpt.com are not intercepted.
Will my Word or Excel file still open after masking?
Yes. DOCX, XLSX and PPTX are edited inside their ZIP container, so styles, tables and layout survive. In XLSX only shared strings are changed; numbers and formulas are untouched.
Does Shadow mask text in images or screenshots?
No. Shadow does not run OCR yet. Images pass through unchanged by default; set images = "block" to refuse requests that carry them. Local OCR is on the roadmap.
What happens to a file uploaded earlier without Shadow?
It is not re-scanned. If a request references a file by an earlier upload id, the provider already holds the original. Upload files through Shadow from the start.
How do I check what Shadow will find in a document?
Export the document's text and run shadow scan on it. It reports each finding's kind, position and detector without changing anything or touching the vault.