DocumentationBrowse
Scramble and unscramble
Send the case, not the client. /scramble pseudonymizes a case file before any frontier model sees it: every person, organisation, address, docket, account and identifier replaced by a stable placeholder, on our hardware, by a local model that never writes text anyone reads. /unscramble puts the names back in the answer. The mapping is encrypted per caller and per matter, and no route ever returns it.
Measured, not promised
From the proof run of 2026-09-16 (scripts/scrambler-proof.ts, re-runnable; the full table is published with the Scrambler page):
| What was measured | Result |
|---|---|
| Real dockets scrambled end to end | 40 dockets, 1,984 entities |
| Entities leaked to the outbound text | 0 |
| Byte-exact document restores | 6,200/6,200 |
| Reporter citations in, citations harmed | 3,994 in, 0 harmed |
| Adversarial filings with typed ground truth (oracle, hijacked and lazy models) | 167, zero leaks |
| Generated filings fuzzed across three adversarial models | 1,000 |
A document the gate cannot fully account for is refused with a 422 naming the reason and carrying no text. The local liaison refuses any model id that is not local/, and the no-egress proof runs with fetch replaced by a tripwire: not one network call.
POST /scramble
matter_idstringrequiredThe matter this document belongs to. Placeholders are stable per matter: the same person is[PERSON_1]in every filing of the matter, across calls and days.textstringrequiredThe document as plain text, up to 60,000 characters per call. Longer filings are sent a document per call.
The response carries the scrambled text, per-type counts, the reasons anything was rejected, andrestore_basis. A re-submitted document is served from an encrypted content-addressed cache without touching the model again (cached: true). With no local model configured the route answers 503, never a degraded scramble.
{
"matter_id": "smith-v-jones",
"scrambled": "…[CLIENT_1]… [PERSON_2] …[DOCKET_1]…",
"stats": { "PERSON": 12, "ORG": 4, "SSN": 1, "DOCKET": 2 },
"rejected": { "is-citation": 3, "is-public": 2 },
"degraded": false,
"restore_basis": "normalized",
"cached": false,
"took_ms": 1840
}POST /unscramble
matter_idstringrequiredThe same matter the text was scrambled under. The mapping is decrypted for this caller and this matter only, used, and never returned.textstringrequiredThe text to restore: a frontier model’s answer, or a scrambled document.mode"answer" | "document" | "canonical"answer(default) renders the way a lawyer writes: full name first, surname after, surname after an honorific.documentrestores a scrambled document’s own bytes from its sealed ledger: byte-exact or refused, never partial.canonicalrestores one name per entity with no rendering.
Unknown placeholders are reported, never guessed: an answer that mints a [PERSON_9] the matter never had is a hallucinated entity or an injection, and it comes back in unknown_placeholders. Indocument mode a text with no sealed ledger entry is refused with 409, and the refusal confirms nothing about the matter.
Quickstart
# 1. scramble on DocketRouter (local model; nothing leaves the box)
jq -Rs '{matter_id: "smith-v-jones", text: .}' filing.txt \
| curl https://docketrouter.ai/api/v1/scramble -H "Authorization: Bearer $DOCKETROUTER_API_KEY" \
-H "Content-Type: application/json" --data-binary @- > scrambled.json
# 2. send ONLY the placeholders to your frontier model, get answer.txt
# 3. put the names back
jq -Rs '{matter_id: "smith-v-jones", text: .}' answer.txt \
| curl https://docketrouter.ai/api/v1/unscramble -H "Authorization: Bearer $DOCKETROUTER_API_KEY" \
-H "Content-Type: application/json" --data-binary @- | jq -r .textLimits
| Limit | Value | On breach |
|---|---|---|
text | 60,000 characters per document | 400 |
| Scramble calls per key | 20 per minute, burst 5 | 429 with Retry-After |
| Unscramble calls per key | 60 per minute, burst 20 | 429 with Retry-After |
| Release gate | refuses what it cannot account for | 422 with the reason, no text |
| Site demo | not available | the mapping is per caller; a demo would mint one |
Design and threat model: the Scrambler page on docketx.ai. Pair it with private pods when the frontier call itself must also stay on hardware you control.
Something here wrong or missing? Mail hello@docketrouter.ai with the request_id and we will fix the docs or the API, whichever is broken.