Webhooks
Receive real-time HTTP POST notifications the instant posts go live, fail on third-party networks, or when social channel access tokens require reconnection.
Supported events (4)
Subscribe your endpoint to any combination of these events when creating a webhook:
post.publishedFired when a post publishes successfully to all connected target channels.
Includes post_id, live post_urls per channel, and published timestamp.post.failedFired when publishing attempts fail across all target channels.
Includes post_id, error messages, and network failure reason codes.post.partially_publishedFired when a multi-channel post publishes to some channels but encounters errors on others.
Includes per-channel status breakdown with post_url or error_message for each.channel.disconnectedFired when a connected social account token expires, is revoked by user, or disconnected.
Includes channel_id, provider, handle, and reason code.Payload structure
Every webhook dispatch includes an event identifier, ISO timestamp, workspace context, and the entity details:
{
"id": "evt_90f12c8a91",
"event": "post.published",
"timestamp": "2026-11-05T12:00:00Z",
"workspace_id": "ws_brand_primary",
"payload": {
"post_id": "pst_89fa12b",
"results": [
{
"channel_id": "ch_ig_01",
"status": "success",
"post_url": "https://instagram.com/p/DB18f921"
},
{
"channel_id": "ch_li_02",
"status": "success",
"post_url": "https://linkedin.com/feed/update/urn:li:share:9812"
}
]
}
}
Foxli-Signature header
Every webhook request arrives with a Foxli-Signature HTTP header formatted as:
Where t is the Unix epoch timestamp (seconds) when the webhook was sent, and v1 is the hex-encoded HMAC-SHA256 signature calculated over the concatenated string {timestamp}.{raw_request_body} using your webhook secret (whsec_...).
Verification code (Node & Python)
Always verify the signature before processing payloads to guarantee authenticity and prevent replay attacks:
import crypto from "crypto"; export function verifyFoxliWebhook(rawBody, signatureHeader, secret, toleranceSeconds = 300) { if (!signatureHeader) { throw new Error("Missing Foxli-Signature header"); } // Parse header: t=1730808000,v1=9fa81bc32e... const parts = Object.fromEntries( signatureHeader.split(",").map((item) => item.split("=")) ); const timestamp = parseInt(parts.t, 10); const expectedSig = parts.v1; if (!timestamp || !expectedSig) { throw new Error("Malformed Foxli-Signature header"); } // Prevent replay attacks const now = Math.floor(Date.now() / 1000); if (Math.abs(now - timestamp) > toleranceSeconds) { throw new Error("Webhook timestamp expired or outside tolerance window"); } // Compute HMAC-SHA256 signature const signedPayload = `${timestamp}.${rawBody}`; const computedSig = crypto .createHmac("sha256", secret) .update(signedPayload, "utf8") .digest("hex"); // Constant-time comparison (lengths must match first, or timingSafeEqual throws) const a = Buffer.from(computedSig, "hex"); const b = Buffer.from(expectedSig, "hex"); const isValid = a.length === b.length && crypto.timingSafeEqual(a, b); if (!isValid) { throw new Error("Invalid Foxli webhook signature"); } return true; }
Delivery & retry schedule
Your endpoint must acknowledge delivery by returning an HTTP 2xx status code within 10 seconds.
If delivery fails or times out, Foxli retries on an exponential schedule:
1 min → 5 min → 30 min → 2 hours → 6 hours.
Every webhook dispatch attempt, duration, response status, and error snippet is retained in your Foxli dashboard for 7 days.