FS-DOC-05Technical
Integration and API Spec
Timesheet Collection and Approval
[YourCompany.com] · HR Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative integration reference for the Timesheet Collection and Approval automation. It covers every tool connected across the three agents (Collection Agent, Approval Agent, and Payroll Sync Agent), including exact OAuth scopes, webhook configuration, field-level mappings, credential store layout, and defined failure behaviours. The FullSpec team uses this specification to build and verify each integration point. No credentials, IDs, or configuration values should be hardcoded in workflow logic; all such values are stored in the shared credential store as documented 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, and retry logic
N/A (internal)
Standard/paid tier with webhook and scheduler support
All agents
Google Forms
Standardised employee timesheet input; responses feed the Collection Agent
OAuth 2.0 (Google account)
Free (Google Workspace or personal)
Agent 1 (Collection)
Google Sheets
Master timesheet and approval tracker; read and written by all three agents
OAuth 2.0 (Google account)
Free (Google Workspace or personal)
Agents 1, 2, 3
Gmail
Employee submission reminders and manager approval routing emails
OAuth 2.0 (Google account)
Free (Google Workspace or personal)
Agents 1, 2
Slack
Chase messages to employees and payroll-ready confirmation to payroll owner
OAuth 2.0 (Slack app)
Free tier supports API; Pro recommended for audit logging
Agents 1, 3
Xero
Payroll data destination; approved hours pushed via Xero Payroll API
OAuth 2.0 (Xero app)
Xero Payroll plan (AU/NZ/UK) or Xero Payroll add-on
Agent 3 (Payroll Sync)
Before you connect anything: sandbox-test every integration using non-production credentials before any production credentials are entered into the credential store. For Google services, use a dedicated service account or a test Google Workspace user. For Xero, use the Xero Demo Company environment. For Slack, use a private test workspace. No live payroll data should be present in any environment until the Test and QA Plan sign-off is complete.
02Per-tool integration detail
Google Forms
Provides the standardised timesheet input form that employees complete each pay period. The Collection Agent reads new form responses via the Google Forms API and also listens for new response events via a Google Apps Script webhook or Forms trigger. All responses are written to the linked Google Sheet response tab automatically by Forms.
Auth method
OAuth 2.0, authorised under the Google account that owns the Form. Access token refreshed automatically using the stored refresh token.
Required scopes
https://www.googleapis.com/auth/forms.responses.readonly | https://www.googleapis.com/auth/drive.readonly (to enumerate Form IDs)
Webhook / trigger setup
Google Forms does not support native outbound webhooks. A Google Apps Script installable trigger (onFormSubmit) must be deployed against the Form. The script POSTs a payload containing responseId, employeeEmail, and submittedAt to the orchestration layer inbound webhook URL. The inbound webhook URL is stored in the credential store as FORMS_WEBHOOK_TARGET_URL. The Apps Script project ID is stored as FORMS_APPS_SCRIPT_PROJECT_ID.
Required configuration
Form ID stored as GOOGLE_FORMS_FORM_ID in the credential store. The Form must contain fields matching the canonical field names in Section 03. The linked response Google Sheet tab name must match SHEETS_RESPONSES_TAB_NAME.
Rate limits
Google Forms API: 1,000 requests/100 seconds per project. At 40 to 80 submissions per pay cycle, peak volume is well below this limit. No throttling logic required at current volume.
Constraints
The Form must be linked to exactly one Google Sheet. If the Form is duplicated or the response Sheet is changed, the GOOGLE_FORMS_FORM_ID and SHEETS_RESPONSES_TAB_NAME values must be updated in the credential store immediately. Employees must submit via this Form only; email or attachment submissions will not be processed by the Collection Agent.
// Inbound webhook payload from Apps Script trigger
{
"responseId": "string",
"employeeEmail": "string",
"submittedAt": "ISO8601 timestamp",
"formId": "string"
}
// Collection Agent then calls Forms API to retrieve full response body
GET https://forms.googleapis.com/v1/forms/{GOOGLE_FORMS_FORM_ID}/responses/{responseId}Google Sheets
Acts as the shared state layer across all three agents. The Collection Agent reads the employee list and writes validated hours. The Approval Agent reads manager assignments and writes approval timestamps. The Payroll Sync Agent reads finalised rows before pushing to Xero. All agents use the Google Sheets API v4.
Auth method
OAuth 2.0, same Google account as Forms. A single set of credentials covers both services. Refresh token stored in credential store as GOOGLE_OAUTH_REFRESH_TOKEN.
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and write to master sheet)
Webhook / trigger setup
No native outbound webhook. The Collection Agent polls the Responses tab on a configurable interval (default: every 5 minutes during the 48-hour collection window) using the Sheets API. The Approval Agent uses a separate Apps Script onEdit trigger on the Approvals tab to notify the orchestration layer when a manager approval is written. Trigger target URL stored as SHEETS_APPROVAL_WEBHOOK_TARGET_URL.
Required configuration
GOOGLE_SHEETS_MASTER_SHEET_ID stored in credential store. Tab names: SHEETS_EMPLOYEES_TAB_NAME (employee roster), SHEETS_RESPONSES_TAB_NAME (raw form responses), SHEETS_APPROVALS_TAB_NAME (manager sign-offs), SHEETS_SYNC_TAB_NAME (rows ready for Xero sync). Column header names must exactly match canonical field names in Section 03 for all read and write operations.
Rate limits
Sheets API v4: 300 read requests/minute per project, 300 write requests/minute per project. At 80 employees and three agents, peak concurrent writes are estimated at under 50 requests/minute. No throttling required at current volume, but write operations should be batched per cycle rather than row-by-row where possible.
Constraints
Do not use merged cells anywhere in the master sheet. Merged cells break range reads. Column order must not be changed once the sheet schema is locked. If columns are reordered, all agent field mappings must be updated and re-tested. The sheet must not be shared with edit access beyond the automation service account and authorised HR users.
// Read: employee roster (Collection Agent, cycle start)
GET spreadsheets/{GOOGLE_SHEETS_MASTER_SHEET_ID}/values/{SHEETS_EMPLOYEES_TAB_NAME}!A:E
// Write: validated hours row (Collection Agent, per employee)
PUT spreadsheets/{GOOGLE_SHEETS_MASTER_SHEET_ID}/values/{SHEETS_RESPONSES_TAB_NAME}!A{row}:H{row}
// Write: approval timestamp (Approval Agent, per manager response)
PUT spreadsheets/{GOOGLE_SHEETS_MASTER_SHEET_ID}/values/{SHEETS_APPROVALS_TAB_NAME}!F{row}:G{row}
// Read: sync-ready rows (Payroll Sync Agent)
GET spreadsheets/{GOOGLE_SHEETS_MASTER_SHEET_ID}/values/{SHEETS_SYNC_TAB_NAME}!A:JGmail
Used by the Collection Agent to send personalised submission reminder emails to employees, and by the Approval Agent to send pre-formatted approval request emails to line managers. The Gmail API v1 is used to compose and send messages. Approval emails include a one-click token-based approve link.
Auth method
OAuth 2.0, authorised under the HR administrator Gmail account (or a dedicated automation Gmail account). Refresh token stored as GMAIL_OAUTH_REFRESH_TOKEN.
Required scopes
https://www.googleapis.com/auth/gmail.send (compose and send only; no inbox read access required)
Webhook / trigger setup
Gmail is send-only in this automation. No inbound webhook is required. Manager approval responses are handled via token-based URL clicks, not email reply parsing. The token-based endpoint is hosted on the orchestration layer and stored as APPROVAL_TOKEN_BASE_URL.
Required configuration
GMAIL_SENDER_ADDRESS stored in credential store. Email template IDs or raw HTML templates stored as GMAIL_REMINDER_TEMPLATE and GMAIL_APPROVAL_TEMPLATE. Templates must contain the following placeholders: {{employee_name}}, {{pay_period_label}}, {{form_url}} (reminder template) and {{manager_name}}, {{team_summary_table}}, {{approve_url}}, {{query_url}} (approval template). APPROVAL_TOKEN_SECRET used to sign and verify token URLs.
Rate limits
Gmail API send quota: 250 email units/second (batch), 1,000,000,000 units/day. At 80 employees plus 80 manager approval emails per cycle, total send volume is well within limits. No throttling required.
Constraints
Approval token links must expire after 72 hours. APPROVAL_TOKEN_TTL_SECONDS should be set to 259200. Tokens must be single-use; once a manager clicks approve, the token must be invalidated immediately to prevent duplicate approvals. If the organisation enforces SPF/DKIM on outbound email, the sending address must be an authorised sender in the domain DNS records.
// Send reminder email (Collection Agent)
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
{
"to": "employee@yourdomain.com",
"subject": "Action required: Submit your timesheet for {{pay_period_label}}",
"body": "<rendered GMAIL_REMINDER_TEMPLATE>"
}
// Send approval request email (Approval Agent)
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
{
"to": "manager@yourdomain.com",
"subject": "Timesheet approval required: {{pay_period_label}}",
"body": "<rendered GMAIL_APPROVAL_TEMPLATE with approve_url and query_url>"
}Slack
Used by the Collection Agent to send chase messages to employees who have not submitted, and by the Payroll Sync Agent to send the payroll-ready confirmation to the payroll owner. Messages are posted via the Slack Web API using a dedicated Slack app installed to the workspace.
Auth method
OAuth 2.0, Slack app bot token. Bot token stored as SLACK_BOT_TOKEN in the credential store.
Required scopes
chat:write | chat:write.public | users:read | users:read.email (to resolve employee email addresses to Slack user IDs)
Webhook / trigger setup
Outbound messages only; no inbound Slack events are consumed by this automation. The Slack app does not require Event Subscriptions to be enabled. SLACK_PAYROLL_OWNER_USER_ID and SLACK_HR_CHANNEL_ID are stored in the credential store for targeting confirmation messages.
Required configuration
SLACK_BOT_TOKEN, SLACK_PAYROLL_OWNER_USER_ID, SLACK_HR_CHANNEL_ID stored in credential store. Slack message templates for the employee chase (SLACK_REMINDER_TEMPLATE) and payroll confirmation (SLACK_CONFIRMATION_TEMPLATE) must use Slack Block Kit JSON. The app must be added to all relevant channels before first use.
Rate limits
Slack Web API: 1 message/second per channel (Tier 3 rate limit for chat.postMessage). At up to 80 employee messages per cycle, the automation must introduce a 1.1-second delay between successive chat.postMessage calls to avoid rate-limit errors. This is required at current volume.
Constraints
The bot must be invited to any private channels where confirmation messages are sent. If an employee's Slack account email does not match their Google Sheets email field exactly, the users.lookupByEmail call will fail and the agent must fall back to Gmail only for that employee and log the mismatch.
// Resolve Slack user ID from email (Collection Agent)
GET https://slack.com/api/users.lookupByEmail?email={{employee_email}}
// Send DM chase to employee (Collection Agent)
POST https://slack.com/api/chat.postMessage
{
"channel": "{{slack_user_id}}",
"blocks": "<SLACK_REMINDER_TEMPLATE Block Kit JSON>"
}
// Send payroll-ready confirmation (Payroll Sync Agent)
POST https://slack.com/api/chat.postMessage
{
"channel": "SLACK_PAYROLL_OWNER_USER_ID",
"blocks": "<SLACK_CONFIRMATION_TEMPLATE with employee_count and total_hours>"
}Xero
The payroll data destination. The Payroll Sync Agent reads approved hours from the master Google Sheet and submits them to Xero via the Xero Payroll API. The integration targets the Xero Timesheets endpoint, which creates or updates timesheet entries for each employee in the current pay period.
Auth method
OAuth 2.0 (PKCE flow for app authorisation). Access token expires after 30 minutes; refresh token valid for 60 days. Both stored in credential store as XERO_ACCESS_TOKEN and XERO_REFRESH_TOKEN. The Payroll Sync Agent must check token expiry before each sync run and refresh proactively.
Required scopes
payroll.timesheets | payroll.employees.read | payroll.settings.read
Webhook / trigger setup
The Payroll Sync Agent is triggered by the orchestration layer when the Approval Agent writes the final approval timestamp to the master sheet. No Xero webhook is consumed. After the sync, the agent calls the Xero Timesheets GET endpoint to verify each submitted entry was accepted before sending the Slack confirmation.
Required configuration
XERO_CLIENT_ID, XERO_CLIENT_SECRET, XERO_TENANT_ID, XERO_ACCESS_TOKEN, XERO_REFRESH_TOKEN all stored in credential store. XERO_PAYROLL_CALENDAR_ID stored to identify the correct pay period. Each employee in the master sheet must have a matching XERO_EMPLOYEE_ID stored in the SHEETS_EMPLOYEES_TAB_NAME roster column xero_employee_id. Pay item IDs for ordinary hours (XERO_PAY_ITEM_ORDINARY_ID) and overtime hours (XERO_PAY_ITEM_OVERTIME_ID) stored in credential store.
Rate limits
Xero API: 60 calls/minute per app per tenant, 5,000 calls/day. At 80 employees, a single sync run makes approximately 80 to 90 API calls. Well within limits. No throttling required at current volume, but the agent should add a 200ms delay between per-employee POST calls to avoid burst rejections.
Constraints
Xero Payroll API is region-specific: AU, NZ, and UK endpoints differ. The correct base URL must be set in XERO_PAYROLL_BASE_URL. Employee records in Xero must be in 'Active' status or the timesheet POST will be rejected. Any mismatch between the Google Sheets employee list and Xero employee records must be resolved before the Payroll Sync Agent is enabled. The Xero Demo Company must be used for all pre-production testing.
// Refresh access token before sync
POST https://identity.xero.com/connect/token
{ grant_type: refresh_token, refresh_token: XERO_REFRESH_TOKEN, client_id: XERO_CLIENT_ID, client_secret: XERO_CLIENT_SECRET }
// Submit timesheet entry per employee
POST https://api.xero.com/payroll/2.0/timesheets (AU example)
{
"Timesheets": [{
"EmployeeID": "{{xero_employee_id}}",
"StartDate": "{{pay_period_start}}",
"EndDate": "{{pay_period_end}}",
"Status": "Draft",
"TimesheetLines": [{
"EarningsRateID": "{{XERO_PAY_ITEM_ORDINARY_ID}}",
"NumberOfUnits": [0, 0, 0, 0, 0, 0, 0]
}]
}]
}
// Verify submission
GET https://api.xero.com/payroll/2.0/timesheets/{{TimesheetID}}Integration and API SpecPage 1 of 4
FS-DOC-05Technical
03Field mappings between tools
The tables below define the exact field mappings for each agent handoff. All field names are case-sensitive and must match the Google Sheets column headers and API field names exactly as written. Changes to any field name in any tool require a corresponding update to the mapping in the orchestration layer.
Handoff 1: Google Forms to Google Sheets (Collection Agent, responses tab write)
Source tool
Source field
Destination tool
Destination field
Google Forms
`response_id`
Google Sheets
`form_response_id`
Google Forms
`employeeEmail` (question answer)
Google Sheets
`employee_email`
Google Forms
`employeeName` (question answer)
Google Sheets
`employee_name`
Google Forms
`payPeriodLabel` (question answer)
Google Sheets
`pay_period_label`
Google Forms
`mondayHours` (question answer)
Google Sheets
`hours_mon`
Google Forms
`tuesdayHours` (question answer)
Google Sheets
`hours_tue`
Google Forms
`wednesdayHours` (question answer)
Google Sheets
`hours_wed`
Google Forms
`thursdayHours` (question answer)
Google Sheets
`hours_thu`
Google Forms
`fridayHours` (question answer)
Google Sheets
`hours_fri`
Google Forms
`totalHours` (calculated)
Google Sheets
`total_hours`
Google Forms
`timestamp` (submission time)
Google Sheets
`submitted_at`
Orchestration layer
`anomaly_flag` (validation output)
Google Sheets
`anomaly_flag`
Handoff 2: Google Sheets to Gmail (Approval Agent, approval request email composition)
Source tool
Source field
Destination tool
Destination field
Google Sheets
`manager_email`
Gmail
`to` (recipient address)
Google Sheets
`manager_name`
Gmail
`{{manager_name}}` (template placeholder)
Google Sheets
`pay_period_label`
Gmail
`{{pay_period_label}}` (template placeholder)
Google Sheets
`employee_name`
Gmail
`{{team_summary_table}}` (row in summary table)
Google Sheets
`total_hours`
Gmail
`{{team_summary_table}}` (hours column)
Orchestration layer
`approve_token`
Gmail
`{{approve_url}}` (signed token URL)
Orchestration layer
`query_token`
Gmail
`{{query_url}}` (signed token URL)
Handoff 3: Gmail approval click to Google Sheets (Approval Agent, approval log write)
Source tool
Source field
Destination tool
Destination field
Orchestration layer
`approve_token` (from URL param)
Google Sheets
`approval_token`
Orchestration layer
`manager_email` (decoded from token)
Google Sheets
`approved_by`
Orchestration layer
`approved_at` (server timestamp)
Google Sheets
`approved_at`
Orchestration layer
`approval_status` (approved or queried)
Google Sheets
`approval_status`
Handoff 4: Google Sheets to Xero (Payroll Sync Agent, timesheet POST)
Source tool
Source field
Destination tool
Destination field
Google Sheets
`xero_employee_id`
Xero API
`EmployeeID`
Google Sheets
`pay_period_start`
Xero API
`StartDate`
Google Sheets
`pay_period_end`
Xero API
`EndDate`
Google Sheets
`hours_mon`
Xero API
`NumberOfUnits[0]` (Monday)
Google Sheets
`hours_tue`
Xero API
`NumberOfUnits[1]` (Tuesday)
Google Sheets
`hours_wed`
Xero API
`NumberOfUnits[2]` (Wednesday)
Google Sheets
`hours_thu`
Xero API
`NumberOfUnits[3]` (Thursday)
Google Sheets
`hours_fri`
Xero API
`NumberOfUnits[4]` (Friday)
Credential store
`XERO_PAY_ITEM_ORDINARY_ID`
Xero API
`EarningsRateID`
Credential store
`XERO_TENANT_ID`
Xero API
`Xero-tenant-id` (HTTP header)
Handoff 5: Xero verification to Slack (Payroll Sync Agent, confirmation message)
Source tool
Source field
Destination tool
Destination field
Xero API
`Status` (verified as Processed)
Slack
`{{sync_status}}` in SLACK_CONFIRMATION_TEMPLATE
Orchestration layer
`employee_count` (total rows synced)
Slack
`{{employee_count}}` in SLACK_CONFIRMATION_TEMPLATE
Orchestration layer
`total_hours_all` (sum of all hours)
Slack
`{{total_hours}}` in SLACK_CONFIRMATION_TEMPLATE
Orchestration layer
`sync_completed_at` (timestamp)
Slack
`{{sync_completed_at}}` in SLACK_CONFIRMATION_TEMPLATE
Integration and API SpecPage 2 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three separate workflows, one per agent: Collection Agent Workflow, Approval Agent Workflow, and Payroll Sync Agent Workflow. Each workflow is independently triggerable and restartable. All three share a single credential store; no credentials are duplicated across workflows.
Collection Agent trigger
Scheduled trigger. Fires at the configured pay period close date and time (stored as PAY_PERIOD_CLOSE_CRON in the credential store). Also accepts an inbound webhook from the Google Apps Script Forms trigger (FORMS_WEBHOOK_TARGET_URL) to process individual submissions as they arrive during the collection window. Webhook signature validated using WEBHOOK_SIGNING_SECRET (HMAC-SHA256); any request without a valid signature is rejected with HTTP 401.
Approval Agent trigger
Inbound webhook. Triggered by the Google Apps Script onEdit trigger on the Approvals tab of the master sheet when the Collection Agent marks the sheet as fully populated. Target URL: SHEETS_APPROVAL_WEBHOOK_TARGET_URL. Also triggered by manager token-link clicks on APPROVAL_TOKEN_BASE_URL/{token}. Both webhook endpoints validate the WEBHOOK_SIGNING_SECRET before processing.
Payroll Sync Agent trigger
Event-based, initiated by the Approval Agent Workflow when all approval_status fields in the master sheet are set to 'approved'. No polling; the Approval Agent calls the Payroll Sync Agent via an internal workflow trigger (not a public webhook). Token expiry check on XERO_ACCESS_TOKEN is performed as the first step of every Payroll Sync Agent run.
Credential store access
All secrets are read from the orchestration platform's encrypted credential store at runtime. No secret values appear in workflow logic, code nodes, or template bodies. Credentials are referenced by key name only (e.g. $credentials.XERO_CLIENT_ID).
Escalation timer (Approval Agent)
If a manager has not clicked the approve or query link within the window defined by APPROVAL_ESCALATION_HOURS (default: 24), the Approval Agent sends a follow-up Gmail and Slack message to the manager and a notification to SLACK_HR_CHANNEL_ID. If no response after a second window (APPROVAL_FINAL_ESCALATION_HOURS, default: 48), the agent alerts the payroll owner and halts the Payroll Sync Agent pending manual resolution.
The credential store is the single source of truth for all secrets, IDs, and configuration values. If a value changes in production (for example, a Xero token rotation or a new Google Forms ID), update it in the credential store only. Never update it inline in a workflow node.
Credential store contents (all keys required before first production run):
All values in angle brackets must be replaced with real values before production deployment. No default or placeholder values should remain.
# Google OAuth (shared across Forms, Sheets, Gmail)
GOOGLE_OAUTH_CLIENT_ID = "<from Google Cloud Console OAuth 2.0 app>"
GOOGLE_OAUTH_CLIENT_SECRET = "<from Google Cloud Console OAuth 2.0 app>"
GOOGLE_OAUTH_REFRESH_TOKEN = "<generated during OAuth authorisation flow>"
# Google Forms
GOOGLE_FORMS_FORM_ID = "<Form ID from Forms URL>"
FORMS_APPS_SCRIPT_PROJECT_ID = "<Apps Script project ID>"
FORMS_WEBHOOK_TARGET_URL = "<orchestration inbound webhook URL for form submissions>"
# Google Sheets
GOOGLE_SHEETS_MASTER_SHEET_ID = "<Spreadsheet ID from Sheets URL>"
SHEETS_EMPLOYEES_TAB_NAME = "Employees"
SHEETS_RESPONSES_TAB_NAME = "Responses"
SHEETS_APPROVALS_TAB_NAME = "Approvals"
SHEETS_SYNC_TAB_NAME = "SyncReady"
SHEETS_APPROVAL_WEBHOOK_TARGET_URL = "<orchestration inbound webhook URL for approval events>"
# Gmail
GMAIL_SENDER_ADDRESS = "<hr-automation@yourdomain.com or Google account email>"
GMAIL_REMINDER_TEMPLATE = "<template ID or inline HTML stored as secret>"
GMAIL_APPROVAL_TEMPLATE = "<template ID or inline HTML stored as secret>"
# Slack
SLACK_BOT_TOKEN = "xoxb-<bot token from Slack app config>"
SLACK_PAYROLL_OWNER_USER_ID = "U<Slack user ID of payroll owner>"
SLACK_HR_CHANNEL_ID = "C<Slack channel ID for HR notifications>"
SLACK_REMINDER_TEMPLATE = "<Block Kit JSON template>"
SLACK_CONFIRMATION_TEMPLATE = "<Block Kit JSON template>"
# Xero
XERO_CLIENT_ID = "<from Xero developer app>"
XERO_CLIENT_SECRET = "<from Xero developer app>"
XERO_TENANT_ID = "<Xero organisation tenant ID>"
XERO_ACCESS_TOKEN = "<current short-lived access token; auto-refreshed>"
XERO_REFRESH_TOKEN = "<long-lived refresh token; rotate on each use>"
XERO_PAYROLL_CALENDAR_ID = "<Pay calendar ID from Xero Payroll Settings>"
XERO_PAYROLL_BASE_URL = "https://api.xero.com/payroll/2.0"
XERO_PAY_ITEM_ORDINARY_ID = "<EarningsRate GUID for ordinary hours in Xero>"
XERO_PAY_ITEM_OVERTIME_ID = "<EarningsRate GUID for overtime hours in Xero>"
# Orchestration and security
WEBHOOK_SIGNING_SECRET = "<HMAC-SHA256 shared secret for inbound webhook validation>"
APPROVAL_TOKEN_SECRET = "<HMAC-SHA256 secret for signing manager approval tokens>"
APPROVAL_TOKEN_TTL_SECONDS = "259200"
APPROVAL_TOKEN_BASE_URL = "<orchestration base URL for manager approve/query endpoints>"
APPROVAL_ESCALATION_HOURS = "24"
APPROVAL_FINAL_ESCALATION_HOURS = "48"
PAY_PERIOD_CLOSE_CRON = "<cron expression for pay period close schedule>"
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 behaviour. If an error is not covered by the table below, the default behaviour is: log the full error payload to the orchestration error log, send an alert to SLACK_HR_CHANNEL_ID with the error details, and halt the affected workflow step pending manual resolution.
Integration
Scenario
Required behaviour
Google Forms (Apps Script webhook)
Inbound webhook payload arrives with invalid or missing HMAC signature
Reject with HTTP 401. Log the source IP and payload hash. Do not process the submission. Alert SLACK_HR_CHANNEL_ID.
Google Forms (Apps Script webhook)
Duplicate responseId received (re-delivery from Apps Script)
Check form_response_id against the Responses tab before writing. If already present, discard silently and return HTTP 200 to prevent re-delivery loop.
Google Sheets (read, any agent)
API returns 429 (rate limit exceeded) or 503 (service unavailable)
Retry with exponential backoff: 5s, 15s, 45s, then 120s. After 4 failed attempts, halt the workflow step and alert SLACK_HR_CHANNEL_ID with the affected step name.
Google Sheets (write, Collection Agent)
Write to Responses or SyncReady tab fails mid-cycle (partial write)
Log the last successfully written row index. On retry, resume from that row. Never overwrite already-written rows. Alert HR if a partial write is detected at cycle end.
Gmail (send, Collection Agent or Approval Agent)
Send fails for a specific recipient (invalid address or quota exceeded)
Log the failed recipient email and error code. Continue sending to remaining recipients. At cycle end, compile a list of failed sends and write to SHEETS_HR_CHANNEL_ID alert and to the Responses tab anomaly_flag field for that employee.
Gmail (approval token link)
Manager clicks an expired token (past APPROVAL_TOKEN_TTL_SECONDS)
Return a user-friendly expiry page with a contact email. Log the expired token attempt. The Approval Agent must issue a new token and re-send the approval email automatically, then alert SLACK_HR_CHANNEL_ID.
Gmail (approval token link)
Same token clicked more than once (duplicate approval attempt)
Return HTTP 200 with a confirmation message to the manager. Discard the duplicate. Do not write a second approval timestamp to the master sheet.
Slack (chat.postMessage, any agent)
API returns 429 (rate_limited)
Respect the Retry-After header value. Pause the send queue for the specified duration, then resume. If Retry-After exceeds 300 seconds, fall back to Gmail for the affected recipient and log the fallback.
Slack (users.lookupByEmail)
Employee email not found in Slack workspace
Log the unmatched email. Skip Slack message for that employee. Send reminder via Gmail only. Write a note to the anomaly_flag field in the Responses tab.
Xero API (token refresh)
Refresh token has expired (inactive for more than 60 days)
Halt the Payroll Sync Agent immediately. Alert SLACK_HR_CHANNEL_ID and SLACK_PAYROLL_OWNER_USER_ID with instructions to re-authorise the Xero connection via the OAuth flow. Do not attempt to sync until a valid token is confirmed.
Xero API (timesheet POST)
POST rejected with 400 (validation error, e.g. employee not found or pay item mismatch)
Log the specific EmployeeID and error body. Skip that employee row and continue with remaining employees. At sync completion, compile all failed rows into an error report, write to SHEETS_SYNC_TAB_NAME with status 'sync_failed', and alert SLACK_PAYROLL_OWNER_USER_ID.
Xero API (timesheet POST)
API returns 503 or 429 during bulk sync
Retry with exponential backoff: 10s, 30s, 90s. After 3 failed attempts for a specific employee row, mark as 'sync_failed' in the master sheet and continue to next row. Do not halt the entire sync run for a single row failure.
Xero API (verification GET)
Timesheet status not 'Processed' after POST confirmation
Wait 30 seconds and re-check up to 3 times. If still not confirmed, mark the row as 'sync_unverified' in the master sheet, exclude from the Slack confirmation count, and alert SLACK_PAYROLL_OWNER_USER_ID separately.
Orchestration layer (escalation timer)
Manager has not responded within APPROVAL_ESCALATION_HOURS
Send escalation Gmail and Slack message to manager. Notify SLACK_HR_CHANNEL_ID. If no response within APPROVAL_FINAL_ESCALATION_HOURS, alert SLACK_PAYROLL_OWNER_USER_ID and pause Payroll Sync Agent trigger pending manual resolution.
Integration and API SpecPage 4 of 4