Sanitize File
Upload images, PDFs, or DICOM files for PII/PHI scanning and redaction. Supports pixel-level redaction, PS3.15 de-identification, and single-use download tokens.
The Sanitize File API handles file-level content cleaning. Upload an image, PDF, or DICOM file, choose a policy and mode, and get back a sanitized file with a full findings report.
For text sanitization, see the Sanitize API. For a conceptual overview, see the Sanitize guide.
Upload and sanitize a file
Scan and optionally redact an uploaded file.
POST /api/v1/sanitize/file
Content-Type: multipart/form-dataRequires a valid API key or JWT.
Form fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file | file | Yes | File to scan: image, PDF, or DICOM | |
policy | string | No | "default" | Policy pack: default, hipaa, pci, legal |
mode | string | No | "redact" | One of redact, tokenize, block, flag |
ai_model | string | No | null | Model ID for AI-assisted second-pass review |
include_pixel_redaction | bool | No | false | Enable pixel-level redaction for DICOM burned-in text |
Max file size: 50 MB
Modes
| Mode | Behavior |
|---|---|
| redact | Black-box PII in the file, return a single-use download token |
| tokenize | Replace PII with reversible tokens, return token mapping. The file is also redacted and downloadable |
| block | If PII is found, block the file entirely (no download token) |
| flag | Scan only — return findings without modifying the file |
Policy packs
| Pack | Libraries | Use case |
|---|---|---|
default | pii, credentials, prompt_injection | General-purpose scanning |
hipaa | pii, credentials, prompt_injection, healthcare | PHI: MRNs, diagnoses, dates, providers |
pci | pii, credentials | Card numbers, account data |
legal | pii, credentials | Names, emails, case-adjacent PII |
File type detection
Files are identified by magic bytes, not the Content-Type header or extension. This prevents file-type spoofing. The extension is used only as a fallback.
| Type | Magic signature |
|---|---|
| JPEG | \xff\xd8\xff |
| PNG | \x89PNG\r\n\x1a\n |
| GIF | GIF87a / GIF89a |
| BMP | BM |
| TIFF | II*\0 (little-endian) / MM\0* (big-endian) |
%PDF | |
| DICOM | DICM at byte offset 128 |
Mode behavior per file type
| Mode | Image | DICOM | |
|---|---|---|---|
| redact | Presidio pixel-level redaction + OCR text scan | PyMuPDF in-place redaction | PS3.15 Annex E tag removal + optional pixel redaction |
| tokenize | Pixel redaction + token mapping | In-place redaction + token mapping | PS3.15 de-identification + token mapping |
| block | Reject if PII found | Reject if PII found | Reject if any finding has critical severity |
| flag | OCR scan only, return findings | Text scan only, return findings | Tag text scan only, return findings |
AI-assisted review
When ai_model is set, the extracted text (OCR for images, page text for PDFs, tag text for DICOM) is sent through an AI second-pass review after the rule-based scan. For images, any additional entities found by the AI model are mapped back to OCR word bounding boxes and blacked out at the pixel level.
Example
curl -X POST https://api.airaproof.com/api/v1/sanitize/file \
-H "Authorization: Bearer $AIRA_API_KEY" \
-F "file=@patient-record.pdf" \
-F "policy=hipaa" \
-F "mode=redact"Response
{
"file_type": "pdf",
"original_filename": "patient-record.pdf",
"findings": [
{
"entity_type": "ner_person",
"severity": "warning",
"action_taken": "redacted",
"library": "pii_ner",
"description": "NER: PERSON (score 0.92)",
"count": 3
},
{
"entity_type": "us_ssn",
"severity": "critical",
"action_taken": "redacted",
"library": "pii",
"description": "US Social Security Number",
"count": 1
}
],
"blocked": false,
"mode": "redact",
"policy": "hipaa",
"input_hash": "sha256:abc123...",
"output_hash": "sha256:def456...",
"download_token": "a1b2c3d4e5f6...",
"download_url": "https://api.airaproof.com/api/v1/sanitize/file/a1b2c3d4e5f6.../download",
"tokenized_text": "Patient [REDACTED]\nSSN: [REDACTED]\n...",
"token_mapping": null,
"dicom_tag_actions": null,
"pixel_redactions": null,
"receipt_id": "550e8400-e29b-41d4-a716-446655440000",
"request_id": "req_xyz789"
}Response fields
| Field | Type | Description |
|---|---|---|
file_type | string | Detected type: image, pdf, or dicom |
original_filename | string | The uploaded filename |
findings | array | List of detected entities (see Findings object) |
blocked | boolean | true if the file was blocked (block mode only) |
mode | string | The mode that was applied |
policy | string | The policy pack that was applied |
input_hash | string | SHA-256 hash of the original file |
output_hash | string or null | SHA-256 hash of the sanitized file. null if blocked |
download_token | string or null | One-time token for downloading the sanitized file. null if blocked or flag mode with no redaction |
download_url | string or null | Full URL for downloading the sanitized file |
tokenized_text | string or null | The extracted text after sanitization (redacted or tokenized) |
token_mapping | object or null | Token-to-original mapping. Only present in tokenize mode |
dicom_tag_actions | array or null | DICOM tag de-identification actions. Only present for DICOM files |
pixel_redactions | array or null | Pixel-level redactions applied. Only present for DICOM files with include_pixel_redaction |
receipt_id | string or null | Ed25519-signed receipt ID for audit trail |
request_id | string | Unique request identifier |
Tokenize mode
When mode is "tokenize", the file is redacted (download token provided) and the response includes reversible tokens in tokenized_text and token_mapping:
{
"file_type": "image",
"tokenized_text": "Patient <NER_PERSON_001>, DOB <NER_DATE_TIME_001>",
"token_mapping": {
"<NER_PERSON_001>": "John Smith",
"<NER_DATE_TIME_001>": "1985-03-15"
},
"download_token": "a1b2c3d4e5f6...",
"download_url": "https://api.airaproof.com/api/v1/sanitize/file/a1b2c3d4e5f6.../download"
}Use the token mapping with the Detokenize endpoint to reverse the text substitution.
DICOM response
DICOM files include additional metadata about the PS3.15 Annex E de-identification process.
dicom_tag_actions
Each entry describes an action taken on a DICOM metadata tag:
| Field | Type | Description |
|---|---|---|
tag_name | string | Human-readable tag name (e.g., PatientName) |
tag_number | string | DICOM tag number (e.g., (0010,0010)) |
action | string | Action taken: removed |
original_hash | string | SHA-256 hash of the original tag value (for audit, not reversal) |
pixel_redactions
When include_pixel_redaction is true, burned-in text detected in the pixel data is blacked out:
| Field | Type | Description |
|---|---|---|
text_found | string | The text detected in the image |
bounding_box | array | Pixel coordinates [x, y, width, height] |
confidence | number | OCR confidence score (0.0 to 1.0) |
Example (DICOM redact with pixel redaction)
curl -X POST https://api.airaproof.com/api/v1/sanitize/file \
-H "Authorization: Bearer $AIRA_API_KEY" \
-F "file=@brain-scan.dcm" \
-F "policy=hipaa" \
-F "mode=redact" \
-F "include_pixel_redaction=true"{
"file_type": "dicom",
"original_filename": "brain-scan.dcm",
"findings": [
{
"entity_type": "ner_person",
"severity": "warning",
"action_taken": "redacted",
"library": "pii_ner",
"description": "NER: PERSON (score 0.95)",
"count": 1
},
{
"entity_type": "patient_id",
"severity": "critical",
"action_taken": "redacted",
"library": "healthcare",
"description": "DICOM PatientID tag",
"count": 1
}
],
"blocked": false,
"mode": "redact",
"policy": "hipaa",
"input_hash": "sha256:abc123...",
"output_hash": "sha256:def456...",
"download_token": "f7e8d9c0...",
"download_url": "https://api.airaproof.com/api/v1/sanitize/file/f7e8d9c0.../download",
"tokenized_text": "Patient [REDACTED], ID [REDACTED]",
"token_mapping": null,
"dicom_tag_actions": [
{
"tag_name": "PatientName",
"tag_number": "(0010,0010)",
"action": "removed",
"original_hash": "sha256:a1b2..."
},
{
"tag_name": "PatientID",
"tag_number": "(0010,0020)",
"action": "removed",
"original_hash": "sha256:c3d4..."
},
{
"tag_name": "PatientBirthDate",
"tag_number": "(0010,0030)",
"action": "removed",
"original_hash": "sha256:e5f6..."
}
],
"pixel_redactions": [
{
"text_found": "John Smith",
"bounding_box": [100, 50, 250, 80],
"confidence": 0.91
}
],
"receipt_id": "550e8400-e29b-41d4-a716-446655440000",
"request_id": "req_dicom_001"
}Download sanitized file
Download the cleaned file using the one-time token from the sanitize response.
GET /api/v1/sanitize/file/{token}/downloadNo authentication required. The download token itself is the authorization.
- Tokens expire after 1 hour
- Tokens are single-use -- the file is deleted after the first download
- The response filename is
sanitized_<original_name>.<ext> - The
Content-Typeheader matches the original file type
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | The download_token from the sanitize response |
Response headers
| Header | Value |
|---|---|
Content-Type | Matches the original file type (e.g., application/pdf, image/png, application/dicom) |
Content-Disposition | attachment; filename="sanitized_<original_name>.<ext>" |
X-Token-Single-Use | true |
Example
curl -o sanitized-record.pdf \
"https://api.airaproof.com/api/v1/sanitize/file/a1b2c3d4e5f6.../download"Findings object
Every sanitize response includes a findings array:
| Field | Type | Description |
|---|---|---|
entity_type | string | What was detected (e.g., us_ssn, ner_person, mrn_pattern) |
severity | string | critical, warning, or info |
action_taken | string | redacted, tokenized, blocked, flagged |
library | string | Which scanner found it (pii, credentials, pii_ner, healthcare) |
description | string | Human-readable description |
count | integer | How many instances of this entity type were found |
Severity behavior
| Severity | Redact/Tokenize | Block | Flag |
|---|---|---|---|
critical | Replaced | Blocked | Flagged |
warning | Replaced | Blocked | Flagged |
info | Not replaced (flagged only) | Blocked | Flagged |
Info-severity entities (IP addresses, URLs, organization names) are reported in findings but never redacted or tokenized. This prevents false positives from being blacked out.
Error responses
| Status | Description |
|---|---|
| 401 | Missing or invalid authentication |
| 413 | File exceeds 50 MB limit |
| 415 | Unsupported file type (not image, PDF, or DICOM) |
| 422 | Empty file, corrupt file, or missing server dependency (e.g., PyMuPDF for PDF, pydicom for DICOM) |