Compliance Redaction, End to End
The workflow a data-protection request or a disclosure order actually demands: not "we drew
boxes" but a chain of artifacts proving what was found, what a human approved, what was
destroyed, and that it is gone. Every step emits evidence; store all of it together.
Cookbook conventions apply; the document rides as $FILE_ID from one
upload.
1Detect: what personal data is in this document?#
PII detection reads the whole document (OCR included) and returns each entity with its occurrences located on the page:
curl -s "$LMKIT_URL/lmkit/v1/pii-detection" \
-H "Authorization: Bearer $LMKIT_API_KEY" -H "Content-Type: application/json" \
-d '{
"input": "'$FILE_ID'",
"input_format": "FileIdentifier",
"custom_labels": ["Employee id"],
"guidance": "Company names are not personal data in this workflow."
}'
{
"overall_confidence": 0.93,
"entities": [
{ "label": "Person name", "entity_type": "PersonFullName", "value": "Amelie Durand",
"confidence": 0.98,
"occurrences": [ { "page_index": 0, "start_offset": 141, "length": 13,
"bounding_box": { "left": 96.2, "top": 214.7, "width": 118.4, "height": 12.9 } } ] },
{ "label": "IBAN", "entity_type": "Iban", "value": "FR7630004000031234567890K43",
"confidence": 0.99,
"occurrences": [ { "page_index": 1, "start_offset": 88, "length": 27,
"bounding_box": { "left": 60.0, "top": 640.2, "width": 208.9, "height": 11.8 } } ] }
]
}
Omitting built_in_types considers every built-in type; custom_labels adds your
domain's identifiers per request, no training involved.
2Review: detection proposes, a person disposes#
The one human checkpoint, and it belongs here, BEFORE destruction. Render pages with the
bounding_box coordinates drawn (this is exactly what the boxes are for) and let the
reviewer confirm, drop false positives, and add anything missed. The output of review is
two lists your code assembles: confirmed strings and, for visual-only targets like a
signature, confirmed page areas.
3Destroy: redaction that removes, not covers#
Feed both lists to redaction in one call. Content under the selectors is DELETED from the file: glyphs, pixels, paths, annotations.
curl -s "$LMKIT_URL/lmkit/v1/pdf-redact" \
-H "Authorization: Bearer $LMKIT_API_KEY" -H "Content-Type: application/json" \
-d '{
"input": "'$FILE_ID'",
"input_format": "FileIdentifier",
"search_terms": ["Amelie Durand", "FR7630004000031234567890K43"],
"areas": [ { "page_index": 2, "left": 380.0, "top": 700.0, "width": 160.0, "height": 60.0 } ],
"redact_images": true,
"redact_vector_graphics": true,
"remove_annotations": true,
"recurse_into_forms": true,
"draw_fill_boxes": true
}'
{
"file_id": "b7c3...",
"content_removed": true,
"pages_processed": 3,
"search_matches": 5,
"removed_glyphs": 214, "removed_text_objects": 3, "edited_text_objects": 4,
"edited_images": 1, "removed_images": 0, "removed_paths": 2,
"edited_forms": 1, "removed_annotations": 1
}
The counters are evidence artifact number one: what the operation did, exactly. Note the
scope toggles are explicit policy: this call scrubs images and vectors too, and paints the
classic boxes; an invisible-trace workflow sets draw_fill_boxes to false and nothing else
changes.
4Prove: search the output for what must be gone#
Absence is testable (Search, Layout, and Inspection). Run each redacted string against the OUTPUT file, in fuzzy mode so OCR-flavored near-misses cannot hide:
curl -s "$LMKIT_URL/lmkit/v1/pdf-search" \
-H "Authorization: Bearer $LMKIT_API_KEY" -H "Content-Type: application/json" \
-d '{
"input": "b7c3...",
"input_format": "FileIdentifier",
"query": "Amelie Durand",
"search_mode": "Fuzzy",
"max_edit_distance": 2
}'
{ "total_matches": 0, "scanned_pages": 3, "limited_by_max_results": false, "matches": [] }
Zero matches, full scan, no truncation: evidence artifact number two, re-runnable by any auditor against the artifact itself.
5Archive: conformance with a report#
Redaction rewrote the file, so the archival copy is minted now: convert to PDF/A (the response embeds an independent validation of its own output) and store the artifact with its reports.
curl -s "$LMKIT_URL/lmkit/v1/pdf-to-pdfa" \
-H "Authorization: Bearer $LMKIT_API_KEY" -H "Content-Type: application/json" \
-d '{ "input": "b7c3...", "input_format": "FileIdentifier", "level": "PdfA2b" }'
{
"file_id": "e19a...", "level": "PdfA2b", "conforms": true,
"fixes_applied": ["FontsEmbedded", "MiscKeys"],
"validation": { "verdict": "compliant", "flavor_validated": "PdfA2b",
"rules_evaluated": 73, "rules_failed": 0, "findings": [] }
}
Download e19a... as the record of note. The compliance folder now holds four artifacts:
the detection report, the reviewer's confirmation, the redaction counters plus the
zero-match search, and the conversion-with-validation report. That chain answers the audit
before it is asked.
6Production notes#
- Never skip step 2. Detection at high confidence is still a proposal; the workflow's legal weight comes from a person approving the exact list that step 3 executes.
- Sweep at scale, redact per document. For a folder-level obligation, run detection
across the corpus first and queue only documents with findings; detection is
job-friendly with
Prefer: respond-async(the contract). - Metadata is part of disclosure. Check
pdf-infoon the final artifact: titles, authors, and keywords travel with files, and the archival conversion normalizes what redaction does not target.