SeoboxSeobox

Webhooks

S
By Seobox Eng Team
Published on: 2026-07-168 min readLast reviewed: 2026-07-16
TL;DR

Get notified the instant an article is ready for review, published, or fails — instead of polling the API. Includes signature verification for securing your endpoint.

Why use webhooks instead of polling

Content generation is asynchronous — a POST /generate call returns immediately with a run_id, and the actual article can take anywhere from seconds to a couple of minutes depending on length and current queue load. You can poll GET /v1/api/blogs/:uid on an interval to check status, but a registered webhook pushes an event to your own server the moment something happens, with no polling delay and far fewer wasted requests.

Key takeaway: Register a webhook URL once in Account Settings → Webhooks, and every event below fires automatically — no per-request opt-in needed.

Setting up a webhook

  1. Go to Account Settings → Webhooks and enter an HTTPS endpoint you control.
  2. Save it — Seobox generates a webhook secret at the same time, shown once. Store it securely; it's used to verify incoming payloads.
  3. Your endpoint must respond with a 2xx status within 15 seconds. Anything else is treated as a failed delivery.

Only one webhook URL is supported per account today — route to multiple internal systems from your own receiving endpoint if needed.

Events

Event Fires when
generation.completed An article has finished generating and is ready (either for review or publish, depending on your account's auto-publish setting)
blog.ready_for_review An article is generated and held for manual approval before publishing
blog.published An article has been successfully pushed live to the connected CMS
blog.failed Generation or publishing failed for a run
opportunity.created The opportunity engine surfaces a new content gap, decay, or ranking-gap opportunity for a connected site

Payload shape

Every event is delivered as a POST request with an identical envelope:

{
  "event": "blog.published",
  "account_id": "acct_4d1e...",
  "data": {
    "blog_uid": "blog_1a4f...",
    "site_uid": "site_9f2a...",
    "title": "Keyword Research That Finds Intent, Not Just Volume",
    "published_url": "https://example.com/blog/keyword-research"
  },
  "timestamp": "2026-07-16T14:02:31.000Z"
}

data varies by event type, but event, account_id, and timestamp are always present.

Verifying the signature

Every delivery includes an X-SEOBOX-Signature header — an HMAC-SHA256 digest of the raw request body, signed with your webhook secret. Always verify this before trusting a payload; without verification, anyone who discovers your endpoint URL could send fabricated events.

const crypto = require('crypto');

function isValidSignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Compute the HMAC over the raw, unparsed request body — if your framework has already parsed the JSON into an object by the time you check the signature, re-stringifying it can produce a different byte sequence (key order, whitespace) and fail verification even for a legitimate request. Capture the raw body before any JSON-parsing middleware runs.

Delivery and retries

  • Delivery attempts time out after 15 seconds.
  • A 4xx response is treated as a permanent client-side failure and is not retried — fix your endpoint and the next event will deliver normally.
  • A 5xx response or timeout is retried up to 3 times with exponential backoff.
  • Only publicly resolvable HTTPS URLs are accepted; internal, private, or loopback addresses are rejected when the webhook URL is registered.

FAQ

What happens if my endpoint is down when an event fires? Delivery is retried up to 3 times with exponential backoff for server errors and timeouts. If all retries are exhausted, the event is dropped — for critical workflows, periodically reconcile via the API rather than relying on webhooks as the sole source of truth.

Can I register more than one webhook endpoint? Not directly — one URL per account. Fan out to multiple internal consumers from your own receiving service if you need to notify several systems.

Do webhook failures block content generation or publishing? No. Webhook delivery is fire-and-forget and never blocks or fails the underlying generation or publish operation — it's purely a notification layer.