Back to the build library
Agencies
Advanced

Client Reporting Automation

Monthly client reports assembled from platform data, approved once, and sent on schedule without anyone rebuilding a deck.

Industry
Agencies
Build effort
Advanced
Typical time to launch
2–3 weeks
On this page
The problemWhat gets builtHow it runsAutomations in detailWhat changesBefore you start
Want this built for you?

Bring your stack to a call and we will scope it against how your business actually runs.

Book a call
01

The problem

An agency running the platform on behalf of a roster of clients owes each of them a monthly accounting of what the retainer bought. In practice that accounting is assembled by hand. An account manager opens each client workspace, screenshots the pipeline view, exports a call log, copies lead counts into a spreadsheet, pulls ad spend from a separate ad account, writes two paragraphs of commentary, and drops the whole thing into a slide template. Multiply that by fifteen clients and the first week of every month disappears into assembly labor that no client is paying for directly.

The second-order damage is worse than the hours. Because assembly is manual, reports go out late and unevenly. The three loudest clients get theirs on the third of the month; the quiet ones get theirs on the fourteenth, or not at all. For a retainer client who is not on a weekly call, the monthly report is the single largest piece of evidence that work is happening. When it arrives late, or arrives as a wall of numbers with no narrative, the client builds their own story about the value they are getting, and that story is almost always worse than reality. Cancellations that look sudden are usually preceded by two or three reporting cycles that landed flat.

There is an internal cost too. Because the numbers are only assembled once a month under deadline pressure, nobody is watching them in between. A client whose booked-call volume halved in week two is discovered in week five, when the report is finally built. The same data that should function as an early-warning system is instead a historical artifact produced too late to act on. The money leaks in three places at once: unbilled assembly hours, retainers lost to silence, and remediation work that would have been cheap if it had started two weeks earlier.

02

What gets built

Reporting pipeline and stages

The cycle lives in a pipeline named Client Reporting Cycle inside the agency's own workspace, not inside client workspaces. One opportunity per client per month, created automatically on the first of the month.

  • Data Collecting - the snapshot workflow is pulling and writing metric fields.
  • Draft Assembled - numbers are in, report page is live at a unique link, commentary is still empty.
  • Commentary Pending - the account manager has been asked for narrative and has not submitted it.
  • Internal Review - full draft awaiting a second pair of eyes.
  • Revisions - reviewer sent it back with notes.
  • Approved to Send - cleared, waiting for the client's scheduled send day.
  • Sent to Client - delivered, open tracking running.
  • Review Call Booked - client took the optional walkthrough slot.
  • Cycle Closed - archived at month end, metrics rolled into prior-month fields.

Calendars

  • Monthly Report Review - 30 minutes, one calendar per account manager, offered in the report email. Two-day minimum notice, 21-day booking window, buffer of 10 minutes after.
  • Quarterly Strategy Review - 60 minutes, offered only in the March, June, September and December cycles, and only to clients tagged reporting-premium or reporting-executive.
  • Internal Report QA - a recurring internal block on the reviewer's calendar for the second and third of each month, so approval is not competing with client work.

Custom fields

Created on the client record held in the agency workspace. Every number here is written by a workflow, never typed by hand.

  • client_account_name, client_workspace_ref - the internal reference for the client's workspace.
  • client_reporting_tier - Standard, Premium or Executive.
  • report_recipients_primary, report_recipients_cc.
  • report_send_day - numeric, 1 to 28. Never 29 to 31.
  • reporting_timezone.
  • report_commentary_owner - the account manager responsible for narrative.
  • kpi_primary, kpi_primary_target, kpi_secondary, kpi_secondary_target - set once at onboarding so the report is judged against something the client agreed to.
  • mtd_leads, mtd_booked_calls, mtd_shows, mtd_no_shows, mtd_won, mtd_pipeline_value, mtd_inbound_calls, mtd_reviews_collected.
  • prior_month_leads, prior_month_booked_calls, prior_month_won, prior_month_pipeline_value.
  • variance_flag - Up, Flat or Down, computed against prior month.
  • commentary_wins, commentary_misses, commentary_next_focus - long text, filled from the capture form.
  • report_link_current, report_cycle_status, last_report_sent_date, last_report_opened_date, report_open_count.
  • review_call_booked - yes or no.
  • reporting_consecutive_unopened - numeric counter, incremented each cycle a report goes unopened and reset on any open.

