Webhooks

Get notified when a new draft is ready — then pull the full post via API.

Configure a webhook URL and secret in Integrations. When a draft is ready, we POST article.ready with slug and fetch hints. Your backend verifies HMAC, then calls GET /posts/{slug} to import contentHtml.

Events

  • article.ready — A quality-checked draft is ready — fetch full HTML via API using slug
  • webhook.test — Sent when you call POST /api/v1/webhooks/test

Signature verification

We send X-GrowthDaily-Timestamp (Unix seconds) and X-GrowthDaily-Signature (hex HMAC-SHA256 of `{timestamp}.{rawBody}` using your webhook secret).

Verify the signature (Node.js)
import { createHmac, timingSafeEqual } from "crypto";

function verify(secret, rawBody, timestamp, signatureHex) {
  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(signatureHex, "utf8");
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}
article.ready payload
{
  "event": "article.ready",
  "siteId": "uuid",
  "postId": "uuid",
  "slug": "my-post",
  "title": "My post",
  "trustScore": 94,
  "qualityBand": "premium",
  "assuranceLabel": "High confidence",
  "language": "en",
  "sourcePostId": null,
  "status": "draft",
  "at": "2026-07-21T12:00:00.000Z",
  "fetch": {
    "path": "/api/v1/sites/{siteId}/posts/{slug}",
    "hint": "GET with Bearer token to import contentHtml"
  }
}

After article.ready

Use the slug from the payload (or fetch.path) to call GET /api/v1/sites/{siteId}/posts/{slug}. Import contentHtml, seo, and faqJsonLd into your CMS — publish on your schedule.

Delivery guarantees

Each event is sent once as a single HTTP POST with an 8 second timeout. We do not automatically retry failed deliveries. A failed webhook does not block new drafts. Every attempt is logged under Integrations → Recent deliveries.

Idempotent handlers

Respond with HTTP 2xx quickly. Deduplicate imports using postId (or siteId + slug) so duplicate deliveries never create two CMS entries. If you miss an event, poll GET /posts with updatedAfter — see the API reference.

Delivery log

Success and failure rows appear in your integrations panel with HTTP status and timestamp.