Implementation Recipes
Build with Attesto without guessing.
Attesto is easiest to adopt when you choose the evidence surface first: a direct SDK event, a Proofstream stream, an inbound webhook, a connector, a Local Vault relay, or a verifier-only workflow. This page maps those choices to concrete implementation steps.
Choose a path
| Goal | Use | Start with | Verification outcome |
|---|---|---|---|
| Log AI decisions from a backend | Python or TypeScript SDK | SDKs | Event receipt and v1/v2 verification. |
| Support Article 13 transparency with one integration line | SDK decorator, attested fetch, or gateway | This page | Automatic event logging evidence, Article 12 report, Article 13 support narrative, and verifier path. |
| Create an append-only decision history | Proofstream API | Proofstream | Receipt, stream order, windows, checkpoints, witnesses, anchors, bundle. |
| Notify customer systems when evidence changes | Tenant webhooks | Webhooks | Signed delivery with retry and dedupe. |
| Capture evidence from external tools | Connectors | Connectors | Source reference, source commitment, relay receipt. |
| Keep secrets and spooling customer-side | Local Vault | Local Vault | Signed source attestation, encrypted spool, optional customer witness. |
| Wrap AI provider calls without changing every app | Inference Gateway | This page | Provider request/response commitments and Attesto receipts. |
| Let agent tools write verifiable actions | MCP server | This page | Tool-call receipts, stream heads, and offline receipt verification. |
| Record workflow automation evidence | n8n node | This page | Workflow-step receipts and signed webhook verification. |
| Audit without backend trust | Verifier bundles | Verifier bundles | Offline PASS/FAIL report. |
Recipe: log a production event
Use this when your application already knows the decision or action that must become evidence. Send from server-side code only. Keep the system key in your secret manager, preserve idempotency keys across retries, and log stable source references that your team can trace.
pip install attesto
python - <<'PY'
import os
from datetime import UTC, datetime
from attesto import AttestoClient
attesto = AttestoClient(api_key=os.environ["ATTESTO_API_KEY"])
ack = attesto.log_event(
type="ai.decision",
status="verified",
ts=datetime.now(UTC),
payload={
"decision": "manual_review",
"score": 91,
"policy_id": "policy-2026-01",
"model": "risk-service-v4"
},
)
print(ack.id)
PY
Recipe: support Article 13 with one line and generate the Article 12 report
When we say "Meet Article 13 with 1 line of code", we mean: add one Attesto integration line that turns an AI workflow into receipt-backed, verifier-ready evidence for transparency and instructions-for-use work. The deterministic CLI command is still attesto report article12 because the report maps the technical logging trail to Article 12-style logging evidence. Article 13 support depends on how the customer uses that evidence in documentation, user information, oversight, and legal interpretation. Attesto provides evidence support; it does not certify legal compliance by itself.
Use the decorator when you own the function boundary, attestedFetch when you own the HTTP transport in TypeScript, and the Inference Gateway when you want a language/framework-neutral proxy for OpenAI-compatible calls.
from attesto import AttestoClient, AttestoV2Client, attest, article12
import os
capture = AttestoClient(api_key=os.environ["ATTESTO_API_KEY"])
@attest(capture, stream_id="str_...")
def decide(payload: dict) -> dict:
return {"decision": "manual_review", "policy_id": "policy-2026-01"}
result = decide({"case_id": "case-2026-0001"})
operator = AttestoV2Client.with_bearer_token(os.environ["ATTESTO_TENANT_TOKEN"])
print(article12(operator, "str_..."))
The capture path can run with a system API key. The report path reads tenant stream events, so generate the final report from a tenant/operator context or pass a tenant token to the CLI.
import { AttestoV2Client, attestedFetch } from "@attesto/sdk";
const attesto = new AttestoV2Client({ apiKey: process.env.ATTESTO_API_KEY! });
const fetchWithEvidence = attestedFetch(attesto, { streamId: "str_...", strict: true });
attesto --token-env ATTESTO_TENANT_TOKEN \
report article12 --stream str_... --output report.md
Recipe: create a Proofstream stream
Use Proofstream when order matters. A stream is normally scoped to a tenant, system, use case, and policy. Every event receives a sequence number and a signed receipt. Later windows, checkpoints, witnesses, anchors, and bundles build on that append-only sequence.
from attesto import AttestoV2Client
from datetime import UTC, datetime
import os
with AttestoV2Client(api_key=os.environ["ATTESTO_API_KEY"]) as attesto:
stream = attesto.create_stream(
use_case="ai-decision-history",
policy_id="policy-2026-01",
metadata={"owner": "risk-platform", "environment": "production"},
)
receipt = attesto.log_event(
stream_id=stream.stream_id,
source_ref="case-2026-0001:decision-1",
event_type="ai.decision",
occurred_at=datetime.now(UTC),
payload={"decision": "manual_review", "score": 91},
)
print(receipt.stream_event_id, receipt.seq_no)
Recipe: verify evidence before trusting it
Verification should be part of your receiving workflow. If your system receives an Attesto receipt or bundle, verify it before accepting it into an audit file, data room, assurance report, or regulator package.
curl -X POST https://verify.attesto.eu/v2/verify \
-H "Content-Type: application/json" \
--data-binary @attesto-bundle.json
attesto --json bundles verify \
--file ./attesto-bundle.json > ./verification-report.json
A failing report is useful evidence too. It tells you whether the object was malformed, incomplete, changed, stale, missing witness quorum, using a wrong signature, bound to a wrong anchor, or carrying fork evidence.
Recipe: receive Attesto webhooks
Webhooks are for notifications. They are not a replacement for verification. Verify the signature over the raw request body, dedupe the delivery ID, return quickly, and fetch or verify the referenced evidence object in your own worker.
import crypto from "node:crypto";
function verifyAttestoWebhook({ rawBody, timestamp, signature, secret }) {
const message = `${timestamp}.${rawBody}`;
const expected = crypto
.createHmac("sha256", secret)
.update(message)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
}
Recipe: use connectors safely
Connectors translate source-system changes into Attesto evidence. Use signed webhook connectors for custom systems, GitHub/GitLab connectors for repository change references, and S3/R2 commitments for object evidence. Each connector must have real authentication, replay behavior, diagnostics, and revocation.
| Connector | Captures | Security requirement |
|---|---|---|
| Signed webhook | Custom source events posted by your system. | HMAC/Ed25519 envelope, timestamp tolerance, replay cache. |
| GitHub/GitLab | Repository refs, commit IDs, merge events, release references. | Provider signature validation and installation scope. |
| S3/R2 | Object key, content hash, metadata, version reference. | Least-privilege bucket access and immutable object policy where available. |
Recipe: deploy Local Vault at the customer edge
Local Vault is for enterprise environments where connector secrets, source attestations, and outage spooling should stay customer-side. It relays outbound to Attesto and can optionally act as a customer witness in a quorum policy.
pipx install attesto-local-vault
export ATTESTO_LOCAL_VAULT_SPOOL_DB=/var/lib/attesto/local-vault.sqlite3
export ATTESTO_LOCAL_VAULT_INSTALLATION_ID="$ATTESTO_LOCAL_VAULT_INSTALLATION_ID"
export ATTESTO_LOCAL_VAULT_RELAY_URL="https://verify.attesto.eu/v2/local-vault/installations/$ATTESTO_LOCAL_VAULT_INSTALLATION_ID/events"
attesto-local-vault drain-loop
- Store connector secrets encrypted locally.
- Sign source attestations before relay.
- Spool when outbound network is unavailable.
- Replay with stable idempotency when connectivity returns.
- Expose witness status to the tenant and verifier bundle.
Recipe: route AI calls through the Attesto Inference Gateway
Use the gateway when teams already call OpenAI-compatible provider APIs and want evidence without rewriting every application. The gateway forwards requests to the configured provider and records commitments, receipt references, and safe metadata. Raw prompts, completions, API keys, and provider secrets must stay inside the gateway process and are not written to Attesto.
export ATTESTO_API_KEY="$ATTESTO_SYSTEM_KEY"
export ATTESTO_BASE_URL=https://verify.attesto.eu
export OPENAI_BASE_URL=http://localhost:8765/v1
attesto-gateway --listen 127.0.0.1:8765 \
--admin-listen 127.0.0.1:8766 \
--attesto-base-url "$ATTESTO_BASE_URL" \
--upstream "$AI_PROVIDER_BASE_URL" \
--stream-id "$ATTESTO_STREAM_ID" \
--capture commitments
Use commitments mode for production evidence. Use
none only when a deployment intentionally disables
evidence capture for a specific environment. The gateway fails open by
default with operator-visible warnings and a fsynced dead-letter
spool; add --strict when policy requires receipt creation
before upstream work continues. Monitor /healthz and
/metrics on the admin listener, and run
attesto-gateway replay-spool after outages.
The gateway attests OpenAI-compatible
/v1/chat/completions, /v1/completions,
/v1/embeddings, and /v1/responses calls as
model-decision evidence. Unknown upstream paths are proxied and
recorded as generic HTTP-call commitment events instead of being
silently dropped.
Recipe: expose Attesto to agent tools through MCP
The Attesto MCP server gives agentic systems a narrow, deterministic set of tools: log an action, fetch a receipt, verify a receipt offline, inspect a stream head, and check completeness. It is not a general admin surface and it refuses secret-looking values before they become evidence payloads.
pip install attesto-mcp
{
"mcpServers": {
"attesto": {
"command": "attesto-mcp",
"env": {
"ATTESTO_API_KEY": "${ATTESTO_SYSTEM_KEY}",
"ATTESTO_BASE_URL": "https://verify.attesto.eu"
}
}
}
}
export ATTESTO_BASE_URL=https://verify.attesto.eu
export ATTESTO_API_KEY="$ATTESTO_SYSTEM_KEY"
attesto-mcp --stdio
Keep MCP credentials in the agent runtime secret store. Do not pass dashboard cookies, admin credentials, private keys, provider API keys, or raw customer secrets through MCP tool arguments.
Recipe: add Attesto evidence to n8n workflows
Use the Attesto n8n node when a workflow step should produce an evidence receipt. The action node logs typed compliance events, fetches receipts, and verifies receipts offline. The trigger node validates signed Attesto webhooks with timestamp tolerance before a workflow continues.
npm install n8n-nodes-attesto
- Store Attesto credentials in n8n credentials, not workflow JSON.
- Use stable workflow execution IDs as source references.
- Verify incoming webhook signatures before branching on their content.
- Record failure events for rejected or incomplete automation steps.
Production rollout checklist
Choose streams, source references, event types, payload commitments, and policy IDs before writing code.
System keys, webhook secrets, connector secrets, and Local Vault keys stay server-side or edge-side only.
Use idempotency keys and keep request bodies stable across retries.
Receipts and bundles should be verified by the receiving system, not just displayed.
Decide who investigates missing quorum, failed anchors, connector conflicts, and fork evidence.
When your implementation changes public API, SDK, webhook, connector, or verifier behavior, update docs and changelog.
