Attesto

Webhooks

Signed tenant webhooks

Attesto envía signed HTTP POST deliveries para lifecycle events importantes. Su endpoint debe verificar la signature, deduplicar delivery IDs y devolver una respuesta 2xx en 10 segundos.

Cuándo usar webhooks

Webhooks notifican a sus sistemas que Attesto evidence cambió. Úselos para workflow automation: iniciar una revisión interna después de que un bundle esté listo, notificar un case system después de que cierre un checkpoint, o pausar reliance en un stream cuando aparece fork evidence. No trate un webhook como la evidence en sí. Obtenga o verifique el receipt, checkpoint, fork evidence o bundle referenciado antes de confiar en él.

NeedUseWhy
Receive lifecycle notificationsTenant webhooksAttesto llama a su endpoint después de cambios de evidence.
Send source evidence into AttestoConnectors or SDK/APISu sistema crea el event; Attesto lo recibe.
Verify a received bundleOffline verifier or POST /v2/verifyVerification revisa evidence integrity; notification no lo hace.

Supported events

EventCuándo se dispara
batch.confirmedUn Merkle batch de tenant events fue confirmado on-chain.
batch.failedUn batch falló permanentemente o superó la orphan window.
event.anchoredPer-event notification después de confirmar el related batch.
proofstream.checkpointedUn Proofstream checkpoint cerró y está disponible para verification.
proofstream.fork_detectedFork evidence fue creada para un stream history conflict.
proofstream.bundle_readyUn verifier bundle está listo para la range configurada.

Crear 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

Verificar delivery signatures

Cada delivery incluye estos 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 y dedupe

Delivery es at least once. Cualquier respuesta non-2xx, timeout o refused connection se reintenta. Dedupe por X-Attesto-Delivery-Id antes de side effects.

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

Endpoint requirements

Use HTTPS, verifique el raw body antes de parsear, almacene delivery IDs para dedupe, responda rápido y mueva procesamiento costoso a su propia queue después de que signature verification tenga éxito.