---
name: hubspot-marketing-email-report
description: "Every Friday afternoon: opens, clicks and unsubscribes for each HubSpot marketing email sent this week, with click rate as the number to watch. Use when the user asks for this, or when it runs on a schedule."
---

# HubSpot marketing email report

Every Friday afternoon: opens, clicks and unsubscribes for each HubSpot marketing email sent this week, with click rate as the number to watch.

Email results in HubSpot live on each email's own page, so comparing this week's sends means opening them one at a time. Open rates have also been unreliable since Apple Mail Privacy Protection began preloading images, which makes the number everyone quotes the least useful one.

The agent collects every marketing email published this week, reads its statistics through the HubSpot API and leads with click rate, the signal that still means a person acted. Opens and unsubscribes are there for context, and an email whose unsubscribe rate spikes gets called out.

## Two ways to get the data

Use the API or MCP route when you have an MCP server, a CLI or an API token for the app. Otherwise, if you can drive a browser the user is signed in to (Claude in Chrome, a Playwright or Chrome DevTools MCP server), use the browser route. Say which one you used.

## Route A: API or MCP server

### What you need

- Marketing Hub email in your HubSpot account, and a private app token with the `marketing.email.read` scope (`content` also works), or an OAuth app that has it.
- The token in `HUBSPOT_ACCESS_TOKEN`, or a HubSpot MCP server or connector the agent can call.

If something is missing, say what and stop; do not invent numbers.

### Steps

1. **Find this week's sends** (HubSpot): Call `GET /marketing/v3/emails` (or the date-versioned path, `/marketing/emails/2026-09`) with `includeStats=true` and `publishedAfter` set to Monday 00:00 in the portal's time zone. Leave out drafts and automated emails unless you ask for them.
2. **Read the counters** (HubSpot): From each email's `stats.counters`, take sent, delivered, open, click, unsubscribed, bounce and spam report counts. For one total across the week, `GET /marketing/v3/emails/statistics/list` with `startTimestamp` and `endTimestamp` returns the aggregate and a per-campaign breakdown.
3. **Work out the rates**: Divide by delivered, not sent: open rate, click rate, click-to-open rate and unsubscribe rate. Flag an email whose unsubscribe rate is above 0.5% or twice your 8-week average.
4. **Mark the early numbers**: An email sent this morning has hours of data and one sent on Monday has days. Mark anything under 48 hours old as early so it is not judged against the rest.
5. **Post the tile**: The week's click rate is the number, with 8 weeks of history. The glance holds delivered, open rate and unsubscribes; the card opens to one row per email.

## Route B: the browser

### What you need

- Signed in to HubSpot as a user with Marketing email access (Marketing tools, Email, at least View) in a Marketing Hub account.

If something is missing, say what and stop; do not invent numbers.

### Steps

Start at https://app.hubspot.com/. The user is signed in already; never type passwords or codes, and ask if a sign-in page appears.

1. **Open the email Analyze tab**: Open `https://app.hubspot.com/`, pick the account if more than one shows, and go to Marketing, `Email`, then the `Analyze` tab.
2. **Set the week and the type**: Set `Date range` to a custom range from this Monday to today and `Email type` to regular emails only, leaving automated emails out. Read `Recipient engagement` (open rate, click rate) and `Delivery` (delivered, unsubscribes, bounces, spam reports).
3. **Read each email**: Scroll to the `Performance dashboard` table, which lists every email sent in the range with delivered, open rate, click rate and unsubscribe figures. Mark emails sent less than 48 hours ago as early. Rates in HubSpot already divide by delivered.
4. **Add the 8-week trend**: Change the date range to the last 8 weeks and set `Performance over time` to `Click rate` at `Weekly` frequency; read the weekly values for the trend and the average.
5. **Flag and conclude**: Flag any email with an unsubscribe rate above 0.5% or twice the 8-week average, and any bounce rate above 2%. Name the email with the best click rate and what it did differently.

Note: The Analyze tab needs a date range that is not too wide; 8 weeks is fine. For many sends in one week, use Actions, Compare, then Export as a spreadsheet instead of reading the table. Automated emails are filtered out here because they send continuously and would mix old and new recipients.

