Back to Payroll Processing

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

Payroll Processing Automation

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

This document is the primary technical reference for the FullSpec team building the Payroll Processing automation. It covers the full current-state process map with bottleneck flags, all three agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. The FullSpec team uses this document to configure, build, and test the automation end to end. Your team's responsibility is to confirm API access, supply accurate leave and deduction rule documentation, and be available during the parallel run review.

01Process snapshot

Step
Name
Description
1
Notify Managers of Timesheet Deadline
Finance Manager sends individual reminder emails via Gmail to each manager before cut-off. Time cost: 20 min.
2
Collect Timesheets from Managers
Timesheets are gathered manually from Deputy, email, or spreadsheet into one place. BOTTLENECK. Time cost: 45 min.
3
Reconcile Hours Against Schedule
Submitted hours are compared against the Deputy schedule to find discrepancies, missed shifts, and unclaimed overtime. BOTTLENECK. Time cost: 40 min.
4
Apply Leave and Deduction Adjustments
Annual leave, sick leave, and other deductions are manually applied to each employee record referencing the HR leave tracker. Time cost: 30 min.
5
Calculate Gross Pay and Withholdings
Gross pay, tax withholdings, and benefits deductions are calculated and entered into QuickBooks. Time cost: 35 min.
6
Prepare Payroll Summary for Approval
A payroll summary report is compiled in Google Sheets and sent to the business owner for sign-off. Time cost: 25 min.
7
Owner Reviews and Approves Payroll
Business owner reviews the summary, raises queries, and gives approval via email. BOTTLENECK. Time cost: 20 min.
8
Enter Approved Figures into Gusto
Approved hours and adjustments are manually entered into Gusto, including one-off bonuses or prior-period corrections. Time cost: 30 min.
9
Submit Payroll Run in Gusto
The payroll run is submitted in Gusto, triggering direct deposits to employees. Time cost: 10 min.
10
Reconcile Payroll to General Ledger
Payroll totals are reconciled in QuickBooks to confirm payroll expense journals are accurate before month-end. Time cost: 25 min.
11
Notify Employees of Payment
Employees are notified by email via Gmail that pay has been processed and their payslip is available. Time cost: 15 min.
Time cost summary: Total manual time per cycle is 295 minutes (approximately 4 hours 55 minutes). At 26 bi-weekly runs per year, this equates to approximately 128 manual hours per year on these steps alone, or roughly 6 hours per week when blended across the schedule. The automation replaces steps 1, 2, 3, 4, 5, 6, 8, 9, and 11. Steps 7 (owner approval) and 10 (GL reconciliation) retain a human involvement, though step 10 is partially supported by the reconciliation agent's QuickBooks journal staging output.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Timesheet Collection Agent

Monitors the configured pay period close date using a scheduled trigger. On trigger, calls the Deputy API to pull all approved shift hours for the closed period. Identifies any employees or cost centres with missing or unapproved timesheets and sends automated reminder messages to the responsible managers via Slack and Gmail. Continues polling the Deputy API at a defined interval until all timesheets are approved or the escalation timeout is reached, at which point it flags unresolved items for human resolution and passes the confirmed data downstream. Complexity: Moderate.

Trigger
Scheduled: fires on the configured pay period close date (bi-weekly or monthly cadence set in the orchestration layer environment variables).
Tools
Deputy (API read), Slack (channel post), Gmail (reminder email send).
Replaces steps
Step 1 (Notify Managers of Timesheet Deadline), Step 2 (Collect Timesheets from Managers).
Estimated build
10 hours. Complexity: Moderate.
// Input
pay_period_start_date: ISO8601 date string
pay_period_end_date: ISO8601 date string
employee_roster: array of { employee_id, manager_id, deputy_employee_id }
reminder_channels: { slack_channel_id, manager_email }
escalation_timeout_hours: integer (default 24)

