EarlyForge
GuidesPublishing

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

EventWhen it firesKey payload fields
article.publishedAn article is published to a destinationarticle_id, title, destination, published_url
article.updatedAn article's content changesarticle_id, title, changed_fields
article.deletedAn article is deletedarticle_id, title
trend.alertA smart alert condition is matchedalert_id, trend_id, trend_name, score
brief.completedA brief finishes generating articlesbrief_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 200 right away and process the event asynchronously to avoid timeouts.
  • Handle duplicates — use event_id to 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?

On this page