Anjin Design
Docs navigation

Docs

Webhooks

Attach a URL to a render and be told when it completes — instead of polling. Every delivery is signed so your receiver can prove it came from us.

Attaching a webhook#

Pass webhook_url in the body of POST /v1/renders — or at either level of a batch (row-level wins):

shell
curl -X POST https://api.anjin.design/v1/renders \
  -H "Authorization: Bearer $ANJIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "acme_banner_1200x630",
    "modifications": [{"name": "headline", "text": "It shipped"}],
    "webhook_url": "https://example.com/hooks/anjin"
  }'

The payload#

When the render completes, we POST JSON to your URL:

completion delivery
{
  "id": "6f1c…",
  "status": "completed",
  "url": "https://…signed…",        // ~1h, like output_url
  "asset_url": "https://…permanent…",
  "width": 1200,
  "height": 630,
  "credits_charged": 1,
  "metadata": { "warnings": [] }
}

credits_charged reflects scale — a scale-4 render says 16. Store asset_url, not url.

Verifying the signature#

Each delivery carries:

X-Anjin-Signature: sha256=<hex of HMAC-SHA256(secret, raw body)>

Fetch your signing secret once from the API (keep it server-side, like your API key):

GET/v1/webhook-secret

Verify with a constant-time comparison against the raw request body:

node
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
  const a = Buffer.from(signatureHeader);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Delivery rules#

RuleNotes
targetpublic URLPrivate, loopback and link-local addresses are rejected — the endpoint must be reachable from the public internet.
whenon completionDelivered when the render completes. Failed renders are refunded rather than delivered; poll GET /v1/renders/:id if you need failure states pushed into your system.
unsignedrejectTreat a delivery without a valid signature as untrusted and drop it.