Developers · Webhooks

Outbound webhooks

HMAC-signed HTTPS callbacks so your systems react the moment a review analysis, report, or recommendation is ready. Endpoints and signing secrets are managed in the signed-in developer portal.

What webhooks are

A webhook is an HTTPS request Biz Review Radar sends to a URL you own when something happens in your account. Instead of polling the REST API, you register an endpoint once and we push a small JSON event to it. Each event is signed so you can verify it came from us, and delivery attempts are recorded so you can inspect failures.

Supported event categories

These are the only event types the platform emits today. Subscribing to an unknown event type is rejected.

analysis.completed
Analyses

A competitor analysis finished successfully for a business.

analysis.failed
Analyses

A competitor analysis failed. It is not retried automatically.

report.generated
Reports

A report is ready for the business (subscription or one-time purchase).

recommendation.created
Recommendations

A new prioritised recommendation was created for a business.

webhook.test
Diagnostics

Sent only when you trigger a test delivery from the developer portal or the test endpoint.

How to create a webhook endpoint

  1. Sign in and open the developer portal (API access is required on your plan).
  2. Add an endpoint URL you control and select the event types you want.
  3. Copy the signing secret shown once at creation. We store only an encrypted copy — it is never displayed again.
  4. Send a test delivery and confirm your server returns a 2xx response.
  5. Rotate the secret at any time from the portal if it is ever exposed.
# Create an endpoint (API key auth)
curl -X POST https://bizreviewradar.com/api/v1/webhooks \
  -H "Authorization: Bearer brr_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/webhooks/brr",
    "event_types": ["analysis.completed", "report.generated"]
  }'

# List endpoints
curl https://bizreviewradar.com/api/v1/webhooks \
  -H "Authorization: Bearer brr_test_YOUR_KEY"

# Inspect recent delivery attempts
curl https://bizreviewradar.com/api/v1/webhooks/wh_123/deliveries \
  -H "Authorization: Bearer brr_test_YOUR_KEY"

HTTPS requirement

Endpoint URLs must use https:// with a valid, publicly trusted certificate. Plain http:// URLs, private or loopback addresses, and non-standard internal hosts are rejected at registration time. If you need to develop locally, use a tunnelling service that terminates TLS on a public hostname.

HMAC-SHA256 signature

Every delivery carries an X-BRR-Signature header of the form sha256=<hex>. The signature is HMAC-SHA256(signing_secret, "{timestamp}.{raw_body}"), where timestamp is the Unix seconds value sent in X-BRR-Timestamp and raw_body is the exact bytes of the request body. Always verify against the raw body before parsing JSON, and compare using a timing-safe function.

import crypto from "node:crypto";

export function verify(rawBody, headers, secret) {
  const signature = headers["x-brr-signature"] || "";
  const timestamp = Number(headers["x-brr-timestamp"]);

  // 1. Reject stale or missing timestamps (replay protection).
  const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - timestamp);
  if (!Number.isFinite(timestamp) || ageSeconds > 300) return false;

  // 2. Recompute the signature over "timestamp.rawBody".
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret)
      .update(timestamp + "." + rawBody)
      .digest("hex");

  // 3. Timing-safe compare.
  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Replay protection and idempotency

The timestamp is part of the signed string, so an attacker cannot reuse an old body with a new timestamp. We recommend rejecting deliveries whose X-BRR-Timestamp is more than five minutes away from your server clock.

Deliveries also include X-BRR-Event-Id and X-BRR-Delivery-Id. Retries reuse the same event ID, so store processed event IDs and treat repeats as duplicates — at-least-once delivery means the same event can legitimately arrive more than once.

Retry behaviour

A delivery succeeds on any 2xx response. Anything else — a non-2xx status, a timeout, or a connection error — is retried on a fixed exponential-style schedule, up to five attempts total. After the last attempt the delivery is marked dead-lettered and stays visible in the portal so you can resend it manually.

1st retry

30 seconds

2nd retry

2 minutes

3rd retry

10 minutes

4th retry

1 hour

5th retry

6 hours

Keep your handler fast: acknowledge with a 2xx immediately and process asynchronously. Slow endpoints are treated as failures once the delivery attempt times out.

Test delivery

Trigger a webhook.test event for any endpoint you own. It is signed exactly like a real event, so it is the right way to validate your verification code end to end. You can also resend a specific past delivery.

# Send a signed test event
curl -X POST https://bizreviewradar.com/api/v1/webhooks/wh_123/test \
  -H "Authorization: Bearer brr_test_YOUR_KEY"

# Resend a previous delivery
curl -X POST https://bizreviewradar.com/api/v1/webhook-deliveries/dlv_789/resend \
  -H "Authorization: Bearer brr_test_YOUR_KEY"

# Rotate the signing secret (new secret returned once)
curl -X POST https://bizreviewradar.com/api/v1/webhooks/wh_123/rotate-secret \
  -H "Authorization: Bearer brr_test_YOUR_KEY"

Example payload

Illustrative values only — the IDs below are dummy data.

POST https://your-app.example.com/webhooks/brr
Content-Type: application/json
X-BRR-Event-Type: analysis.completed
X-BRR-Event-Id: 4821
X-BRR-Delivery-Id: dlv_789
X-BRR-Timestamp: 1785315154
X-BRR-Signature: sha256=9f1c0d...

{
  "version": 1,
  "id": "evt_4821",
  "type": "analysis.completed",
  "created_at": "2026-07-28T09:12:34Z",
  "data": {
    "business_id": "biz_123",
    "analysis_id": "an_456",
    "competitors_analyzed": 9
  }
}

Security best practices

  • Verify the signature on every request before doing any work, and fail closed.
  • Verify against the raw request body — re-serialising JSON changes the bytes.
  • Enforce a timestamp freshness window (five minutes is a good default).
  • Use a timing-safe comparison, never === on the signature string.
  • Store signing secrets in a secret manager; never commit them or log them.
  • Deduplicate on the event ID so retries cannot double-charge or double-post.
  • Return 2xx quickly and queue the work; do not call slow third parties inline.
  • Rotate the secret immediately if a destination or log may have leaked it.
  • Treat payload contents as data about your own account — do not forward it onward without reviewing where it lands.

Current limitations

  • Only the event types listed above exist. There are no review-level or billing events.
  • Delivery is at-least-once and ordering is not guaranteed; use timestamps and event IDs.
  • Five attempts maximum, then dead-lettered. After that, resend manually from the portal.
  • Signing secrets are shown once at creation or rotation and cannot be retrieved later.
  • Webhook access follows your plan's API entitlement and shares your account rate limits.
  • Endpoints are scoped to a single account and its locked business.

Questions or a destination that needs different guarantees? Email sales@bizreviewradar.com.