MultiMail signs every outbound agent email with an ECDSA identity header. Decode the X-MultiMail-Identity header to confirm agent origin, model metadata, and disclosure state — without relying on sender logs.
When an AI agent sends email on behalf of a user or organization, downstream systems, security teams, and recipients have no reliable way to confirm the message is authentic. Application logs can be tampered with. Claimed sender identities can be spoofed. There is no standard mechanism to cryptographically bind an email to the specific AI agent that generated it, the model version it ran, or the oversight mode it operated under at send time. This gap creates audit failures, compliance exposure under the EU AI Act's transparency requirements, and opens the door to spoofed automation masquerading as legitimate agent output.
MultiMail attaches a signed X-MultiMail-Identity header to every email dispatched through the API. The header contains a base64url-encoded JSON payload — agent ID, model metadata, tenant ID, oversight mode, disclosure state, and a timestamp — along with an ECDSA signature using the P-256 curve. The signing key is tenant-scoped and rotatable. MultiMail publishes the corresponding verification public key at a well-known endpoint so any recipient system can independently verify the signature without calling back into your application. Verification is purely cryptographic: no trust is placed in the sending application's claims alone.
Call GET /v1/emails/{email_id} to fetch the full email record. The response includes a `headers` object containing X-MultiMail-Identity alongside standard fields like Message-ID and Date. For inbound emails that need verification, the same header is preserved when MultiMail receives and stores the message.
The X-MultiMail-Identity header value is a dot-separated structure: base64url(payload).base64url(signature). Decode the payload to a JSON object containing fields: agent_id, model_id, model_provider, tenant_id, oversight_mode, ai_disclosure, mailbox, sent_at, and nonce. These fields describe the exact agent state at send time.
Retrieve the signing public key from GET /v1/identity/public-key?tenant_id={tenant_id}. The endpoint returns a JWK object with the P-256 public key. Keys are cached with a 24-hour TTL on the verifier side to avoid per-message roundtrips. Key rotation events are published via webhook so verifiers can invalidate their cache immediately.
Reconstruct the signed message as base64url(payload) and verify the signature bytes against the public key using ES256 (ECDSA with P-256 and SHA-256). A valid signature confirms the payload has not been tampered with and was produced by MultiMail's signing infrastructure using the tenant's key at the stated timestamp.
After signature verification, assert that the decoded fields match your policy: agent_id matches the expected agent, oversight_mode is not `autonomous` if your policy requires human review, ai_disclosure is true if EU AI Act disclosure is required, and sent_at falls within an acceptable time window to prevent replay attacks.
Write the verification outcome — pass/fail, agent_id, model_id, oversight_mode, and timestamp — to your audit log. This record satisfies EU AI Act Article 13 transparency requirements and provides evidence for security incident investigations. MultiMail also stores a verification-ready receipt accessible via GET /v1/emails/{email_id}/identity-receipt.
import httpx
import base64
import json
MM_API_KEY = "$MULTIMAIL_API_KEY"
EMAIL_ID = "em_01HZ8QXYZ1234567890"
response = httpx.get(
f"https://api.multimail.dev/v1/emails/{EMAIL_ID}",
headers={"Authorization": f"Bearer {MM_API_KEY}"},
)
response.raise_for_status()
email = response.json()
identity_header = email["headers"].get("X-MultiMail-Identity")
if not identity_header:
raise ValueError("No X-MultiMail-Identity header found — message was not sent via MultiMail agent")
payload_b64, signature_b64 = identity_header.split(".")
payload = json.loads(base64.urlsafe_b64decode(payload_b64 + "=="))
print("Agent ID:", payload["agent_id"])
print("Model:", payload["model_id"], "(", payload["model_provider"], ")")
print("Oversight mode:", payload["oversight_mode"])
print("AI disclosure:", payload["ai_disclosure"])
print("Sent at:", payload["sent_at"])
Retrieve a sent email and extract its X-MultiMail-Identity header using the MultiMail REST API.
import httpx
import base64
import json
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.serialization import load_der_public_key
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidSignature
from jwt.algorithms import ECAlgorithm
MM_API_KEY = "$MULTIMAIL_API_KEY"
def verify_identity_header(identity_header: str, tenant_id: str) -> dict:
payload_b64, signature_b64 = identity_header.split(".")
"cm"># Fetch tenant public key (JWK format)
jwk_response = httpx.get(
f"https://api.multimail.dev/v1/identity/public-key",
params={"tenant_id": tenant_id},
headers={"Authorization": f"Bearer {MM_API_KEY}"},
)
jwk_response.raise_for_status()
jwk = jwk_response.json()
"cm"># Load public key from JWK
public_key = ECAlgorithm.from_jwk(json.dumps(jwk))
"cm"># Verify signature over the raw payload bytes
signed_message = payload_b64.encode("ascii")
signature = base64.urlsafe_b64decode(signature_b64 + "==")
try:
public_key.verify(signature, signed_message, ECDSA(SHA256()))
except InvalidSignature:
raise ValueError("X-MultiMail-Identity signature is INVALID — message may be tampered or spoofed")
payload = json.loads(base64.urlsafe_b64decode(payload_b64 + "=="))
return payload
"cm"># Usage
payload = verify_identity_header(identity_header, tenant_id=payload["tenant_id"])
print("Signature VALID")
print(json.dumps(payload, indent=2))
Fetch the tenant public key from MultiMail and verify the P-256 signature in the identity header.
import { createPublicKey, verify } from 'crypto';
const MM_API_KEY = process.env.MM_API_KEY!;
const BASE_URL = 'https://api.multimail.dev';
interface IdentityPayload {
agent_id: string;
model_id: string;
model_provider: string;
tenant_id: string;
oversight_mode: 'read_only' | 'gated_all' | 'gated_send' | 'monitored' | 'autonomous';
ai_disclosure: boolean;
mailbox: string;
sent_at: string;
nonce: string;
}
async function verifyAgentEmail(emailId: string): Promise<{ valid: boolean; payload: IdentityPayload }> {
const emailRes = await fetch(`${BASE_URL}/v1/emails/${emailId}`, {
headers: { Authorization: `Bearer ${MM_API_KEY}` },
});
if (!emailRes.ok) throw new Error(`Failed to fetch email: ${emailRes.status}`);
const email = await emailRes.json();
const identityHeader: string | undefined = email.headers?.['X-MultiMail-Identity'];
if (!identityHeader) throw new Error('Missing X-MultiMail-Identity header');
const [payloadB64, signatureB64] = identityHeader.split('.');
const payload: IdentityPayload = JSON.parse(
Buffer.from(payloadB64, 'base64url').toString('utf8')
);
"cm">// Fetch public key for this tenant
const keyRes = await fetch(
`${BASE_URL}/v1/identity/public-key?tenant_id=${payload.tenant_id}`,
{ headers: { Authorization: `Bearer ${MM_API_KEY}` } }
);
if (!keyRes.ok) throw new Error(`Failed to fetch public key: ${keyRes.status}`);
const jwk = await keyRes.json();
const publicKey = createPublicKey({ key: jwk, format: 'jwk' });
const signatureBytes = Buffer.from(signatureB64, 'base64url');
const signedMessage = Buffer.from(payloadB64, 'ascii');
const valid = verify('sha256', signedMessage, publicKey, signatureBytes);
if (!valid) throw new Error('Signature verification failed');
"cm">// Policy assertions
if (!payload.ai_disclosure) {
throw new Error(`EU AI Act violation: ai_disclosure is false for agent ${payload.agent_id}`);
}
const sentAt = new Date(payload.sent_at).getTime();
const ageMs = Date.now() - sentAt;
if (ageMs > 5 * 60 * 1000) {
console.warn(`Identity header is ${Math.round(ageMs / 1000)}s old — check for replay`);
}
return { valid: true, payload };
}
"cm">// Audit log
const { valid, payload } = await verifyAgentEmail('em_01HZ8QXYZ1234567890');
console.log(JSON.stringify({
event: 'identity_verification',
result: valid ? 'PASS' : 'FAIL',
agent_id: payload.agent_id,
model_id: payload.model_id,
oversight_mode: payload.oversight_mode,
ai_disclosure: payload.ai_disclosure,
verified_at: new Date().toISOString(),
}));
End-to-end verification: fetch email, decode header, verify signature, assert policy, and emit audit log entry.
// In Claude Desktop or any MCP client connected to MultiMail:
// Step 1: Read the email and inspect headers
read_email({
email_id: "em_01HZ8QXYZ1234567890"
})
// Returns: { subject, from, body, headers: { "X-MultiMail-Identity": "...", ... }, agent_metadata: { agent_id, model_id, oversight_mode, ai_disclosure } }
// Step 2: If the email is in the pending/approval queue, decide on it after verifying identity
decide_email({
email_id: "em_01HZ8QXYZ1234567890",
decision: "approve",
reason: "Identity header verified — agent_id matches expected automation, ai_disclosure confirmed"
})
// The MCP server surfaces agent_metadata as a first-class field so you do not need
// to manually decode the X-MultiMail-Identity header in conversational workflows.
// For programmatic verification pipelines, use the REST API approach above.
Use the MultiMail MCP server's read_email tool to fetch the email and inspect identity metadata directly in an MCP-compatible client.
"cm"># GET /v1/emails/{email_id}/identity-receipt returns a signed JSON receipt
"cm"># containing the identity payload, the verification public key fingerprint,
"cm"># and a MultiMail-issued timestamp. Safe to store in audit logs as-is.
curl -s \
-H "Authorization: Bearer $MM_API_KEY" \
"https://api.multimail.dev/v1/emails/em_01HZ8QXYZ1234567890/identity-receipt" \
| jq &"cm">#039;{
agent_id: .payload.agent_id,
model_id: .payload.model_id,
oversight_mode: .payload.oversight_mode,
ai_disclosure: .payload.ai_disclosure,
key_fingerprint: .key_fingerprint,
receipt_issued_at: .issued_at
}&"cm">#039;
"cm"># Example output:
"cm"># {
"cm"># "agent_id": "agt_support_responder_v2",
"cm"># "model_id": "claude-sonnet-4-6",
"cm"># "oversight_mode": "monitored",
"cm"># "ai_disclosure": true,
"cm"># "key_fingerprint": "SHA256:k3Fg9...",
"cm"># "receipt_issued_at": "2026-04-19T14:22:31Z"
"cm"># }
Fetch MultiMail's stored verification receipt for an email — useful when you need a third-party-readable audit artifact.
The ECDSA signature in X-MultiMail-Identity is verifiable by any party with the public key. You do not need to query the sending application's logs or database to confirm authenticity — the header is self-contained evidence.
The identity payload includes ai_disclosure state, model_id, and model_provider, giving recipients the information required under EU AI Act transparency obligations for automated decision-making systems. The identity receipt provides a storable audit artifact.
Each identity header includes a nonce and sent_at timestamp. Verifiers can reject headers outside an acceptable time window and track nonces to prevent replayed headers from being accepted as fresh verification evidence.
Tenant signing keys can be rotated via the API without interrupting email sending. MultiMail issues key rotation webhooks so verifier caches can invalidate immediately rather than serving stale public keys for the full TTL period.
Identity headers are attached regardless of whether the email was sent under gated_all, monitored, or autonomous oversight. Security teams can verify not just that an agent sent a message but what constraints it was operating under at send time.
Email infrastructure built for AI agents. Verifiable identity, graduated oversight, and a 50-tool MCP server. Formally verified in Lean 4.