DemoBitesDemoBites
AnalyticsDestinations
Grow+

Analytics Destinations

Destinations streams every Update Center, Demo Center, and subscriber interaction to a webhook you control, near real time. Pipe DemoBites engagement into your warehouse, CDP, CRM, or alerting, no polling, no export jobs.


How Destinations works

As visitors browse your Update Center and Demo Center, watch videos, click CTAs, search, and subscribe, DemoBites records those interactions. Destinations forwards them to your own HTTPS endpoints as signed JSON batches, typically within about a minute of the interaction. You choose exactly which events each endpoint receives from a 26-event catalog.

Where
Analytics → Destinations
Who
Workspace admins
Plan
Grow and above

You’ll find it in Analytics, the Destinations control sits on its own, to the right of the view switcher. Only workspace admins can configure endpoints.


Adding an endpoint

Each workspace can have up to 3 endpoints, useful for separating, say, a production warehouse pipeline from a staging consumer or a Slack-alerting relay.

1
Name it and point it at an HTTPS URL

Give the endpoint a name you'll recognize in the delivery log, and enter the URL that will receive deliveries. HTTPS is required.

2
Store the signing secret

A signing secret (dbw_…) is shown once, at creation. Store it in your receiver's configuration, it's what you'll verify signatures with. You can rotate it later at any time.

3
Pick the events

Select which of the 26 events this endpoint should receive. You can change the selection whenever you like.

4
Send a test event

Use the test event button to fire a sample delivery and confirm your receiver accepts and verifies it before real traffic flows.


Choosing events

Subscription is per event, not all-or-nothing. The catalog holds 26 events in four groups, and the in-product picker documents when each one fires:

Update Center14 events

Page views, video engagement, CTAs, search, filters, calendar, and subscribe attempts.

Demo Center5 events

Demo Center views, demo playback milestones, and CTA clicks.

Subscribers6 events

The full subscriber lifecycle, from confirmed signup to bounce and suppression.

AI crawlers1 event

Visits from AI crawlers fetching your public pages.


Event reference

The full catalog, every event name and when it fires.

Update Center (14 events)

EventFires when
update_center_viewedA visitor opens your Update Center home page.
latest_page_viewedA visitor opens the Latest page.
release_page_viewedA visitor opens a release page.
update_page_viewedA visitor opens an individual update page.
update_video_startedA visitor starts playing a video on an Update Center page.
update_video_progressFires once at each of 25%, 50%, and 75% of playback.
update_video_completedA visitor watches a video to the end.
cta_clickedA visitor clicks a call-to-action link on an Update Center page. Includes the destination host.
search_performedA visitor runs a search. Includes the query and the result count — zero-result searches included.
search_result_clickedA visitor clicks a search result. Includes the query, the result, and its rank.
filter_appliedA visitor applies a filter to the updates feed.
chapter_clickedA visitor jumps to a chapter inside an update video.
calendar_interactedA visitor interacts with the release calendar.
subscribe_form_submittedA visitor submits the subscribe form. This is the attempt — the confirmed signup arrives as subscriber_created.

Demo Center (5 events)

EventFires when
demo_center_viewedA visitor opens your Demo Center page.
demo_video_startedA visitor starts playing a demo video.
demo_video_progressFires once at each of 25%, 50%, and 75% of demo playback.
demo_video_completedA visitor watches a demo video to the end.
demo_cta_clickedA visitor clicks a Demo Center call-to-action, including Play all and sign-up.

Subscribers (6 events)

EventFires when
subscriber_createdA subscriber confirms their email — double opt-in complete. Includes consent details.
subscriber_pausedA subscriber pauses their update emails.
subscriber_resumedA paused subscriber resumes their update emails.
subscriber_unsubscribedA subscriber unsubscribes — via their manage link or removed by a workspace owner.
subscriber_bouncedAn email to a subscriber permanently bounces; delivery to that address stops.
subscriber_suppressedA subscriber is suppressed after a spam complaint or a provider block; delivery to that address stops.

AI crawlers (1 event)

EventFires when
crawler_visitAn AI crawler — GPTBot, ClaudeBot, PerplexityBot, and others — fetches one of your public pages. Includes which crawler.

Subscriber events carry identity:they include the subscriber’s email address and their consent record (consent time and source surface), so treat endpoints receiving them as processors of personal data.


