Webhooks
Receive real-time state changes for every publish.
Webhooks push per-network state changes to your endpoint, so you do not have to poll.
Events
| Event | When it fires |
|---|---|
post.published | A post went live on a network |
post.scheduled | A post was scheduled |
post.queued | A publish was accepted and queued |
post.failed | A publish failed permanently |
post.retrying | A transient failure triggered an automatic retry |
post.deleted | A post was deleted or unscheduled |
Configuring
Webhook URLs are set in Settings → API → Webhooks, or per key. You can configure:
- The target URL (HTTPS only)
- Which events to deliver
- Optional signing secret (recommended)
Payload
{
"id": "evt_01JK9...",
"type": "post.published",
"created_at": "2026-08-16T09:00:00Z",
"data": {
"post_id": "pub_01JK9...",
"platform": "x",
"status": "published",
"network_post_id": "1940...",
"network_options": {}
}
}Delivery and retries
- Sent within a few seconds of the state change
- Retried with exponential backoff (1 min, 5 min, 30 min, 2 h, 6 h) until the endpoint answers
2xx - Dead after 5 failed attempts; the failure appears in the dashboard
- Deliveries can arrive out of order — rely on
created_at+id, not arrival order
Signature verification
When a signing secret is configured, every request carries:
X-Agentiq-Signature: t=1720000000,v1=<hmac-sha256-hex>Verify by computing an HMAC-SHA256 over t.<body> with your signing secret and comparing v1. Example:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifySignature(
body: string,
signature: string,
secret: string,
) {
const [t, v1] = signature.split(",").map((part) => part.split("=")[1]);
const expected = createHmac("sha256", secret)
.update(`${t}.${body}`)
.digest("hex");
return timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}Reject timestamps older than 5 minutes to prevent replay attacks.