ReachBellDocs

Analytics

Every click, open, bounce, complaint, and unsubscribe is recorded as an event, tagged with its channel, campaign, platform, and UTM parameters. The dashboard rolls those events up into six reports. This page explains what each one shows and how to read it.

All reports are scoped to a single project and live under one path:

/analytics/project/:projectId/<report>

They're dashboard reports — you view them signed in, not through your API key. Most accept a days window (1–90, default 30); cohort retention takes weeks (1–12, default 8). Everything is aggregated in the database, so the windows are bounded to keep queries fast.

Engagement events are retained for 90 days. The reports look back that far; for longer-term trends, export what you need before it ages out.

Overview

The headline numbers. GET …/overview returns five counters for the current window — clicks, opens, bounces, complaints, unsubscribes — alongside the previous window of equal length and the percentage change between them.

{
  "days": 30,
  "current":  { "clicks": 4120, "opens": 9300, "bounces": 88, "complaints": 3, "unsubscribes": 41 },
  "previous": { "clicks": 3870, "opens": 8800, "bounces": 95, "complaints": 5, "unsubscribes": 52 },
  "deltaPct": { "clicks": 6.5, "opens": 5.7, "bounces": -7.4, "complaints": -40 }
}

The deltas are the point: a number on its own says little, but "complaints down 40% since last month" tells you whether a change worked. These totals span every channel.

Click heatmap

GET …/heatmap returns a 7×24 grid of click counts — day of week by hour of day — plus the busiest cell as max for colour scaling.

{ "days": 30, "channel": "all", "grid": [[0, 0, …], [12, 8, …], …], "max": 142 }

Read it to find when your audience actually engages, then schedule sends into the hot cells. Pass channel=push or channel=email to see whether your push audience and your email audience wake up at different times — they usually do.

UTM breakdown

GET …/utm groups clicks by the source, medium, and campaign UTM tags captured on each link, sorted by clicks (top 200 rows).

{
  "days": 30,
  "rows": [
    { "source": "email",    "medium": "campaign", "campaign": "promo-jan", "clicks": 142 },
    { "source": "(direct)", "medium": "(none)",   "campaign": "(unnamed)", "clicks": 87 }
  ]
}

Links without UTM tags fall into (direct) / (none) / (unnamed), so a big direct bucket is usually a sign you forgot to tag something. Tag your links consistently and this report becomes your channel-ROI ledger — see UTM tracking for the tagging conventions.

Email report

GET …/email is the email-only deep dive. It returns totals for the five event types, the same counts broken down by recipient mailbox provider, and a daily trend.

{
  "days": 30,
  "totals":  { "click": 152, "open": 203, "bounce": 12, "complaint": 1, "unsubscribe": 0 },
  "byDomain": {
    "gmail.com": { "click": 85, "open": 110, "bounce": 2, "complaint": 0, "unsubscribe": 0 },
    "yahoo.com": { "click": 38, "open": 55,  "bounce": 5, "complaint": 1, "unsubscribe": 0 }
  },
  "trend": [ { "day": "2026-06-20T00:00:00.000Z", "type": "open", "count": 22 } ]
}

The per-domain split is the one to watch. Deliverability problems are rarely uniform — bounces and complaints concentrating at one provider (a spike at gmail.com, say) points at a reputation issue with that mailbox, not your list as a whole.

Cohort retention

GET …/cohort?weeks=8 groups subscribers by the ISO week they signed up, then tracks how many of each cohort clicked at least once in each following week.

{
  "weeks": 8,
  "cohortSize": [150, 132, 98, 87, 65, 54, 42, 28],
  "grid": [
    [150, 130, 105, 92, 71, 58, 45, 31],
    [132, 110, 88, 75, 58, 47, 35, null],
    [98,  76,  61, 48, 36, 25, null, null]
  ]
}

cohortSize[w] is how many subscribers joined in week w; grid[cohort][offset] is how many of them were still clicking offset weeks later. null marks weeks that haven't happened yet for younger cohorts. Read each row left to right to watch a cohort decay — a row that holds its numbers means your onboarding is sticking; a cliff in week two means it isn't.

Cross-channel attribution

GET …/attribution answers "which channel is actually driving engagement?" It's last-touch: each click is credited to the channel it happened on, then grouped per channel with a unique-subscriber count.

{
  "days": 30,
  "channels": [
    { "channel": "email", "clicks": 287, "uniqueSubscribers": 142 },
    { "channel": "push",  "clicks": 165, "uniqueSubscribers": 89 }
  ]
}

The gap between clicks and uniqueSubscribers is your repeat-engagement signal: many clicks from few people means a small, loyal core; clicks ≈ subscribers means broad but shallow reach. Compare channels to see where to invest.

A note on the numbers

  • Clicks and opens are tracked, the latter via a pixel and the former via wrapped links — so opens undercount anyone whose mail client blocks images, and overcount where a privacy proxy pre-fetches the pixel. Treat clicks as the harder signal.
  • Bounces, complaints, and unsubscribes come from the email provider and from the preference center, so they reflect real delivery and real opt-outs.

See also