docketrouter
DocumentationBrowse
API reference · Docket Scrambler

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.

POST/api/v1/scrambleAPI key or session (no site demo)
POST/api/v1/unscrambleAPI key or session (no site demo)

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 measuredResult
Real dockets scrambled end to end40 dockets, 1,984 entities
Entities leaked to the outbound text0
Byte-exact document restores6,200/6,200
Reporter citations in, citations harmed3,994 in, 0 harmed
Adversarial filings with typed ground truth (oracle, hijacked and lazy models)167, zero leaks
Generated filings fuzzed across three adversarial models1,000
The release gate refuses rather than degrades

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_idstringrequired
    The 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.
  • textstringrequired
    The 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.

200 OK (shape)
{
  "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_idstringrequired
    The same matter the text was scrambled under. The mapping is decrypted for this caller and this matter only, used, and never returned.
  • textstringrequired
    The 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. document restores a scrambled document’s own bytes from its sealed ledger: byte-exact or refused, never partial. canonical restores 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 .text

Limits

LimitValueOn breach
text60,000 characters per document400
Scramble calls per key20 per minute, burst 5429 with Retry-After
Unscramble calls per key60 per minute, burst 20429 with Retry-After
Release gaterefuses what it cannot account for422 with the reason, no text
Site demonot availablethe 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.