Attesto

Webhooks

Signed tenant webhooks

Attesto invia signed HTTP POST deliveries per lifecycle events importanti. Il tuo endpoint deve verificare la signature, deduplicare delivery IDs e restituire una response 2xx entro 10 secondi.

Quando usare webhooks

Webhooks notifica ai tuoi sistemi che Attesto evidence è cambiata. Usali per workflow automation: avvia una review interna dopo che un bundle è pronto, notifica un case system dopo la chiusura di un checkpoint, o sospendi reliance su uno stream quando appare fork evidence. Non trattare un webhook come evidence stessa. Recupera o verifica il receipt, checkpoint, fork evidence o bundle referenziato prima di farvi affidamento.

NeedUseWhy
Receive lifecycle notificationsTenant webhooksAttesto chiama il tuo endpoint dopo cambiamenti di evidence.
Send source evidence into AttestoConnectors or SDK/APIIl tuo sistema crea l'event; Attesto lo receiptta.
Verify a received bundleOffline verifier or POST /v2/verifyVerification controlla evidence integrity; notification no.

Supported events

EventQuando scatta
batch.confirmedUn Merkle batch di tenant events è stato confermato on-chain.
batch.failedUn batch è fallito permanentemente o ha superato l'orphan window.
event.anchoredPer-event notification dopo la conferma del related batch.
proofstream.checkpointedUn Proofstream checkpoint si è chiuso ed è disponibile per verification.
proofstream.fork_detectedFork evidence creata per uno stream history conflict.
proofstream.bundle_readyUn verifier bundle è pronto per la range configurata.

Crea una subscription

: "${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

Verifica delivery signatures

Ogni delivery include questi 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)

Minimal 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 e dedupe

Delivery è at least once. Ogni non-2xx response, timeout o refused connection viene retryed. Dedupe per X-Attesto-Delivery-Id prima dei side effects.

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

Endpoint requirements

Usa HTTPS, verifica il raw body prima del parsing, archivia delivery IDs per dedupe, rispondi rapidamente e sposta il processing costoso nella tua queue dopo che signature verification riesce.