Data Handling
This page documents exactly what LetsCompl.ai redacts, what it persists, and where pattern-based redaction has known gaps. It is the source of truth for the retention and redaction claims made on the Terms of Service, Privacy Policy, and Security Policy pages.
What is persisted
Every evaluation creates an EvaluationEvent row. It stores:
redactedPayload— the payload after redaction (server-side redaction always runs before this write; see below).- Verdict and policy metadata:
verdict,citationRuleId/citationRuleName/citationRegulation/citationControlMappings,bundleRevision,enforcementMode. - Operational metadata:
createdAt,source,latencyMs,llmUsed.
Raw, unredacted payload content is not a field on this record and is not written to any table. This is the accurate version of the "no raw data persisted" claim: it describes what the evaluation pipeline writes to Postgres, not a guarantee that every sensitive value has been detected and stripped from the payload it does write.
The redact → evaluate → persist pipeline
1. Local SDK redaction (optional, on by default). @letscomplai/sdk and letscomplai (PyPI) run the same pattern set locally before the request leaves your process. Pass redact: false (TS) / redact=False (Python) to skip this and send the payload as submitted — this is a deliberate customer opt-out, not a platform default, and it means raw values travel over the network (TLS-encrypted) to the gateway.
2. Server-side redaction (always on, unconditional). lib/engine/engine.ts calls the same redactor on every payload it receives, regardless of whether the SDK already redacted it or the caller opted out. The evaluation path never intentionally persists raw payload content: even a redact: false request is redacted again before the result is written to EvaluationEvent.redactedPayload.
3. Evaluate. Rules run against the redacted payload — never the raw one.
4. Persist. The redacted payload and verdict metadata (above) are written to Postgres.
Pattern coverage (what is actually detected)
Redaction is regex/pattern-based, not a general-purpose PII/PHI classifier. The exact same pattern set and match order is implemented in three places — lib/engine/redactor.ts (gateway), packages/sdk/src/redactor.ts (TS SDK), packages/python-sdk/letscomplai/redactor.py (Python SDK) — and is covered by a cross-implementation parity test (test/sdk/client.test.ts).
| Category | Pattern | Notes |
|---|---|---|
| SSN | \d{3}-\d{2}-\d{4} | Hyphenated format only. |
| Card number | 13–16 digit run, optional spaces/hyphens | |
| Email | standard email shape | |
| Phone | US formats ((555) 123-4567, 555-123-4567, +1 555 123 4567, etc.) | |
| MRN | digits explicitly prefixed with MRN | Unprefixed medical record numbers are not recognized. |
| ICD code | ICD-9/ICD-10 shaped codes, with or without the ICD- prefix | |
| Clinical term | fixed dictionary of 16 condition names (e.g. cancer, diabetes, hiv) | Any clinical term outside this list is not recognized. |
| Name | patient / Mr. / Ms. / Mrs. / Dr. followed by a two-word capitalized name | |
Known gaps
The following are not detected by the current pattern set and will pass through into the persisted redactedPayload unredacted:
- Unhyphenated SSNs (
123456789) - IBANs and non-US bank/routing numbers
- Dates of birth
- Home/mailing addresses
- Passport numbers
- Driver's license numbers
- Non-US phone number formats
- Clinical terms, diagnoses, or medications outside the 16-word dictionary above
If your workspace handles data in these categories, treat the redactor as a supplementary control, not a substitute for not sending that data to the API in the first place, and consider pre-scrubbing it yourself before calling evaluate().
Versioning
Pattern coverage changes are tracked like any other product change — additions or changes to the pattern set land in the same PR review process as the rest of the engine, and the three implementations (gateway, TS SDK, Python SDK) are kept in lockstep by the parity test referenced above. This document is updated whenever pattern coverage changes.