Webhooks

Webhooks send real-time HTTP POST notifications to your server when events occur in Envizion AI. Use them to trigger downstream workflows, update dashboards, or send alerts without polling the API.

How Webhooks Work

1

Register a webhook

Create a webhook with a URL and list of events you want to receive.

2

Events are triggered

When a subscribed event occurs (e.g., a run completes), we prepare a JSON payload.

3

Signed delivery

We POST the payload to your URL with an HMAC-SHA256 signature header for verification.

4

Your server processes the event

Verify the signature, process the event, and respond with a 2xx status code within 10 seconds.

Available Events

There are 8 event types you can subscribe to. Click any event to see its payload format.

Setting Up Webhooks

Via API

Create a webhook
curl -s -X POST https://api.envizion.ai/v1/webhooks \
  -H "X-API-Key: vk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/vision",
    "events": ["run.completed", "run.failed", "render.completed"]
  }'

Save your signing secret

The response includes a secret field (e.g., whsec_...). This is only shown once. Store it securely -- you will need it to verify webhook signatures.

Signature Verification

Every webhook delivery includes an X-Vision-Signature header containing an HMAC-SHA256 hash of the request body, signed with your webhook secret. Always verify this signature before processing the event.

How it works

  1. We compute HMAC-SHA256(webhook_secret, request_body)
  2. The result is sent as X-Vision-Signature: sha256=<hex_digest>
  3. On your server, compute the same HMAC and compare using a constant-time comparison

Delivery Headers

HeaderDescription
X-Vision-SignatureHMAC-SHA256 signature: sha256=<hex>
X-Vision-EventEvent type (e.g., run.completed)
Content-Typeapplication/json

Python Verification

Python
import hashlib
import hmac

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    """Verify an Envizion AI webhook signature."""
    if not signature.startswith("sha256="):
        return False

    expected = signature[7:]  # Strip "sha256=" prefix
    computed = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(computed, expected)


# In your Flask/FastAPI handler:
@app.post("/webhooks/vision")
async def handle_webhook(request: Request):
    body = await request.body()
    signature = request.headers.get("X-Vision-Signature", "")

    if not verify_webhook(body, signature, WEBHOOK_SECRET):
        return JSONResponse(status_code=401, content={"error": "Invalid signature"})

    data = json.loads(body)
    event = data["event"]

    if event == "run.completed":
        # Process completed run
        pass

    return {"ok": True}

TypeScript Verification

TypeScript (Node.js)
import crypto from "crypto";
import type { Request, Response } from "express";

function verifyWebhook(
  payload: string,
  signature: string,
  secret: string
): boolean {
  if (!signature.startsWith("sha256=")) return false;

  const expected = signature.slice(7);
  const computed = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(computed),
    Buffer.from(expected)
  );
}

// Express handler
app.post("/webhooks/vision", (req: Request, res: Response) => {
  const payload = JSON.stringify(req.body);
  const signature = req.headers["x-vision-signature"] as string;

  if (!verifyWebhook(payload, signature, WEBHOOK_SECRET)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const { event, data } = req.body;

  if (event === "run.completed") {
    // Process completed run
  }

  res.json({ ok: true });
});

Using the Python SDK

Python SDK
from envizion.webhooks import verify_webhook_signature

# Returns True if valid, raises ValueError if format is wrong
is_valid = verify_webhook_signature(
    payload=request_body,
    signature=request.headers["X-Vision-Signature"],
    secret="whsec_your_secret_here",
    tolerance=300  # Max age in seconds (5 min)
)

Testing with cURL

cURL
# Send a test event to your webhook
curl -s -X POST https://api.envizion.ai/v1/webhooks/{webhook_id}/test \
  -H "X-API-Key: vk_your_key"

# Response:
# {"status": "delivered", "response_status": 200, "response_body": "OK"}

Retry Policy

If your endpoint does not respond with a 2xx status within 10 seconds, we retry delivery with exponential backoff.

AttemptDelayCumulative
1st attemptImmediate0s
2nd attempt30 seconds30s
3rd attempt2 minutes2m 30s
4th attempt (final)10 minutes12m 30s

If all 4 attempts fail, the delivery is marked as failed and the webhook failure counter is incremented.

Auto-Disable Behavior

After 10 consecutive delivery failures, the webhook is automatically disabled to prevent further failed deliveries. The failure counter resets to 0 after any successful delivery.

To re-enable a disabled webhook, delete it and create a new one. Check the failure_count field in the webhook list response to monitor health.

Delivery Log

Every delivery attempt is logged. Use the deliveries endpoint to inspect payloads, response codes, and retry attempts:

curl -s "https://api.envizion.ai/v1/webhooks/{webhook_id}/deliveries?limit=10" \
  -H "X-API-Key: vk_your_key"

# Response:
{
  "deliveries": [
    {
      "id": "del_1...",
      "event_type": "run.completed",
      "payload": {"event": "run.completed", "data": {...}},
      "response_status": 200,
      "response_body": "OK",
      "attempt": 1,
      "created_at": "2026-02-12T15:33:00Z"
    }
  ]
}

Best Practices

Respond with 2xx quickly

Return a 200 or 202 status as fast as possible (within 10 seconds). Process the event asynchronously in a background job if needed.

Always verify signatures

Never skip signature verification, even in development. This prevents replay attacks and ensures the payload has not been tampered with.

Handle duplicate deliveries

Due to retries, you may receive the same event more than once. Use the run_id or render_id as an idempotency key to deduplicate.

Use HTTPS endpoints

Always use HTTPS URLs for webhook endpoints. While HTTP is accepted for development, it provides no transport security.

Monitor failure counts

Periodically check the failure_count on your webhooks. If it is climbing, investigate your endpoint's availability.

Subscribe only to events you need

Do not subscribe to all events. Only receive the events your integration actually processes to reduce noise and load.