This guide adds a tamper-evident receipt to an AI agent’s important decisions, in any Python stack — LangChain, LlamaIndex, a raw OpenAI loop, or your own framework. The receipt is signed, hash-chained, and verifiable by anyone with no Marturia account. Budget about fifteen minutes.
If you only want the verify side (you’ve been handed a receipt to check), skip to Verify a receipt.
1. Get a project and an API key¶
In the dashboard at marturia.dev/app:
- Create a project (Projects → + New project). The slug shows up in the public verifier URL.
- Mint an API key (open the project → API Keys → Mint key). You’ll see the key once — it starts with
mtu_live_. Copy it; only its hash is stored.
Keep the key in your environment, not in code:
export MARTURIA_API_KEY=mtu_live_xxxxxxxxxxxx
2. Install¶
The SDK and the offline verifier ship in one package:
pip install marturia-verify
That gives you marturia_sdk (to record receipts) and the marturia-verify CLI (to check them).
3. Record a receipt at a decision point¶
Pick the moments that would matter in a dispute — money moving, an approval, an irreversible action — not every token. At that point, call record_receipt with a short agent_name and a JSON-serializable payload:
import os
from marturia_sdk import SyncClient
client = SyncClient(api_key=os.environ["MARTURIA_API_KEY"])
# ... your agent decides something consequential ...
receipt = client.record_receipt(
agent_name="refund_approver",
payload={
"decision": "approve_refund",
"order_id": "A-10293",
"amount_usd": 1200,
"model": "your-model-id",
"reason": "within policy window",
},
)
# Store the receipt reference next to your normal logs.
log.info("marturia receipt recorded", verify_url=receipt.verify_url)
A few rules that keep receipts honest:
- Hash, not dump. Put the decision in the payload — inputs that determined it, the chosen action, the model id — not megabytes of raw context. The receipt binds and proves whatever you include.
- Record after the decision, before the action where you can, so the receipt reflects what was actually committed to.
- It’s best-effort by design. Wrap the call so a Marturia hiccup never blocks your agent:
try:
receipt = client.record_receipt(agent_name="refund_approver", payload=payload)
except Exception:
log.warning("marturia receipt failed; continuing") # never block the agent
Optionally pass agent_run_id="..." to group every receipt from one agent run.
4. Store the reference¶
You don’t need to store the whole receipt — just enough to find it later. Keep the verify URL (or the tenant id + sequence it encodes) in your existing logs or database, alongside the decision it covers. The receipt chain itself lives in Marturia; the verifier pulls what it needs.
Verify a receipt¶
Anyone — an auditor, a customer, opposing counsel — can verify without an account. Download the receipt JSON from its public verify URL, then check it against your tenant’s public key (available in the project’s settings):
curl -s https://marturia.dev/v1/verify/<tenant>/<seq> > receipt.json
pip install marturia-verify
marturia-verify --receipt receipt.json --pubkey-hex <tenant-public-key>
A clean receipt reports VALID (signature good, hash chain intact, sequence confirmed). If anyone altered the payload or signature, it reports INVALID and which check failed. To verify a whole run at once, use --chain receipts.jsonl instead of --receipt.
That’s the entire loop: one record_receipt call at the decisions that matter, and a one-line verify that needs nothing but the public key.
What this is (and isn’t)¶
It’s an integrity layer, not a tracing tool. Keep your observability stack for debugging; add a receipt only where you’d need to prove to someone else that a decision happened and hasn’t changed. Most of your agent’s activity will never touch it.
Closed beta is open:
pip install marturia-verifyfor the SDK + verifier/docs/quickstart.htmlfor the full reference
Related Marturia resources - /guides/integrating-marturia-with-langchain.html - /guides/bulk-receipt-verification.html - /blog/logs-arent-evidence.html