Skip to main content
Optional redact before upload replaces structured PII with consistent placeholder tokens on the client’s machine. Watchdoc only ever receives the redacted file plus a content-free forensic sidecar. The playground does this in the browser; API users can do the same on their side and tell us via redacted + redaction_sidecar.

What gets redacted automatically

Detection patterns for structured identifiers are ported from Microsoft Presidio (MIT). Contextual names and street lines are also scored by Rampart (CC BY 4.0) — a ~15 MB MiniLM NER model that runs entirely in the browser via WASM. Monetary amounts, dates, BIC/SWIFT codes, and ISO currency codes are kept so balance-math and structural fraud signals still work. The playground preview still lets the user drag boxes over anything the detectors missed.

Token semantics

  • Same original value → same token everywhere (1234-5678 and 1234 5678 share [ACCT_1]).
  • Different values → different indices ([NAME_1] vs [NAME_2] is a real inconsistency).
  • The engine is told these rules and will not treat placeholders themselves as fraud.

Coverage tradeoffs

Kept
  • Content checks on non-redacted text
  • Cross-field consistency via tokens
  • Font forensics on remaining text
  • Structural signals via the sidecar (version count, editors, font hints)
  • Incremental-save history: each save of the original is analyzed locally with the same token registry, so a PII value edited between saves surfaces as a token diff ([NAME_2] removed, [NAME_1] added) without shipping old bytes
  • Incremental-edit content: text spans added by later saves (edited amounts, dates) are diffed locally and shipped with PII tokenized — non-PII edits like value changes stay verbatim, so “the net pay was edited after creation” still reaches the engine
  • Editing-tool fingerprints per save, scraped from each save’s appended raw bytes (catches e.g. an Adobe touch on a Chrome-made PDF whose parsed metadata still claims the original producer)
  • The playground X-ray viewer: each incremental save is rendered and redacted on-device (same tokens, same main-pass coverage including OCR hits and signatures, same manual boxes) and uploaded alongside the final document, together with a copy of the final pages with changed regions boxed in red — computed with the same last-two-saves cardinality diff the server runs on unredacted uploads — so the version slider and “pages flagged” navigation work identically, without any original bytes leaving the device
  • Visual / AI analysis outside replaced regions
  • Paste-detection features at replaced regions as numeric pixel forensics (noise / background / sharpness / ELA / baseline deltas)
  • Provenance verdicts the engine normally reads off the raw file, which flattening destroys: C2PA AI-generation markers (PDF info/XMP and DCT-embedded full-page images), the Photoshop-authored JPEG marker, and the empty-shell revision structure (see ai_provenance, embedded_ai_provenance, photoshop_saved, text_layer). These decide the result on their own, so omitting them from the sidecar means an AI-generated or Photoshop-authored document can come back clear. FlateDecode-compressed embedded images are a residual gap — the client cannot inflate them in-browser, so only DCT-embedded JPEGs are scanned.
Lost
  • Open-ended visual judgment of the replaced patches beyond those features
  • Semantic checks on the redacted values themselves
  • Edit-history analysis beyond what the sidecar captured
OCR can miss on poor scans — the preview (and manual boxes) is the last line of defense.

Playground

On the Documents upload modal, enable Redact before upload. Each file is OCR’d and redacted locally; you preview the result (and can drag extra boxes) before anything is sent. For PDFs with incremental saves, the browser also uploads a redacted render of each save plus a changed-region highlight of the final pages (redaction_versions_N / redaction_highlight_N form fields), which power the X-ray version slider on the result page. Every covered region from the main pass (text-layer hits, OCR-only hits, signatures, and manual boxes) is re-projected onto those historical renders so scanned PII and ink cannot leave the device under the “Previous save” side.

API: redacting on your side

No SDK is required. Redact locally, then submit the redacted bytes with:
redacted: true alone is not enough to activate the engine’s redaction-artifact suppression (the prompt addendum that tells the model to ignore black boxes, overlays, crisp placeholder labels, etc.). The server only emits that addendum when extracted content contains [PREFIX_N] placeholders, or the sidecar’s pixel_forensics inventory lists covered regions with both a token and page. A bare tokens list (or a bare flag with an empty sidecar) is not verification — visual fraud cues stay valid. Recommended open-source tooling for server-side redaction:
  • Microsoft Presidio — detection + anonymization to typed placeholders
  • A PDF renderer (e.g. pypdfium2) if you need to flatten scanned pages safely

Token format

Placeholders must look like [PREFIX_N] where PREFIX is one of NAME, ACCT, ROUTING, CARD, SSN, EMAIL, PHONE, IBAN, ADDR, ID, MRZ (or your own), and N is a stable 1-based index for that prefix. Same original value → same token — including across saves in pdf.versions, which is what makes version_diffs meaningful.

Sidecar schema (all fields optional)

Harvest the sidecar from the original file before you flatten/redact. Never include raw PII in the sidecar. Incremental-edit new_texts are scrubbed for known registry values (≥2 characters) before they leave the device; a residual gap remains for PII that existed only in a historical save and was never detected on the final document (so it never entered the registry).

Provenance fields

These fields carry verdicts the engine would otherwise read off the raw bytes. Flattening destroys all of them, so if you omit them a document the engine would have rejected outright reaches the model as an ordinary picture and can come back clear. pdf.text_layer only convicts alongside eof_count > 1, every earlier revision in versions reporting page_count 0 (or failing to parse), final_span_count of at least 20, and 48h or more between creation_date and mod_date.

Response

serialize_check exposes document.redacted: true so consumers know coverage was reduced. Extracted fields may contain tokens, e.g. "name": "[NAME_1]".

Attribution

  • Structured-PII recognizers adapted from Microsoft Presidio (MIT).
  • Contextual name/address NER: Rampart by National Design Studio (CC BY 4.0). Model weights are self-hosted under /static/vendor/rampart/.