Setting up your webhook
Enter your webhook URL
Paste the full HTTPS URL of your endpoint (e.g.,
https://yoursite.com/api/seopilot). SEOPilot will POST article payloads to this address. The URL must use https.Set a secret
Add a secret token. SEOPilot uses it to sign every request so you can verify payloads on your end before processing them. A secret is required.
Webhook payload
When an article is generated, SEOPilot sends aPOST request with a JSON body structured as follows. The payload includes everything you need to render and serve the article from your own domain.
payload-example.json
| Field | Type | Description |
|---|---|---|
event | string | Always article.generated. |
delivery_id | string | Unique identifier for this delivery attempt. |
created_at | string | ISO 8601 timestamp of when the envelope was created. |
data.site | object | id, url, and name of the site the article belongs to. |
data.article.id | string | Unique identifier for the article in SEOPilot. |
data.article.title | string | The article’s full title. |
data.article.slug | string | URL-safe slug for use in your routing. |
data.article.meta_title | string | SEO title for the <title> tag. |
data.article.meta_description | string | SEO meta description for the <meta> tag. |
data.article.body_md | string | The article body in Markdown. |
data.article.internal_links | array | Suggested internal links — each item has a slug and anchor. |
data.article.generated_at | string | ISO 8601 timestamp of when the article was generated. |
data.article.hero_image | object | null | Hero image with url, alt, photographer (name, url), and source_url. May be null. |
data.keyword | object | The target keyword — id and keyword. |
The article body is delivered as Markdown in
body_md. There is no separate HTML or MDX field — render the Markdown with your own pipeline, or write it straight to a Markdown/MDX file (see Static Sites).Verifying webhook signatures
SEOPilot signs each request with an HMAC-SHA256 signature and sends it in theX-SEOPilot-Signature header. The header has the form t=<timestamp>,v1=<signature>, where timestamp is a Unix timestamp in seconds and signature is the hex HMAC-SHA256 of the string `${timestamp}.${rawBody}` using your secret. Verify it before doing anything with the payload — it guarantees the request genuinely came from SEOPilot and hasn’t been tampered with.
SEOPilot also sends X-SEOPilot-Event (the event name) and X-SEOPilot-Delivery (the delivery ID) headers, and a User-Agent of SEOPilot-Webhook/1.0.
webhook-handler.js
rawBody — do not pass a pre-parsed JSON object, because re-serializing it may produce a different byte sequence and break the signature. If verifyWebhook returns false, reject the request with a 401 and do not process the article.
Handling failed deliveries
Your endpoint must respond with a2xx status code for SEOPilot to consider a delivery successful. Respond promptly — a non-2xx response or a network/timeout error is recorded as a failed delivery.
When a delivery fails, SEOPilot automatically retries it with exponential backoff — up to 5 attempts total, doubling the wait each time (starting at roughly 2 seconds and capped at 60 seconds between attempts). If every attempt fails, the delivery is marked failed.
The most recent delivery status is shown on the webhook in Settings → Integrations — for example, Last delivery: … — 200 OK or Last delivery: … — failed: <error>. You can re-send a sample payload at any time with the Test webhook button to confirm your endpoint is healthy again.