WEBHOOKS
Receive account, quota and deployment signals.
A webhook endpoint is an HTTPS URL you own. When a subscribed event occurs, Eventium sends an HTTP POST with a JSON body and signature headers. Verify the signature, queue the work, and return 2xx quickly.
What you receive today
Four families of signal are delivered: billing plan changes, quota thresholds and request rejections, agent deployment updates, and webhook endpoint lifecycle changes. The authoritative list is served by the API rather than maintained here:
GET /api/webhooks/events
Event catalog
| Family | Event types |
|---|---|
| Billing | billing.plan.changed |
| Quota | quota.threshold.reached, quota.request.rejected |
| Deployments | agent.deployment.created, agent.deployment.updated, agent.deployment.deleted |
| Webhook lifecycle | webhook.endpoint.created, webhook.endpoint.updated, webhook.endpoint.deleted |
Payload contract
Every delivery uses the same envelope. Only data varies by event type.
{
"id": "0c3b1d5b-1d66-4a16-b2b5-7aa3c2c9b5ab",
"type": "quota.threshold.reached",
"created_at": "2026-05-26T09:14:02Z",
"data": {
"scope": "daily",
"threshold": 80,
"limit": 10000,
"count": 8123,
"plan": "builder"
}
}
Three headers accompany the body: X-TitanVX-Signature, X-TitanVX-Event and X-TitanVX-Delivery. The X-TitanVX- prefix is retained for compatibility with endpoints written before the surface was renamed to Eventium; the header names are not changing underneath you.
Verify the signature
Deliveries are signed with HMAC SHA-256 over "{timestamp}.{raw_body}":
X-TitanVX-Signature: t=1764072842,v1=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
Verify against the raw request body, before any JSON parsing or re-serialisation — re-encoding the body changes the bytes and the signature will not match.
import hashlib
import hmac
def verify_signature(secret: str, header: str, raw_body: bytes, tolerance: int = 300) -> bool:
parts = dict(
piece.split("=", 1) for piece in header.split(",") if "=" in piece
)
timestamp, received = parts.get("t", ""), parts.get("v1", "")
if not timestamp or not received:
return False
expected = hmac.new(
secret.encode("utf-8"),
f"{timestamp}.{raw_body.decode('utf-8')}".encode("utf-8"),
hashlib.sha256,
).hexdigest()
# compare_digest, never ==: an ordinary comparison leaks the signature
# one byte at a time through its own timing.
if not hmac.compare_digest(expected, received):
return False
# Reject replays of an old, genuinely signed delivery.
import time
return abs(time.time() - int(timestamp)) <= tolerance
A FastAPI handler that uses it:
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
ENDPOINT_SECRET = "YOUR_ENDPOINT_SECRET"
@app.post("/eventium/webhooks")
async def eventium_webhook(request: Request):
raw_body = await request.body()
signature = request.headers.get("X-TitanVX-Signature", "")
if not verify_signature(ENDPOINT_SECRET, signature, raw_body):
raise HTTPException(status_code=400, detail="Invalid signature")
event = await request.json()
# Queue by delivery id and return immediately -- see Retries below.
enqueue(request.headers.get("X-TitanVX-Delivery"), event)
return {"received": True}
Manage endpoints
POST /api/webhooks/endpoints
GET /api/webhooks/endpoints
PATCH /api/webhooks/endpoints/{id}
DELETE /api/webhooks/endpoints/{id}
GET /api/webhooks/deliveries
POST /api/webhooks/test
GET /api/webhooks/events
These require a signed-in session. Registering an endpoint with requests:
import requests
response = requests.post(
"https://api.eventium.ai/api/webhooks/endpoints",
json={
"url": "https://example.com/eventium/webhooks",
"events": ["quota.threshold.reached", "agent.deployment.updated"],
},
headers={"X-Api-Key": "YOUR_API_KEY"},
timeout=30,
)
response.raise_for_status()
endpoint = response.json()
# The signing secret is returned once, at creation. Store it now.
print(endpoint["secret"])
The webhook console creates endpoints, shows recent deliveries with their status and last error, and sends a test event — without writing any of the calls above.
Retries and idempotency
- Treat deliveries as at-least-once, and use
X-TitanVX-Deliveryas your idempotency key. - Return
2xxas soon as the work is queued. A delivery with no response within 10 seconds is treated as failed and retried. - A failing endpoint is retried six times and then left alone. The gaps are 1 minute, 5 minutes, 30 minutes, 2 hours, 12 hours and 24 hours, so a broken handler has roughly a day and a half of grace before deliveries stop.
GET /api/webhooks/deliveriesreports status, attempt count, last error and the next scheduled attempt.
Those figures are the deployed delivery worker's, not a target. The schedule, the attempt ceiling and the timeout are all configurable server-side, and this page is written from the running configuration rather than from intent.
Test your handler
Two ways, both real deliveries to your endpoint — there is no mock mode:
- The console, which sends a test event to a chosen endpoint and shows the result.
POST /api/webhooks/testdirectly:
POST /api/webhooks/test
{
"event_type": "webhook.test",
"data": { "hello": "world" }
}
A test event is delivered and signed exactly like a production one, so a handler that accepts it will accept the real thing.
Client libraries
Eventium does not publish a client library for any language. The examples above use requests and the standard library only, and they are the supported way to integrate from Python.
A "Python SDK" example here was withdrawn on 2026-11-11 rather than restyled. It imported a client package that exists in no Eventium repository, in no branch history and on no package index, and the page never gave an install command — so it could not run for anyone who followed it. For the record, the withdrawn example imported titanvx_sdk and called client.webhooks.create_endpoint(...); if you copied it, replace it with the requests calls above. SDKs and libraries records what is actually available, and will list a real package name and version if one ships.