Webhook
We POST each article's full payload to a URL you choose the moment it's published — no polling needed.
Setup
From Integrations → Webhook, enter your endpoint URL. We'll generate a signing secret — store it, since it's shown only once.
Payload
POST to your URL with a JSON body shaped like the API's single-article response, plus coverImageUrl:
{
"id": "...",
"slug": "best-seo-tools-2026",
"title": "Best SEO Tools 2026",
"metaTitle": "Best SEO Tools 2026",
"metaDescription": "...",
"html": "<h1>...</h1>...",
"markdown": "# ...",
"angle": "listicle",
"language": "en-US",
"publishedAt": "2026-08-01",
"coverImageUrl": "https://.../article-images/<business>/<article>.png",
"jsonLd": { "@type": "Article", "...": "..." }
}coverImageUrl is a publicly-fetchable URL for the article's generated cover image, or null if it hasn't generated yet. Any inline images an article contains are already in html/markdown as regular <img> tags, however many there are — there's no separate field per image.
Compatibility: webhook payloads may gain new optional fields over time. Your webhook handler should ignore fields it does not recognize. Existing fields will remain backwards compatible unless a future breaking contract is explicitly introduced (which would be announced and versioned separately).
Verifying the signature
Every request includes an X-HoneyTraffic-Signature header: sha256=<hex>, an HMAC-SHA256 of the raw request body using your signing secret. Recompute it and compare:
import { createHmac, timingSafeEqual } from "crypto";
function isValidSignature(rawBody, header, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}Delivery behavior
- We expect a
2xxresponse within 5 seconds. - Failed deliveries are retried automatically with backoff.
- Use the Send a test event step in the connect wizard to verify your endpoint before relying on it.