Guides
Webhooks
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.
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"] }'{
"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
| Event | When |
|---|---|
generation.succeeded | The article is ready. Fetch it with GET /articles/{data.article_id}. |
generation.failed | The 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:
{
"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:
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:
- Split the header on
,and=to gettandv1. - Compute
HMAC_SHA256(secret, t + "." + rawBody)over the raw bytes — not re-serialized JSON. - 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
2xxwithin 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
Delete
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.