Endpoint registration
Register an endpoint from Settings → Webhooks. Each subscription returns a 32-byte base64 signing secret that is shown exactly once; store it in your secret manager and never log it. You can subscribe an endpoint to all event types or to a specific allowlist; only events whose type is in the allowlist dispatch to that endpoint.
Event types
The current catalog is stable; new types are additive.
run.started,run.succeeded,run.failed,run.cancelledapproval.requested,approval.granted,approval.denied,approval.expiredaudit.event.recordedbilling.invoice.finalized,billing.charge.failed
Payload shape
Every delivery is a POST whose JSON body carries the same envelope. Receivers should validate the envelope keys and treat unknown fields in data as additive.
{
"event_id": "evt_01HZF5K3...",
"event_type": "run.succeeded",
"created_at": "2026-05-19T18:42:11.041Z",
"tenant_id": "org_123",
"data": {
"run_id": "run_42",
"agent_id": "agt_billing-summarizer",
"duration_ms": 4128,
"status": "succeeded"
}
}Signing and verification
Each delivery carries an X-AgentHub-Signature header that pairs a unix timestamp with an HMAC-SHA-256 over <timestamp>.<raw_body>. Verify the signature on every receiver — never trust the source IP or a non-cryptographic header.
// Node.js receiver
import crypto from "node:crypto";
export function verify(req, secret) {
const header = req.headers["x-agenthub-signature"]; // "t=1716148931,v1=abc..."
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${req.rawBody}`)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))) {
throw new Error("bad signature");
}
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) {
throw new Error("stale delivery"); // older than 5 minutes
}
}Retries and idempotency
AgentHub delivers at-least-once. Receivers must dedupe on event_id and treat any timestamp outside a five-minute window as stale. Failed deliveries (any non-2xx response, or a timeout past 10 seconds) retry with exponential backoff: 30s, 2m, 10m, 30m, 1h, 2h, 4h, 8h, then drop after 24 hours.
Test panel and replay
Each endpoint’s detail page exposes the last 100 delivery attempts with the response code, latency, and retry count. From there you can:
- Manually re-fire any delivery to debug a receiver bug.
- Send a synthetic test event of any type with sample data.
- Rotate the signing secret with a 24-hour overlap window for zero-downtime cuts.
- Pause the endpoint without losing the queue — paused endpoints buffer up to 24 hours of events.