Webhooks
Send signed, event-driven webhooks to your own integrations and verify them with HMAC-SHA256.
Webhooks
Webhooks are HTTP callbacks EarlyForge sends to your server when something happens on a site. Use them to connect EarlyForge to your own workflows, automation tools, chat notifications, or internal services. Every delivery is signed so you can confirm it really came from EarlyForge.
Supported events
| Event | When it fires | Key payload fields |
|---|---|---|
article.published | An article is published to a destination | article_id, title, destination, published_url |
article.updated | An article's content changes | article_id, title, changed_fields |
article.deleted | An article is deleted | article_id, title |
trend.alert | A smart alert condition is matched | alert_id, trend_id, trend_name, score |
brief.completed | A brief finishes generating articles | brief_id, article_ids, article_count |
Every payload also includes event (the event type), event_id (a unique delivery ID), timestamp (ISO 8601), and site_id identifying the site it came from.
Create a webhook
Open the Webhooks settings
Go to Sites → select your site → Webhooks and click New webhook.
Enter your endpoint URL
Provide the HTTPS URL that will receive payloads. Plain HTTP is not accepted — your endpoint must use TLS.
Choose events
Select one or more events for this webhook. Create separate webhooks if different events should reach different endpoints.
Copy the signing secret
EarlyForge generates a unique signing secret for the webhook. Copy it and store it securely on your server — you need it to verify incoming deliveries. The secret is stored in the webhook's settings and encrypted by EarlyForge.
Send a test
Click Send test. EarlyForge delivers a ping event so you can confirm your endpoint responds with a 200 status. The result appears in the delivery log.
Save and activate
Click Save. The webhook now fires on the events you selected.
Verify the signature (HMAC-SHA256)
Every delivery includes an X-Webhook-Signature header set to sha256={digest}, where the digest is an HMAC-SHA256 of the raw request body using your webhook's signing secret. Always verify it before processing a payload — this prevents anyone from sending forged events.
import crypto from 'crypto';
export async function POST(request) {
const body = await request.text();
const signature = request.headers.get('X-Webhook-Signature') ?? '';
const secret = process.env.EARLYFORGE_WEBHOOK_SECRET;
const expected =
'sha256=' +
crypto.createHmac('sha256', secret).update(body).digest('hex');
const valid =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(body);
// Process asynchronously, then return quickly
return Response.json({ received: true });
}Always verify the signature before processing a payload. Without verification, anyone could send forged events to your endpoint. Compare digests in constant time (as shown above) to avoid timing attacks, and keep the secret server-side only.
Delivery logs and retries
Each webhook has a Delivery log at Sites → Webhooks → select a webhook → Delivery log. Every entry shows the timestamp, event type, HTTP status, response time, and retry count.
If your endpoint returns a non-2xx status or does not respond in time, EarlyForge retries with exponential backoff. After the final retry the delivery is marked failed, and you can replay it from the log.
Best practices
- Respond quickly — return
200right away and process the event asynchronously to avoid timeouts. - Handle duplicates — use
event_idto deduplicate, since a retry may deliver the same event twice. - Monitor the log — repeated failures usually mean a misconfigured or down endpoint.
- Rotate a leaked secret — if a secret is exposed, delete the webhook and create a new one with a fresh secret.
What's next?
- Publish to WordPress — publish through the WordPress REST API
- Publish to Ghost — newsletter-driven distribution
- Next.js Direct — publish to your own Next.js site with on-demand revalidation