Tags

  • Tier: reporting-standard, reporting-premium, reporting-executive.
  • Cycle state: report-cycle-open, report-draft-ready, report-commentary-overdue, report-approved, report-sent, report-reviewed-live.
  • Attention: report-unopened-5d, report-unopened-streak, variance-down, variance-up, reporting-paused.

Forms and surveys

  • Reporting Setup Intake - internal, completed once per client at onboarding. Captures recipients, send day, timezone, tier, the two KPIs and their targets, and commentary owner. Writes straight to the custom fields so nothing about a client's reporting is held in someone's head.
  • Commentary Capture - internal, three long-text questions: what went well, what did not, what we are doing next month. Pre-filled with the client name and the current month's numbers so the account manager writes against real figures rather than from memory.
  • Report Feedback Pulse - client-facing, embedded at the foot of the report page. One scale question, Does this report tell you what you needed to know?, plus an optional comment box. Submissions route to the reviewer, not the account manager who wrote it.

BO's role and guardrails

  • BO drafts the narrative paragraph from commentary_wins, commentary_misses, commentary_next_focus and the metric fields, and drafts the covering email in the client's preferred tone.
  • BO answers inbound client questions about the report by restating the relevant field values and linking to the section of the report page that contains them.
  • BO never originates a number. Every figure BO writes must come from a field on the record. If a field is empty, BO says the figure is not yet available and flags the cycle, rather than estimating.
  • BO never discusses pricing, retainer levels, contract terms or scope changes. Any message touching those is tagged and handed to the account manager with no reply sent.
  • BO never sends a report. Delivery is gated on the report-approved tag, which only a human applies.
  • BO does not editorialise on a down month. Where variance_flag is Down, BO states the movement plainly and defers the explanation to the human commentary.

Numbers and channels

  • Email is the delivery channel, sent from a dedicated subdomain with SPF, DKIM and DMARC aligned, separate from the agency's prospecting domain so reporting deliverability is never collateral damage from outbound.
  • SMS is used only for the optional nudge to a primary contact who has explicitly opted in, and only inside that client's local business hours.
  • Internal notifications go to a Slack channel per pod, so QA and overdue-commentary alerts do not sit in anyone's inbox.
  • Inbound replies land in a shared conversation inbox assigned to the commentary owner.

Reporting surface

  • A report page built as a funnel page with a unique link per client per cycle, populated from the metric and commentary fields. The client sees a page, not an attachment, so opens and time-on-page are measurable and the link never goes stale in a thread.
  • An internal roster dashboard listing every client with its cycle stage, variance flag, send day and unopened streak, so the state of the whole month is one screen rather than fifteen tabs.
03

How it runs, step by step

