Back to Payroll Processing

Integration and API Spec

The reference during the build: every tool connection, auth scope, field mapping, and error-handling rule.

4 pagesPDF · Technical
FS-DOC-05Technical

Integration and API Spec

Payroll Processing Automation

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

This document specifies every integration point required to build the Payroll Processing automation. It covers tool authentication, exact OAuth scopes, webhook and trigger configuration, field-level data mappings between agents, the orchestration layer structure, the credential store schema, and the required error-handling behaviour for every integration. The FullSpec team uses this document as the authoritative technical reference during build and test. No credential or integration detail should be hardcoded into workflow logic; all secrets are stored in the credential store as defined in Section 04.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agents
Orchestration layer
Hosts all three agent workflows, manages scheduling, credential store, retry logic, and inter-agent handoffs
Internal service account
Self-hosted or cloud instance with API plugin support
All agents
Deputy
Source of approved shift hours and employee schedule data for the pay period
OAuth 2.0 (Authorization Code)
Premium (API access required)
Agent 1, Agent 2
Slack
Delivers exception alerts to the finance channel and posts final payroll confirmation
OAuth 2.0 (Bot Token)
Free tier sufficient; Pro recommended for audit log retention
Agent 1, Agent 3
Gmail
Sends automated timesheet reminder emails and the owner approval request
OAuth 2.0 (Authorization Code, Google Identity)
Google Workspace (any tier)
Agent 1, Agent 3
Google Sheets
Payroll summary staging, deduction rule lookup table, and audit log
OAuth 2.0 (Service Account JSON key)
Google Workspace (any tier)
Agent 2
QuickBooks
General ledger journal entry staging for payroll expense reconciliation
OAuth 2.0 (Authorization Code, Intuit Identity Platform)
QuickBooks Online Essentials or above
Agent 2
Gusto
Receives finalised payroll data via API write and triggers employee payments
OAuth 2.0 (Authorization Code, Gusto Developer Platform)
Gusto Plus or above (API access required; Gusto Simple does not expose payroll write API)
Agent 3
Before you connect anything: configure and validate every integration against a sandbox or staging environment before applying production credentials. Deputy, Gusto, and QuickBooks all provide sandbox tenants for this purpose. Do not point any agent at a live payroll dataset until all three agents have passed the full QA plan in sandbox. Connecting to production prematurely is the single most common cause of data integrity issues in payroll automations.

02Per-tool integration detail

Deputy

REST API v2. Used by the Timesheet Collection Agent (Agent 1) to retrieve approved shift records for the closed pay period, and by the Reconciliation Agent (Agent 2) to cross-reference scheduled versus worked hours.

Auth method
OAuth 2.0 Authorization Code flow. Access token and refresh token stored in the credential store. Token endpoint: https://once.deputy.com/my/oauth/access_token. Tokens expire after 1 hour; refresh automatically using the stored refresh token.
Required scopes
longlife_refresh_token, read:employee, read:roster, read:timesheet, read:leave
Webhook / trigger setup
No inbound webhook is used. Agent 1 polls the Deputy API on a schedule aligned to the pay period close date. Configure the poll as a scheduled trigger (cron) set to fire at 00:01 on the morning after each period close date. The schedule must match the configured payroll frequency (bi-weekly or monthly) stored in the credential store as PAYROLL_SCHEDULE_CRON.
Required configuration
DEPUTY_SUBDOMAIN stored in credential store (e.g. yourcompany.au.deputy.com). Pay period date range is derived at runtime from PAYROLL_SCHEDULE_CRON and the current execution date. Do not hardcode date ranges. Employee ID mapping table maintained in the Google Sheets config tab (column: deputy_employee_id) links Deputy records to Gusto employee IDs.
Rate limits
200 requests per minute per access token. At current volume (26 pay runs per year, approximately 5 to 150 employees per pull), a single pay-period pull will generate at most 5 paginated requests. No throttling logic required at current volume. Add throttling if employee count exceeds 500.
Constraints
Only timesheets in 'Approved' status are pulled. Timesheets in 'Pending' or 'Open' status are treated as exceptions and routed to the exception report. The Deputy API does not return deleted timesheets; any retroactive deletion by a manager after the period close will not be detected automatically and must be flagged in the manual exception review.
// Endpoint used
GET /api/v2/resource/Timesheet
  ?search={"select":["EmployeeId","Date","StartTime","EndTime","TotalTime","ApprovalStatus","LeaveId"]}
  &search={"where":[{"field":"Date","type":"ge","data":"{{period_start}}"}]}
