AuthOrigin
Documentation menu
Guides

Webhooks

Webhooks push events to your systems as they happen — a scan, a suspicious event, a diversion — so you can react in real time instead of polling. Every delivery is signed and retried.

Create an endpoint

Register an HTTPS URL and the events you want. The response includes a signing secret (whsec_...), shown only once — store it to verify deliveries.

POST /webhook-endpoints
curl -X POST "https://api.getauthorigin.com/api/v1/webhook-endpoints" \
  -H "X-Api-Key: $AUTHORIGIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/authorigin",
    "events": ["scan.recorded", "suspicious_scan.detected"]
  }'

Subscribe to "*" to receive every event type.

Event types

EventFires when
scan.recordedA product code was scanned.
suspicious_scan.detectedA scan was risk-scored as suspicious (clone, impossible travel, excessive scanning).
product.divertedA genuine unit was scanned outside its authorized markets.
counterfeit_report.createdA consumer reported a suspected fake.
barcode_job.completedA code-generation job finished.
barcode_job.failedA code-generation job failed.
consignment.receivedA distributor confirmed receipt of a consignment.
consignment.recalledA consignment was recalled.

Delivery format

Each delivery is a POST with a JSON body and these headers:

  • X-AuthOrigin-Event — the event type
  • X-AuthOrigin-Event-Id — a stable id for the event (use it to deduplicate)
  • X-AuthOrigin-Delivery — a unique id for this delivery attempt
  • X-AuthOrigin-Signature — the signature (see below)
Example payload
{
  "id": "evt_...",
  "type": "scan.recorded",
  "createdAt": "2026-08-22T12:34:56Z",
  "data": {
    "scanId": "scan_...",
    "productUnitId": "unit_...",
    "result": "genuine",
    "country": "US"
  }
}

Verifying signatures

AuthOrigin signs every delivery so you can confirm it's genuine and unmodified. The scheme mirrors the widely-used Stripe convention, so off-the-shelf code works. The header looks like:

X-AuthOrigin-Signature
t=1755865200,v1=5257a869e7ecebeda32affa62cdca3fa...

To verify, compute an HMAC-SHA256 over "{t}.{raw body}"using your endpoint's signing secret, then compare it to v1 in constant time. Reject deliveries whose timestamp is too old to prevent replays.

Node.js
import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const ok = crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(parts.v1),
  );
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return ok && fresh;
}
Sign over the raw body
Compute the signature against the exact bytes you received, before any JSON parsing or re-serialization — re-encoding can change the bytes and break verification.

Retries & failures

Respond with a 2xx status within a few seconds to acknowledge a delivery. Any other response (or a timeout) is treated as a failure and retried with exponential backoff. Endpoints that keep failing are backed off automatically; inspect recent attempts at GET /webhook-endpoints/{id}/deliveries.

NextErrors & rate limits