One client, one cycle, end to end. The client in this walkthrough is on the Premium tier with a send day of 5.

  1. At 00:30 on the first of the month the cycle opener fires. It finds every client record not tagged reporting-paused, creates an opportunity in Client Reporting Cycle at Data Collecting, names it with the client name and month, and applies report-cycle-open.
  2. The snapshot workflow copies the outgoing month's figures into the prior-month fields, then clears and rewrites the mtd_ fields from the client's workspace: leads created, calls booked, shows, no-shows, opportunities won, pipeline value, inbound calls answered, reviews collected.
  3. Variance is computed. Booked calls moved from 41 to 29, a fall of more than 15 percent, so variance_flag is set to Down and variance-down is applied. That tag fires the internal variance alert the same morning, which is the first useful consequence of the build: the account manager knows before the client does.
  4. The report page is generated at a unique link. Numbers, targets and the comparison to last month are all rendered from fields. The link is written to report_link_current. The opportunity moves to Draft Assembled.
  5. A commentary request goes to the account manager named in report_commentary_owner, with the figures already in the message and a pre-filled Commentary Capture link. The opportunity moves to Commentary Pending.
  6. The account manager submits commentary the next morning. Because variance_flag is Down, the form requires the what did not go well field to be non-empty before it will submit. The three commentary fields are written to the record.
  7. BO drafts the narrative from the commentary fields and the metric fields, and drafts the covering email. Both are written back to the record as drafts. Nothing is sent. The opportunity moves to Internal Review and a QA task lands on the reviewer.
  8. The reviewer opens the report page, reads BO's narrative, tightens two sentences, and applies report-approved. Had they instead moved the opportunity to Revisions, the build would have bounced it back to the account manager with the reviewer's notes attached.
  9. Nothing is sent yet. The send gate holds the approved cycle until the client's own report_send_day, which here is the fifth, at 08:00 in reporting_timezone. Approval and delivery are deliberately separate so the agency can work ahead without the client learning that reports are batch-produced.
  10. On the fifth the report email goes to report_recipients_primary and report_recipients_cc, carrying the narrative, the two headline KPIs against target, and the report page link. The opportunity moves to Sent to Client, report-sent is applied, last_report_sent_date is stamped.
  11. Because this client is Premium and the month is a quarter end, the email also carries a Quarterly Strategy Review booking link. A Standard-tier client would get the 30-minute Monthly Report Review link instead.
  12. The client opens the report page within the hour. last_report_opened_date and report_open_count are written, reporting_consecutive_unopened is reset to zero, and the unopened-recovery sequence is canceled before it starts.
  13. The client replies asking why booked calls fell. BO answers inside the shared inbox using only field values and a link to the relevant report section, then tags the thread for the account manager because the question invites a judgment about strategy rather than a restatement of data.
  14. The client books the quarterly review for the ninth. The opportunity moves to Review Call Booked, report-reviewed-live is applied, and reminders run at 24 hours and 2 hours before.
  15. On the last day of the month the closer moves the opportunity to Cycle Closed, removes the cycle-state tags, and leaves reporting_consecutive_unopened, variance_flag and the prior-month fields in place as the inputs to next month's run and to the churn-risk build.
04

Automations in detail

1. Cycle Opener and Metric Snapshot

Trigger: scheduled, 00:30 on day 1 of each month, agency timezone.

Conditions: runs for every client record where client_account_name is not empty and the tag reporting-paused is absent. Records tagged reporting-paused are skipped silently and written to a skipped list posted to Slack, so a pause is visible rather than invisible.

Actions, in order:

  1. Copy mtd_leads to prior_month_leads, mtd_booked_calls to prior_month_booked_calls, mtd_won to prior_month_won, mtd_pipeline_value to prior_month_pipeline_value. Rollover happens before the clear, never after.
  2. Clear all mtd_ fields.
  3. Read the client workspace and write the new period's figures into each mtd_ field.
  4. Wait 10 minutes to let the write settle.
  5. Compute variance on mtd_booked_calls against prior_month_booked_calls. Branch: fall of more than 15 percent sets variance_flag to Down and applies variance-down; rise of more than 15 percent sets Up and applies variance-up; anything between sets Flat and removes both tags.
  6. Create an opportunity in Client Reporting Cycle at stage Data Collecting, named with client name plus month and year.
  7. Apply report-cycle-open. Set report_cycle_status to Collecting.

Stop or exit: if any mtd_ field is still empty 30 minutes after step 3, halt the record, apply no cycle tags, and post a named data-failure alert to Slack. A cycle with missing data must never progress to draft.

2. Report Draft Assembly

Trigger: opportunity enters Data Collecting with report_cycle_status of Collecting and all mtd_ fields populated.

Actions:

  1. Generate the report page at a unique link and write it to report_link_current.
  2. Populate the page from fields: the two KPIs against kpi_primary_target and kpi_secondary_target, the month-on-month comparison, call and pipeline figures, review count.
  3. Branch on client_reporting_tier. Standard renders the headline block only. Premium adds the channel breakdown. Executive adds the breakdown plus a rolling three-month trend.
  4. Move the opportunity to Draft Assembled. Apply report-draft-ready.
  5. Wait 5 minutes, then hand off to workflow 3.

Stop or exit: exits if the opportunity is moved out of Data Collecting by a human, or if reporting-paused is applied mid-run.

3. Commentary Request and Chase

Trigger: tag report-draft-ready applied.