// Output fields consumed downstream
timesheet.EmployeeId -> maps to EMPLOYEE_ID_MAP[deputy_employee_id]
timesheet.TotalTime  -> gross_hours (decimal hours)
timesheet.LeaveId    -> leave_flag (boolean, non-null = leave applied)
timesheet.ApprovalStatus -> filter: pass only 'Approved'
Slack

Slack Web API (Bot). Used by Agent 1 to post exception reports to the finance channel when missing or unapproved timesheets are found, and by Agent 3 to post the final payroll confirmation message after Gusto submission.

Auth method
OAuth 2.0 Bot Token (xoxb-*). Stored in credential store as SLACK_BOT_TOKEN. Scopes granted during app installation via Slack App admin panel.
Required scopes
chat:write, chat:write.public, channels:read, files:write
Webhook / trigger setup
Outbound only. No inbound webhook or event subscription is required for this automation. The orchestration layer calls chat.postMessage on the Slack Web API at two points: exception report dispatch (Agent 1) and payroll confirmation (Agent 3).
Required configuration
SLACK_FINANCE_CHANNEL_ID stored in credential store (do not use channel name, use the channel ID string, e.g. C04XXXXXYZ). SLACK_BOT_TOKEN stored in credential store. The Slack app must be added to the finance channel before first run.
Rate limits
Tier 3: 50+ requests per minute for chat.postMessage. At two messages per pay run (26 runs per year) the rate limit is not a constraint. No throttling required.
Constraints
Messages must be structured using Slack Block Kit JSON for readability. Plain-text fallback must be included in every block payload for notification previews. The bot must not post to the channel unless it has been explicitly invited.
// Exception report message (Agent 1)
POST https://slack.com/api/chat.postMessage
{
  "channel": "{{SLACK_FINANCE_CHANNEL_ID}}",
  "text": "Payroll exception report — {{period_label}}",
  "blocks": [ ...Block Kit exception list... ]
}
// Confirmation message (Agent 3)
{
  "channel": "{{SLACK_FINANCE_CHANNEL_ID}}",
  "text": "Payroll submitted to Gusto — {{period_label}} | Total: {{total_payroll_usd}}",
  "blocks": [ ...Block Kit confirmation... ]
}
Gmail

Gmail API v1 via Google Workspace OAuth. Used by Agent 1 to send automated timesheet deadline reminder emails to managers and by Agent 3 to send the approval request email to the business owner.

