Webhooks
Subscribe to real-time agent lifecycle events. Theazo delivers signed HTTP POST requests to your endpoint whenever an agent, session, workflow, or primitive changes state.
Creating a webhook
Register a URL and the set of events you want to receive. The secret field is optional — supply your own or Theazo generates one. Store it securely; you need it to verify signatures.
import { Theazo } from class="cb-str">'theazo'
const theazo = new Theazo({ apiKey: class="cb-str">'th_live_...' })
const webhook = await theazo.webhooks.create({
url: class="cb-str">'https://yourapp.com/webhooks/theazo'.com/webhooks/theazo',
events: [
class="cb-str">'agent.completed',
class="cb-str">'agent.failed',
class="cb-str">'approval.requested',
class="cb-str">'fleet.completed',
],
secret: process.env.WEBHOOK_SECRET, class="cb-cmt">// optional — generated if omitted
})
console.log(webhook.id) class="cb-cmt">// 'wh_abc123'
console.log(webhook.secret) class="cb-cmt">// shown once — store immediatelysecret is returned in full only on creation. Subsequent calls to theazo.webhooks.list() return the first 8 characters only. If you lose it, delete the webhook and create a new one.Managing webhooks
theazo.webhooks.create(opts)Promise<Webhook>Register a new endpoint. Returns the full secret once.theazo.webhooks.list()Promise<Webhook[]>List all registered webhooks. Secrets are masked to first 8 chars.theazo.webhooks.test(id)Promise<void>Send a test agent.completed payload to the endpoint. Useful for verifying connectivity.theazo.webhooks.delete(id)Promise<void>Remove the webhook. No further events will be delivered.// List all webhooks
const webhooks = await theazo.webhooks.list()
// [{ id: 'wh_abc123', url: '...', events: [...], secret: 'a1b2c3d4...' }]
// Send a test delivery
await theazo.webhooks.test(class="cb-str">'wh_abc123')
// Delete a webhook
await theazo.webhooks.delete(class="cb-str">'wh_abc123')Event reference
Subscribe to any combination of events. You can register multiple webhooks with different event subsets and different endpoints.
Agent events
agent.startedAn agent has been provisioned and its compute environment is ready. Fired before the first model call.
agent.completedThe agent finished successfully. Payload includes output text, artifact paths, and a structured cost object.
agent.failedThe agent terminated with an error. Payload includes the error code and message.
agent.pausedThe agent was paused — either manually via agent.pause() or because a cost/time limit was breached.
Session events
session.limit_reachedA session hit a configured limit (maxCost, maxAgents, or maxComputeMinutes). All running agents in the session are paused.
Workflow events
workflow.startedA workflow run was initiated. Payload includes the workflowId, runId, and total step count.
workflow.step_completedA single DAG step finished. Payload includes the step name, output, and cost for that step.
workflow.completedAll steps completed successfully. Payload includes the final output and aggregate cost across all steps.
workflow.failedA step failed and retries were exhausted. Payload includes which step failed and the error.
Fleet events
fleet.startedA fleet job began dispatching agents. Payload includes total item count and concurrency limit.
fleet.item_completedA single fleet item finished. Payload includes itemIndex, output, and per-item cost.
fleet.completedAll items processed. Payload includes completed count, failed count, and total cost.
fleet.cost_limitedThe fleet hit its costCap. Remaining items are cancelled. Payload includes items processed so far.
Approval events
approval.requestedAn agent reached a checkpoint requiring human sign-off. Payload includes the action name and parameters preview.
approval.approvedA reviewer approved the action. The agent resumes automatically.
approval.deniedA reviewer denied the action. The agent receives a denial reason and may try an alternative path.
approval.expiredNo decision was made within the configured TTL. The agent is paused awaiting manual intervention.
Schedule events
schedule.firedA cron schedule triggered and dispatched its configured agent.
trigger.firedA webhook trigger received an inbound request and dispatched its configured agent.
Knowledge events
knowledge.sync_completedA knowledge source finished ingesting and embedding documents. Payload includes chunk count and storage used.
knowledge.sync_failedIngestion failed for a knowledge source. Payload includes the source ID and error details.
Example payload
All events share the same envelope shape. The data field is event-specific. Cost values are always structured objects with integer cent amounts — never strings.
// POST https://yourapp.com/webhooks/theazo
// Content-Type: application/json
// X-Theazo-Signature: sha256=a1b2c3...
// X-Theazo-Delivery: del_xyz789
{
class="cb-str">"id": class="cb-str">"evt_01HX3K2...",
class="cb-str">"event": class="cb-str">"agent.completed",
class="cb-str">"createdAt": class="cb-str">"2025-05-06T14:23:01.000Z",
class="cb-str">"data": {
class="cb-str">"agentId": class="cb-str">"agt_01HX3K1...",
class="cb-str">"sessionId": class="cb-str">"ses_01HX3J9...",
class="cb-str">"userId": class="cb-str">"user_123",
class="cb-str">"status": class="cb-str">"completed",
class="cb-str">"output": class="cb-str">"Competitor pricing analysis complete...",
class="cb-str">"artifacts": [class="cb-str">"/data/report.pdf"],
class="cb-str">"cost": { class="cb-str">"amount": class="cb-num">47, class="cb-str">"currency": class="cb-str">"usd" },
class="cb-str">"compute": {
class="cb-str">"provider": class="cb-str">"e2b",
class="cb-str">"durationMs": class="cb-num">12400
},
class="cb-str">"model": {
class="cb-str">"provider": class="cb-str">"anthropic",
class="cb-str">"model": class="cb-str">"claude-3-5-sonnet-20241022",
class="cb-str">"inputTokens": class="cb-num">1820,
class="cb-str">"outputTokens": class="cb-num">634
}
}
}Signature verification
Every delivery includes an X-Theazo-Signature header. It is an HMAC-SHA256 of the raw request body, signed with your webhook secret. Always verify before processing.
import { createHmac, timingSafeEqual } from class="cb-str">'crypto'
export function verifyWebhookSignature(
rawBody: Buffer,
signatureHeader: string,
secret: string,
): boolean {
const expected = class="cb-str">'sha256=' + createHmac(class="cb-str">'sha256', secret)
.update(rawBody)
.digest(class="cb-str">'hex')
const actual = signatureHeader
if (expected.length !== actual.length) return false
return timingSafeEqual(
Buffer.from(expected, class="cb-str">'utf8'),
Buffer.from(actual, class="cb-str">'utf8'),
)
}
// Next.js route handler example
export async function POST(req: Request) {
const rawBody = Buffer.from(await req.arrayBuffer())
const sig = req.headers.get(class="cb-str">'x-theazo-signature') ?? class="cb-str">''
const isValid = verifyWebhookSignature(rawBody, sig, process.env.WEBHOOK_SECRET!)
if (!isValid) {
return new Response(class="cb-str">'Unauthorized', { status: class="cb-num">401 })
}
const payload = JSON.parse(rawBody.toString())
class="cb-cmt">// process payload.event ...
return new Response(class="cb-str">'OK')
}timingSafeEqual to compare signatures. A naive string comparison leaks timing information that an attacker can use to forge signatures.Delivery and retries
Theazo expects a 2xx response within 10 seconds. If your endpoint times out or returns a non-2xx status, the delivery is retried up to 3 times with exponential backoff (5 s, 30 s, 5 min). After 3 failures the delivery is marked as permanently failed and logged in your dashboard.
Each delivery carries an X-Theazo-Delivery header with a unique ID. Use it to deduplicate retried deliveries in your handler — the same event may arrive more than once.