Actions:

  1. Move the opportunity to Commentary Pending.
  2. Send an internal email and Slack message to report_commentary_owner. Copy: Numbers are in for [client] - [month]. Booked calls [mtd_booked_calls] against [prior_month_booked_calls] last month, leads [mtd_leads], won [mtd_won]. Draft page: [report_link_current]. Commentary form, pre-filled: [link]. This needs to be in by end of day tomorrow.
  3. Branch: where variance_flag is Down, the message leads with the fall and the form hard-requires commentary_misses. Copy prefix: Heads up - booked calls are down more than 15 percent for [client]. The client will see this. Your commentary needs to say what happened and what we are doing.
  4. Wait 24 hours. If the form has not been submitted, send reminder one to the owner.
  5. Wait a further 12 hours. If still unsubmitted, apply report-commentary-overdue, notify the owner's manager in Slack by name, and post to the pod channel.
  6. Wait a further 24 hours. If still unsubmitted, escalate to the agency principal and flag the cycle at risk of missing its send day.

Stop or exit: cancels immediately on form submission, which writes commentary_wins, commentary_misses and commentary_next_focus, removes report-commentary-overdue, and hands off to workflow 4. Also exits on reporting-paused.

4. Narrative Drafting and Internal QA Gate

Trigger: Commentary Capture form submitted.

Actions:

  1. BO drafts the narrative from the three commentary fields plus the metric fields, constrained to figures present on the record, and writes it back to the opportunity as a draft.
  2. BO drafts the covering email in the client's recorded tone, with subject [Client] - [Month] performance summary.
  3. Move the opportunity to Internal Review. Create a QA task for the reviewer due the same day, with the report link and a note of the variance flag.
  4. Wait 8 working hours. If the opportunity is still in Internal Review, remind the reviewer.
  5. Wait a further 8 working hours. If still unreviewed, notify the principal. Do not auto-approve.

Branches: reviewer applies report-approved, which moves the opportunity to Approved to Send and hands off to workflow 5. Or the reviewer moves it to Revisions, which sends the reviewer's notes back to the commentary owner, clears report-approved if present, and restarts the chase clock at step 4 of workflow 3 with a 12-hour rather than 24-hour first wait.

Stop or exit: never auto-approves and never sends. An unreviewed cycle misses its send day and is reported as a miss, which is the correct failure mode.

5. Scheduled Delivery and Open Tracking

Trigger: tag report-approved applied.

Conditions: holds in a wait-until step for day report_send_day at 08:00 in reporting_timezone. If approval lands after that moment in the current month, send within the hour rather than waiting a month.

Actions:

  1. Send the report email to report_recipients_primary, copying report_recipients_cc. Body carries the narrative, the two KPIs against target, and the report link as a prominent button.
  2. Branch on tier and month. Premium and Executive in a quarter-end month get the Quarterly Strategy Review link; everyone else gets the Monthly Report Review link. Copy: If you would like to walk through this together, here is my calendar: [link].
  3. Move to Sent to Client. Apply report-sent, remove report-draft-ready, stamp last_report_sent_date, set report_cycle_status to Sent.
  4. Wait 4 hours. If the primary contact has the SMS consent flag set and local time is inside quiet hours, send: Hi [first name], your [month] summary from [agency] is in your inbox. Reply STOP to opt out of these notices.
  5. On any open of the report page or email, write last_report_opened_date, increment report_open_count, set reporting_consecutive_unopened to 0, and remove report-unopened-5d and report-unopened-streak.

Stop or exit: exits if report-approved is removed before the send moment, which is the abort handle for a report caught as wrong after approval.

6. Unopened Report Recovery

Trigger: tag report-sent applied.

Conditions: every step checks last_report_opened_date against last_report_sent_date and exits the moment an open is recorded.

Actions:

  1. Wait 3 days. If unopened, send a plain-text nudge from the account manager's own address, no images, subject Did this reach you? Copy: Hi [first name] - sending this again in case it got buried. Your [month] summary is here: [report_link_current]. The short version: [one-line headline from the narrative]. Happy to walk through it if that is easier.
  2. Wait 2 days. If still unopened, apply report-unopened-5d and create a task for the account manager to call the client, with the report link on the task.
  3. Wait 5 days. If still unopened, increment reporting_consecutive_unopened and post to the pod channel: [client] has not opened the [month] report after 10 days. Unopened streak now [value].
  4. Branch: where reporting_consecutive_unopened reaches 3, apply report-unopened-streak, which is a declared input to the churn-risk build, and notify the principal rather than the pod.

