Features

Webhook delivery

Every notification BrandBanta fires (alerts, scan completions, weekly digests) can be delivered to a webhook. Slack is the most common destination; any generic JSON endpoint works.

What gets delivered

Webhooks fire for every notification kind, gated by your per-channel preferences in Settings → Notifications:

Notification kindWhen it fires
mention_rate_dropYour mention rate fell ≥10% with cross-platform corroboration
competitor_surgeA competitor's mention rate rose ≥10% on ≥2 platforms
sentiment_swingSentiment mix shifted toward negative ≥15 percentage points
new_competitor_detectedAn unseen brand is now appearing alongside yours
tier1_source_gapA high-DR domain cites competitors but not you for ≥5 queries
weekly_digestWeekly snapshot narrative delivered
scan_completedA specific scan you've subscribed to finished

You enable each kind independently per channel (in-app / email / webhook).

Setting up a Slack webhook

  1. In Slack, go to your workspace → Apps → Incoming Webhooks
  2. Add a new webhook to the channel you want notifications in
  3. Copy the webhook URL (looks like https://hooks.slack.com/services/T0.../B0.../...)
  4. In BrandBanta, go to Settings → Notifications → Webhook delivery
  5. Paste the URL, label it "Slack #brand-tracking" (or whatever fits), and save
  6. Click Test webhook to send a sample payload

You should see a card appear in your Slack channel within a few seconds.

Setting up a generic JSON endpoint

If you want to pipe notifications into your own backend, Zapier, n8n, or any data warehouse:

  1. Build an endpoint that accepts POST application/json
  2. In Settings → Notifications → Webhook delivery, paste the URL with Format: generic JSON selected
  3. Save and test

Your endpoint receives the payload schema below.

Payload schema

{
	"notification": {
		"id": "ckq4...",
		"type": "mention_rate_drop",
		"organizationId": "org_abc...",
		"createdAt": "2026-05-23T13:42:00.000Z",
		"data": {
			"headline": "Mention rate dropped 14% this week",
			"title": "Visibility regression detected",
			"message": "Your mention rate fell from 27% to 23% across 92 scans. Drop appears on ChatGPT and Perplexity; Claude and Gemini are flat.",
			"details": {
				"previousMentionRate": 0.27,
				"currentMentionRate": 0.23,
				"platformBreakdown": {
					"openai": -0.06,
					"anthropic": 0.01,
					"googleai": -0.0,
					"perplexity": -0.07
				}
			}
		},
		"link": "https://app.brandbanta.com/<org-handle>/insights"
	},
	"organization": {
		"id": "org_abc...",
		"name": "Get Vocal",
		"slug": "get-vocal-abc..."
	},
	"deliveredAt": "2026-05-23T13:42:01.247Z"
}

data.headline / data.title / data.message are present on every payload. data.details varies by notification kind — see the per-kind schema in Webhook schema reference for the full shape catalogue.

Slack-specific formatting

When the URL is a Slack incoming webhook (hooks.slack.com), we wrap the payload in Slack Block Kit so it renders as a card with:

  • Title (clickable link to the dashboard)
  • Headline in bold
  • Message in regular text
  • A footer with the workspace name + timestamp

You don't need to change anything on Slack's side beyond creating the incoming webhook.

Retries and delivery guarantees

  • Webhooks deliver with at-least-once semantics
  • HTTP 2xx response is treated as success
  • Non-2xx responses are retried with exponential backoff (3 attempts over ~10 minutes)
  • If all retries fail, the delivery is dropped — the in-app notification still appears, so you don't lose visibility into what fired

Make your endpoint idempotent by using notification.id as a dedup key — same notification.id = same event, even if delivered twice.

Verifying webhook authenticity

We sign every payload with HMAC-SHA256 using the webhook secret displayed when you save the webhook config. The signature is in the X-BrandBanta-Signature header.

To verify in your endpoint (Node.js example):

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

function verifyBrandBantaWebhook(
	rawBody: string,
	signatureHeader: string,
	secret: string,
): boolean {
	const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
	const expectedBuf = Buffer.from(expected, "hex");
	const receivedBuf = Buffer.from(signatureHeader, "hex");
	if (expectedBuf.length !== receivedBuf.length) return false;
	return timingSafeEqual(expectedBuf, receivedBuf);
}

Always verify on production endpoints to prevent forged payloads.

Rate limits

We don't impose webhook-side rate limits. The Slack-side limit is ~1 message/second per webhook URL; we batch and queue if you hit it.

For high-frequency notifications (e.g. scan-completed on a bulk of 25 scans), the queue ensures Slack doesn't 429 your channel.

See also

On this page