matrix

Your agent said it sent the email. It didn’t.

Agents report success for actions that never happened. There is no error, no exception, no retry. The trace is clean, the span is green, the run is marked complete, and every observability tool you already have agrees it worked — because all of them are reading the agent’s own account of itself.

This queries the real system instead. It takes the claim, finds the authoritative record — the actual Gmail mailbox, not the trace — and returns contradicted, confirmed, or inconclusive, with the evidence attached. Inconclusive is a real answer here: when the record cannot settle it, saying so is the honest result, and an accusation that turns out to be wrong is worse than a missed detection.

A finding, as it renders
The agent said
“Email sent successfully to dana@northwind.example”
It called
gmail.send_emailto=dana@northwind.example from=ops@northwind.example subject=August invoice has_attachment=false
We checked
Gmail sent messages to dana@northwind.example, 09:42–09:53 UTC on 2026-09-04
We found
nothing — no sent message in that window
Verdict
contradictedhigh confidence

The recipient is right. The call returned without error. Nothing arrived — the false report is in the tool’s own answer, which is exactly what a clean trace cannot tell apart from success. That is a different failure from the agent addressing the wrong person, which is the last item below, and which nothing here catches.

Setup

npm install @matrixverify/verify

Then, on LangChain, four calls:

import { verify } from "@matrixverify/verify";
import { verifyCallbackHandler } from "@matrixverify/verify/langchain";

verify.init({
  apiKey: process.env.MATRIX_API_KEY,
  endpoint: "https://matrixverify.dev/api/traces",
});

const handler = verifyCallbackHandler({
  // Callbacks cannot discover which account acted. Without this,
  // every email claim comes back account_unverified.
  fromResolver: () => process.env.AGENT_EMAIL ?? null,
});

await agent.invoke(input, { callbacks: [handler] });

// Flushes the buffer. Nothing is sent without it.
await verify.shutdown();

The handler reads the tool calls your agent already makes — there is nothing to wrap by hand. The two comments are the parts worth reading twice: without fromResolver every email claim comes back account_unverified, and without shutdown the spans never leave the process.

Not on LangChain?

Tell me what you’re building on and I’ll wire it up.

What it doesn’t do

  • Gmail only, so far. Email claims against a real mailbox. Nothing else is checked against anything.
  • No n8n, no CrewAI. LangChain through the callback handler, or the plain SDK by hand. That is the whole list.
  • It cannot catch a correct call with a wrong argument. If the agent is asked to mail one person and confidently mails someone else, the send is real, Gmail confirms it, and the verdict is confirmed — correctly. The finding shows you the instruction next to the arguments so you can see it. No verdict catches it.
Start verifying →Email and password. The findings page is empty until your agent runs.