Stop or exit: cancels on any open, on a reply from the client, or on report-reviewed-live.

7. Review Call Booking, Reminders and No-Show Handling

Trigger: booking confirmed on either review calendar.

Actions:

  1. Move the opportunity to Review Call Booked. Apply report-reviewed-live. Set review_call_booked to yes. Cancel workflow 6.
  2. Send confirmation with the report link attached to the invite so the client arrives having seen the numbers.
  3. Wait until 24 hours before. Send email reminder with the report link and the agenda: Three things to cover: [kpi_primary] against target, what moved and why, and the focus for next month.
  4. Wait until 2 hours before. Send SMS reminder to consented contacts only, inside quiet hours.
  5. Wait until 30 minutes after the slot ends. Branch on attendance. Attended: create a task to log outcomes to commentary_next_focus for the following cycle. No-show: send Sorry we missed each other - here is my calendar if you would like to pick another time: [link], and leave the opportunity at Review Call Booked rather than reverting it.
  6. Second consecutive no-show on review calls notifies the account manager directly and is logged as a disengagement signal.

Stop or exit: cancels on cancellation of the booking, which also sets review_call_booked back to no and re-arms workflow 6 from step 2.

8. Internal Variance Alert

Trigger: tag variance-down or variance-up applied.

Conditions: fires once per client per cycle. Suppressed entirely for clients inside their first full month, where no meaningful prior-month baseline exists.

Actions:

  1. Post to the pod Slack channel within 15 minutes. Down copy: [client] booked calls [mtd_booked_calls] against [prior_month_booked_calls] - down [percentage]. Commentary is due before this report goes out on day [report_send_day]. Owner: [report_commentary_owner].
  2. Create a task for the commentary owner titled Diagnose [client] decline before report send, due two days before report_send_day.
  3. Branch: a second consecutive variance-down escalates to the principal and recommends a call ahead of the report rather than with it. Copy: [client] is down two cycles running. Recommend a call before the day [report_send_day] send, not after.
  4. For variance-up, post a lighter note prompting the owner to name the cause in commentary, so a good month is explained rather than just enjoyed.

Stop or exit: exits if variance_flag returns to Flat before the send, or if reporting-paused is applied.

9. Cycle Close and Archive

Trigger: scheduled, 23:00 on the last day of each month.

Conditions: applies to every opportunity in Client Reporting Cycle not already at Cycle Closed.

Actions:

  1. Branch on stage. Opportunities at Sent to Client or Review Call Booked move to Cycle Closed cleanly.
  2. Opportunities still at Commentary Pending, Internal Review or Revisions are closed as missed, logged by owner and reason, and reported in a month-end summary to the principal. Do not quietly close a miss as a success.
  3. Remove report-cycle-open, report-draft-ready, report-approved, report-sent and report-commentary-overdue. Leave reporting_consecutive_unopened, variance_flag and report-unopened-streak in place as carried state.
  4. Expire the previous cycle's report page link, keeping the current one live for one more cycle.
  5. Post the month-end summary to Slack: on-time sends, misses by owner, open rate, review bookings, clients at an unopened streak of 3 or more.

Stop or exit: a single run per month, no retries. If the run fails it alerts and waits for a human rather than re-firing against partially closed records.

05

What changes

What is different afterwards

  • Report assembly stops being a person's job. The account manager's contribution shrinks from a morning of copying figures to roughly ten minutes of writing commentary against numbers that are already correct.
  • Every client gets a report, on their own day, in the same shape, whether or not they are loud. The quiet accounts stop being the ones that silently fall off the schedule.
  • The agency learns about a bad month on the first, not the fifth. Variance detection moves upstream of the client conversation, which converts an awkward explanation into a plan the client hears about before they notice the problem.
  • Open tracking turns reporting into a usable churn signal. A client who has not opened three consecutive reports is a different risk from one who opens and replies, and the build can now tell the difference.
  • Approval is a real gate. One human signs off every report, with notes recorded, instead of fifteen people each deciding privately what good enough looks like.
  • Reporting becomes portable. Onboarding a new client means filling one intake form rather than teaching someone a bespoke ritual.

