PII Masking

Two contact cards side by side. The first shows a name, email, phone number, and SSN in plain text. The second shows the same card with each value covered by a solid black bar.

Captain can strip personally identifiable information (PII) from content at index time. Set mask_pii: true on any indexing request and Captain detects and masks PII in the parsed text, the layout regions, and the pixels of images — before anything is embedded or written to the search index.

It is opt-in and per-job: the flag defaults to false, so existing indexing is unchanged, and only the files you index with mask_pii: true are masked.

What gets masked

Masking covers three layers, so PII cannot leak through any of them:

  • Text — the parsed content of documents, media transcripts, and the text Captain reads out of images. Detected PII is replaced with an entity tag, for example:

    Contact Jane Doe at jane.doe@example.com, SSN 123-45-6789, phone (415) 555-0142.

    becomes

    Contact <PERSON> at <EMAIL_ADDRESS>, SSN <US_SSN>, phone <PHONE_NUMBER>.
  • Images — for standalone image files and images embedded in PDFs, PII text that is visible in the pixels is covered with solid boxes (shown above). This applies to both the stored, retrievable image and the visual embedding.

  • Regions — when you query with include.regions, the per-region text is masked as well, so the layout view carries no raw PII either.

Enable it

Add "mask_pii": true to any indexing request. It works on every indexing endpoint (S3, GCS, Azure, R2, Supabase, Backblaze, Dropbox, Google Drive, SharePoint, OneDrive, URL, YouTube, and text/file upload), across the bucket, directory, and single-file variants.

Index a file with masking

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/s3" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "bucket_name": "my-documents",
> "aws_access_key_id": "AKIA...",
> "aws_secret_access_key": "...",
> "processing_type": "advanced",
> "mask_pii": true
> }'

Index raw text with masking

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/text" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "content": "Contact Jane Doe at jane.doe@example.com, SSN 123-45-6789.",
> "filename": "note.txt",
> "mask_pii": true
> }'

Query the collection afterwards and the returned results[].text (and regions, if requested) contains the entity tags, never the original values.

What is and isn’t masked

Captain masks a curated set of high-value identifiers: emails, phone numbers, US SSN, credit cards, IBAN and US bank numbers, passports, driver’s licenses, ITINs, IP addresses, person names, and locations.

It deliberately does not mask dates or generic numbers — masking those would corrupt ordinary content (a revenue figure, a row count, a timestamp) without meaningfully protecting anyone.

Image masking covers OCR-readable text in images. Detecting and blurring faces, handwriting, and signatures is on the roadmap but not available yet.

Notes

  • Opt-in, per job. Files indexed without mask_pii: true are stored as-is; the flag does not retroactively mask previously indexed data.
  • Source files are untouched. Captain only masks the copies it generates and stores (parsed text, region data, figure crops). Your original file in your own bucket is never modified.
  • Fail-safe. If masking cannot run for a file, that file fails to index rather than being stored unmasked.
© 2026 Captain