## The report

- **Click rate** across this week's marketing emails, with an 8-week trend.
- **Delivered, open rate and unsubscribes** for the week.
- **Per email**: delivered, open rate, click rate and unsubscribes, with early sends marked.
- **Flags**: unsubscribe spikes and bounce rates above 2%.
- The best performer and what it did differently, in one line.

## Post it to Briefkin

When `BRIEFKIN_TOKEN` is set, or `~/.config/briefkin/env` exists (the installer from https://briefkin.dev/docs writes it), also send the result to the user's Briefkin board as one card. Keep the `item_key` the same on every run so the card updates in place, use a new `event_id` per run, and put today's real numbers in place of the example ones. If the request fails, say so once and carry on.

```bash
[ -n "${BRIEFKIN_TOKEN:-}" ] || . ~/.config/briefkin/env
curl -sS -X POST "${BRIEFKIN_URL:-https://briefkin.dev}/v1/events" \
  -H "authorization: Bearer $BRIEFKIN_TOKEN" \
  -H "content-type: application/json" \
  -d @- <<'JSON'
{
  "v": 1,
  "event_id": "hubspot-marketing-email-report/<date of this run, e.g. 2026-10-16>",
  "item_key": "hubspot-marketing-email-report",
  "project": "Acme",
  "source": "HubSpot marketing email report skill",
  "kind": "metric",
  "title": "Email click rate 3.4% this week",
  "metric": {
    "label": "Click rate",
    "value": 3.4,
    "unit": "%",
    "better": "higher",
    "period_start": "<start of the period, ISO 8601>",
    "period_end": "<end of the period, ISO 8601>"
  },
  "glance": {
    "type": "Grid",
    "props": {
      "columns": 3
    },
    "children": [
      {
        "type": "Stat",
        "props": {
          "label": "Delivered",
          "value": 34220
        }
      },
      {
        "type": "Stat",
        "props": {
          "label": "Open rate",
          "value": 39.3,
          "unit": "%"
        }
      },
      {
        "type": "Stat",
        "props": {
          "label": "Unsubscribed",
          "value": 85,
          "better": "lower"
        }
      }
    ]
  },
  "widget": [
    {
      "type": "Table",
      "props": {
        "columns": [
          "Email",
          "Delivered",
          "Open rate",
          "Click rate",
          "Unsubs"
        ],
        "align": [
          "left",
          "right",
          "right",
          "right",
          "right"
        ],
        "rows": [
          [
            "October product update",
            18240,
            "41.2%",
            "3.1%",
            46
          ],
          [
            "Webinar invite: closing the books faster",
            9860,
            "38.5%",
            "4.6%",
            21
          ],
          [
            "Customer story: Acme Bakery (early)",
            6120,
            "35.0%",
            "2.2%",
            18
          ],
          [
            "Total",
            34220,
            "39.3%",
            "3.4%",
            85
          ]
        ]
      }
    },
    {
      "type": "Callout",
      "props": {
        "text": "The webinar invite clicked best at 4.6% with a single call to action. The customer story went out yesterday, so its numbers are still early.",
        "tone": "info"
      }
    }
  ],
  "size": "md"
}
JSON
```

Widgets come from the Briefkin catalog (https://briefkin.dev/v1/catalog). Without a token, just give the user the report.

Set `period_start` and `period_end` to the window the number covers: the tile names it from them ("Last 7 days", "Today", "This month"). For a count right now (open items, a balance), send `"period_label": ""` so no period shows; for another name, send it as `period_label` (at most 24 characters).

## Run it on a schedule

Suggested: Fridays at 16:00 (`0 16 * * 5`). From cron, with the agent signed in on that machine:

```bash
0 16 * * 5 claude -p "Run the hubspot-marketing-email-report skill"
# Codex: codex exec, Gemini CLI: gemini -p, Copilot CLI: copilot -p, OpenCode: opencode run
```

Or use the agent's own scheduler (Claude Code routines, Codex automations, Claude or ChatGPT scheduled tasks): https://briefkin.dev/skills/hubspot-marketing-email-report#schedule

Source: https://briefkin.dev/skills/hubspot-marketing-email-report