Metrics to watch

  • On-time send rate - cycles delivered on the client's own send day, as a share of active clients. This is the headline number and should be the first thing to reach and hold at full coverage.
  • Hours per cycle - measured as time from commentary request to approval. Track the median, not the mean, and watch for the long tail of accounts that still need hand-holding.
  • Report open rate and time to first open, by tier. A tier whose opens are drifting down is telling you the format has stopped being read.
  • Consecutive-unopened distribution - how many clients sit at one, two, three or more. The count at three or more is a direct churn-risk queue.
  • Review call take-up - bookings per report sent, split by tier and by variance flag. Down-month clients who decline the walkthrough are the ones to call manually.
  • Commentary turnaround - hours from request to form submission, by account manager. This is where the cycle actually stalls.
  • Revision rate - share of drafts the reviewer bounces. A rising rate usually means commentary prompts need rewriting, not that people got careless.
  • Feedback pulse score - rolling three-month average per client, watched alongside renewal dates.
06

Before you start

Prerequisites

  • A workspace structure where the agency has a parent-level view across client workspaces. Reporting is assembled at the agency level and reads from each client workspace, so the permission model has to allow that read before anything else is built.
  • A settled definition of each metric, written down, before a single field is created. What counts as a lead, when a call counts as booked, what makes an opportunity won. If two account managers answer differently, the reports are not comparable and the whole build is decorative.
  • The two KPIs and their targets agreed with each client in writing, captured through the Reporting Setup Intake form. A report with no target is a list of numbers.
  • Client workspaces already generating the data. If pipelines are not being kept current inside a client's workspace, fix that first; automating the reporting of bad data just distributes it faster.
  • Named owners: one commentary owner per client, and at least one reviewer who is not also a commentary owner.

Integrations

  • Google Calendar or Outlook connected per account manager, with real availability, or the review-call links will book over existing commitments.
  • Ad platform connections where paid media is in scope, so spend and cost-per-result reach the metric fields rather than being pasted in.
  • Slack for internal QA, overdue-commentary and variance alerts.
  • Stripe or QuickBooks if the report is to carry a billing or spend summary alongside performance.
  • Zapier or Make for any data source without a native connection. Keep these to a minimum and document each one: an undocumented connector that silently stops is how a metric field quietly goes stale.

Email and number provisioning

  • A dedicated sending subdomain for reporting, authenticated with SPF, DKIM and DMARC, warmed before the first full cycle. Do not share it with outbound prospecting.
  • If the optional SMS nudge is in scope, provision a number and complete A2P 10DLC brand and campaign registration before sending. Registration takes time to clear and is not something to discover on the fourth of the month. Register the campaign under the agency's own brand with the use case described accurately.
  • Keep SMS within quiet-hours rules for each recipient's local time. Default the window to 08:00 to 20:00 and never override it for a report nudge, which is never urgent.
  • Honor STOP, UNSTOP and HELP on every number, and make sure an opt-out on the reporting number does not silently suppress operational messages the client has separately consented to.

Compliance and data protection

  • Client performance data frequently contains end-customer personal data. Decide deliberately what the report page shows: aggregate counts are usually sufficient, and named end-customer records on a link-accessible page rarely are.
  • Report page links should be unguessable, expire at the end of the following cycle, and carry no indexing. Treat a report link as credential-equivalent.
  • Agree in the client agreement who may receive reports, and keep report_recipients_primary and report_recipients_cc current when staff change. A report that keeps going to a departed employee is a disclosure, not an administrative slip.
  • Where the agency processes personal data on a client's behalf, the arrangement normally needs a written processing agreement and a defined retention period for snapshots and archived report pages.
  • None of the above is legal advice. Confirm the specifics of data processing terms, retention and messaging consent with the client's own counsel, and have the agency's own counsel review the standard client agreement before the build goes live.

Scope, configuration and effort vary by roster size and by how many data sources are in play. To talk through what this looks like for a specific agency, book a call at bookings.appbo.io.

Field notes

Build it with us.

Every build on this page is something we configure, test and hand over with the SOPs your team needs to run it.

Book a call