Verifying signatures

Every delivery is signed with your endpoint’s secret so your receiver can prove it came from DemoBites. The signature travels in one header:

X-DemoBites-Signature: t=<unix>,v1=<hex>

t is the Unix timestamp of the delivery, andv1 is HMAC-SHA256(secret, t + “.” + rawBody) as hex. Compute the same HMAC over the raw request body exactly as received, then compare with a timing-safe comparison:

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

// header: the X-DemoBites-Signature value, "t=<unix>,v1=<hex>"
// rawBody: the request body EXACTLY as received (don't re-serialize)
function verifySignature(header, rawBody, secret) {
  const parts = Object.fromEntries(
    header.split(",").map((pair) => pair.split("="))
  );
  const expected = createHmac("sha256", secret)
    .update(parts.t + "." + rawBody)
    .digest("hex");
  return (
    expected.length === parts.v1.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
  );
}

Verify against the raw body bytes, parsing and re-serializing the JSON first will change the string and break the signature. As an extra replay guard, you can reject deliveries whose t is older than your tolerance.


Payload envelope

Deliveries are HTTPS POSTs of a JSON object with a single events array, batching up to 50 events per delivery. Every event carries the same envelope:

FieldMeaning
event_idStable unique id for this event, the key to dedup on if a retry delivers it twice.
eventThe event name from the catalog above.
occurred_atWhen the interaction happened.
schema_versionEnvelope version, currently 1.
workspace{ id, slug }. The numeric id is the stable key, the slug follows renames.
sourceWhich surface produced the event.
pageThe page the interaction happened on.
session_idAnonymous session identifier, lets you group one visitor's events.
context{ referrer_host, utm, country }, acquisition context for the session.
propertiesEvent-specific details, e.g. playback progress, search query, CTA destination host.

A realistic two-event batch:

JSON
{
  "events": [
    {
      "event_id": "evt_01j9wxr8q4nd6t",
      "event": "update_video_progress",
      "occurred_at": "2026-08-04T14:07:31Z",
      "schema_version": 1,
      "workspace": { "id": 4821, "slug": "acme" },
      "source": "update_center",
      "page": "/updates/summer-release",
      "session_id": "b3c9e2a4-5f7d-4c1e-9a2b-8d4f6e1c0a73",
      "context": {
        "referrer_host": "news.ycombinator.com",
        "utm": { "utm_source": "newsletter" },
        "country": "DE"
      },
      "properties": { "progress": 50 }
    },
    {
      "event_id": "evt_01j9wxz2k7rtq8",
      "event": "subscriber_created",
      "occurred_at": "2026-08-04T14:08:02Z",
      "schema_version": 1,
      "workspace": { "id": 4821, "slug": "acme" },
      "source": "update_center",
      "page": "/updates",
      "session_id": "b3c9e2a4-5f7d-4c1e-9a2b-8d4f6e1c0a73",
      "context": {
        "referrer_host": null,
        "utm": null,
        "country": "DE"
      },
      "properties": {
        "email": "dana@example.com",
        "consent": {
          "granted_at": "2026-08-04T14:08:02Z",
          "source": "update_center"
        }
      }
    }
  ]
}

Illustrative values. The exact properties each event carries are documented alongside it in the event picker.


Delivery semantics

Respond with a 2xx status promptly, anything else (or a timeout) counts as a failed delivery and enters the retry schedule.

BehaviorDetails
LatencyNear real time, events typically arrive about a minute after the interaction.
BatchingUp to 50 events per delivery, in one events array.
DedupRetries can deliver an event more than once. event_id is stable, dedup on it consumer-side.
RetriesFailed deliveries are retried automatically, 6 attempts spread over roughly 11 hours.
Auto-pauseAn endpoint that keeps failing is paused automatically, so a dead receiver doesn't retry forever.
Delivery logEvery delivery is logged, and failed deliveries can be replayed from the log.
Test eventFire a sample delivery on demand to verify your receiver end to end.
BackfillNone. Streaming starts when the endpoint connects, historical events are not replayed.

Key your data on the workspace numeric id. The workspace slug can change when a workspace is renamed, the numeric workspace.id stays stable across renames.


Availability

Destinations is available on Grow and above (Grow, Global, and Custom), and configuration is limited to workspace admins. The dashboards themselves are part of Analytics on Launch and above.

Billing & plans