Skip to content

Webhooks

Webhooks push events to your server the moment they happen — no polling, no missed events, no rate-limit pressure. Every delivery is HMAC-SHA256 signed so you can trust the payload came from us.

Available events

Nine event types, grouped by domain:

EventFires when
article.createdA new article record is created (draft or published)
article.updatedAny field on an article changes
article.publishedArticle transitions to PUBLISHED status
article.deletedArticle is hard-deleted
keyword.createdA keyword is added to a project
keyword.rankedNew SERP ranking data lands for a tracked keyword
generation.startedA generation job picks up from the queue
generation.completedA generation finishes successfully (article is ready)
generation.failedA generation fails — payload includes error details

The full live list is also at GET /api/public/v1/webhooks/events if you want to enumerate them programmatically.

Setting up a webhook

  1. Expose a public HTTPS endpoint that accepts POST requests. For local development use ngrok or similar — webhook URLs must be reachable from the internet.

  2. Register the webhook with the events you care about:

    Terminal window
    curl -X POST https://api.autoranq.ai/api/public/v1/webhooks \
    -H "Authorization: Bearer $AUTORANQ_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "url": "https://your-app.example.com/webhooks/autoranq",
    "events": ["generation.completed", "generation.failed"]
    }'
  3. Save the secret from the response. It’s a 64-character hex string used to verify every delivery. Like API keys, it’s shown only once — if you lose it, rotate via POST /webhooks/:id/rotate-secret.

Delivery format

Every event is delivered as POST to your URL with this body shape:

{
"id": "clxwhk123def456ghi789",
"event": "generation.completed",
"created_at": "2026-05-26T14:32:11.000Z",
"data": {
"generation_id": "clxgen123def456ghi789",
"project_id": "clxprj123def456ghi789",
"article_id": "clxart123def456ghi789",
"keyword": "how to start a blog",
"status": "COMPLETED"
}
}

The data payload varies per event type. See the API Reference once it lands for per-event schemas.

Request headers

Every delivery includes these headers:

HeaderPurpose
X-Webhook-EventEvent type, e.g. generation.completed
X-Webhook-Delivery-IdUnique delivery ID — use for idempotency
X-Webhook-TimestampISO 8601 timestamp of the delivery attempt
X-Webhook-Signaturesha256=<hex> — HMAC signature of the raw body
Content-TypeAlways application/json
User-AgentAutoRanq-Webhook/1.0

Verifying signatures

Always verify the signature before processing a webhook. Without verification, anyone who guesses your URL can submit fake events.

The signature is HMAC-SHA256(secret, raw_body), prefixed with sha256=. Compare using a constant-time function — never ==.

import express from 'express';
import crypto from 'node:crypto';
const app = express();
// Capture raw body for signature verification
app.use(express.json({
verify: (req, _res, buf) => { req.rawBody = buf.toString('utf8'); }
}));
const SECRET = process.env.AUTORANQ_WEBHOOK_SECRET;
app.post('/webhooks/autoranq', (req, res) => {
const signature = req.get('X-Webhook-Signature') || '';
const expected = 'sha256=' + crypto
.createHmac('sha256', SECRET)
.update(req.rawBody)
.digest('hex');
const valid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
if (!valid) return res.sendStatus(401);
// Idempotency: skip if you've seen this delivery ID before
const deliveryId = req.get('X-Webhook-Delivery-Id');
console.log('Verified delivery', deliveryId, 'event', req.body.event);
res.sendStatus(200);
});
app.listen(3000);

Retries

If your endpoint returns anything other than a 2xx, or doesn’t respond within 30 seconds, we retry with exponential backoff:

AttemptDelay after previous
1 (initial)
25 seconds
325 seconds
Final stateAfter 3 failed attempts, delivery is marked FAILED

Each retry includes the same X-Webhook-Delivery-Id — your idempotency check should treat them as one logical delivery.

Replay & manual retry

Inspect delivery history per webhook:

Terminal window
curl https://api.autoranq.ai/api/public/v1/webhooks/<id>/deliveries \
-H "Authorization: Bearer $AUTORANQ_KEY"

Each delivery shows status (DELIVERED, FAILED, RETRYING), HTTP code received, and response body for debugging.

Testing locally

Use the POST /webhooks/:id/test endpoint to fire a sample payload at your URL without waiting for a real event:

Terminal window
curl -X POST https://api.autoranq.ai/api/public/v1/webhooks/<id>/test \
-H "Authorization: Bearer $AUTORANQ_KEY"

The test payload uses the event type generation.completed with placeholder IDs but a real signature — useful for verifying your verification code before going live.

Best practices

Verify, always

Treat unsigned or wrong-signature payloads as a security incident — log, alert, drop.

Idempotency keys

Use X-Webhook-Delivery-Id to dedupe — retries reuse the same ID, your queue insertions shouldn’t.

Respond fast

Acknowledge 2xx in <5 seconds. Push real work to a background queue.

Monitor failures

Periodically GET /webhooks/:id/deliveries?status=FAILED and alert on unexpected drops.

Next steps

  • Quickstart — fire a real generation and watch the webhook deliver
  • Authentication — secure your webhook-management API access
  • CLIautoranq listen --forward-to localhost:3000/webhook for local-dev forwarding (coming in Phase 3)