Purpose
Turn raw Beehiiv publication data into a report a non-technical stakeholder (a sponsor, a
client, a newsletter owner) can read in under a minute and still trust the numbers in.
This skill generalizes a report built for a real newsletter sponsorship client, stripped of
any client-specific branding or copy, so the same structure can be reused for any Beehiiv
publication or wired into an MCP server that answers questions like "how did last month's
issues perform."
When to use this skill
Use it when asked to build, extend, or automate a newsletter/sponsor performance report
backed by Beehiiv, or when exposing Beehiiv metrics through an MCP tool, API, or chatbot and
needing the correct formulas and section structure rather than reinventing them.
Prerequisites
- A Beehiiv publication ID and an API key (Bearer token) with read access to that publication.
- Base URL:
https://api.beehiiv.com/v2/publications/{pub_id}/...
- All Beehiiv field mappings, formulas, and pagination details live in
references/beehiiv-api-reference.md. Read it before implementing any of the sections below;
several of Beehiiv's own fields are misleading (see Pitfalls) and getting them wrong produces
a report that contradicts what the client sees on their own Beehiiv dashboard.
Report anatomy
A complete report has nine sections. Build them in roughly this order, since later sections
depend on data assembled for earlier ones.
- Hero stats: total growth since launch, current audience size, average open rate over a
recent window (e.g. trailing several months). Three numbers, no chart, at the very top.
- Website / lead magnet links: a short list of outbound links relevant to the newsletter
itself (its homepage) and any sponsor/lead-magnet destination being tracked (e.g. a landing
page a sponsor cares about). Plain outbound links, not chart data.
- Audience growth chart: one time series of subscriber count, spanning the newsletter's
full history to date.
- Open rate analysis: a per-issue open-rate bar chart plus a short structured narrative
(a question, an answer/verdict, supporting points, one forward-looking opportunity).
- Click rate analysis: the same narrative structure as open rate analysis, charting clicks
or click rate per issue instead.
- Audience insights: an engagement segmentation (how loyal is the audience, by lifetime
open rate) and a list-health breakdown (active / inactive / invalid).
- Month-over-month tabs: one tab per calendar month, each with a synopsis (headline,
highlights, opportunities), that month's rollup stats, and every issue published that month.
- Engaged-reader export: for reports tracking clicks to a specific sponsor/partner link,
a table of who clicked, exportable as CSV, with a searchable full-list view and a breakdown
by company/industry category.
- Per-issue KPI cards: nested inside each month tab: recipients, delivered, open rate,
click rate, unsubscribes, spam reports, per individual issue.
Full data shapes and exact chart/card content for each section are in
references/report-sections.md.
Workflow to build one
- Confirm credentials and the publication ID; verify a single
GET /posts?limit=1 call
succeeds before building anything else.
- Fetch confirmed posts with stats (
expand[]=stats, status=confirmed, ordered by publish
date ascending). This one call powers sections 4, 5, 7, and 9.
- Compute per-issue fields and per-month rollups using the formulas in
references/beehiiv-api-reference.md (do not trust Beehiiv's click_rate field at face
value; see Pitfalls).
- Build the audience growth series (section 3). Decide the growth mode up front:
per-issue
(derived from posts) or subscriber-timeline (a hand-curated periodic snapshot). See
references/beehiiv-api-reference.md for when each applies.
- Fetch the full subscriber list with
expand[]=stats, cursor-paginating to completion, and
compute the engagement segments and list-health split for section 6.
- Write the narrative content for sections 4, 5, and 7 (the only genuinely generated prose in
the report). Keep it short, specific to the actual numbers, and free of hedging.
- If the report needs section 8, treat it as a periodic manual/semi-automated step, not a live
API call. See Pitfalls.
- Render nothing for any section whose backing data is empty or absent (no polls, no audience
data yet, no engaged-reader export configured) rather than showing an empty or placeholder
state. This lets sections be added to a report incrementally as data becomes available.
Automating a refresh
Once a report exists, keep it current on a schedule (e.g. weekly) rather than hand-updating it.
The refresh should treat "recompute deterministic numbers" and "regenerate narrative" as two
different jobs with two different reliability bars. See references/automation-pipeline.md for
the full pattern, including a multi-tenant registry design for running the same pipeline across
several publications/clients at once.
Pitfalls
These are the mistakes most likely to produce a report that looks fine but is wrong:
- Beehiiv's
click_rate field is click-to-open (clicks / opens), not clicks-per-delivered.
If the report is meant to show "what fraction of the send resulted in a click," compute
unique_clicks / delivered yourself. Consider displaying whichever of the two figures is
larger, since that is usually the number the client already sees emphasized on their own
Beehiiv dashboard, and a report showing a smaller number reads as wrong even when it is
arguably more correct.
- The poll
poll_responses expand on a poll's GET endpoint is capped at 10 responses and
cannot paginate. Any poll with more than 10 responses will silently under-report through
that path. Get accurate per-choice counts by cursor-paginating
/polls/{id}/responses and tallying poll_choice_id client-side instead.
- Beehiiv exposes no subscriber location data. Do not build or fake a geographic view; it
does not exist in the API and any resemblance to one will be pure invention.
- Compute engagement segments only over subscribers who have received enough issues to have
a meaningful lifetime open rate (e.g. at least 3). Otherwise a batch of freshly imported
subscribers who have only seen one issue distorts the segmentation, since anyone who has not
opened yet reads identically to someone who never will.
- A "who clicked this specific sponsor link" export is generally not a clean, documented,
single API call. Beehiiv's own dashboard supports building a subscriber segment from click
behavior and exporting it as CSV; the practical path is to pull that CSV periodically and
enrich it yourself (e.g. classify each subscriber's email domain into a company/industry
category with a lightweight heuristic or an LLM pass), rather than trying to reproduce it as
a live query inside an automated refresh.
- A publication's early history may predate structured per-issue tracking or was migrated
from a different platform. For that period, a hand-curated subscriber-count timeline (which
can rise or fall) is more honest than deriving growth from per-issue recipient counts, which
is roughly monotonic by construction and will paper over real churn.
- Never regenerate all narrative sections wholesale on every refresh. Update only the month
or analysis affected by new data, and preserve the tone, length, and untouched prose from
prior periods; wholesale regeneration produces visible tonal drift and makes diffs impossible
to review.
For MCP tool / integration builders
If exposing this as MCP tools or an API instead of (or in addition to) a rendered report page,
map each section to its own tool so a client can compose them conversationally or a UI can call
them independently:
get_publication_summary → hero stats (section 1)
get_audience_growth → growth series (section 3)
get_rate_analysis (parameterized by open or click) → sections 4 and 5
get_audience_segments → engagement segmentation and list health (section 6)
get_monthly_report (parameterized by month) → that month's synopsis, rollups, and issues
(section 7)
export_engaged_subscribers → section 8, backed by the periodic CSV/enrichment pipeline, not
a live Beehiiv call
get_issue_kpis (parameterized by post ID or date range) → section 9
Cache subscriber-list pagination results where possible; a full engagement-segment computation
requires walking the entire subscriber list, which can be thousands of paginated records, on
every call otherwise.
What good looks like
A report a non-technical stakeholder can read in under a minute and still trust the numbers in — click rates that match what the client sees on their own Beehiiv dashboard, poll tallies from full pagination rather than the capped expand, engagement segments that exclude subscribers too new to have a meaningful lifetime open rate, and narrative that changes only where the numbers did. The failure modes: a click rate that contradicts the client's dashboard, a silently under-reported poll, an invented geographic view, or wholesale-regenerated prose drifting in tone between refreshes.
1---2name: beehiiv-newsletter-report3description: Use this skill when building a sponsor or client performance report on top of a Beehiiv publication, or when designing a Beehiiv MCP tool or integration that needs to expose the same metrics. It covers audience growth, open and click rate analysis (including the click-to-open trap in Beehiiv's own API), audience segmentation, month-over-month synopsis, an engaged-reader export, per-issue KPIs, and a pattern for keeping the report refreshed automatically without the narrative drifting.4---56## Purpose78Turn raw Beehiiv publication data into a report a non-technical stakeholder (a sponsor, a9client, a newsletter owner) can read in under a minute and still trust the numbers in.10This skill generalizes a report built for a real newsletter sponsorship client, stripped of11any client-specific branding or copy, so the same structure can be reused for any Beehiiv12publication or wired into an MCP server that answers questions like "how did last month's13issues perform."1415## When to use this skill1617Use it when asked to build, extend, or automate a newsletter/sponsor performance report18backed by Beehiiv, or when exposing Beehiiv metrics through an MCP tool, API, or chatbot and19needing the correct formulas and section structure rather than reinventing them.2021## Prerequisites2223- A Beehiiv publication ID and an API key (Bearer token) with read access to that publication.24- Base URL: `https://api.beehiiv.com/v2/publications/{pub_id}/...`25- All Beehiiv field mappings, formulas, and pagination details live in26 `references/beehiiv-api-reference.md`. Read it before implementing any of the sections below;27 several of Beehiiv's own fields are misleading (see Pitfalls) and getting them wrong produces28 a report that contradicts what the client sees on their own Beehiiv dashboard.2930## Report anatomy3132A complete report has nine sections. Build them in roughly this order, since later sections33depend on data assembled for earlier ones.34351. **Hero stats**: total growth since launch, current audience size, average open rate over a36 recent window (e.g. trailing several months). Three numbers, no chart, at the very top.372. **Website / lead magnet links**: a short list of outbound links relevant to the newsletter38 itself (its homepage) and any sponsor/lead-magnet destination being tracked (e.g. a landing39 page a sponsor cares about). Plain outbound links, not chart data.403. **Audience growth chart**: one time series of subscriber count, spanning the newsletter's41 full history to date.424. **Open rate analysis**: a per-issue open-rate bar chart plus a short structured narrative43 (a question, an answer/verdict, supporting points, one forward-looking opportunity).445. **Click rate analysis**: the same narrative structure as open rate analysis, charting clicks45 or click rate per issue instead.466. **Audience insights**: an engagement segmentation (how loyal is the audience, by lifetime47 open rate) and a list-health breakdown (active / inactive / invalid).487. **Month-over-month tabs**: one tab per calendar month, each with a synopsis (headline,49 highlights, opportunities), that month's rollup stats, and every issue published that month.508. **Engaged-reader export**: for reports tracking clicks to a specific sponsor/partner link,51 a table of who clicked, exportable as CSV, with a searchable full-list view and a breakdown52 by company/industry category.539. **Per-issue KPI cards**: nested inside each month tab: recipients, delivered, open rate,54 click rate, unsubscribes, spam reports, per individual issue.5556Full data shapes and exact chart/card content for each section are in57`references/report-sections.md`.5859## Workflow to build one60611. Confirm credentials and the publication ID; verify a single `GET /posts?limit=1` call62 succeeds before building anything else.632. Fetch confirmed posts with stats (`expand[]=stats`, `status=confirmed`, ordered by publish64 date ascending). This one call powers sections 4, 5, 7, and 9.653. Compute per-issue fields and per-month rollups using the formulas in66 `references/beehiiv-api-reference.md` (do not trust Beehiiv's `click_rate` field at face67 value; see Pitfalls).684. Build the audience growth series (section 3). Decide the growth mode up front: `per-issue`69 (derived from posts) or `subscriber-timeline` (a hand-curated periodic snapshot). See70 `references/beehiiv-api-reference.md` for when each applies.715. Fetch the full subscriber list with `expand[]=stats`, cursor-paginating to completion, and72 compute the engagement segments and list-health split for section 6.736. Write the narrative content for sections 4, 5, and 7 (the only genuinely generated prose in74 the report). Keep it short, specific to the actual numbers, and free of hedging.757. If the report needs section 8, treat it as a periodic manual/semi-automated step, not a live76 API call. See Pitfalls.778. Render nothing for any section whose backing data is empty or absent (no polls, no audience78 data yet, no engaged-reader export configured) rather than showing an empty or placeholder79 state. This lets sections be added to a report incrementally as data becomes available.8081## Automating a refresh8283Once a report exists, keep it current on a schedule (e.g. weekly) rather than hand-updating it.84The refresh should treat "recompute deterministic numbers" and "regenerate narrative" as two85different jobs with two different reliability bars. See `references/automation-pipeline.md` for86the full pattern, including a multi-tenant registry design for running the same pipeline across87several publications/clients at once.8889## Pitfalls9091These are the mistakes most likely to produce a report that looks fine but is wrong:9293- **Beehiiv's `click_rate` field is click-to-open (clicks / opens), not clicks-per-delivered.**94 If the report is meant to show "what fraction of the send resulted in a click," compute95 `unique_clicks / delivered` yourself. Consider displaying whichever of the two figures is96 larger, since that is usually the number the client already sees emphasized on their own97 Beehiiv dashboard, and a report showing a smaller number reads as wrong even when it is98 arguably more correct.99- **The poll `poll_responses` expand on a poll's GET endpoint is capped at 10 responses and100 cannot paginate.** Any poll with more than 10 responses will silently under-report through101 that path. Get accurate per-choice counts by cursor-paginating102 `/polls/{id}/responses` and tallying `poll_choice_id` client-side instead.103- **Beehiiv exposes no subscriber location data.** Do not build or fake a geographic view; it104 does not exist in the API and any resemblance to one will be pure invention.105- **Compute engagement segments only over subscribers who have received enough issues to have106 a meaningful lifetime open rate** (e.g. at least 3). Otherwise a batch of freshly imported107 subscribers who have only seen one issue distorts the segmentation, since anyone who has not108 opened yet reads identically to someone who never will.109- **A "who clicked this specific sponsor link" export is generally not a clean, documented,110 single API call.** Beehiiv's own dashboard supports building a subscriber segment from click111 behavior and exporting it as CSV; the practical path is to pull that CSV periodically and112 enrich it yourself (e.g. classify each subscriber's email domain into a company/industry113 category with a lightweight heuristic or an LLM pass), rather than trying to reproduce it as114 a live query inside an automated refresh.115- **A publication's early history may predate structured per-issue tracking or was migrated116 from a different platform.** For that period, a hand-curated subscriber-count timeline (which117 can rise or fall) is more honest than deriving growth from per-issue recipient counts, which118 is roughly monotonic by construction and will paper over real churn.119- **Never regenerate all narrative sections wholesale on every refresh.** Update only the month120 or analysis affected by new data, and preserve the tone, length, and untouched prose from121 prior periods; wholesale regeneration produces visible tonal drift and makes diffs impossible122 to review.123124## For MCP tool / integration builders125126If exposing this as MCP tools or an API instead of (or in addition to) a rendered report page,127map each section to its own tool so a client can compose them conversationally or a UI can call128them independently:129130- `get_publication_summary` → hero stats (section 1)131- `get_audience_growth` → growth series (section 3)132- `get_rate_analysis` (parameterized by `open` or `click`) → sections 4 and 5133- `get_audience_segments` → engagement segmentation and list health (section 6)134- `get_monthly_report` (parameterized by month) → that month's synopsis, rollups, and issues135 (section 7)136- `export_engaged_subscribers` → section 8, backed by the periodic CSV/enrichment pipeline, not137 a live Beehiiv call138- `get_issue_kpis` (parameterized by post ID or date range) → section 9139140Cache subscriber-list pagination results where possible; a full engagement-segment computation141requires walking the entire subscriber list, which can be thousands of paginated records, on142every call otherwise.143144## What good looks like145146A report a non-technical stakeholder can read in under a minute and still trust the numbers in — click rates that match what the client sees on their own Beehiiv dashboard, poll tallies from full pagination rather than the capped expand, engagement segments that exclude subscribers too new to have a meaningful lifetime open rate, and narrative that changes only where the numbers did. The failure modes: a click rate that contradicts the client's dashboard, a silently under-reported poll, an invented geographic view, or wholesale-regenerated prose drifting in tone between refreshes.