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

EventWhen it fires
post.publishedA post went live on a network
post.scheduledA post was scheduled
post.queuedA publish was accepted and queued
post.failedA publish failed permanently
post.retryingA transient failure triggered an automatic retry
post.deletedA 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.

On this page