Most logging advice for AI systems starts with "store the prompt and the answer". That creates a second copy of every conversation, full of personal data, that your data protection officer then has to defend. ComplyEdge does the opposite: it never stores prompts or outputs. This post explains how a record that holds no text can still be checked against the text, end to end, with the exact calls.
What the record holds
Every check writes one record. It holds the decision and its basis:
- which rule fired and the provision of law it applies, with a rule ID from the open rule set;
- the decision (block, allow or flag) and when it was taken, in UTC;
- the exact rule set that decided (
bundle_id,bundle_digest); - a keyed hash of the checked text (
text_hash, HMAC-SHA-256 under your account's own key), and user or session identifiers only as keyed pseudonyms; - its place in your account's hash chain, set in the same write as the record.
The record on its own proves the control ran: which rule, which law, what decision, and when. It does not hold the text, so it cannot show what was said. That part is yours.
The one step: keep the event_id next to your copy
Every call to /v1/check returns an event_id. Save it wherever you already keep the prompt and the answer: your chat history, your application logs, your case file.
result = ce.check(prompt, direction="prompt")
answer = call_your_model(prompt)
chat_log.save(prompt=prompt, answer=answer, complyedge_event_id=result.event_id)
Check the model's answer too, and it gets its own event_id; save that one next to the answer.
When someone asks
Take your copy of the text and its event_id, and ask whether they match:
POST /v1/audit/verify
{ "event_id": "uuid-v4", "text": "the prompt from your own log" }
200
{ "event_id": "uuid-v4", "match": true, "text_hash_alg": "hmac-sha256-1" }
The SDKs wrap the same call: ce.verify_input(event_id, text) in Python and ce.verifyInput(eventId, text) in TypeScript, both returning true or false. The text is used for that one request: it is not stored and not logged.
A yes ties the two halves together. Your copy says what was said; the record says which rule ran, under which law, with what decision and when; and the match says your copy is the exact text the decision was about. Change one character and the answer is no.
A worked example: a credit decision
The new Consumer Credit Directive, Directive (EU) 2023/2225, applies from 20 November 2026. Where a creditworthiness assessment involves automated processing, its Article 18 gives the consumer the right to ask for human intervention, including an explanation of the assessment. Suppose a consumer asks why an AI-assisted assessment went against them.
- The lender's own case file holds the application text and the
event_idof each check that ran on it. - The record for that
event_idnames the rule that fired, the provision it applies, and the rule-set version. POST /v1/audit/verifyconfirms the text in the case file is the text that was checked.
None of that makes the explanation for the lender, and a record is not a credit decision. What it gives is a fixed, checkable account of which control ran on which text, under which rule, at which moment, kept outside the AI system.
The limits, stated plainly
- Verify confirms a match. It does not show that your own log is complete; that part is your log's job.
- If you delete your copy of the text, the record still proves the control ran, but nobody can show what the text said any more, us included.
- If your account's key is destroyed (a crypto-shred), the answer is
match: falsewith the date the key was destroyed, so a destroyed key is never mistaken for a different text. - Records are kept for 184 days by default, which covers the six months Article 26(6) asks for where it applies, from any start date. Enterprise accounts can keep them for 7 or 11 years.
- A record exists only for the calls you send for a check.
The full reference, with both SDKs and every response field: how audit reconciliation works. Every field in the record, why it exists and how long it is kept: the DPO pack.