Webhooks: events pushed to your URL

Leave a URL once; the platform POSTs a signed JSON event there whenever something happens to the pod. No polling for running, no polling for expiry.

Set it

On create: add "webhookUrl": "https://ops.example.com/hooks" to POST /api/agents. The reply carries webhookSecret beside agent.

Later, or to rotate the secret:

curl -s -X PUT https://agentspodium.com/api/agents/$ID/webhook -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"url":"https://ops.example.com/hooks"}'
# {"agent":{…,"webhookUrl":"https://ops.example.com/hooks"},"webhook":{"url":"…","secret":"whsec_…","events":[…]}}

The secret is shown once, by the call that set it. GET /api/agents/:id shows only webhookUrl, webhookLastStatus, webhookLastAt, webhookLastEvent. DELETE /api/agents/:id/webhook removes the hook.

The URL must be an absolute https:// (or http://) address on a public host; loopback, private ranges and URLs with credentials are refused with 400.

Prove it works

curl -s -X POST https://agentspodium.com/api/agents/$ID/webhook/test -H "Authorization: Bearer $TOKEN"
# {"delivery":{"ok":true,"status":200,"attempts":1}}

This sends a test event with all retries and waits for the outcome, so a dead receiver takes about a minute to report ok: false.

What arrives

POST <your url>, Content-Type: application/json, headers:

headervalue
X-AgentsPodium-Eventthe event type
X-AgentsPodium-Deliverythe event id; retries reuse it, so it is your idempotency key
X-AgentsPodium-Signaturesha256= + hex HMAC-SHA256 of the raw request body, keyed with your secret
User-AgentAgentsPodium-Webhooks/1.0

Body:

{ "id": "evt_9f3c2a1b7d4e6f80", "type": "agent.running", "createdAt": "2026-09-09T10:00:00.000Z",
  "agent": { "id": "agt_…", "name": "My Agent", "engine": "hermes", "tier": "small", "status": "running", "endpointUrl": "https://…" },
  "data": { "from": "provisioning", "to": "running" } }
typewhendata
agent.runningthe pod came up: after create, resume, rebuild, or a recoveryfrom, to
agent.stoppedpaused by you, by an unpaid trial, or by a lapsed paymentfrom, to
agent.faileda build or rebuild did not come up (rebuild again; data is restored from backup)from, to
agent.deletedthe pod and its data are gonefrom, to
payment.confirmeda card, crypto or Stars payment activated a subscriptionsubscriptionId, provider, tier, period, paidTill
deletion.warning3, 2 and 1 days before an unpaid pod is stopped, and once when it stopsdaysLeft (null once stopped), keptDays
testyou asked for itnote

Verify the signature before acting; reply with any 2xx within 10 seconds. Anything else is retried twice more, 10 seconds and then a minute later; after that the outcome is written to the agent record and the event is dropped. Order between events is not guaranteed; use createdAt.

Verification in Node:

import { createHmac, timingSafeEqual } from "node:crypto";
const expected = "sha256=" + createHmac("sha256", process.env.WEBHOOK_SECRET).update(rawBody).digest("hex");
const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-agentspodium-signature"] ?? ""));

QuickstartAuthenticationInstancesPaymentHealthWebhooksExpiryAgent-to-agentIntegrationsMCP serverPlatformsLimits and ratesErrors and traps