Skip to main content

Reading the SMS click-through rollup (Insights → SMS click-through)

The link tracking analytics guide closes with a one-line pointer to this page. This guide is where that pointer lands: Insights → SMS click-through (/insights/sms-ctr) rolls every tracked short link up into a per-campaign (or per-queue) click-through rate — the channel-level answer the per-link view can’t give. If you need the per-link drill-down, go back to Link tracking; if you need the reconciliation math, mint mechanics, or email-parity reading, the SMS click-through parity guide covers those in depth. This page is the operator walkthrough of the rollup itself.

1. The tile map — what the page is made of

Open the dashboard sidebar’s Insights entry; the SMS click-through tile on the hub grid routes into /insights/sms-ctr. One card holds the whole page, top to bottom:
  1. Header — the title plus two SegmentedControls: Group by (Campaign / Queue) and the time window (24h / 7d / 30d).
  2. Totals strip — five KPI tiles aggregating every bucket in the window (Section 2).
  3. Bucket table — one row per campaign or queue, with the per-bucket counters and rates (Section 2).
  4. Pagination bar — appears only when the window holds more than 25 buckets.
The page honors the standard all-states contract: skeleton placeholders while loading, a retryable error lane (“Couldn’t load the SMS click-through report”) on a failed fetch, and a first-run empty state (“No tracked click-throughs yet”) that links through to the Link tracking page — it never renders a healthy-looking blank dashboard.

2. KPI tiles and the bucket table

The totals strip carries five tiles, recomputed on every window change:
  • Sends (window) — outbound SMS in the window, whether or not they carried a tracked link.
  • Sends with tracked links — the trackable slice: sends that carried at least one tracked short link.
  • Sends clicked — sends whose tracked links recorded at least one click.
  • Overall CTR — sends clicked ÷ sends with tracked links, formatted as a one-decimal percentage.
  • Verified CTR — verified (bot- and preview-filtered) clicks ÷ sends with tracked links. This is the number that compares against the email channel’s Clicked metric.
Below the strip, the bucket table breaks the same counters down per campaign (or per queue, on the queue grouping). Its columns, left to right: Campaign/Queue, Sends, Tracked, Clicked, Clicks (raw), Verified, CTR, Verified CTR. A bucket with no tracked sends shows in both rate columns rather than a misleading zero. Rows sort by tracked sends descending, so buckets where click-through was measurable rank first; the ordering is fixed and the column headers don’t re-sort. Campaign, variant, and link-level follow-up all start from this table (Section 4).

3. Filters and window semantics

Two controls scope every number on the page:
  • Group by: Campaign / QueueCampaign buckets on the campaign_id stamped at send time. Queue buckets on the messaging-service id recorded at send time, falling back to the provider tag when no queue resolves — exactly the precedence the API’s group parameter uses.
  • Window: 24h / 7d / 30d — the trailing aggregation window, defaulting to 7 days. There is deliberately no 90d option here; the schema-backed enum is the three values above, unlike the links page’s wider 1–90 day range.
Switching either control restarts the table at page 1 — the old page index would otherwise slice rows that no longer exist. Pagination runs 25 buckets per page, and the totals strip always aggregates all buckets in the window, not just the page on screen. Numbers refetch every 60 seconds to stay in lockstep with the server-side cache, so a fresh send shows up on the strip within about a minute without a manual reload. Sends where the scoped key never resolved — no campaign_id on the campaign grouping, no resolvable queue on the queue grouping — collect in the unattributed bucket. That is a catch-all, not an error grouping; the fix lives at send time (stamp the key on the payload), and the parity guide’s unattributed-bucket section walks through it.

4. Actioning a CTR movement

Reading the number is the start; the page exists to tell you where to go next:
  • CTR dipped on one campaign — identify the bucket, then open Insights → Link tracking and scope it with campaign_id for the per-link breakdown: which URL dragged the rate, and whether raw vs verified says it’s real engagement loss or preview-fetch noise.
  • Comparing variants — when the campaign ran A/B variants, the campaign A/B testing guide is the read surface for variant-level splits; the rollup here is the campaign-level backstop that tells you whether the whole campaign regressed or one variant did.
  • Queue grouping disagrees with campaign grouping — a queue bucket with low CTR and healthy campaign buckets usually means a routing change moved sends into a queue whose recipients behave differently; check the smart-send fallback chains guide if fallback swapping is in play.
  • Tracked share is the problem, not CTR — if untracked (sends − tracked) is the large number, no creative change moves CTR; shorten more of what you send. The parity guide covers the mint switches in precedence order.
  • Export for a spreadsheet or warehouse — the rollup numbers pull straight from the API (Section 5), which is the reliable way to snapshot tiles into a CSV pipeline. For the per-link CSV, the Export button on the Link tracking page writes a windowed file with summary, top-links, and timeline sections.
  • Alert instead of watch — wire a threshold or anomaly rule so a degrading CTR pages you rather than waits for a manual read; the parity guide’s alerting section maps the two rule surfaces.

5. API parity

Every tile on the page maps to one endpoint — GET /api/v1/analytics/sms-click-through — with group and window taking exactly the control values (campaign/queue, 24h/7d/30d) plus page and pageSize (default 25, max 100). The response returns the paged rows, the window-wide totals object, and pagination under the standard { data, meta } envelope. Rate fields come back null when a bucket had no tracked sends — never a fabricated zero.
cURL
All four workspace roles (owner, admin, developer, viewer) can read it, and the response is cached 60 seconds server-side. For request and response schemas, see the SMS click-through API reference.

6. Common misreads

  • Clicks vs link detection. The first is engagement; the second is coverage. Clicks counts taps on links that were minted; Sends − Tracked is the volume that structurally cannot click. A high CTR on a small Tracked base is correct, not inflated — read the tracked share before the rate.
  • CTR vs delivered rate. The denominator is tracked sends, not delivered messages — SMS has no bounce equivalent, so undelivered sends stay in the denominator and depress CTR. If CTR moved, check delivery health first (usage and delivery anomaly alert rules) before treating it as an engagement problem.
  • Raw vs verified CTR. Raw counts bot and link-preview fetches, which spike on send day; verified applies the same quality filter the per-link analytics use. Comparing raw SMS CTR to email’s Clicked metric is apples-to-oranges — email’s number is already filtered.
  • is not zero. A bucket with no tracked sends has an undefined rate, and both the page and the API return null. Treating it as a 0.0% regression invents a problem.
  • unattributed is not an error. It’s the catch-all bucket for sends with no resolvable campaign or queue key; its size is an attribution hygiene signal, not a pipeline fault.
  • The page has no 90-day window. The enum is 24h / 7d / 30d by design. For quarter-scale trend lines, pull the API on a schedule or use the Links insights endpoint’s wider range.

Troubleshooting

  • The totals strip disagrees with the visible rows. It aggregates every bucket in the window, including pages beyond the one on screen — that is the correctness guarantee, not a bug.
  • A campaign I sent today isn’t a row yet. Buckets appear once the rollup resolves attribution; the 60-second cache means a fresh send lands within about a minute. If it stays absent past that, the send carried no tracked link — check the mint switches.
  • The empty state shows but I sent traffic this week. The window may be narrower than your send cadence (a 24h window reads empty between weekly waves). Widen to 7d, and confirm at least one send carried a tracked link — the empty state only clears on sends the pipeline could track.

See also