โšก Outbound Real-time Streams API Version: v1 Secret: Mandatory

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.

1

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.
Verification Code Snippets (Secret Required) Select language:
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"}
2

Interactive Event Payload Explorer

Select any event below to preview its exact canonical JSON envelope for api_version: v1.

API VERSION: v1
๐Ÿงพ Transactions
๐Ÿ‘ฅ Parties & CRM
๐Ÿ“ฆ Items & Stock
Selected Event Payload sale_invoice.created

            
3

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.

GET:

๐Ÿ›ก๏ธ 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.