Skip to content
GitHubOpen source
Product
Docs
Solutions
IndustriesAll industries
CompareCompare all AI data-leak tools
Benchmarks
GitHubApache-2.0

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.

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.

TransportExamples
multipart/form-data uploadsOpenAI POST /v1/files, POST /v1/uploads parts
Base64 files inside JSONOpenAI input_file.file_data, Anthropic document blocks with a base64 source, Gemini inline_data
data: URIsdata:text/csv;base64,... anywhere in a JSON string

The format is recognised from the declared MIME type and the file name.

FormatTreatmentResult
Plain text, MarkdownMasked as textSame file, masked
CSVMasked cell by cellSame columns and rows
JSONString values maskedValid JSON
HTML, XMLText maskedMarkup intact
DOCXText runs masked in placeA valid Word document
XLSXShared strings masked in placeA valid workbook; numbers and formulas untouched
PPTXSlide text runs masked in placeA valid presentation
PDFText extracted, masked, sent as text (pdf = "text")The model receives masked text, not the PDF
ImagesPassed through (images = "pass") or refused ("block")No OCR
Anything elsePassed 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”
Terminal window
curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh
shadow init --org "Acme GmbH"
shadow run
[files]
enabled = true # default
pdf = "text" # text | block | pass
images = "block" # no OCR: refuse screenshots and scans instead of sending them unmasked
max_bytes = 52428800 # 50 MiB; larger files are not masked
block_oversize = true # refuse files above max_bytes instead of forwarding them unmasked

If 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.

Customer names, matter or project codenames and colleagues are not something a regex can know:

Terminal window
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.

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:

Terminal window
shadow scan contract.txt # redacted previews
shadow scan contract.txt --reveal # full values, on your screen only
shadow scan customers.csv --json # kind, line, column, detector, score, value

Anything missing from the output will reach the provider as is. Add terms until the scan covers what matters.

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 transit

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.

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.