Skip to content

Guides

Webhooks

Instead of polling, let reblogger call your server the moment an article is ready — signed, retried and easy to verify.

Register an endpoint

Webhooks are available on Starter, Pro and Agency. Register an HTTPS URL in the dashboard or via the API. The response contains the signing secret — it's shown only once, so store it with your other secrets.

POST/api/v1/webhooks
Shell
curl https://reblogger.ai/api/v1/webhooks \
  -H "Authorization: Bearer $REBLOGGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/hooks/reblogger", "events": ["generation.succeeded", "generation.failed"] }'
201 Created
{
  "id": "cmh1x0whk2b8n0001",
  "object": "webhook_endpoint",
  "url": "https://example.com/hooks/reblogger",
  "events": ["generation.succeeded", "generation.failed"],
  "active": true,
  "created_at": "2026-10-31T09:24:11.000Z",
  "secret": "whsec_9JcU2v…"
}

Events

Webhook events
EventWhen
generation.succeededThe article is ready. Fetch it with GET /articles/{data.article_id}.
generation.failedThe generation failed. data.error explains why; credits were already refunded.

Payload

We POST JSON with the headers content-type: application/json, x-reblogger-event and x-reblogger-signature. data is the generation object:

JSON
{
  "id": "evt_cmh1x0gen7f2k0001_generation.succeeded",
  "type": "generation.succeeded",
  "created": "2026-10-31T09:26:02.114Z",
  "data": {
    "id": "cmh1x0gen7f2k0001",
    "object": "generation",
    "status": "succeeded",
    "stage": "done",
    "progress": 100,
    "article_id": "cmh1x0art9q3m0001",
    "model": "claude-sonnet-5.5",
    "credits_charged": 1,
    "source": "api",
    "error": null,
    "created_at": "2026-10-31T09:24:11.000Z",
    "started_at": "2026-10-31T09:24:12.530Z",
    "finished_at": "2026-10-31T09:26:01.987Z"
  }
}

The payload intentionally doesn't include the article body. Fetch the article with your API key — that way a leaked webhook URL never leaks content.

Verifying signatures

Every request to a registered endpoint carries a signature header:

HTTP
x-reblogger-signature: t=1793452800,v1=5f2b8c0e9d…

v1 is the hex-encoded HMAC-SHA256 of <t>.<raw request body> keyed with your endpoint secret. To verify:

  1. Split the header on , and = to get t and v1.
  2. Compute HMAC_SHA256(secret, t + "." + rawBody) over the raw bytes — not re-serialized JSON.
  3. Compare with a constant-time function and reject timestamps older than five minutes to stop replays.
import crypto from 'node:crypto';
import express from 'express';

const app = express();

// Use the raw body — re-serialized JSON will not match the signature.
app.post('/hooks/reblogger', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.get('x-reblogger-signature') ?? '';
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = crypto
    .createHmac('sha256', process.env.REBLOGGER_WEBHOOK_SECRET)
    .update(`${t}.${req.body}`)
    .digest('hex');

  const valid =
    v1 && v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300; // 5 minute tolerance
  if (!valid || !fresh) return res.status(400).send('invalid signature');

  const event = JSON.parse(req.body);
  if (event.type === 'generation.succeeded') {
    // fetch GET /v1/articles/{event.data.article_id} and publish it
  }
  res.sendStatus(200);
});

Delivery & retries

  • Respond with any 2xx within 10 seconds. Do heavy work (fetching and publishing the article) asynchronously.
  • Non-2xx responses, timeouts and connection errors are retried up to 6 times with exponential backoff.
  • Deliveries can arrive more than once or out of order. De-duplicate on the event id.
  • Redirects are not followed, and endpoints must resolve to public IP addresses.

One-off callback_url

You can also pass callback_url when creating an article. That URL receives the same payload once the generation finishes, but unsigned — treat it as a ping and always re-fetch the generation or article with your API key before trusting it.

Use registered endpoints for production systems; reach for callback_url when each article should notify a different place (for example, a per-site WordPress endpoint).

Manage endpoints

List

GET/api/v1/webhooks

Delete

DELETE/api/v1/webhooks/{id}

You can register up to 10 endpoints per account. To rotate a secret, register a new endpoint, deploy the new secret, then delete the old endpoint.