earlyseo

Webhook

On every article publish we POST a signed JSON payload to the URL you configured at Settings → Integrations.

Authentication

Two layers, both optional but both recommended:

Payload

{
  "event": "article.published",
  "siteId": "<uuid>",
  "article": { id, title, slug, metaDescription, contentRawHtml, contentCss, tags, publishedAt }
}

Retries

ResponseAction
2xxmarked success, response_code recorded
4xxmarked failed, no retry (your endpoint rejected)
5xx / network errorattempt += 1, next_retry_at = now + 60s × 2^attempt
10 attemptsmarked exhausted, no further retries

Receivers

Next.js

// app/api/earlyseo/route.ts
import crypto from "crypto";
export async function POST(req) {
  const body = await req.text();
  const sig = req.headers.get("x-earlyseo-signature");
  const expected = crypto.createHmac("sha256", process.env.EARLYSEO_HMAC_SECRET).update(body).digest("hex");
  if (sig !== expected) return new Response("invalid", { status: 401 });
  const { article } = JSON.parse(body);
  // ... persist or proxy
  return Response.json({ ok: true });
}

Express

app.post("/earlyseo", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.header("x-earlyseo-signature");
  const expected = crypto.createHmac("sha256", process.env.EARLYSEO_HMAC_SECRET).update(req.body).digest("hex");
  if (sig !== expected) return res.status(401).end();
  // ...
});

Flask

@app.post("/earlyseo")
def receive():
    body = request.get_data()
    sig = request.headers.get("X-Earlyseo-Signature")
    expected = hmac.new(SECRET.encode(), body, "sha256").hexdigest()
    if not hmac.compare_digest(sig, expected): abort(401)
    # ...
    return {"ok": True}