// Output (on success)
approved_timesheets: array of {
  employee_id: string,
  deputy_employee_id: string,
  period_hours: float,
  overtime_hours: float,
  timesheet_status: 'approved' | 'manager_confirmed',
  approved_at: ISO8601 timestamp
}
unresolved_flags: array of {
  employee_id: string,
  reason: 'missing' | 'unapproved' | 'timeout',
  escalated_at: ISO8601 timestamp
}
collection_complete: boolean
handoff_timestamp: ISO8601 timestamp
  • Deputy API access requires the Deputy Premium or Enterprise plan for full Timesheets endpoint availability. Confirm the connected account has the 'Timesheets:Read' OAuth scope before build. The endpoint is GET /api/v1/resource/Timesheet with filters for modified_start and modified_end aligned to the pay period.
  • The polling interval for unapproved timesheets should be set to 4 hours by default. The escalation timeout should be configurable via environment variable (TIMESHEET_ESCALATION_HOURS, default 24) rather than hardcoded.
  • Slack reminders must be sent to a named channel (e.g. #payroll-alerts) rather than direct messages to individual managers, to preserve an audit thread. The channel name must be confirmed with the process owner before build.
  • Gmail reminders use a templated draft. The subject line and body template must be stored as environment variables so the process owner can adjust wording without a code change.
  • Dedupe: if a reminder has already been sent to a manager within the same polling cycle, suppress the duplicate. Use a transient in-memory flag keyed on manager_id and reset each polling cycle.
  • If collection_complete is false when the downstream agent is triggered, the Reconciliation and Calculation Agent must receive the unresolved_flags array and treat those employees as exceptions, not as missing data errors.
  • Confirm before build: pay period close dates, whether the schedule is bi-weekly or monthly, and whether any employees are on a different frequency.
Reconciliation and Calculation Agent

Receives the confirmed timesheet data from the Timesheet Collection Agent. Pulls the published schedule from Deputy for the same period and compares actual approved hours against scheduled hours to identify discrepancies, unclaimed overtime, and leave conflicts. Applies leave and deduction rules sourced from the HR configuration sheet (a designated Google Sheet tab confirmed during data audit). Calculates gross pay per employee using the rate card stored in configuration. Stages corresponding journal entries in QuickBooks via API. Writes the completed payroll summary, including gross pay, deductions, and net pay per employee, to the designated Google Sheets tab. Marks the summary as ready for approval and triggers the downstream agent. Complexity: Complex.

Trigger
Timesheet Collection Agent sets collection_complete: true and passes approved_timesheets array.
Tools
Deputy (API read, schedule data), Google Sheets (API read for config, API write for payroll summary), QuickBooks (API write for journal entry staging).
Replaces steps
Step 3 (Reconcile Hours Against Schedule), Step 4 (Apply Leave and Deduction Adjustments), Step 5 (Calculate Gross Pay and Withholdings), Step 6 (Prepare Payroll Summary for Approval).
Estimated build
18 hours. Complexity: Complex.
// Input
approved_timesheets: array (from Timesheet Collection Agent output)
unresolved_flags: array (passed through for exception annotation)
pay_period_start_date: ISO8601 date string
pay_period_end_date: ISO8601 date string
config_sheet_id: string (Google Sheets document ID)
config_tab_leave_rules: string (sheet tab name, e.g. 'LeaveRules')
config_tab_rate_card: string (sheet tab name, e.g. 'RateCard')
payroll_summary_sheet_id: string
payroll_summary_tab: string (e.g. 'PayrollSummary_YYYY-MM-DD')
quickbooks_realm_id: string

// Output
payroll_summary: array of {
  employee_id: string,
  employee_name: string,
  scheduled_hours: float,
  actual_hours: float,
  overtime_hours: float,
  leave_hours: float,
  leave_type: string,
  gross_pay: float,
  deductions_total: float,
  net_pay: float,
  exception_flag: boolean,
  exception_reason: string | null
}
summary_sheet_url: string
quickbooks_journal_id: string
total_gross_payroll: float
exception_count: integer
summary_ready: boolean
summary_timestamp: ISO8601 timestamp

// On exception
exception_report: array of {
  employee_id: string,
  exception_type: 'hours_discrepancy' | 'missing_rate' | 'leave_conflict' | 'unresolved_timesheet',
  detail: string
}
  • QuickBooks API access requires the QuickBooks Online Accounting scope (com.intuit.quickbooks.accounting). OAuth 2.0 credentials must be provisioned and stored in the shared credential store before build. Confirm the connected entity's realm_id.
  • The rate card and leave rules must exist in a confirmed, structured Google Sheet tab before the agent can be built. This is the primary dependency flagged in the data audit stage. Do not proceed to agent build until these tabs are validated.
  • Gross pay calculation applies the hourly rate from the rate card multiplied by regular hours, plus the overtime multiplier (typically 1.5x) for any hours beyond the employee's contracted weekly threshold. Overtime threshold must be stored as a config field per employee, not hardcoded.
  • Leave deductions reduce net pay according to the leave type: unpaid leave deducts at the full hourly rate, sick leave and annual leave are paid at the standard rate per the HR policy. The leave type mapping must be confirmed with the process owner before build.
  • Google Sheets writes should use batch update calls rather than row-by-row writes to stay within API rate limits (100 requests per 100 seconds per user).
  • QuickBooks journal entry staging uses the JournalEntry endpoint. Debit the payroll expense account and credit the payroll liability account. Chart of accounts mapping must be confirmed with the finance manager before build.
  • If any exception_flag is true, a structured exception report must be posted to the #payroll-alerts Slack channel before the summary_ready signal is sent downstream, giving the finance manager an opportunity to intervene before the approval email is generated.
  • A new Google Sheets tab must be created per pay period, named using the convention PayrollSummary_YYYY-MM-DD, to maintain a clean audit trail.
Approval and Submission Agent

Receives the summary_ready signal from the Reconciliation and Calculation Agent. Generates a structured approval request email including the payroll summary totals, a link to the Google Sheets summary tab, and a one-click approval link (implemented as a unique tokenised webhook URL). Sends the email to the business owner via Gmail. Monitors for the approval signal by listening for an inbound webhook call on the unique approval token. On approval, calls the Gusto API to push the finalised payroll data and initiate the payroll run. Posts a confirmation message to the Slack finance channel including the total payroll value and submission timestamp. Does not submit to Gusto without explicit approval. Complexity: Moderate.

Trigger
Reconciliation and Calculation Agent sets summary_ready: true and passes payroll_summary and total_gross_payroll.
Tools
Gmail (API send for approval email), Gusto (API write for payroll submission), Slack (channel post for confirmation).
Replaces steps
Step 8 (Enter Approved Figures into Gusto), Step 9 (Submit Payroll Run in Gusto), Step 11 (Notify Employees of Payment).
Estimated build
10 hours. Complexity: Moderate.
// Input
payroll_summary: array (from Reconciliation and Calculation Agent output)
total_gross_payroll: float
summary_sheet_url: string
pay_period_end_date: ISO8601 date string
approval_recipient_email: string (business owner, from environment config)
approval_webhook_base_url: string
gusto_company_id: string
slack_confirmation_channel_id: string
approval_timeout_hours: integer (default 48)

// On approval (webhook inbound)
approval_token: string (UUID, matched against stored token)
approved_by: string (email of approver)
approved_at: ISO8601 timestamp

// Output (on successful submission)
gusto_payroll_id: string
gusto_submission_timestamp: ISO8601 timestamp
gusto_total_submitted: float
slack_confirmation_sent: boolean
run_complete: boolean

// On timeout (no approval within approval_timeout_hours)
escalation_alert: {
  channel: slack_confirmation_channel_id,
  message: 'Payroll approval pending. No response from approver within timeout window.',
  timestamp: ISO8601
}
  • Gusto API access requires the Gusto Employer API with the payroll:write scope. The company must be on a Gusto plan that includes API access (Gusto Plus or above). Confirm company_id and OAuth credentials before build.
  • The approval token must be a UUID generated fresh per payroll cycle and stored in the orchestration layer's credential store with a TTL matching approval_timeout_hours. Expired tokens must return a 403 and post an alert to Slack.
  • The approval email must include: the pay period dates, total employee count, total gross payroll amount, a link to the Google Sheets summary, and the one-click approval link. The business owner must be able to make a genuine decision from the email alone without opening the sheet.
  • Do not implement an 'auto-approve' fallback. If the approval timeout is reached, escalate via Slack and halt the run. The process owner must re-trigger approval manually.
  • Gusto payroll submission uses the POST /v1/companies/{company_id}/payrolls endpoint. Map net pay and deduction fields from the payroll_summary array to Gusto's employee_compensations structure. Confirm field mapping against the Gusto API reference for the connected plan tier.
  • The Slack confirmation message must include: the Gusto payroll ID, total gross payroll value, submission timestamp, and a link to the Google Sheets summary tab for the period.
  • Gmail sending must use the authenticated Finance Manager's Gmail account (via OAuth 2.0 with the gmail.send scope), not a generic service account, to preserve the email thread context for compliance purposes.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full end-to-end data flow: Payroll Processing Automation. Field names are the canonical names used across all agent IO contracts.
// ============================================================
// PAYROLL PROCESSING AUTOMATION: END-TO-END DATA FLOW
// ============================================================

// TRIGGER
// Scheduled trigger fires on pay_period_end_date (env var: PAYROLL_SCHEDULE_CRON)
// Fields initialised at trigger:
//   pay_period_start_date: '2025-04-15'
//   pay_period_end_date:   '2025-04-28'
//   employee_roster:       loaded from config Google Sheet tab 'Roster'

// ============================================================
// AGENT 1: Timesheet Collection Agent
// ============================================================

// Step 1a: Pull approved timesheets from Deputy
// Deputy API: GET /api/v1/resource/Timesheet
// Params: modified_start=pay_period_start_date, modified_end=pay_period_end_date
// OAuth scope required: Timesheets:Read
deputy_response -> parse {
  deputy_employee_id,     // string, Deputy internal ID
  employee_id,            // string, mapped from Roster tab
  manager_id,             // string
  start_time,             // ISO8601 datetime
  end_time,               // ISO8601 datetime
  total_time,             // float, decimal hours
  status                  // 'approved' | 'pending' | 'open'
}

// Step 1b: Identify missing or unapproved timesheets
// Compare deputy_response[].employee_id against employee_roster[].employee_id
// Where status != 'approved': add to unresolved_flags[]
unresolved_flags[] -> {
  employee_id:    string,
  reason:         'missing' | 'unapproved' | 'timeout',
  escalated_at:   ISO8601 timestamp
}

// Step 1c: Send reminders for unresolved items
// Slack: POST /api/chat.postMessage -> channel: SLACK_PAYROLL_ALERTS_CHANNEL
// Gmail: POST /gmail/v1/users/me/messages/send -> to: manager_email
// Polling interval: TIMESHEET_ESCALATION_HOURS (default 24h)

// Step 1d: Handoff to Agent 2
// Condition: collection_complete = true (all approved) OR escalation_timeout reached
handoff_1_to_2 -> {
  approved_timesheets[]:  array of approved timesheet records
  unresolved_flags[]:     array (may be empty)
  collection_complete:    boolean
  handoff_timestamp:      ISO8601
}

// ============================================================
// AGENT 2: Reconciliation and Calculation Agent
// ============================================================

// Step 2a: Pull published schedule from Deputy for comparison
// Deputy API: GET /api/v1/resource/Schedule
// Params: start=pay_period_start_date, end=pay_period_end_date
deputy_schedule_response -> parse {
  deputy_employee_id,
  scheduled_start,
  scheduled_end,
  scheduled_hours   // float
}

// Step 2b: Compare actual vs scheduled hours per employee
// hours_discrepancy = actual_hours - scheduled_hours
// If abs(hours_discrepancy) > DISCREPANCY_THRESHOLD_HOURS (env var, default 1.0):
//   exception_flag = true, exception_type = 'hours_discrepancy'

// Step 2c: Load leave and deduction rules from Google Sheets
// Google Sheets API: GET spreadsheets/{config_sheet_id}/values/{config_tab_leave_rules}
// Google Sheets API: GET spreadsheets/{config_sheet_id}/values/{config_tab_rate_card}
leave_rules -> {
  employee_id,
  leave_type,         // 'annual' | 'sick' | 'unpaid' | 'public_holiday'
  leave_hours,
  leave_balance_remaining
}
rate_card -> {
  employee_id,
  hourly_rate,        // float, USD
  overtime_threshold, // float, weekly hours before OT kicks in
  overtime_multiplier // float, default 1.5
}

// Step 2d: Calculate gross pay per employee
// regular_hours  = min(actual_hours, overtime_threshold)
// overtime_hours = max(0, actual_hours - overtime_threshold)
// gross_pay = (regular_hours * hourly_rate) + (overtime_hours * hourly_rate * overtime_multiplier)
// deductions = sum of applicable deductions from leave_rules and config
// net_pay = gross_pay - deductions_total

// Step 2e: Write payroll summary to Google Sheets
// Google Sheets API: POST spreadsheets/{payroll_summary_sheet_id}/values:batchUpdate
// Tab name: PayrollSummary_{pay_period_end_date}
summary_row -> {
  employee_id, employee_name,
  scheduled_hours, actual_hours, overtime_hours, leave_hours, leave_type,
  gross_pay, deductions_total, net_pay,
  exception_flag, exception_reason
}

// Step 2f: Stage journal entry in QuickBooks
// QuickBooks API: POST /v3/company/{quickbooks_realm_id}/journalentry
// OAuth scope: com.intuit.quickbooks.accounting
journal_entry -> {
  Line[0]: { DetailType: 'JournalEntryLineDetail', Amount: total_gross_payroll,
             JournalEntryLineDetail: { PostingType: 'Debit', AccountRef: payroll_expense_account } }
  Line[1]: { DetailType: 'JournalEntryLineDetail', Amount: total_gross_payroll,
             JournalEntryLineDetail: { PostingType: 'Credit', AccountRef: payroll_liability_account } }
  DocNumber: 'PAY_{pay_period_end_date}'
}

// Step 2g: Post exception report to Slack if exception_count > 0
// Slack: POST /api/chat.postMessage -> channel: SLACK_PAYROLL_ALERTS_CHANNEL

// Step 2h: Handoff to Agent 3
handoff_2_to_3 -> {
  payroll_summary[]:         array of summary_row objects
  total_gross_payroll:       float
  summary_sheet_url:         string
  quickbooks_journal_id:     string
  exception_count:           integer
  summary_ready:             boolean
  summary_timestamp:         ISO8601
}

// ============================================================
// AGENT 3: Approval and Submission Agent
// ============================================================

// Step 3a: Generate unique approval token
// approval_token = UUID v4, stored with TTL = APPROVAL_TIMEOUT_HOURS (default 48h)
// approval_webhook_url = '{APPROVAL_WEBHOOK_BASE_URL}/approve?token={approval_token}'

// Step 3b: Send approval request email via Gmail
// Gmail API: POST /gmail/v1/users/me/messages/send
// OAuth scope: gmail.send
// To: APPROVER_EMAIL (env var)
// Payload includes:
//   pay_period_start_date, pay_period_end_date,
//   employee_count (len(payroll_summary)),
//   total_gross_payroll,
//   summary_sheet_url,
//   approval_webhook_url

// Step 3c: Wait for approval webhook
// Inbound: GET/POST {APPROVAL_WEBHOOK_BASE_URL}/approve?token={approval_token}
// Validate: approval_token matches stored token AND token not expired
// On valid: record approved_by, approved_at; proceed to Step 3d
// On expired token: return 403, post Slack escalation, halt
// On timeout (APPROVAL_TIMEOUT_HOURS exceeded): post Slack escalation, halt

// Step 3d: Push approved payroll to Gusto
// Gusto API: POST /v1/companies/{gusto_company_id}/payrolls
// OAuth scope: payroll:write
gusto_payload -> {
  pay_period: { start_date, end_date },
  employee_compensations[]: {
    employee_id: string,
    fixed_compensations: [{ name: 'Salary', amount: net_pay }],
    hourly_compensations: [{ name: 'Regular', hours: regular_hours },
                           { name: 'Overtime', hours: overtime_hours }]
  }
}
gusto_response -> parse {
  gusto_payroll_id,
  gusto_submission_timestamp,
  gusto_total_submitted
}

// Step 3e: Post Slack confirmation
// Slack: POST /api/chat.postMessage -> channel: SLACK_CONFIRMATION_CHANNEL
// Payload: gusto_payroll_id, gusto_total_submitted, gusto_submission_timestamp,
//          summary_sheet_url, approved_by, approved_at

// ============================================================
// RUN COMPLETE
// run_complete = true
// All outputs written. No further automation steps.
// ============================================================
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool providing one workflow per agent (three workflows total: Timesheet Collection, Reconciliation and Calculation, Approval and Submission). All three workflows share a single credential store so that OAuth tokens for Deputy, Google Sheets, QuickBooks, Gusto, Gmail, and Slack are managed in one place and rotated without touching workflow logic. Workflows are triggered by a scheduled cron (Agent 1), an inbound webhook from Agent 1 output (Agent 2), and an inbound webhook from Agent 2 output and the approval callback (Agent 3).
Webhook configuration
Two internal webhook endpoints are required for agent-to-agent handoff: one for Agent 1 to Agent 2 (POST, payload: handoff_1_to_2 schema), and one for Agent 2 to Agent 3 (POST, payload: handoff_2_to_3 schema). A third external-facing webhook handles the owner approval callback (GET or POST, validated by approval_token UUID). All webhook URLs must be stored as environment variables and must not be hardcoded. The approval callback webhook must enforce token expiry and return 403 on invalid or expired tokens.
Templating approach
All user-facing message content (Gmail approval email subject and body, Slack alert messages, Slack confirmation message) is stored as template strings in environment variables. Templates use named placeholders (e.g. {{pay_period_end_date}}, {{total_gross_payroll}}, {{approval_webhook_url}}) resolved at runtime. This allows the process owner to adjust message wording without modifying workflow logic. Template variables are validated at runtime; a missing required variable raises an error and halts the workflow before sending.
Error logging
All workflow errors, exceptions, and key state transitions are written to a Supabase table (table: payroll_automation_log) with fields: run_id (UUID), agent_name, event_type ('error' | 'exception' | 'state_change'), event_detail (JSON), timestamp. Any event_type of 'error' triggers an immediate Slack alert to #payroll-alerts. The Supabase table is the primary audit trail and supports the finance manager's month-end reconciliation review. Retention policy: 24 months minimum, to satisfy standard payroll compliance requirements.
Testing approach
All three agents are built and tested in a sandbox environment first, using anonymised data from two prior pay periods provided during the data audit stage. Sandbox credentials are stored separately from production credentials in the credential store. Agent 1 is tested with simulated missing and late timesheet scenarios. Agent 2 is tested with known leave and overtime edge cases. Agent 3 is tested with both valid and expired approval tokens. End-to-end integration testing is performed in the parallel run (Week 4), running the automation alongside the manual process for one full pay cycle and reconciling outputs before go-live.
Estimated total build time
Timesheet Collection Agent: 10 hours. Reconciliation and Calculation Agent: 18 hours. Approval and Submission Agent: 10 hours. End-to-end integration testing and parallel run support: 0 hours (covered in UAT stage, not counted in per-agent totals but included in the 38-hour total build effort). Total: 38 hours.
Pre-build blockers to resolve before any agent build starts: (1) Confirm Deputy plan tier and enable Timesheets:Read OAuth scope. (2) Confirm Gusto plan tier supports API access and provision OAuth credentials with payroll:write scope. (3) Complete the leave rules and rate card data audit and populate the confirmed Google Sheet config tabs. (4) Confirm QuickBooks chart of accounts mapping for payroll expense and liability accounts. (5) Confirm the Slack channel name for alerts and confirmation posts. (6) Confirm the business owner's email address as the approval recipient (APPROVER_EMAIL env var). None of these items can be substituted with assumptions during build.
For support during build or after go-live, contact the FullSpec team at support@gofullspec.com. Reference your process name (Payroll Processing) and template ID (finance-payroll-processing) in all correspondence.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Payroll Processing.

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