Webhooks & Real-Time Event Streams
Deliver instant cryptographically-signed notifications directly to your AI agents, automation workflows (Zomo, n8n, Make), and custom backends whenever invoices, parties, items, or inventory stocks are modified.
Cryptographic Signature & Replay Protection
Every webhook request includes secure headers ensuring authenticity, integrity, and replay-attack prevention. Signing secret is mandatory for all webhooks.
| Header Name | Format / Value | Purpose |
|---|---|---|
| X-Hasabi-Signature | t=1758880000,v1=a1b2c3d4e5f6... | HMAC SHA-256 signature calculated as hash_hmac("sha256", "$timestamp.$rawBody", $secret). |
| X-Hasabi-Timestamp | 1758880000 | Unix epoch timestamp (seconds). Receivers should reject any payload where |now - timestamp| > 300s (5 mins) to prevent replay attacks. |
| X-Hasabi-Event | sale_invoice.created | Canonical event identifier for fast routing. |
| X-Hasabi-Delivery | evt_01j7xyz... | Unique event delivery ID. Use as an idempotency key to prevent double execution. |
import hmac
import hashlib
import time
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
WEBHOOK_SECRET = "your_64_char_webhook_secret_key" # Required
@app.post("/webhooks/hasabi")
async def handle_hasabi_webhook(request: Request):
raw_body = await request.body()
signature_header = request.headers.get("X-Hasabi-Signature")
timestamp = request.headers.get("X-Hasabi-Timestamp")
if not signature_header or not timestamp:
raise HTTPException(status_code=401, detail="Missing signature headers")
if abs(time.time() - int(timestamp)) > 300:
raise HTTPException(status_code=403, detail="Timestamp expired (replay protection)")
parts = dict(item.split("=") for item in signature_header.split(","))
received_hash = parts.get("v1")
signed_payload = f"{timestamp}.".encode("utf-8") + raw_body
expected_hash = hmac.new(WEBHOOK_SECRET.encode("utf-8"), signed_payload, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected_hash, received_hash):
raise HTTPException(status_code=401, detail="Invalid HMAC signature")
payload = await request.json()
print(f"Verified event: {payload['event']} (API Version: {payload['api_version']})")
return {"status": "ok"}
Interactive Event Payload Explorer
Select any event below to preview its exact canonical JSON envelope for api_version: v1.
Development Sandbox Verification API (?sandbox=true)
Make a GET request during local development to obtain verified mock payloads, calculated HMAC headers, and instant curl commands. Secret is required.
๐ก๏ธ Production Reliability Guarantees
Strict 4-Second Timeout
Webhook delivery calls timeout after 4,000 milliseconds to prevent worker thread saturation. Ensure your receiver returns HTTP 200 OK immediately and offloads heavy background processing.
Circuit Breaker Protection
Endpoints experiencing consecutive errors are marked as degraded (at 10 fails) and auto-paused (at 50 fails) to protect both your server and Hasabi queue performance.
Automated Log Pruning
Delivery history and payload logs are automatically pruned nightly at 03:30 AM (14 days for successful events, 30 days for failed events) keeping your database light and fast.