Attesto

Webhooks

Signed Tenant Webhooks

Attesto sendet signed HTTP POST Deliveries für wichtige Lifecycle Events. Ihr Endpoint muss die Signature verifizieren, Delivery IDs deduplizieren und innerhalb von 10 Sekunden eine 2xx Response zurückgeben.

Wann Webhooks nutzen

Webhooks informieren Ihre Systeme, dass Attesto Evidence geändert wurde. Nutzen Sie sie für Workflow Automation: interne Review starten, wenn ein Bundle bereit ist, ein Case System benachrichtigen, wenn ein Checkpoint schließt, oder Reliance auf einen Stream pausieren, wenn Fork Evidence erscheint. Behandeln Sie einen Webhook nicht als die Evidence selbst. Fetch oder verify das referenzierte Receipt, Checkpoint, Fork Evidence oder Bundle, bevor Sie sich darauf verlassen.

NeedUseWhy
Receive lifecycle notificationsTenant webhooksAttesto ruft Ihren Endpoint nach Evidence Changes auf.
Send source evidence into AttestoConnectors or SDK/APIIhr System erstellt das Event; Attesto receiptiert es.
Verify a received bundleOffline verifier or POST /v2/verifyVerification prüft Evidence Integrity; Notification nicht.

Supported Events

EventWann es auslöst
batch.confirmedEin Merkle Batch von Tenant Events wurde on-chain bestätigt.
batch.failedEin Batch ist permanent fehlgeschlagen oder überschritt das Orphan Window.
event.anchoredPer-event Notification, nachdem der related Batch bestätigt wurde.
proofstream.checkpointedEin Proofstream Checkpoint wurde geschlossen und ist für Verification verfügbar.
proofstream.fork_detectedFork Evidence wurde für einen Stream History Conflict erstellt.
proofstream.bundle_readyEin Verifier Bundle ist für die konfigurierte Range bereit.

Subscription erstellen

: "${ATTESTO_WEBHOOK_URL:?set this to an HTTPS endpoint controlled by your organization}"

curl -X POST https://dashboard.attesto.eu/v1/webhooks \
  -H "Authorization: Bearer $ATTESTO_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @- <<JSON
{
  "name": "audit-notifications",
  "url": "$ATTESTO_WEBHOOK_URL",
  "eventTypes": ["batch.confirmed", "batch.failed"],
  "description": "Posts lifecycle notifications to the configured endpoint"
}
JSON

Delivery Signatures verifizieren

Jede Delivery enthält diese Headers:

Delivery Body Shape:

{
  "id": "delivery_...",
  "event": "proofstream.bundle_ready",
  "tenant_id": "tenant_...",
  "created_at": "2026-06-07T12:00:00Z",
  "data": {
    "stream_id": "str_...",
    "bundle_id": "bundle_...",
    "from_seq_no": 1,
    "to_seq_no": 250,
    "verification_url": "https://verify.attesto.eu/v2/verify"
  }
}
import hashlib
import hmac
import time

def verify(signing_value: str, body: bytes, timestamp: str, signature: str) -> bool:
    ts = int(timestamp)
    if abs(int(time.time()) - ts) > 300:
        return False
    expected = hmac.new(
        signing_value.encode("ascii"),
        f"{ts}.".encode("ascii") + body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Minimaler Node Handler

import crypto from "node:crypto";
import express from "express";

const app = express();
app.use(express.raw({ type: "application/json" }));

function verifyDelivery(signingValue, body, timestamp, signature) {
  const ts = Number(timestamp);
  if (!Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) {
    return false;
  }
  const expected = crypto
    .createHmac("sha256", signingValue)
    .update(`${timestamp}.`)
    .update(body)
    .digest("hex");
  const expectedBytes = Buffer.from(expected, "hex");
  const receivedBytes = Buffer.from(signature || "", "hex");
  return expectedBytes.length === receivedBytes.length
    && crypto.timingSafeEqual(expectedBytes, receivedBytes);
}

app.post("/attesto/webhook", async (req, res) => {
  const ok = verifyDelivery(
    process.env.ATTESTO_WEBHOOK_SIGNING_VALUE,
    req.body,
    req.header("X-Attesto-Timestamp"),
    req.header("X-Attesto-Signature"),
  );
  if (!ok) return res.status(401).end();

  const deliveryId = req.header("X-Attesto-Delivery-Id");
  const payload = JSON.parse(req.body.toString("utf8"));
  await recordDeliveryOnce(deliveryId, payload);
  res.status(204).end();
});

Retry und Dedupe

Delivery ist at least once. Jede non-2xx Response, Timeout oder refused Connection wird retryed. Dedupe nach X-Attesto-Delivery-Id vor Side Effects.

AttemptDelay
1Immediate
230 seconds
32 minutes
410 minutes
51 hour
66 hours
7Final attempt

Endpoint Requirements

Verwenden Sie HTTPS, verifizieren Sie den Raw Body vor dem Parsing, speichern Sie Delivery IDs für Dedupe, antworten Sie schnell und verschieben Sie teures Processing in Ihre eigene Queue, nachdem Signature Verification erfolgreich war.