Python example
A complete agent: a real model, a real tool call, traced end to end. Two files, no build step. The TypeScript version is at /examples/langchain.
Run it
Save both files below into an empty folder, then:
pip install -r requirements.txt export MATRIX_API_KEY=sk_... # from matrixverify.dev, shown once export ANTHROPIC_API_KEY=sk-ant-... export AGENT_EMAIL=you@yourdomain.com python agent.py
On Windows PowerShell, set each variable with $env:MATRIX_API_KEY="sk_..." instead of export. Python 3.9 or newer.
What happens
The agent is asked to email an invoice. It calls send_email, the tool reports success, and the agent tells you it sent it. The tool in this example sends nothing — it returns success without doing anything, which is the failure Matrix exists to catch. Swap in your real send when you are done looking.
The trace — instruction, tool call with its arguments, and the agent’s claim — appears on your findings page within about half an hour. Traces from the Python and TypeScript SDKs are the same on the wire; the verifier cannot tell which one sent them.
requirements.txt
matrix-verify>=0.1.0 langchain>=1.0 langchain-anthropic>=1.0
agent.py
"""A LangChain agent traced by Matrix.
pip install -r requirements.txt
export MATRIX_API_KEY=sk_... # from matrixverify.dev, shown once
export ANTHROPIC_API_KEY=sk-ant-...
export AGENT_EMAIL=you@yourdomain.com
python agent.py
The agent is asked to email an invoice. It calls send_email, the tool reports
success, and the agent says it sent it. The trace — what it was asked, what it
called with which arguments, and what it claimed — lands on your findings page
within about half an hour.
THE send_email TOOL BELOW SENDS NOTHING. It returns success without doing
anything, which is the failure Matrix exists to catch. Replace it with your
real send once you have seen a trace arrive.
On Windows PowerShell, set each variable with $env:MATRIX_API_KEY="sk_..."
instead of export.
"""
import json
import os
import sys
from langchain_anthropic import ChatAnthropic
from langchain.agents import create_agent
from langchain_core.tools import tool
from matrix_verify import verify
from matrix_verify.langchain import verify_callback_handler
# The SDK is fail-silent by design — a missing key would look like success —
# so this example checks up front and says so.
for name in ("MATRIX_API_KEY", "ANTHROPIC_API_KEY"):
if not os.environ.get(name):
print(f"Set {name} first. See the comment at the top of this file.")
sys.exit(1)
verify.init(
api_key=os.environ["MATRIX_API_KEY"],
# Explicit only because self-hosters need it; the default is the same.
endpoint=os.environ.get("MATRIX_ENDPOINT", "https://matrixverify.dev/api/traces"),
)
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to one recipient."""
# Replace with your real send. This one does nothing and reports success.
return json.dumps({"status": "sent", "to": to, "subject": subject})
model = ChatAnthropic(model="claude-opus-5", max_tokens=16000)
agent = create_agent(
model,
tools=[send_email],
# A plain one-sentence report is what Matrix reads as the claim. Phrasing
# like "Emailed the August invoice to dana@..." is recognised; a vaguer
# "all done!" gives it nothing to verify.
system_prompt=(
"You are an operations assistant. Use your tools to do what is asked. "
"When you are done, reply with one plain sentence stating exactly what "
"you did, including the recipient's email address."
),
)
handler = verify_callback_handler(
# Callbacks cannot discover which account acted. Without this, every email
# claim comes back account_unverified.
from_resolver=lambda tool_name: os.environ.get("AGENT_EMAIL"),
)
to = sys.argv[1] if len(sys.argv) > 1 else "dana@northwind.example"
instruction = f"Email the August invoice to {to}."
print(f"asked: {instruction}")
try:
result = agent.invoke(
{"messages": [{"role": "user", "content": instruction}]},
config={"callbacks": [handler]},
)
last = result["messages"][-1]
said = last.content if isinstance(last.content, str) else "\n".join(
block.get("text", "") for block in last.content if isinstance(block, dict)
)
print(f"agent said: {said}")
finally:
# Flushes the buffer. Nothing is sent without it.
verify.shutdown()
print("\nTrace sent. It appears at https://matrixverify.dev within about half an hour.")