Attesto

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

GoalUseStart withVerification outcome
Log AI decisions from a backendPython or TypeScript SDKSDKsEvent receipt and v1/v2 verification.
Support Article 13 transparency with one integration lineSDK decorator, attested fetch, or gatewayThis pageAutomatic event logging evidence, Article 12 report, Article 13 support narrative, and verifier path.
Create an append-only decision historyProofstream APIProofstreamReceipt, stream order, windows, checkpoints, witnesses, anchors, bundle.
Notify customer systems when evidence changesTenant webhooksWebhooksSigned delivery with retry and dedupe.
Capture evidence from external toolsConnectorsConnectorsSource reference, source commitment, relay receipt.
Keep secrets and spooling customer-sideLocal VaultLocal VaultSigned source attestation, encrypted spool, optional customer witness.
Wrap AI provider calls without changing every appInference GatewayThis pageProvider request/response commitments and Attesto receipts.
Let agent tools write verifiable actionsMCP serverThis pageTool-call receipts, stream heads, and offline receipt verification.
Record workflow automation evidencen8n nodeThis pageWorkflow-step receipts and signed webhook verification.
Audit without backend trustVerifier bundlesVerifier bundlesOffline 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.

ConnectorCapturesSecurity requirement
Signed webhookCustom source events posted by your system.HMAC/Ed25519 envelope, timestamp tolerance, replay cache.
GitHub/GitLabRepository refs, commit IDs, merge events, release references.Provider signature validation and installation scope.
S3/R2Object 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

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

Production rollout checklist

Model the evidence

Choose streams, source references, event types, payload commitments, and policy IDs before writing code.

Protect credentials

System keys, webhook secrets, connector secrets, and Local Vault keys stay server-side or edge-side only.

Make retries deterministic

Use idempotency keys and keep request bodies stable across retries.

Verify before relying

Receipts and bundles should be verified by the receiving system, not just displayed.

Define failure response

Decide who investigates missing quorum, failed anchors, connector conflicts, and fork evidence.

Update documentation

When your implementation changes public API, SDK, webhook, connector, or verifier behavior, update docs and changelog.