TectraTectra
Webhooks

Webhooks

Receive real-time HTTP notifications when content is signed, batches are anchored on blockchain, or content is verified. Webhooks let you build event-driven workflows without polling.

Creating a Webhook

Register a webhook endpoint via the REST API. You'll receive a secret for HMAC signature verification.

bash
curl -X POST https://tectra.vision/api/v1/webhooks \
  -H "Authorization: Bearer iai_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/tectra",
    "events": ["content.signed", "batch.anchored", "content.verified"]
  }'
json
{
  "id": "wh_abc123",
  "url": "https://example.com/webhooks/tectra",
  "events": ["content.signed", "batch.anchored", "content.verified"],
  "secret": "whsec_k7x9m2...",
  "created_at": "2025-06-14T09:32:00Z"
}

Save the secret - it is only returned once and is required to verify webhook signatures.

Event Types

content.signed

Fired when a piece of content is signed and registered. Includes the record ID, hashes, signer info, and blockchain status.

batch.anchored

Fired when a Merkle batch is successfully anchored on the Polygon blockchain. Includes the batch ID, Merkle root, leaf count, and transaction hash.

content.verified

Fired when content is verified through the API. Includes the verification result, confidence score, and individual check results.

Payload Format

content.signed

json
{
  "event": "content.signed",
  "timestamp": "2025-06-14T09:32:00Z",
  "data": {
    "record_id": "uuid",
    "sha256": "abc123...",
    "perceptual_hash": "def456...",
    "signature_hex": "ghi789...",
    "original_filename": "photo.jpg",
    "origin_type": "camera_image",
    "signer": {
      "org_name": "Reuters Media",
      "org_type": "news_agency",
      "key_name": "production"
    },
    "blockchain_status": "pending"
  }
}

batch.anchored

json
{
  "event": "batch.anchored",
  "timestamp": "2025-06-14T09:33:12Z",
  "data": {
    "batch_id": "batch-uuid",
    "merkle_root": "0xabc123...",
    "leaf_count": 247,
    "tx_hash": "0x4a7f...",
    "block_number": 58203947,
    "network": "polygon",
    "record_ids": ["uuid1", "uuid2", "..."]
  }
}

content.verified

json
{
  "event": "content.verified",
  "timestamp": "2025-06-14T09:34:00Z",
  "data": {
    "authentic": true,
    "confidence": 0.75,
    "record_id": "uuid",
    "checks": {
      "watermark": { "passed": true, "details": "Payload matched" },
      "sha256": { "passed": true, "details": "Exact match" },
      "perceptual_hash": { "passed": true, "details": "Distance 2" },
      "blockchain": { "passed": false, "details": "Pending" }
    },
    "signer": {
      "org_name": "Reuters Media",
      "org_type": "news_agency"
    }
  }
}

Signature Verification

Always verify signatures

Every webhook request includes an X-Tectra-Signature header. This is the HMAC-SHA256 of the raw request body, using your webhook secret as the key. Always verify this signature before processing the event.

Headers included with every webhook delivery:

HeaderDescription
X-Tectra-SignatureHMAC-SHA256 hex digest of the request body
X-Tectra-EventEvent type (e.g. content.signed)
X-Tectra-DeliveryUnique delivery ID for deduplication
X-Tectra-TimestampISO 8601 timestamp of the event

Receiving Webhooks (Node.js)

typescript
import express from "express";
import crypto from "crypto";

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

const WEBHOOK_SECRET = process.env.TECTRA_WEBHOOK_SECRET!;

function verifySignature(payload: Buffer, signature: string): boolean {
  const expected = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(payload)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}

app.post("/webhooks/tectra", (req, res) => {
  const signature = req.headers["x-tectra-signature"] as string;

  if (!signature || !verifySignature(req.body, signature)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(req.body.toString());

  switch (event.event) {
    case "content.signed":
      console.log("Content signed:", event.data.record_id);
      break;
    case "batch.anchored":
      console.log("Batch anchored:", event.data.tx_hash);
      break;
    case "content.verified":
      console.log("Verified:", event.data.authentic);
      break;
  }

  res.status(200).json({ received: true });
});

app.listen(3000);

Receiving Webhooks (Python)

python
import hmac
import hashlib
import json
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["TECTRA_WEBHOOK_SECRET"]

def verify_signature(payload: bytes, signature: str) -> bool:
    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.route("/webhooks/tectra", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Tectra-Signature", "")

    if not verify_signature(request.data, signature):
        return jsonify({"error": "Invalid signature"}), 401

    event = request.json

    if event["event"] == "content.signed":
        print(f"Content signed: {event['data']['record_id']}")
    elif event["event"] == "batch.anchored":
        print(f"Batch anchored: {event['data']['tx_hash']}")
    elif event["event"] == "content.verified":
        print(f"Verified: {event['data']['authentic']}")

    return jsonify({"received": True}), 200

Retry Policy

3 attempts with exponential backoff

If your endpoint returns a non-2xx status code or times out (30 seconds), Tectra retries the delivery up to 3 times with exponential backoff.

1st attempt

Immediate

2nd attempt

After 1 minute

3rd attempt

After 5 minutes

After 3 failed attempts, the delivery is marked as failed. You can view failed deliveries and manually retry them from the dashboard or via the API. Use the X-Tectra-Delivery header for idempotency.

Next: Verification Guide

Understanding confidence scores and the 4-check verification system.

Verification Guide