LM-Kit OneDocs2026.10.6lm-kit.comEULA
Solutions/The Cookbook

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/detect-pii" \
  -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", "entity_type": "Person", "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": "BankAccountNumber", "entity_type": "BankAccountNumber", "value": "FR7630006000011234567890189",
      "confidence": 1.0,
      "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. label is the built-in type's name (Person, EmailAddress, PhoneNumber, PostalAddress, Url, IpAddress, DateOfBirth, SocialSecurityNumber, CreditCardNumber, BankAccountNumber, CompanyIdentifier, VatNumber, Other) or your custom label.

Two detectors run by default ("detectors": ["llm", "patterns"]). The language model finds what only reading understands (names, addresses); the deterministic detectors find what a checksum proves: IBANs (country length and mod-97), payment cards (issuer range and Luhn), French social security numbers (NIR key, Corsica included), SIREN and SIRET (Luhn, La Poste's rule), EU VAT numbers, e-mail addresses, phone numbers and IP addresses. A validated hit replaces the model's copy of the same identifier and is reported with confidence 1.0, every occurrence located on its page (OCR text of scans included). "detectors": ["patterns"] alone answers at once, with no model.

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", "FR7630006000011234567890189"],
    "areas": [ { "page_index": 2, "left": 380.0, "top": 700.0, "width": 160.0, "height": 60.0,
                "label": "Signature" } ],
    "redact_images": true,
    "redact_vector_graphics": true,
    "remove_annotations": true,
    "recurse_into_forms": true,
    "draw_fill_boxes": true,
    "sanitize": true,
    "verify": 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,
  "sanitized": { "metadata": true, "attachments": 0, "javascript": 0, "outlines": true,
                 "piece_info": 0, "xfa": false, "thumbnails": 0, "structure_text": 0 },
  "verified": true, "residual_characters": 0,
  "labels_drawn": 1, "labels_omitted": 0
}

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. sanitize stripped the metadata and bookmarks that repeated the name, and verified says the output was re-read under every area before it was stored: had any character survived, the call would have answered 422 verification_failed and stored nothing.

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. sanitize (on by default) strips titles, authors, keywords, XMP, attachments, scripts, bookmarks and thumbnails; check pdf-info on the final artifact all the same.