Auth method
OAuth 2.0 Authorization Code flow using Google Identity. Access token and refresh token stored in credential store as GMAIL_ACCESS_TOKEN and GMAIL_REFRESH_TOKEN. Token endpoint: https://oauth2.googleapis.com/token.
Required scopes
https://www.googleapis.com/auth/gmail.send, https://www.googleapis.com/auth/gmail.readonly (readonly required for approval signal polling, see Agent 3 trigger)
Webhook / trigger setup
Outbound send: Gmail API users.messages.send. Inbound approval signal: Agent 3 polls the Gmail inbox of the sender account for a reply containing the approval token string (see field mapping, Section 03). Polling interval: every 2 minutes for up to 48 hours after the approval email is sent. Use Gmail search query: from:{{OWNER_EMAIL}} subject:{{APPROVAL_SUBJECT_PREFIX}} after:{{email_sent_timestamp}}.
Required configuration
GMAIL_SENDER_ADDRESS stored in credential store (the Google Workspace account authorised for send). OWNER_EMAIL stored in credential store (the business owner's email address to send approval requests to). APPROVAL_SUBJECT_PREFIX stored in credential store (e.g. '[PAYROLL APPROVAL]'). Email body templates stored as plain-text files in the orchestration layer asset store; do not hardcode email body content in workflow nodes.
Rate limits
Gmail API quota: 250 quota units per user per second; users.messages.send costs 100 units. Maximum sends per pay run: approximately 20 reminder emails plus 1 approval email. Well within quota. No throttling required.
Constraints
The approval email must include a unique, time-limited approval token embedded in the reply subject or body so the polling logic can match the correct response. Tokens must be generated per pay run and stored in the run context. The gmail.readonly scope is required on the sender account; if a dedicated service account is used for send, ensure it also has mailbox delegation to read replies.
// Approval email send (Agent 3)
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
{
  "raw": base64url(MIMEMessage{
    To: {{OWNER_EMAIL}},
    Subject: "{{APPROVAL_SUBJECT_PREFIX}} {{period_label}} — Action required",
    Body: approval_template.txt rendered with {
      period_label, total_gross_pay, employee_count,
      sheets_url, approval_token
    }
  })
}
// Approval signal poll (Agent 3)
GET https://gmail.googleapis.com/gmail/v1/users/me/messages
  ?q=from:{{OWNER_EMAIL}} subject:{{APPROVAL_SUBJECT_PREFIX}} after:{{sent_unix_ts}}
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
Google Sheets

Google Sheets API v4 via Service Account. Used exclusively by Agent 2 (Reconciliation and Calculation Agent) to write the payroll summary, read deduction and leave rule configuration, and maintain the per-run audit log.

Auth method
OAuth 2.0 Service Account with JSON key file. Key file stored in credential store as GSHEETS_SERVICE_ACCOUNT_KEY (base64-encoded JSON). The service account must be granted Editor access to the target spreadsheet. Do not use an individual user's OAuth token for Sheets writes; service account is required for unattended execution.
Required scopes
https://www.googleapis.com/auth/spreadsheets, https://www.googleapis.com/auth/drive.readonly
Webhook / trigger setup
No webhook. Agent 2 writes to the sheet synchronously after reconciliation is complete. No inbound trigger from Sheets is used.
Required configuration
GSHEETS_SPREADSHEET_ID stored in credential store. Spreadsheet must contain three named tabs before go-live: 'PayrollSummary' (output written by Agent 2 each run), 'DeductionRules' (static config: employee_id, deduction_type, deduction_amount_or_pct, effective_date), 'AuditLog' (append-only; one row per run). Column headers for PayrollSummary must match the field mapping in Section 03 exactly. Template spreadsheet is provided by FullSpec during build.
Rate limits
Google Sheets API: 300 write requests per minute per project, 60 requests per minute per user. A single Agent 2 run writes at most 3 batch update requests (summary rows, audit log entry, status flag). No throttling required.
Constraints
Rows in PayrollSummary must never be deleted by the automation; only appended or updated in the designated pay-period block. The AuditLog tab is append-only. Do not allow the automation to clear or overwrite any row outside the current pay period range. The spreadsheet must be shared with the service account email, not opened to public or anyone-with-link access.
// Write payroll summary rows (Agent 2)
POST https://sheets.googleapis.com/v4/spreadsheets/{{GSHEETS_SPREADSHEET_ID}}/values
  /PayrollSummary!A{{row_start}}:M{{row_end}}:append
// Read deduction rules (Agent 2)
GET https://sheets.googleapis.com/v4/spreadsheets/{{GSHEETS_SPREADSHEET_ID}}/values
  /DeductionRules!A:F
// Append audit log entry (Agent 2)
POST .../AuditLog!A:H:append
  { run_id, period_label, employee_count, total_gross, status, timestamp, triggered_by }
QuickBooks

QuickBooks Online Accounting API v3 (Intuit Developer Platform). Used by Agent 2 to stage general ledger journal entries for the payroll expense after the payroll summary is complete.

Auth method
OAuth 2.0 Authorization Code flow via Intuit Identity Platform. Access token (1-hour TTL) and refresh token (100-day TTL) stored in credential store as QBO_ACCESS_TOKEN and QBO_REFRESH_TOKEN. Token endpoint: https://oauth2.platform.intuit.com/oauth2/v1/tokens/bearer. Realm ID (Company ID) stored as QBO_REALM_ID.
Required scopes
com.intuit.quickbooks.accounting
Webhook / trigger setup
No inbound webhook. Agent 2 makes a synchronous POST to the JournalEntry endpoint after payroll calculations are complete. QuickBooks webhooks are not consumed by this automation.
Required configuration
QBO_REALM_ID, QBO_ACCESS_TOKEN, QBO_REFRESH_TOKEN stored in credential store. Payroll expense account ID and payroll liability account ID must be retrieved once during setup and stored as QBO_PAYROLL_EXPENSE_ACCOUNT_ID and QBO_PAYROLL_LIABILITY_ACCOUNT_ID in the credential store. Do not hardcode account IDs; they are company-specific.
Rate limits
QuickBooks Online API: 500 requests per minute per Realm ID; throttle to 50 if the API returns a 429. At one JournalEntry POST per pay run, rate limiting is not a concern at current volume.
Constraints
JournalEntry lines must balance (debits equal credits) or the API will return a 400 validation error. The automation must validate the debit/credit balance before posting. Minor version must be included in all requests (e.g. minorversion=65). QuickBooks Online Essentials or above is required; Simple Start does not include journal entry write access.
// Stage journal entry (Agent 2)
POST https://quickbooks.api.intuit.com/v3/company/{{QBO_REALM_ID}}/journalentry
  ?minorversion=65
{
  "DocNumber": "PAYROLL-{{period_label}}",
  "TxnDate": "{{period_end_date}}",
  "Line": [
    { "Amount": {{total_gross}}, "DetailType": "JournalEntryLineDetail",
      "JournalEntryLineDetail": { "PostingType": "Debit",
        "AccountRef": { "value": "{{QBO_PAYROLL_EXPENSE_ACCOUNT_ID}}" } } },
    { "Amount": {{total_gross}}, "DetailType": "JournalEntryLineDetail",
      "JournalEntryLineDetail": { "PostingType": "Credit",
        "AccountRef": { "value": "{{QBO_PAYROLL_LIABILITY_ACCOUNT_ID}}" } } }
  ]
}
Gusto

Gusto Partner API v1 (Gusto Developer Platform). Used exclusively by Agent 3 (Approval and Submission Agent) to push finalised employee hours and pay data after the business owner's approval is confirmed.

Auth method
OAuth 2.0 Authorization Code flow via Gusto Developer Platform. Access token and refresh token stored in credential store as GUSTO_ACCESS_TOKEN and GUSTO_REFRESH_TOKEN. Token endpoint: https://api.gusto.com/oauth/token. Access tokens expire after 2 hours.
Required scopes
employees:read, payrolls:run, payrolls:write, companies:read
Webhook / trigger setup
No inbound webhook consumed. Agent 3 triggers a payroll update via API write after the approval signal is received from Gmail polling. The Gusto payroll run must already exist for the period (created manually or via a prior scheduled run in Gusto); the automation updates employee inputs on an existing run, it does not create new payroll runs from scratch.
Required configuration
GUSTO_COMPANY_ID stored in credential store. GUSTO_PAYROLL_PERIOD_DATES stored dynamically per run (derived from PAYROLL_SCHEDULE_CRON). Employee ID mapping table in Google Sheets 'DeductionRules' tab must include a gusto_employee_id column mapped to deputy_employee_id. Do not hardcode any employee ID or payroll run ID.
Rate limits
Gusto API: 60 requests per minute per access token. A single pay run update touching 150 employees may require up to 5 paginated PUT requests. Well within limits. Implement a 1-second delay between paginated payroll update requests as a precaution.
Constraints
Gusto API write access requires the Gusto Plus plan or above. Gusto Simple does not expose payroll write endpoints. The payroll run must be in 'unprocessed' state before the automation can update it; if the run has already been submitted or is locked, the automation must raise an error and alert via Slack rather than retrying. All monetary values must be submitted in US dollars as strings with two decimal places (e.g. '1250.00').
// Update employee hours on existing payroll run (Agent 3)
PUT https://api.gusto.com/v1/companies/{{GUSTO_COMPANY_ID}}/payrolls
  /{{payroll_run_id}}/employees/{{gusto_employee_id}}
{
  "employee_id": "{{gusto_employee_id}}",
  "fixed_compensations": [],
  "hourly_compensations": [
    { "name": "Regular Hours", "hours": "{{regular_hours}}" },
    { "name": "Overtime",       "hours": "{{overtime_hours}}" }
  ]
}
// Response: updated payroll_run object; check status == 'unprocessed' before PUT
Integration and API SpecPage 2 of 4
FS-DOC-05Technical

03Field mappings between tools

The tables below define the exact field-level mappings for each inter-agent handoff. Use the monospace field names as the canonical identifiers in all workflow node configurations. Any transformation or type conversion required is noted in the destination field column.

Handoff 1: Deputy to Reconciliation Agent (Agent 1 output to Agent 2 input)

Source tool
Source field
Destination tool / agent
Destination field
Transformation
Deputy
`timesheet.EmployeeId`
Google Sheets / Agent 2
`employee_id`
Lookup via ID map: deputy_employee_id -> internal employee_id
Deputy
`timesheet.TotalTime`
Google Sheets / Agent 2
`gross_hours`
Decimal hours (float); divide by 60 if Deputy returns minutes
Deputy
`timesheet.Date`
Google Sheets / Agent 2
`shift_date`
ISO 8601 string YYYY-MM-DD
Deputy
`timesheet.ApprovalStatus`
Agent 2 exception check
`approval_status`
Pass only 'Approved'; all others -> exception_list[]
Deputy
`timesheet.LeaveId`
Google Sheets / Agent 2
`leave_flag`
Boolean: null -> false, non-null -> true
Deputy
`timesheet.StartTime`
Agent 2 reconciliation
`shift_start`
ISO 8601 datetime; used for overtime threshold check
Deputy
`timesheet.EndTime`
Agent 2 reconciliation
`shift_end`
ISO 8601 datetime; used for overtime threshold check

Handoff 2: Reconciliation Agent output to Google Sheets PayrollSummary tab (Agent 2 write)

Source tool
Source field
Destination tool
Destination field
Transformation
Agent 2 (calculated)
`employee_id`
Google Sheets
`A: employee_id`
String, no transform
Agent 2 (calculated)
`employee_name`
Google Sheets
`B: employee_name`
String; resolved from ID map
Agent 2 (calculated)
`period_label`
Google Sheets
`C: pay_period`
String e.g. '2025-05-01 to 2025-05-14'
Agent 2 (calculated)
`regular_hours`
Google Sheets
`D: regular_hours`
Decimal, 2dp
Agent 2 (calculated)
`overtime_hours`
Google Sheets
`E: overtime_hours`
Decimal, 2dp; 0.00 if none
Agent 2 (calculated)
`leave_hours`
Google Sheets
`F: leave_hours`
Decimal, 2dp; 0.00 if none
Agent 2 (calculated)
`gross_pay`
Google Sheets
`G: gross_pay_usd`
String '0.00' format, USD
Agent 2 (calculated)
`deductions_total`
Google Sheets
`H: deductions_usd`
String '0.00' format, USD
Agent 2 (calculated)
`net_pay`
Google Sheets
`I: net_pay_usd`
String '0.00' format, USD
Agent 2 (calculated)
`exception_flag`
Google Sheets
`J: exception_flag`
Boolean; TRUE if any exception was overridden
Agent 2 (calculated)
`run_id`
Google Sheets
`K: run_id`
UUID generated per pay run
Agent 2 (calculated)
`gusto_employee_id`
Google Sheets
`L: gusto_employee_id`
String; resolved from ID map for Agent 3 use
Agent 2 (calculated)
`summary_status`
Google Sheets
`M: status`
Enum: 'pending_approval', 'approved', 'submitted'

Handoff 3: Google Sheets PayrollSummary to Gusto (Agent 3 write)

Source tool
Source field
Destination tool
Destination field
Transformation
Google Sheets
`L: gusto_employee_id`
Gusto
`employee_id`
String, no transform
Google Sheets
`D: regular_hours`
Gusto
`hourly_compensations[0].hours`
String with 4dp e.g. '40.0000'
Google Sheets
`E: overtime_hours`
Gusto
`hourly_compensations[1].hours`
String with 4dp; omit if 0
Google Sheets
`K: run_id`
Gusto (metadata)
`fixed_compensations[].memo`
Used for traceability only

Handoff 4: Approval email approval token (Gmail Agent 3 approval signal)

Source tool
Source field
Destination tool
Destination field
Transformation
Agent 3 (generated)
`approval_token`
Gmail (email body)
`approval_token`
UUID; embedded in email body and reply-to subject prefix
Gmail (poll result)
`message.subject`
Agent 3 logic
`detected_token`
Regex extract UUID from subject string
Gmail (poll result)
`message.from`
Agent 3 validation
`sender_email`
Must match OWNER_EMAIL exactly; reject if mismatch

04Build stack and orchestration

Orchestration layout
Three separate workflows, one per agent. Agent 1 (Timesheet Collection), Agent 2 (Reconciliation and Calculation), Agent 3 (Approval and Submission). Each workflow is self-contained with its own error handler. Agents communicate via a shared run-context object stored in the orchestration layer's key-value store, keyed by run_id (UUID). A fourth lightweight coordinator workflow manages the scheduled trigger and chains Agent 1 -> Agent 2 -> Agent 3 by writing and watching the run-context status field.
Shared credential store
All secrets are stored in the orchestration layer's built-in encrypted credential store. No credential is written into a workflow node, environment variable file, or Google Sheet. The credential store is the single source of truth for all API tokens and configuration constants.
Agent 1 trigger mechanism
Scheduled (poll). Cron expression derived from PAYROLL_SCHEDULE_CRON (e.g. '1 0 1,15 * *' for bi-weekly on the 1st and 15th). The coordinator workflow fires at schedule, initialises run_id, writes status='collection_started' to key-value store, then triggers Agent 1.
Agent 2 trigger mechanism
Event-driven (key-value store watch). Agent 2 workflow is triggered when the coordinator detects run-context status='collection_complete'. No polling interval; the coordinator checks the key-value store status after Agent 1 completion callback.
Agent 3 trigger mechanism
Event-driven (key-value store watch) plus inbound poll on Gmail. Agent 3 is triggered when status='reconciliation_complete'. After sending the approval email, Agent 3 enters a polling sub-workflow (2-minute interval, 48-hour timeout) watching for the Gmail approval signal. On approval detection, Agent 3 validates the token and proceeds to Gusto write.
Approval signal validation
On each Gmail poll, the detected reply is validated: (1) sender must match OWNER_EMAIL exactly, (2) extracted UUID must match the approval_token stored in the current run context, (3) email timestamp must be after the approval request send time. All three checks must pass before Gusto write proceeds.
Inter-agent data handoff
Structured JSON run-context object stored in orchestration key-value store under key 'payroll_run_{{run_id}}'. Each agent reads and appends to this object. The Google Sheets PayrollSummary URL is also stored in the run context and passed to Agent 3 for inclusion in the approval email.

Credential store contents (all keys required before first run):

All keys stored in orchestration credential store. Never write these values into workflow node fields, code blocks, or version-controlled files.
// ── Deputy ────────────────────────────────────────────────
DEPUTY_SUBDOMAIN              = "yourcompany.au.deputy.com"
DEPUTY_CLIENT_ID              = "<oauth_client_id>"
DEPUTY_CLIENT_SECRET          = "<oauth_client_secret>"
DEPUTY_ACCESS_TOKEN           = "<current_access_token>"
DEPUTY_REFRESH_TOKEN          = "<refresh_token>"

// ── Slack ─────────────────────────────────────────────────
SLACK_BOT_TOKEN               = "xoxb-<token>"
SLACK_FINANCE_CHANNEL_ID      = "C04XXXXXYZ"

// ── Gmail ─────────────────────────────────────────────────
GMAIL_SENDER_ADDRESS          = "payroll-auto@yourcompany.com"
GMAIL_ACCESS_TOKEN            = "<current_access_token>"
GMAIL_REFRESH_TOKEN           = "<refresh_token>"
GMAIL_CLIENT_ID               = "<google_oauth_client_id>"
GMAIL_CLIENT_SECRET           = "<google_oauth_client_secret>"
OWNER_EMAIL                   = "owner@yourcompany.com"
APPROVAL_SUBJECT_PREFIX       = "[PAYROLL APPROVAL]"

// ── Google Sheets ─────────────────────────────────────────
GSHEETS_SPREADSHEET_ID        = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms"
GSHEETS_SERVICE_ACCOUNT_KEY   = "<base64_encoded_service_account_json>"

// ── QuickBooks Online ─────────────────────────────────────
QBO_REALM_ID                  = "<company_id>"
QBO_CLIENT_ID                 = "<intuit_client_id>"
QBO_CLIENT_SECRET             = "<intuit_client_secret>"
QBO_ACCESS_TOKEN              = "<current_access_token>"
QBO_REFRESH_TOKEN             = "<refresh_token>"
QBO_PAYROLL_EXPENSE_ACCOUNT_ID = "<account_id>"
QBO_PAYROLL_LIABILITY_ACCOUNT_ID = "<account_id>"

// ── Gusto ─────────────────────────────────────────────────
GUSTO_COMPANY_ID              = "<gusto_company_uuid>"
GUSTO_CLIENT_ID               = "<gusto_oauth_client_id>"
GUSTO_CLIENT_SECRET           = "<gusto_oauth_client_secret>"
GUSTO_ACCESS_TOKEN            = "<current_access_token>"
GUSTO_REFRESH_TOKEN           = "<refresh_token>"

// ── Orchestration / Schedule ──────────────────────────────
PAYROLL_SCHEDULE_CRON         = "1 0 1,15 * *"
EMPLOYEE_ID_MAP_SHEET_TAB     = "DeductionRules"
RUN_CONTEXT_TTL_HOURS         = 72
Integration and API SpecPage 3 of 4
FS-DOC-05Technical

05Error handling and retry logic

Unhandled exceptions must never fail silently. Every integration point has a defined failure path. If an error cannot be resolved by retry and fallback, the orchestration layer must write the error to the run-context audit log, post a descriptive alert to SLACK_FINANCE_CHANNEL_ID, and halt the affected agent workflow. The run must never proceed to Gusto submission in an unknown or partially-failed state.
Integration
Scenario
Required behaviour
Deputy API
HTTP 401 Unauthorized on timesheet pull
Attempt token refresh using DEPUTY_REFRESH_TOKEN. If refresh succeeds, retry the original request once. If refresh fails or returns 401, halt Agent 1, post alert to Slack with message 'Deputy auth failure — manual token refresh required', and write error to audit log. Do not retry further.
Deputy API
HTTP 429 Rate limit exceeded
Apply exponential backoff: wait 30 seconds, retry. Wait 60 seconds, retry. Wait 120 seconds, retry. After 3 retries, halt Agent 1 and post Slack alert 'Deputy rate limit — payroll collection delayed'. Log retry attempts and timestamps.
Deputy API
Partial response: one or more employees missing from API response
Compare returned EmployeeId list against the full employee roster in the ID map. Any missing employee is added to the exception_list with reason 'No timesheet data returned from Deputy'. Post exception report to Slack. Do not block the run; allow Agent 2 to proceed with available data and flag the gap.
Slack
HTTP 404 channel_not_found on chat.postMessage
Retry once after 10 seconds. If second attempt fails, write error to audit log and continue workflow execution. The Slack notification is non-blocking; failure to post must not halt payroll processing. Alert support@gofullspec.com via a fallback email using Gmail if Slack is unavailable for more than two consecutive pay runs.
Gmail (send)
HTTP 429 or quota exceeded on users.messages.send
Wait 60 seconds and retry up to 3 times with exponential backoff (60s, 120s, 240s). If all retries fail, log the failure and write the unsent email body to the run-context store so the finance manager can send manually. Post Slack alert with the approval request summary text inline.
Gmail (approval poll)
No approval reply received within 48 hours
After 48-hour timeout, halt Agent 3, set run status to 'approval_timeout', post Slack alert 'Payroll approval has not been received — manual follow-up required', and write to audit log. Do not submit to Gusto. The finance manager must reinitiate the approval manually via the SOP runbook.
Gmail (approval poll)
Approval reply received but token mismatch or wrong sender
Reject the reply silently (do not submit to Gusto). Log the invalid attempt with sender address and timestamp. Continue polling for the correct approval until timeout. Post a Slack warning 'Unexpected reply to approval email — possible misdirected message; original approval still awaited'.
Google Sheets
HTTP 403 Forbidden on spreadsheet write
Retry once after 15 seconds. If second attempt fails, halt Agent 2 and post Slack alert 'Google Sheets write failed — service account permissions may have changed'. Log error with spreadsheet ID and attempted range. The finance manager must verify the service account still has Editor access before triggering a manual rerun.
QuickBooks Online
HTTP 400 Bad Request on JournalEntry POST (unbalanced lines)
Do not retry. Log the full request body and response to the audit log. Post Slack alert 'QuickBooks journal entry rejected — debit/credit imbalance detected'. Halt Agent 2 journal staging only; the payroll summary write to Google Sheets and the subsequent approval flow must still proceed. The finance manager must post the journal entry manually for the affected period.
QuickBooks Online
OAuth token expired during Agent 2 execution
Attempt token refresh using QBO_REFRESH_TOKEN before the API call (proactive refresh if token age exceeds 50 minutes). If refresh fails, retry the token exchange once after 30 seconds. If both fail, halt journal staging, log error, post Slack alert. Do not halt the broader payroll run.
Gusto API
Payroll run not in 'unprocessed' state on PUT attempt
Immediately halt Agent 3. Do not attempt the write. Post Slack alert 'Gusto payroll run is locked or already submitted — manual review required before automation can proceed'. Write full run-context state to audit log. Raise an incident with support@gofullspec.com. This scenario indicates a concurrent manual action in Gusto and must be resolved by a human before any further automation attempt.
Gusto API
HTTP 422 Unprocessable Entity on employee hours PUT (validation error)
Log the full error response and the affected employee_id. Skip the failing employee record and continue writing the remaining employees. After all records are attempted, post a Slack alert listing all failed employee IDs with the Gusto validation error text. The finance manager must enter the failed records manually in Gusto before submitting the run.
For build support or to report an integration error during development or go-live, contact the FullSpec team at support@gofullspec.com. Include the run_id from the audit log in any support request to enable fast diagnosis.
Integration and API SpecPage 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
Developer Handover Pack
Technical · Developer
View
Test and QA Plan
Quality · Developer
View