Contexts and restore
How a context keeps tokens stable, how restore brings values back, and what the two redaction modes change.
The context
context_id is a string you choose: a conversation id, a ticket number, a batch run id. Every protect call that carries the same context_id in the same tenant draws its tokens from the same session, so the same person, account or number is the same token throughout. The same context_id in another tenant is a different session with different tokens.
Choose the scope to match the work. A support conversation is one context. A batch job over a customer table is one context, so joins and aggregates still line up over tokens. Two unrelated cases should be two contexts.
There is no expiry on a context. Sessions and tokens stay in the vault until you remove them from the database.
Restore
POST /v1/recover/text takes the answer that came back from the model and the context it belongs to:
{ "document_id": "00000000-0000-0000-0000-000000000000", "context_id": "ticket-48213", "text": "Sayın PERSON_kxmrqa, mart faturanız incelenmiştir …" }
{ "text": "Sayın Ayşe Demir, mart faturanız incelenmiştir …" }
document_id is required by the schema; in vault mode any UUID is accepted and ignored. Tokens are replaced longest first in one pass. A token that is not known in this context passes through unchanged; nothing is guessed. If the token sits inside an all-capitals line, the value is upper-cased with Turkish rules so the restored line reads correctly.
Only values marked restorable come back. A value that first appeared in a model’s output, rather than in your input, is stored with restore disabled and stays a token until the same literal value is later supplied as input. This stops a provider from laundering a guessed value into a restored one.
Restore is authorised by the API key: any key in the tenant with the recover_text permission can restore any context in that tenant. Keys that only protect should not be given this permission. Every restore is logged as a security event with the request id, never with the content.
Vault mode (default)
The token is derived from a keyed hash under your VAULT_KEY. The vault stores a hash of the value, the token, the value encrypted with AES-256-GCM under the same key, and the restore flag, per tenant and context, in PostgreSQL. Losing the key makes the stored values unrecoverable; rotating it does not re-encrypt existing rows.
If the vault database is unreachable at protect time, the call still succeeds with ephemeral tokens and a warning in stats.warnings. Such a result cannot be restored. If you depend on restore, treat that warning as a failure. The LiteLLM guardrail requires the vault and fails closed instead.
FPE mode (opt-in)
With "redaction_mode": "fpe" the value is encrypted in place with format-preserving encryption (NIST FF1, AES) under your FPE_KEY, so an 11-digit identity number becomes another 11-digit number of the same shape. TCKN, IBAN and card numbers are produced with valid checksums; other classes keep their character classes and punctuation. Values that cannot be encrypted in their domain fall back to a bracketed placeholder.
Recovery in FPE mode uses document_id instead of a context: the engine keeps a ledger of ciphertexts for 128 recent documents in memory, or for one hour in Redis. After that, recovery returns 404. FPE mode is for pipelines that need a realistic-looking value, not for long-lived conversations; use the vault for those.
Without a context
Omit context_id and every request gets fresh tokens from a random per-request key. Nothing is stored and recover will not find them. Use this when the answer does not need to come back with real values, for example classification or routing.
All example values in this documentation are synthetic.