Skip to main content
Instead of polling for status, webhooks push events to your server as they happen.

Webhook Delivery Flow

This sequence diagram shows the full lifecycle of a typical content generation integration using webhooks:

How It Works

1

Register a webhook URL

Select which events to subscribe to when creating the webhook.
2

Receive events

When an event occurs, Hrizn sends an HTTP POST to your URL with a JSON payload.
3

Verify the signature

Your server verifies the HMAC-SHA256 signature in the X-Webhook-Signature header.
4

Process and acknowledge

Process the payload and return a 2xx response. If delivery fails, Hrizn retries with exponential backoff.

Setting Up Webhooks

Via the Dashboard

Go to the API tab in the Dealership Manager, switch to the Webhooks section, and click Add Webhook.

Via the API

curl -X POST https://api.app.hrizn.io/v1/public/webhooks \
  -H "X-API-Key: hzk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/horizn",
    "events": ["content.progress", "content.completed", "ideacloud.completed"]
  }'
The response includes a secret for verifying signatures.
Store the signing secret in a secrets manager or environment variable. It is returned only once at creation time and cannot be retrieved later.

Event Payload

Every webhook delivery sends a JSON payload:
{
  "id": "evt_1707000000000_abc1234",
  "type": "content.completed",
  "site_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "article_id": "a1b2c3d4-...",
    "article_type": "basic"
  },
  "timestamp": "2026-02-10T12:00:00.000Z",
  "api_version": "2025-01-01"
}

Verifying Signatures

Every delivery includes an X-Webhook-Signature header with an HMAC-SHA256 signature of the request body:
X-Webhook-Signature: sha256=abc123def456...
import { createHmac } from "crypto";

function verifyWebhookSignature(body: string, signature: string, secret: string) {
  const expected = createHmac("sha256", secret).update(body).digest("hex");
  return `sha256=${expected}` === signature;
}

// Express middleware
app.post("/webhooks/horizn", (req, res) => {
  const signature = req.headers["x-webhook-signature"];
  const isValid = verifyWebhookSignature(req.rawBody, signature, WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(req.rawBody);
  console.log(`Received: ${event.type}`, event.data);

  res.status(200).send("OK");
});

Available Events

EventDescriptionPayload Data
ideacloud.completedIdeaCloud research finishedideacloud_id, keyword
ideacloud.failedIdeaCloud research failedideacloud_id, keyword, error
EventDescriptionPayload Data
content.progressGeneration stage transitionarticle_id, article_type, component_type, stage, message, progress_percent
content.completedContent generation finishedarticle_id, article_type
content.failedContent generation failedarticle_id, article_type, error
EventDescriptionPayload Data
compliance.completedCompliance check finishedarticle_id, overall_status, overall_score
content_tools.completedContent tools generatedarticle_id, tools
EventDescriptionPayload Data
inventory.description.completedVehicle description generatedvin, year, make, model
inventory.description.batch_completedBatch descriptions finishedcount, site_id
inventory.feed.updatedInventory feed refreshedvehicle_count

Content Progress Events

The content.progress event provides real-time visibility into content generation stages. Unlike terminal events (content.completed, content.failed), progress events fire at meaningful stage transitions — typically 4-6 events per content item.

Stages

StageDescriptionTypical %
researchingVehicle data being fetched (comparisons & model landing pages only)5%
outliningArticle structure being created5%
writingContent generation started10-80%
finalizingPost-processing (links, TOC, formatting)95%

Example Payload

{
  "id": "evt_1707000000000_abc1234",
  "type": "content.progress",
  "site_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "article_id": "a1b2c3d4-...",
    "article_type": "comparison",
    "component_type": "Body",
    "stage": "writing",
    "message": "Writing content...",
    "progress_percent": 45
  },
  "timestamp": "2026-02-10T12:00:05.000Z",
  "api_version": "2025-01-01"
}
Subscribers must explicitly include content.progress in their events array when creating or updating a webhook subscription. Existing subscribers only receiving content.completed will not receive progress events unless they update their subscription.

Retry Policy

If your endpoint returns a non-2xx status or times out (15s), Hrizn retries with exponential backoff:
  • Attempt 1: Immediate
  • Attempt 2: ~1 minute later
  • Attempt 3: ~5 minutes later
  • Attempt 4: ~30 minutes later
  • Attempt 5: ~2 hours later
After 5 failed attempts, the delivery is sent to a dead letter queue.
After 10 consecutive failures across different events, the webhook subscription is automatically disabled. You’ll need to re-enable it from the dashboard or via PATCH /webhooks/{id}.

Testing

Send a test ping to verify your webhook URL:
curl -X POST https://api.app.hrizn.io/v1/public/webhooks/{id}/test \
  -H "X-API-Key: hzk_your_key_here"
This sends a test.ping event to your URL and returns the response status.
Use ngrok or a similar tunneling tool to expose a local development server for testing webhooks without deploying.

Managing Webhooks

ActionMethodEndpoint
CreatePOST/webhooks
ListGET/webhooks
UpdatePATCH/webhooks/{id}
DeleteDELETE/webhooks/{id}
TestPOST/webhooks/{id}/test
DeliveriesGET/webhooks/{id}/deliveries
Last modified on March 1, 2026