Back to Board & Investor Reporting

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Board and Investor Reporting Automation

[YourCompany.com] · Management Department · Prepared by FullSpec · [Today's Date]

This document gives the FullSpec build team everything needed to configure, connect, and ship the Board and Investor Reporting automation. It covers the current manual process and its bottlenecks, full specifications for all three agents, the end-to-end data flow with exact field names, and the recommended build stack with total time estimates. You will not need to refer to any other document to begin building. Where the process owner must take an action before build can proceed, that requirement is called out explicitly.

01Process snapshot

Step
Name
Description
1
Confirm Reporting Period and Template
CFO or CEO confirms the reporting month or quarter and opens the board pack template. They check which sections need updating and note agenda items. Time cost: 20 min.
2
Pull Financial Data from Xero
CFO logs into Xero, runs P&L, cash flow, and balance sheet reports, exports to CSV or PDF, and manually copies key figures into the board pack template. BOTTLENECK. Time cost: 45 min.
3
Export Sales Pipeline Data from HubSpot
Sales lead or CFO pulls a pipeline report from HubSpot showing deals by stage, weighted forecast, and closed revenue for the period. Figures are manually entered into the pack. Time cost: 30 min.
4
Collect KPI Updates from Department Heads
CFO or EA sends Slack messages or emails to each department head requesting KPI updates. Responses arrive at different times and require chasing. BOTTLENECK. Time cost: 60 min.
5
Populate KPI Dashboard in Google Sheets
Once all KPI responses are received, the CFO manually enters figures into the master Google Sheets tracker that feeds the board pack charts. Time cost: 40 min.
6
Write Narrative Commentary
CEO or CFO drafts written commentary sections explaining results, highlights, and risks. This step is manual and remains so post-automation. Time cost: 60 min.
7
Format and Proof the Pack
Someone checks every chart, number, and page for formatting errors, outdated figures, and template compliance. Often reveals data inconsistencies requiring fixes. BOTTLENECK. Time cost: 45 min.
8
Route for Internal Sign-Off
Draft pack is shared with the CEO for final review and approval via Slack. Comments exchanged and a revised version is produced if needed. Time cost: 30 min.
9
Distribute Pack to Board or Investors
Approved pack is exported to PDF and emailed or uploaded to a shared folder. Board members are notified individually. Time cost: 20 min.
Time cost summary: Total manual time per cycle is 350 minutes (5 hours 50 min). At approximately 12 cycles per year and a blended rate of $52/hour, this represents around $15,600/year in senior staff time. At 6 manual hours per week (including ad-hoc investor updates), the weekly cost is $312. The automation replaces steps 2, 3, 4, 5, 7, and 9. Steps 1 (period confirmation), 6 (narrative commentary), and 8 (CEO sign-off) remain human. Steps 2, 3, and 5 are replaced by the Data Collector Agent; step 4 by the KPI Collection Agent; steps 7 and 9 by the Pack Assembly Agent.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Data Collector Agent

Connects to Xero and HubSpot on a time-based schedule that fires on the configured report date each period. Authenticates via OAuth 2.0 to both platforms, pulls profit and loss, cash flow, and balance sheet data from Xero, and pulls pipeline by stage, weighted forecast, and closed-won revenue from HubSpot. Applies field mapping rules and basic validation checks before writing all structured results to a designated Google Sheets staging tab. Replaces the manual CSV export, copy-paste, and figure-entry steps that currently consume 45 to 70 minutes of CFO time per cycle.

Trigger
Time-based schedule: fires on the configured report date (e.g. third business day after month end). No manual intervention required.
Tools
Xero (OAuth 2.0, financial reports API), HubSpot (private app token, deals and pipelines API), Google Sheets (service account write access)
Replaces steps
2 (Pull Financial Data from Xero), 3 (Export Sales Pipeline Data from HubSpot), 5 (Populate KPI Dashboard in Google Sheets)
Estimated build
16 hours, Moderate complexity
// Input
reporting_period: string          // e.g. '2024-06' or 'Q2-2024'
xero_tenant_id: string            // obtained during OAuth flow
hubspot_pipeline_id: string       // target pipeline in HubSpot
target_sheet_id: string           // Google Sheets staging document ID
target_tab_name: string           // e.g. 'DataStaging_June2024'

// Output
sheet_row.period: string
sheet_row.revenue_total: number   // from Xero P&L
sheet_row.gross_profit: number    // from Xero P&L
sheet_row.cash_position: number   // from Xero balance sheet
sheet_row.pipeline_total: number  // from HubSpot weighted forecast
sheet_row.closed_won_revenue: number
sheet_row.deals_by_stage: object  // keyed by stage name
sheet_row.validation_flags: array // list of any fields outside expected ranges
sheet_row.written_at: timestamp
  • Xero OAuth 2.0: required scopes are openid, profile, email, accounting.reports.read, and accounting.settings.read. The business must generate a connected app in the Xero developer portal and supply the client ID and secret before build begins. Confirm the correct tenant ID if the account has multiple Xero organisations.
  • HubSpot: use a private app token scoped to crm.objects.deals.read and crm.schemas.deals.read. Confirm the target pipeline ID and deal stage IDs match the live HubSpot account before mapping field names.
  • Google Sheets: provision a service account in Google Cloud Console, share the staging sheet with the service account email, and store the JSON key securely in the credential store. The staging tab must follow the agreed naming convention (e.g. DataStaging_[Period]) to allow downstream agents to locate it reliably.
  • Validation rules: flag any revenue_total or cash_position value that deviates more than 30% from the prior period as a warning in validation_flags. Do not block the workflow; log the flag and continue.
  • Dedupe: if the agent fires twice for the same period (e.g. due to a manual re-trigger), check for an existing row with a matching period value in the staging tab. If found, overwrite rather than append to avoid duplicate rows downstream.
  • Confirm before build: Xero OAuth token is issued and tested, HubSpot pipeline ID and stage names are confirmed, Google Sheets staging document exists with correct sharing permissions, and the reporting period format is agreed.
KPI Collection Agent

Starts immediately after the Data Collector Agent marks its run complete. Sends a structured Slack message to each department head listing the specific KPI fields they are responsible for confirming. Tracks responses against a configurable deadline (default 24 hours). If a department head has not responded within the deadline window, sends a single automated reminder. Once all responses are received, or the final deadline passes, writes confirmed KPI values and any missing flags into the Google Sheets staging tab, appending to the same row as the financial data. This agent replaces the manual chasing process that currently consumes 60 minutes per cycle and introduces response tracking with timestamps.

Trigger
Fires immediately on successful completion of the Data Collector Agent run for the same reporting period.
Tools
Slack (bot token with chat:write and users:read scopes), Google Sheets (service account read and write access)
Replaces steps
4 (Collect KPI Updates from Department Heads)
Estimated build
14 hours, Moderate complexity
// Input
reporting_period: string          // passed from Data Collector Agent
department_heads: array           // [{slack_user_id, name, kpi_fields[]}]
response_deadline_hours: number   // default 24
reminder_offset_hours: number     // default 18 (reminder fires after this delay)
target_sheet_id: string
target_tab_name: string

// Output (written to Google Sheets staging tab)
kpi_row.dept_name: string
kpi_row.respondent_slack_id: string
kpi_row.kpi_field_name: string
kpi_row.kpi_value: number | string
kpi_row.confirmed_at: timestamp
kpi_row.reminder_sent: boolean
kpi_row.missing: boolean          // true if no response by final deadline

// On approval (KPI confirmation)
slack_message_ts: string          // thread timestamp for audit trail
sheet_status: string              // 'complete' | 'partial' | 'missing'
  • Slack bot token must have chat:write (to send messages), users:read (to resolve user IDs), and channels:read (to verify channel membership). The bot must be added to any private channels used by department heads before go-live.
  • KPI field definitions must be finalised and agreed with all department heads before build. The agent sends structured prompts listing exact field names; ambiguous or disputed metrics will produce inconsistent free-text responses that cannot be parsed reliably.
  • Response capture: use Slack's interactive message components (button or short-form modal) rather than free-text replies to enforce a structured numeric or dropdown response. This avoids parsing errors on submission.
  • Reminder logic: send exactly one reminder per non-responding department head at the reminder_offset_hours mark. Do not send further automated chasers. Log reminder_sent: true against the relevant row in Google Sheets.
  • Missing responses: if a department head does not respond by the final deadline, write missing: true and kpi_value: null to the sheet. Pass a list of missing fields to the Pack Assembly Agent so it can flag those cells in the draft pack rather than leaving them blank.
  • Confirm before build: Slack bot is created and installed in the workspace, all department head Slack user IDs are confirmed, KPI field list is finalised and approved by the CFO, and reminder and deadline timings are agreed.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical
Pack Assembly Agent

Starts once the KPI Collection Agent marks the Google Sheets staging tab as complete or partial for the reporting period. Reads all financial and KPI data from the staging tab and uses it to populate the approved board pack template stored in Google Drive, updating named cell references, chart data ranges, and table figures throughout the document. Before producing the draft, checks every target field for a corresponding value in the staging data and flags any missing or out-of-range cells with a visible comment inside the document. Exports the completed draft as a PDF, uploads it to the designated board folder in Google Drive, and sends a Slack notification to the CEO with a direct link. After CEO approval is recorded, sends a distribution notification to all board member email addresses. Replaces the formatting, proofing, and distribution steps that currently consume 65 minutes per cycle.

Trigger
Fires when the Google Sheets staging tab status field is set to 'complete' or 'partial' by the KPI Collection Agent for the matching reporting period.
Tools
Google Sheets (service account read access), Google Drive (service account write and export access), Slack (bot token, chat:write scope)
Replaces steps
1 (partial: template selection), 7 (Format and Proof the Pack), 9 (Distribute Pack to Board or Investors)
Estimated build
14 hours, Moderate complexity
// Input
reporting_period: string
staging_sheet_id: string
staging_tab_name: string
template_doc_id: string           // Google Drive ID of the master board pack template
board_folder_id: string           // Google Drive folder for final output
ceo_slack_user_id: string
board_member_emails: array        // [string]
missing_kpi_fields: array         // passed from KPI Collection Agent

// Output
draft_doc_id: string              // Google Drive ID of the populated draft
draft_pdf_url: string             // shareable link to the exported PDF
flagged_cells: array              // [{cell_ref, reason}] for missing/out-of-range values
ceo_notification_ts: string       // Slack message timestamp
distribution_sent_at: timestamp   // set after CEO marks pack as approved

// On approval
approval_recorded_by: string      // CEO Slack user ID
approval_timestamp: timestamp
final_pdf_drive_id: string
board_notified: boolean
  • Google Drive template: the board pack template must use named ranges or placeholder tokens (e.g. {{revenue_total}}, {{cash_position}}) for every field the agent populates. The template must be locked to prevent structural edits between cycles. If the template changes, named ranges must be updated before the next run.
  • Google Drive service account: the service account must have Editor access to the board pack template document and the board folder. Confirm this before build. Do not grant broader Drive access than required.
  • Chart population: Google Slides or Docs chart data ranges must be linked to the Google Sheets staging tab rather than embedded static figures. Confirm the template uses linked charts before build begins, as static charts cannot be updated programmatically without reimporting image files.
  • Missing field flagging: for each entry in missing_kpi_fields or flagged_cells, insert a comment into the relevant cell or text field in the draft document using the Drive API before exporting the PDF. This ensures the CEO can see exactly which figures need manual review.
  • PDF export: use the Google Drive export endpoint (application/pdf MIME type) rather than a third-party conversion service. Store the exported PDF in the board_folder_id with a filename convention of BoardPack_[Period]_Draft.pdf.
  • CEO approval signal: the simplest implementation is a Slack interactive button ('Approve for distribution') that sets an approval record in Google Sheets and triggers the distribution step. Do not send distribution emails until the approval timestamp is recorded.
  • Confirm before build: the board pack template is finalised and locked, all placeholder tokens or named ranges are in place, the board folder ID is confirmed, and board member email addresses are supplied by the process owner.

03End-to-end data flow

Full trigger-to-output data flow with agent handoffs and field names
// ── TRIGGER ──────────────────────────────────────────────────────────────
schedule_trigger.fires_at = '2024-06-03T08:00:00Z'   // third business day after period close
schedule_trigger.reporting_period = '2024-05'

// ── AGENT 1: Data Collector Agent ────────────────────────────────────────
// Xero API call
GET /api.xero.com/api.xro/2.0/Reports/ProfitAndLoss?fromDate=2024-05-01&toDate=2024-05-31
  -> xero.revenue_total, xero.cost_of_goods_sold, xero.gross_profit
  -> xero.operating_expenses, xero.net_profit

GET /api.xero.com/api.xro/2.0/Reports/BalanceSheet?date=2024-05-31
  -> xero.cash_position, xero.total_assets, xero.total_liabilities

GET /api.xero.com/api.xro/2.0/Reports/CashSummary?fromDate=2024-05-01&toDate=2024-05-31
  -> xero.cash_inflow, xero.cash_outflow, xero.net_cash_movement

// HubSpot API call
POST /api.hubapi.com/crm/v3/objects/deals/search
  body: { pipeline: hubspot_pipeline_id, closedate_range: '2024-05' }
  -> hubspot.deals_by_stage: { prospecting, qualified, proposal, closed_won, closed_lost }
  -> hubspot.pipeline_total_weighted: number  // sum of (amount * probability)
  -> hubspot.closed_won_revenue: number
  -> hubspot.deal_count_open: number

// Write to Google Sheets staging tab
sheet.DataStaging_2024-05.row[1].period                = '2024-05'
sheet.DataStaging_2024-05.row[1].revenue_total         = xero.revenue_total
sheet.DataStaging_2024-05.row[1].gross_profit          = xero.gross_profit
sheet.DataStaging_2024-05.row[1].net_profit            = xero.net_profit
sheet.DataStaging_2024-05.row[1].cash_position         = xero.cash_position
sheet.DataStaging_2024-05.row[1].cash_inflow           = xero.cash_inflow
sheet.DataStaging_2024-05.row[1].cash_outflow          = xero.cash_outflow
sheet.DataStaging_2024-05.row[1].pipeline_total        = hubspot.pipeline_total_weighted
sheet.DataStaging_2024-05.row[1].closed_won_revenue    = hubspot.closed_won_revenue
sheet.DataStaging_2024-05.row[1].deals_by_stage        = hubspot.deals_by_stage (JSON)
sheet.DataStaging_2024-05.row[1].validation_flags      = []  // or [{field, reason}]
sheet.DataStaging_2024-05.row[1].written_at            = '2024-06-03T08:14:22Z'
sheet.DataStaging_2024-05.row[1].agent1_status         = 'complete'

// ── HANDOFF: Data Collector Agent -> KPI Collection Agent ────────────────
// Trigger: agent1_status == 'complete' for reporting_period '2024-05'

// ── AGENT 2: KPI Collection Agent ────────────────────────────────────────
// Slack prompt sent to each department head
slack.postMessage(
  channel: dept_head.slack_user_id,
  text: 'Please confirm your KPI figures for May 2024.',
  blocks: [interactive_fields for each kpi_field in dept_head.kpi_fields]
)
  -> slack_message_ts: '1717401600.000100'

// Reminder (fires at reminder_offset_hours = 18 if no response)
slack.postMessage(
  channel: dept_head.slack_user_id,
  text: 'Reminder: KPI figures for May 2024 are due in 6 hours.',
  thread_ts: slack_message_ts
)

// Response received via Slack interactive callback
slack.callback.action_id = 'kpi_submit'
slack.callback.user.id   = dept_head.slack_user_id
slack.callback.values    = { kpi_field_name: kpi_value }

// Write confirmed KPI to staging tab
sheet.DataStaging_2024-05.kpi_rows[] = {
  dept_name:          'Marketing',
  respondent_slack_id: 'U012AB3CD',
  kpi_field_name:     'website_leads',
  kpi_value:           342,
  confirmed_at:       '2024-06-03T14:22:00Z',
  reminder_sent:       true,
  missing:             false
}

// After all responses or deadline passed
sheet.DataStaging_2024-05.row[1].agent2_status  = 'complete'  // or 'partial'
sheet.DataStaging_2024-05.row[1].missing_fields = ['ops.support_tickets']  // example

// ── HANDOFF: KPI Collection Agent -> Pack Assembly Agent ─────────────────
// Trigger: agent2_status == 'complete' | 'partial' for reporting_period '2024-05'

// ── AGENT 3: Pack Assembly Agent ─────────────────────────────────────────
// Read full staging row
staging_data = sheet.DataStaging_2024-05.row[1]  // all financial + KPI fields

// Copy and populate board pack template in Google Drive
drive.files.copy(template_doc_id) -> draft_doc_id = '1BxiM...Hk2pQ'
drive.docs.replaceAllText(
  {{revenue_total}}       -> staging_data.revenue_total,
  {{gross_profit}}        -> staging_data.gross_profit,
  {{net_profit}}          -> staging_data.net_profit,
  {{cash_position}}       -> staging_data.cash_position,
  {{pipeline_total}}      -> staging_data.pipeline_total,
  {{closed_won_revenue}}  -> staging_data.closed_won_revenue,
  // ... all KPI fields from kpi_rows
)

// Flag missing or out-of-range cells
drive.docs.insertComment(
  doc_id: draft_doc_id,
  cell_ref: '{{ops.support_tickets}}',
  comment: 'Missing: no response received from Operations by deadline.'
)

// Export PDF
drive.files.export(draft_doc_id, mimeType='application/pdf')
  -> draft_pdf stored in board_folder_id as 'BoardPack_2024-05_Draft.pdf'
  -> draft_pdf_url = 'https://drive.google.com/file/d/...'

// Notify CEO via Slack
slack.postMessage(
  channel: ceo_slack_user_id,
  text: 'Draft board pack for May 2024 is ready for review.',
  blocks: [link to draft_pdf_url, Approve button]
)
  -> ceo_notification_ts: '1717416000.000200'

// ── HUMAN STEP: CEO Reviews, Adds Commentary, and Approves ───────────────
// CEO clicks Approve in Slack
slack.callback.action_id = 'pack_approve'
slack.callback.user.id   = ceo_slack_user_id
  -> approval_recorded_by: 'U098ZY7WX'
  -> approval_timestamp:   '2024-06-04T09:05:00Z'
  -> sheet.DataStaging_2024-05.row[1].approved = true

// Export final PDF and distribute
drive.files.export(draft_doc_id, mimeType='application/pdf')
  -> final_pdf stored as 'BoardPack_2024-05_Final.pdf' in board_folder_id
  -> final_pdf_drive_id = '1CyK9...Rp3mN'

// Notify board members
email.send(
  to: board_member_emails[],
  subject: 'Board Pack - May 2024',
  body: 'Please find the board pack for May 2024 linked below.',
  attachment_link: final_pdf_drive_id
)
  -> distribution_sent_at: '2024-06-04T09:07:44Z'
  -> board_notified: true

// ── END ──────────────────────────────────────────────────────────────────
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool (platform TBD at build stage). Configure one workflow per agent: Workflow 1 for the Data Collector Agent, Workflow 2 for the KPI Collection Agent, and Workflow 3 for the Pack Assembly Agent. All three workflows share a single credential store so that Xero, HubSpot, Google, and Slack tokens are managed centrally and rotated without touching individual workflow nodes.
Webhook configuration
Workflow 2 (KPI Collection Agent) requires an inbound webhook endpoint to receive Slack interactive callback payloads (button clicks and modal submissions). Register the webhook URL in the Slack app manifest under 'Interactivity and Shortcuts'. Workflow 3 (Pack Assembly Agent) requires a second inbound webhook to receive the CEO approval callback from Slack. Both endpoints must be HTTPS and should validate the Slack signing secret on every request.
Templating approach
Use token-substitution placeholders in the Google Docs or Slides board pack template (e.g. {{field_name}}) matched to the exact field names in the Google Sheets staging tab. Alternatively, if the template uses linked charts, ensure chart data ranges point to named ranges in the staging sheet so updates propagate automatically on refresh. Document all placeholder tokens in a mapping register before build begins.
Error logging
Write a structured error record to a dedicated Supabase table (suggested table name: automation_error_log) for any failed API call, missing credential, or validation flag that halts a workflow step. Each record should include: workflow_name, step_name, error_code, error_message, reporting_period, and timestamp. Configure a Slack alert to the FullSpec monitoring channel (or a nominated ops channel) whenever a new error record is inserted. Do not rely on email-only alerting for time-sensitive reporting workflows.
Testing approach
Build and test all three agents against sandbox or prior-period data before connecting to live Xero and HubSpot accounts. Use Xero's Demo Company tenant for financial data testing. Use a HubSpot sandbox portal (available on Developer accounts) for pipeline data testing. Run the full end-to-end flow against the most recent closed reporting period and verify every output field in the Google Sheets staging tab matches the source data before go-live.
Estimated total build time
Data Collector Agent: 16 hours. KPI Collection Agent: 14 hours. Pack Assembly Agent: 14 hours. End-to-end integration testing and QA: 4 hours. Total: 48 hours across approximately 4 to 5 build weeks.
Three items must be confirmed by the process owner before any build work begins: (1) Xero OAuth 2.0 credentials and the correct tenant ID are available and tested. (2) HubSpot private app token is issued with the correct scopes and the target pipeline ID is confirmed. (3) The board pack template in Google Drive is finalised, locked, and populated with placeholder tokens or linked chart ranges. Build cannot start on the Pack Assembly Agent until the template is in its final structure. Raise any blockers with the process owner at support@gofullspec.com.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Board & Investor Reporting.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View