# The Weekly Client Report, Written by an Agent Every Monday Before You Wake Up

_2026-09-25 · Build a weekly client report that an agent compiles from Shager: what was published, what failed, clicks per account and post, and how to join it to CRM leads._

An agent can write a client's weekly distribution report by pulling three things from Shager: the posts published in the week and their per-account outcome, clicks per account and per post from `get_analytics`, and the list of anything that failed. It then joins those clicks to leads from the client's CRM using the `utm_content` value that identifies each account. Run it on a schedule with a read-only key, and the report is waiting in a folder on Monday morning.

The part that takes thought is not the code. It is deciding what the client actually needs to see, and being honest about what the numbers measure.

## What should a weekly distribution report contain?

Four sections are enough. Anything else belongs in the monthly review.

1. **Delivery.** How many posts went out, to how many accounts, and which accounts failed. Clients rarely ask about this until something goes missing, and then it is the only thing they ask about.
2. **Clicks by account.** Which pages, branches or channels sent people to the site. This is the section that changes budgets.
3. **Clicks by post.** Which content worked, across all accounts.
4. **Actions.** What the client needs to do: reconnect an account, approve next week's drafts, send missing assets.

Notice what is missing: reach, impressions, likes. Shager measures clicks on its own tracked links, per account and per post. It does not claim to be a social analytics suite, and the report should not pretend otherwise.

## The data calls

For a client workspace, the agent runs this sequence. The tool names are the real MCP tools; the REST equivalents are in the [OpenAPI document](https://app.shager.ai/api/v1/openapi.json).

1. `list_workspaces` to resolve the client's workspace by name.
2. `list_posts` with `status` set to `PUBLISHED`, then again with `PARTIALLY_PUBLISHED` and `FAILED`. `limit` goes up to 100.
3. `get_post` for each post from the week, to get per-account state, live URLs and error messages.
4. `get_analytics` with `from` and `to` covering the week.
5. `list_channels`, to map channel IDs to readable account names and to spot accounts whose connection status has changed.

The analytics call over REST:

```bash
curl -s "https://app.shager.ai/api/v1/workspaces/ws_example/analytics?from=2026-09-14T00:00:00%2B03:00&to=2026-09-20T23:59:59%2B03:00" \
  -H "Authorization: Bearer $SHAGER_READ_KEY"
```

Two details in that URL bite people. The `+` in the offset must be encoded as `%2B` in a query string, or the server reads it as a space. And the week boundaries should be computed in the client's time zone, not the server's. If `from` is omitted, `get_analytics` defaults to the last 30 days, which is not what a weekly report wants.

## Clicks are not leads, so join them

Shager gives every account its own short link and UTM tags, with `utm_content` identifying the account. That is what makes the join possible. The CRM records leads with the UTM values captured on the landing page form; the agent matches them to Shager's per-account click counts on the same `utm_content`.

The resulting table is the heart of the report:

| Account | Posts | Clicks (Shager) | Leads (CRM) | Note |
|---|---|---|---|---|
| Aurora Downtown | 4 | ... | ... | |
| Aurora Northside | 4 | ... | ... | reconnect needed |

The gaps between columns are information. Many clicks and no leads is a landing page or offer problem, not a distribution problem. Our older guide on [per-account lead attribution](/en/blog/utm-per-account-lead-attribution) covers the form fields and CRM mapping that make the leads column trustworthy, and [measuring leads from agent content](/en/blog/measuring-leads-from-agent-content) covers the agent side.

If you do not have CRM access, the report still works with clicks only. Say so in the report header. A report that labels clicks as "results" is how agencies lose trust a quarter later.

## Running it unattended with Claude Code

Claude Code can run non-interactively with `claude -p`, as described in its [headless documentation](https://code.claude.com/docs/en/headless). That makes a weekly report a cron job.

First, a project folder for reports with a `.mcp.json` that reads the key from the environment. Claude Code's [MCP documentation](https://code.claude.com/docs/en/mcp) supports `${VAR}` expansion in headers:

```json
{
  "mcpServers": {
    "shager": {
      "type": "http",
      "url": "https://app.shager.ai/api/mcp",
      "headers": { "Authorization": "Bearer ${SHAGER_READ_KEY}" }
    }
  }
}
```

Then the instructions, saved as a skill so the logic lives in one file rather than in a crontab line. Claude Code loads project skills from `.claude/skills/<name>/SKILL.md`, per its [skills documentation](https://code.claude.com/docs/en/skills):

```markdown
---
name: weekly-report
description: Compile the weekly distribution report for one client workspace from Shager. Use when asked for a weekly client report.
---

The argument is the client workspace name: $ARGUMENTS

1. Resolve the workspace with list_workspaces. Read its timezone.
2. Compute last Monday 00:00 to Sunday 23:59:59 in that timezone, with offsets.
3. Collect posts with list_posts (PUBLISHED, PARTIALLY_PUBLISHED, FAILED) and
   keep those published in the window. Call get_post for each.
4. Call get_analytics for the window. Map channel ids to names with list_channels.
5. If crm/<workspace>.csv exists, join leads on utm_content.
6. Write reports/<workspace>/<YYYY-MM-DD>.md with four sections: Delivery,
   Clicks by account, Clicks by post, Actions. Use tables. No adjectives about
   performance; numbers and one plain sentence per section.
7. Never call any tool that creates, schedules or cancels anything.
```

And the scheduled command, which pre-approves only the read tools and file writing:

```bash
cd ~/agency-reports && claude -p "/weekly-report Aurora" \
  --allowedTools "mcp__shager__list_*,mcp__shager__get_*,mcp__shager__whoami,Write" \
  --output-format json > logs/aurora-$(date +%F).json
```

The allow rule syntax with a `mcp__<server>__` prefix and a trailing glob is documented in Claude Code's [permissions reference](https://code.claude.com/docs/en/permissions). Anything not on that list will not be auto-approved in an unattended run.

## Why a read-only key matters here

The skill tells the model not to write. The key makes it impossible. Create a separate key with only the `read` scope for reporting at the agents dashboard, and use it nowhere else. If that key ever leaks, the worst outcome is someone reading click counts. We go through the full scope model in [API keys, scopes and audit](/en/blog/api-keys-scopes-and-audit).

Every call the reporting agent makes also shows up in the audit log on that dashboard, which is a good way to confirm on the first few Mondays that it calls what you expect and nothing more.

## Writing the narrative part

Let the agent write one or two sentences per section, but constrain it. Without rules, models describe a 4 percent change as "a significant increase." My instruction is blunt: state the number, compare it to the previous week if available, and say nothing about cause unless the data shows it. If a branch page had zero clicks because its connection expired on Tuesday, that is a cause the data shows. "Audience fatigue" is not.

## FAQ

### Can the agent send the report to the client directly?

It can if you give it an email tool, but I would not on day one. Have it write the file, review a few weeks of output, then automate delivery. The report is the client's view of your work, and a wrong number sent automatically costs more than the time saved.

### Does get_analytics include leads?

No. It returns clicks on Shager's tracked links, per account and per post, in a date range. Leads come from the client's CRM or form tool, matched on `utm_content`. The report should keep the two sources visibly separate.

### How far back can the report look?

`get_analytics` accepts any `from` and `to` you send, with a default of the last 30 days when `from` is omitted. For month-over-month comparisons, run the same skill with a different window rather than storing past reports as the source of truth.

## Where to go next

If you do not have a key yet, the [agent entry point](/en/agents) shows how to create one or let an agent sign itself up. Start with one client, read three Mondays of reports yourself, and only then add the rest.

---
Source: https://shager.ai/en/blog/weekly-client-report-by-agent
