FS-DOC-05Technical
Integration and API Spec
Contractor Management Automation
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document specifies every integration point, authentication requirement, field mapping, and failure behaviour for the Contractor Management automation. It is intended for the FullSpec build team and covers the three agents: Contractor Onboarding Agent (Agent 1), Invoice Approval and Payment Agent (Agent 2), and Compliance Monitor Agent (Agent 3). Where exact scope strings, rate limits, and credential keys are referenced they must be stored in the central credential store and never hardcoded in workflow logic.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Orchestration layer
Connects all tools; hosts all three agent workflows and the shared credential store
Service account / API key per connected tool
Paid tier with webhook support and multi-step workflows
All agents
Google Forms
Captures contractor onboarding data; triggers Agent 1 on new submission
OAuth 2.0 (Google Workspace)
Free (Google account required)
Agent 1
Airtable
Central contractor database; stores records, document status, expiry dates, and payment status
Personal Access Token (PAT) or OAuth 2.0
Team plan ($20/month)
Agents 1, 2, 3
Google Drive
Compliance document storage; receives uploaded files and organises into named folders
OAuth 2.0 (Google Workspace)
Free (Google account required)
Agent 1
Xero
Supplier creation and bill management; receives supplier records and approved bills
OAuth 2.0 (Xero App)
Starter or above ($47/month)
Agents 1, 2
Slack
Team notifications, invoice approval routing, and compliance expiry alerts
OAuth 2.0 (Slack App)
Pro plan ($8/user/month)
Agents 1, 2, 3
Before you connect anything: sandbox-test every integration using non-production credentials before switching to live credentials. For Xero, use the Xero Demo Company environment. For Airtable, use a duplicate base. For Google Workspace, use a test Google account. For Slack, use a private sandbox workspace. Never run a connection test against a live Xero organisation, live Airtable base, or production Slack channel until all three agents have passed UAT in full.
02Per-tool integration detail
Google Forms
Trigger source for Agent 1. The form captures all contractor onboarding fields. The orchestration layer polls or receives a webhook push from the Google Forms / Google Sheets response sheet on each new submission.
Auth method
OAuth 2.0 using a Google Workspace service account or user-delegated credentials. Scopes granted during the OAuth consent screen.
Required scopes
https://www.googleapis.com/auth/forms.responses.readonly | https://www.googleapis.com/auth/spreadsheets.readonly (if reading the linked response sheet directly)
Trigger setup
Link the Form to a Google Sheet (Forms > Responses > Link to Sheets). Configure the orchestration layer to poll the response sheet every 5 minutes, or use a Google Apps Script push trigger to POST to the orchestration layer's inbound webhook URL on each new row. Webhook preferred for latency. Validate the inbound payload against the expected column schema before processing.
Required configuration
Form field names must match the canonical field list: contractor_name, contractor_email, abn_or_tax_id, agreed_rate_usd, engagement_start_date, engagement_end_date, document_insurance_expiry, document_wwcc_expiry, bank_bsb, bank_account_number. Store the Google Sheet ID in the credential store as GFORMS_RESPONSE_SHEET_ID. Do not hardcode.
Rate limits
Google Sheets API: 300 read requests/minute per project, 60 requests/minute per user. At ~6 submissions/month, polling every 5 minutes generates ~8,640 read calls/month, well within limits. No throttling required at current volume.
Constraints
Form field labels must not change after go-live without a corresponding update to the field mapping config. If the form is redesigned, all downstream field references in Agents 1 must be remapped before re-enabling the workflow.
// Input
New row in Google Sheets response sheet
// Key fields extracted
contractor_name, contractor_email, abn_or_tax_id,
agreed_rate_usd, engagement_start_date, engagement_end_date,
document_insurance_expiry, document_wwcc_expiry,
bank_bsb, bank_account_number,
file_upload_insurance_cert (Drive URL), file_upload_wwcc (Drive URL)
// Output
Structured JSON object passed to Agent 1 processing steps
Airtable
Central contractor database used by all three agents. Agent 1 creates records; Agent 2 reads agreed rate and updates payment status; Agent 3 reads expiry date fields to determine alert eligibility.
Auth method
Personal Access Token (PAT). Generate a PAT scoped to the target base only. Store as AIRTABLE_PAT in the credential store. OAuth 2.0 available as an alternative if the platform supports it.
Required scopes
data.records:read | data.records:write | schema.bases:read (PAT scope strings as defined in Airtable developer docs)
Webhook/trigger setup
For Agent 3 (Compliance Monitor): no Airtable webhook needed. The orchestration layer runs a scheduled daily poll at 07:00 UTC querying records where document_insurance_expiry or document_wwcc_expiry is within 30 days. Use Airtable List Records API with a filterByFormula parameter. For Agents 1 and 2: Airtable is a destination, not a trigger.
Required configuration
Base ID stored as AIRTABLE_BASE_ID. Contractors table ID stored as AIRTABLE_TABLE_CONTRACTORS. Field IDs (not display names) used in all API calls to prevent breakage if field labels are renamed. Key field IDs: fld_contractor_name, fld_contractor_email, fld_agreed_rate_usd, fld_airtable_xero_supplier_id, fld_payment_status, fld_document_insurance_expiry, fld_document_wwcc_expiry, fld_drive_folder_url, fld_engagement_status. All stored in credential store, not hardcoded.
Rate limits
Airtable API: 5 requests/second per base. At current volume (6 contractors/month, ~248 automation runs/month), peak load is well under this limit. No throttling required. Add a 250ms delay between sequential write calls within a single workflow run as a precaution.
Constraints
The Airtable base schema must not be modified (field deletions or type changes) without coordinating a corresponding update to all agent field-ID references. Field ID references are stable across renames but not across deletions or type changes.
// Agent 1 Input (create record)
contractor_name, contractor_email, abn_or_tax_id, agreed_rate_usd,
engagement_start_date, engagement_end_date, drive_folder_url,
xero_supplier_id, document_insurance_expiry, document_wwcc_expiry
// Agent 2 Input (read + update)
GET record by contractor_email -> agreed_rate_usd
PATCH record -> fld_payment_status = 'Bill Created' | 'Pending Review'
// Agent 3 Input (read for daily poll)
GET records WHERE document_insurance_expiry <= TODAY+30
OR document_wwcc_expiry <= TODAY+30
// Output
Record ID returned on create (stored for subsequent PATCH calls)
Google Drive
Compliance document storage used by Agent 1. Files submitted via Google Forms are moved or copied into a named contractor folder. Folder URL is written back to Airtable.
Auth method
OAuth 2.0 using the same Google Workspace credentials as Google Forms. A single OAuth token covers both if the same service account is used.
Required scopes
https://www.googleapis.com/auth/drive.file (create and organise files the app created) | https://www.googleapis.com/auth/drive.metadata.readonly (read folder structure to confirm naming)
Webhook/trigger setup
Not applicable. Google Drive is a destination only for this automation. Files are written by Agent 1 after a form submission. No Drive-originating triggers are used.
Required configuration
Parent folder ID for contractor documents stored as GDRIVE_CONTRACTORS_ROOT_FOLDER_ID in the credential store. Folder naming convention: {contractor_name}_{engagement_start_date} (e.g. Jane_Smith_2024-05-01). Folder creation uses the Drive Files API (files.create with mimeType folder). File naming convention: {contractor_name}_{document_type}_{expiry_date} (e.g. Jane_Smith_InsuranceCert_2025-04-30). Do not hardcode the root folder ID.
Rate limits
Google Drive API: 1,000 requests/100 seconds per user, 10 requests/second per user. At 6 onboarding events/month each creating one folder and up to 4 files, peak load is negligible. No throttling required.
Constraints
Files uploaded via Google Forms are initially stored in the form owner's Drive. The automation must copy or move them to the designated root folder using the file ID from the Forms response. Confirm that the service account has Editor access to the root folder before go-live.
// Input
file_upload_insurance_cert (Google Drive file ID from Forms response)
file_upload_wwcc (Google Drive file ID from Forms response)
contractor_name, engagement_start_date
// Actions
1. Create folder: {contractor_name}_{engagement_start_date} under GDRIVE_CONTRACTORS_ROOT_FOLDER_ID
2. Copy file insurance_cert to new folder, rename to {contractor_name}_InsuranceCert_{expiry}
3. Copy file wwcc to new folder, rename to {contractor_name}_WWCC_{expiry}
// Output
drive_folder_url -> passed to Airtable record write (fld_drive_folder_url)Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Xero
Used by Agent 1 to create a supplier (contact) record and by Agent 2 to create a bill once an invoice is approved. Requires an OAuth 2.0 connection authorised by the Finance Manager.
Auth method
OAuth 2.0 via a registered Xero App (app.xero.com). The Finance Manager must complete the OAuth consent flow to authorise the connection. Access tokens expire after 30 minutes; refresh tokens are valid for 60 days and must be refreshed automatically by the orchestration layer. Store tokens as XERO_ACCESS_TOKEN and XERO_REFRESH_TOKEN in the credential store. Store the Tenant ID as XERO_TENANT_ID.
Required scopes
openid | profile | email | accounting.contacts | accounting.transactions | offline_access (offline_access is required for refresh token support)
Webhook/trigger setup
Xero webhooks are available but not used as a trigger in this automation. Agent 2 is triggered by an inbound invoice event (email or Slack message detection), not by Xero. Outbound calls to Xero are action steps only. If Xero webhook delivery confirmation is required (for future invoice sync), register the webhook endpoint in the Xero App portal and validate the x-xero-signature header using HMAC-SHA256 with the webhook signing key stored as XERO_WEBHOOK_KEY.
Required configuration
XERO_TENANT_ID: the organisation's Xero tenant identifier. XERO_PAYMENT_ACCOUNT_ID: the bank account ID used for scheduling bill payments (retrieve via GET /BankAccounts and store). XERO_DEFAULT_CURRENCY: USD. XERO_BILL_DUE_DAYS: 14 (bills created with due date = bill_date + 14 days unless overridden). XERO_TAX_TYPE: TAX001 or NONE depending on contractor ABN/tax status. All stored in credential store.
Rate limits
Xero API: 60 calls/minute per app per organisation, 5,000 calls/day per organisation. At 6 supplier creations/month and up to 24 bill creations/month (6 contractors x 4 weeks), total monthly Xero calls are under 100. No throttling required. Add exponential backoff on 429 responses.
Constraints
Xero supplier (contact) names must be unique. Before creating a new contact, query GET /Contacts?where=Name=={contractor_name} to check for an existing record. If found, update rather than create. Xero does not allow duplicate contact names within the same organisation. Bill line items must include an AccountCode; store the default expense account code as XERO_EXPENSE_ACCOUNT_CODE (e.g. 600).
// Agent 1: Create Supplier
POST /api.xro/2.0/Contacts
Body: { Name: contractor_name, EmailAddress: contractor_email,
BankAccountDetails: bank_bsb + '-' + bank_account_number,
TaxNumber: abn_or_tax_id }
Response: ContactID -> stored to Airtable fld_airtable_xero_supplier_id
// Agent 2: Create Bill
POST /api.xro/2.0/Invoices
Body: { Type: 'ACCPAY', Contact: { ContactID: xero_supplier_id },
Date: invoice_date, DueDate: invoice_date + XERO_BILL_DUE_DAYS,
LineItems: [{ Description: invoice_description,
UnitAmount: invoice_amount, AccountCode: XERO_EXPENSE_ACCOUNT_CODE }],
Status: 'AUTHORISED' }
Response: InvoiceID -> stored to Airtable fld_payment_status = 'Bill Created'Slack
Used by Agents 1, 2, and 3 for team notifications, invoice approval routing with interactive buttons, and compliance expiry alerts. Requires a Slack App with bot token and interactive components enabled.
Auth method
OAuth 2.0 via a registered Slack App. Install the app to the workspace and store the Bot User OAuth Token as SLACK_BOT_TOKEN. For interactive message responses (approve/query buttons in Agent 2), the app must have an Interactivity Request URL configured pointing to the orchestration layer's inbound webhook endpoint.
Required scopes
chat:write | chat:write.public | channels:read | im:write | users:read | users:read.email | incoming-webhook (for simple notifications) | commands (if slash commands used for approval)
Webhook/trigger setup
Agent 2 approval flow: the Slack App must have Interactivity enabled. When the approver clicks Approve or Query on the Slack message, Slack POSTs a payload to the orchestration layer's Interactivity Request URL. This URL must be publicly accessible and must respond with HTTP 200 within 3 seconds. Validate the request by checking the x-slack-signature header (HMAC-SHA256 of the raw request body using SLACK_SIGNING_SECRET stored in the credential store).
Required configuration
SLACK_BOT_TOKEN: bot OAuth token. SLACK_SIGNING_SECRET: used to validate interactive payloads. SLACK_CHANNEL_CONTRACTORS: channel ID for contractor active notifications (Agent 1). SLACK_CHANNEL_INVOICES: channel ID or DM target for invoice approval requests (Agent 2). SLACK_CHANNEL_COMPLIANCE: channel ID for compliance expiry alerts (Agent 3). SLACK_APPROVER_USER_ID: Slack user ID of the Hiring Manager (for DM-based approval). All stored in credential store, not hardcoded.
Rate limits
Slack Web API: 1 message/second per channel (Tier 3 methods such as chat.postMessage). At current volume, Agent 1 sends ~6 notifications/month, Agent 2 sends ~24 approval requests/month, Agent 3 sends alerts only when documents are within 30 days. Total Slack calls well under rate limits. No throttling required.
Constraints
Interactive message payloads from Slack expire after 30 minutes. If the approver does not respond within 30 minutes, the action_id in the payload will no longer be valid. Agent 2 must handle this by re-sending the approval request or escalating after a configurable timeout (default: 4 hours). Block Kit layout must be used for interactive buttons; legacy attachments are deprecated.
// Agent 1: Team notification
POST chat.postMessage
channel: SLACK_CHANNEL_CONTRACTORS
blocks: [ text: 'New contractor active: {contractor_name}',
fields: [Start: engagement_start_date, Rate: agreed_rate_usd,
Hiring Manager: hiring_manager_name] ]
// Agent 2: Invoice approval request
POST chat.postMessage
channel: SLACK_APPROVER_USER_ID (DM)
blocks: [ text: 'Invoice approval required: {contractor_name} - ${invoice_amount}',
actions: [ { type: button, text: 'Approve', action_id: 'approve_invoice_{invoice_id}' },
{ type: button, text: 'Query', action_id: 'query_invoice_{invoice_id}' } ] ]
// Agent 3: Compliance expiry alert
POST chat.postMessage
channel: SLACK_CHANNEL_COMPLIANCE
blocks: [ text: '{contractor_name} - {document_type} expires in {days_remaining} days.',
text: 'Re-upload link: {drive_reupload_url}' ]03Field mappings between tools
The tables below define exact field mappings for each agent handoff. Use monospace field names as shown. Any rename or restructure of source fields requires a corresponding update to the mapping configuration before the workflow is re-enabled.
Agent 1 handoff: Google Forms to Airtable
Source tool
Source field
Destination tool
Destination field
Google Forms
`contractor_name`
Airtable
`fld_contractor_name`
Google Forms
`contractor_email`
Airtable
`fld_contractor_email`
Google Forms
`abn_or_tax_id`
Airtable
`fld_abn_or_tax_id`
Google Forms
`agreed_rate_usd`
Airtable
`fld_agreed_rate_usd`
Google Forms
`engagement_start_date`
Airtable
`fld_engagement_start_date`
Google Forms
`engagement_end_date`
Airtable
`fld_engagement_end_date`
Google Forms
`document_insurance_expiry`
Airtable
`fld_document_insurance_expiry`
Google Forms
`document_wwcc_expiry`
Airtable
`fld_document_wwcc_expiry`
Google Drive (derived)
`drive_folder_url`
Airtable
`fld_drive_folder_url`
Xero (derived)
`ContactID`
Airtable
`fld_airtable_xero_supplier_id`
Agent 1 handoff: Google Forms to Xero (supplier creation)
Source tool
Source field
Destination tool
Destination field
Google Forms
`contractor_name`
Xero
`Contacts[].Name`
Google Forms
`contractor_email`
Xero
`Contacts[].EmailAddress`
Google Forms
`abn_or_tax_id`
Xero
`Contacts[].TaxNumber`
Google Forms
`bank_bsb`
Xero
`Contacts[].BankAccountDetails` (prefix)
Google Forms
`bank_account_number`
Xero
`Contacts[].BankAccountDetails` (suffix)
Agent 2 handoff: Airtable to Xero (bill creation)
Source tool
Source field
Destination tool
Destination field
Airtable
`fld_airtable_xero_supplier_id`
Xero
`Invoices[].Contact.ContactID`
Inbound invoice
`invoice_amount`
Xero
`Invoices[].LineItems[].UnitAmount`
Inbound invoice
`invoice_date`
Xero
`Invoices[].Date`
Inbound invoice
`invoice_description`
Xero
`Invoices[].LineItems[].Description`
Credential store
`XERO_EXPENSE_ACCOUNT_CODE`
Xero
`Invoices[].LineItems[].AccountCode`
Xero (derived)
`InvoiceID`
Airtable
`fld_payment_status` (set to 'Bill Created')
Agent 3 handoff: Airtable to Slack (compliance alert)
Source tool
Source field
Destination tool
Destination field
Airtable
`fld_contractor_name`
Slack
`blocks[].text` (contractor name in message)
Airtable
`fld_contractor_email`
Slack
`blocks[].text` (re-upload link recipient)
Airtable
`fld_document_insurance_expiry`
Slack
`blocks[].fields[].value` (expiry date display)
Airtable
`fld_document_wwcc_expiry`
Slack
`blocks[].fields[].value` (expiry date display)
Airtable
`fld_drive_folder_url`
Slack
`blocks[].text` (re-upload link URL)
Integration and API SpecPage 2 of 3
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
One workflow per agent: Workflow-1 (Contractor Onboarding Agent), Workflow-2 (Invoice Approval and Payment Agent), Workflow-3 (Compliance Monitor Agent). All three workflows share a single credential store. No credentials are embedded in workflow logic.
Agent 1 trigger mechanism
Webhook push (preferred): a Google Apps Script trigger attached to the Forms response sheet fires a POST to Workflow-1's inbound webhook URL on each new row. Fallback: poll the response Google Sheet every 5 minutes. Validate inbound payload structure before processing; reject and log malformed submissions.
Agent 2 trigger mechanism
Dual-trigger: (a) email monitor polling a designated invoices inbox (e.g. invoices@[YourCompany.com]) every 10 minutes for messages from known contractor email addresses, OR (b) manual trigger via a Slack slash command (/submit-invoice) posted by the contractor or operations team. On trigger, parse the invoice attachment or body for invoice_amount, invoice_date, and contractor_email. Match contractor_email to an Airtable record to retrieve agreed_rate_usd for comparison. Slack interactive payload (approve/query button response) is also an inbound webhook event to Workflow-2's Interactivity URL. Validate x-slack-signature on all inbound Slack payloads.
Agent 3 trigger mechanism
Scheduled poll: Workflow-3 runs on a daily cron schedule at 07:00 UTC. No inbound webhook. The workflow queries Airtable for records with expiry dates within the next 30 days and dispatches Slack alerts for each match. Idempotency: store the last alert date per contractor per document type in Airtable (fld_last_alert_sent) to prevent duplicate daily alerts.
Credential store structure
All secrets stored in the orchestration platform's encrypted credential/secret store. Referenced by key name only in workflow steps. See code block below.
Inter-workflow data passing
Workflows do not call each other directly. Airtable is the shared state layer. Agent 1 writes the Xero ContactID and Drive folder URL to Airtable. Agent 2 reads the ContactID and agreed rate from Airtable. Agent 3 reads expiry dates from Airtable. No direct API calls between workflows.
Credential store: all keys required before first workflow activation
# Google Workspace
GOOGLE_OAUTH_CLIENT_ID = '<from Google Cloud Console>'
GOOGLE_OAUTH_CLIENT_SECRET = '<from Google Cloud Console>'
GOOGLE_OAUTH_REFRESH_TOKEN = '<generated after consent flow>'
GFORMS_RESPONSE_SHEET_ID = '<Google Sheet ID from Forms response link>'
GDRIVE_CONTRACTORS_ROOT_FOLDER_ID = '<Drive folder ID for contractor docs>'
# Airtable
AIRTABLE_PAT = '<Personal Access Token from airtable.com/account>'
AIRTABLE_BASE_ID = '<from Airtable API docs for target base>'
AIRTABLE_TABLE_CONTRACTORS = '<Table ID, not display name>'
# Xero
XERO_CLIENT_ID = '<from Xero Developer portal>'
XERO_CLIENT_SECRET = '<from Xero Developer portal>'
XERO_ACCESS_TOKEN = '<refreshed automatically; bootstrap manually>'
XERO_REFRESH_TOKEN = '<60-day rolling token>'
XERO_TENANT_ID = '<Xero organisation tenant ID>'
XERO_PAYMENT_ACCOUNT_ID = '<Bank account ID from GET /BankAccounts>'
XERO_EXPENSE_ACCOUNT_CODE = '600' # confirm with Finance Manager
XERO_DEFAULT_CURRENCY = 'USD'
XERO_BILL_DUE_DAYS = 14
XERO_TAX_TYPE = 'NONE' # or TAX001 if GST applies
XERO_WEBHOOK_KEY = '<webhook signing key if Xero webhooks enabled>'
# Slack
SLACK_BOT_TOKEN = '<Bot User OAuth Token from Slack App settings>'
SLACK_SIGNING_SECRET = '<Signing secret from Slack App Basic Information>'
SLACK_CHANNEL_CONTRACTORS = '<channel ID, e.g. C0123456789>'
SLACK_CHANNEL_INVOICES = '<channel ID for invoice routing>'
SLACK_CHANNEL_COMPLIANCE = '<channel ID for compliance alerts>'
SLACK_APPROVER_USER_ID = '<Slack user ID of Hiring Manager approver>'
# Orchestration
WEBHOOK_INBOUND_SECRET = '<HMAC secret for validating inbound webhook calls>'
INVOICE_INBOX_EMAIL = 'invoices@[YourCompany.com]'
INVOICE_INBOX_IMAP_PASSWORD = '<app password for IMAP access>'
COMPLIANCE_ALERT_LEAD_DAYS = 30
APPROVAL_TIMEOUT_HOURS = 4
The XERO_REFRESH_TOKEN must be automatically refreshed by the orchestration layer before each Xero API call if the access token is expired. If the refresh token itself expires (after 60 days of non-use), the Finance Manager must re-authorise the Xero connection manually. Build a monitoring alert to warn the FullSpec team at least 10 days before refresh token expiry.
05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All errors must be logged with a timestamp, the workflow run ID, the failing step name, and the error payload. Where a retry is defined, use exponential backoff starting at 30 seconds with a maximum of 3 attempts unless stated otherwise.
Integration
Scenario
Required behaviour
Google Forms / Sheets
Malformed or incomplete form submission (missing required field)
Reject the record. Log the submission row number and missing fields. Send a Slack alert to SLACK_CHANNEL_COMPLIANCE (ops channel) with the contractor email and a list of missing fields. Do not proceed with downstream steps.
Google Forms / Sheets
Polling connection failure or Sheets API 5xx error
Retry 3 times with exponential backoff (30s, 60s, 120s). If all retries fail, send a Slack alert to the ops channel and halt the workflow run. Log the error. Resume on next scheduled poll.
Google Drive
File copy or folder creation fails (API error or permission denied)
Retry 3 times with 30s backoff. If still failing, log the error, post a Slack alert to the ops channel with the contractor name and the specific file that failed, and continue with remaining Agent 1 steps. Mark the Airtable record's fld_drive_folder_url as 'UPLOAD_FAILED' so the ops team can retry manually.
Airtable
Record creation fails (API error, rate limit 429, or schema mismatch)
On 429: wait 1 second and retry up to 5 times. On 4xx schema error: halt Agent 1 immediately, log the full request and response, and send a Slack alert. Do not proceed to Xero or Drive steps if the Airtable record cannot be created, as downstream steps depend on the Airtable Record ID.
Airtable
Record not found when Agent 2 queries by contractor_email
Halt the invoice processing step. Send a Slack alert to SLACK_CHANNEL_INVOICES: 'Invoice received from {contractor_email} but no matching Airtable record found. Manual review required.' Do not create a Xero bill. Log the unmatched invoice.
Xero
OAuth access token expired
Automatically attempt token refresh using XERO_REFRESH_TOKEN before retrying the failed API call. If refresh succeeds, retry the original call once. If refresh fails (refresh token expired), halt the workflow, send a Slack alert to the ops and finance channels: 'Xero connection requires re-authorisation. Contact the Finance Manager.' Log the event.
Xero
Duplicate contact name on supplier creation (409 or validation error)
Query GET /Contacts?where=Name=={contractor_name} to retrieve the existing ContactID. Use PATCH to update the existing contact rather than creating a new one. Log the deduplication action. Store the existing ContactID to Airtable.
Xero
Bill creation fails (API 5xx or rate limit 429)
Retry 3 times with exponential backoff (30s, 60s, 120s). If all retries fail, do not mark the Airtable record as 'Bill Created'. Send a Slack alert to SLACK_CHANNEL_INVOICES with the contractor name, invoice amount, and error code. The Finance Manager must create the bill manually. Log the failure with full API response.
Slack
chat.postMessage fails (API error or invalid channel ID)
Retry once after 10 seconds. If the retry fails, log the error and the intended message payload. For Agent 1 notifications: non-critical, continue workflow. For Agent 2 approval requests: halt the approval flow and escalate via email to INVOICE_INBOX_EMAIL as a fallback notification. For Agent 3 compliance alerts: log and retry on the next daily run.
Slack
Invoice approval interactive payload not received within APPROVAL_TIMEOUT_HOURS (default 4 hours)
Send a second Slack DM to SLACK_APPROVER_USER_ID: 'Reminder: invoice approval pending for {contractor_name} - ${invoice_amount}. Please approve or query.' If no response after a further 4 hours, send an email escalation to the Finance Manager and log the timeout. Do not auto-approve under any circumstances.
Slack
Inbound interactive payload fails x-slack-signature validation
Reject the payload immediately with HTTP 403. Log the raw payload and the timestamp. Do not process the approval or query action. Send a Slack alert to the ops channel: 'Rejected unsigned Slack payload. Possible replay attack or misconfiguration. Check SLACK_SIGNING_SECRET.'
Agent 3 (Compliance Monitor)
Airtable daily poll returns zero records despite known upcoming expiries
This may indicate a filterByFormula misconfiguration. Log the raw query and the record count returned. If the count is zero for more than 3 consecutive days, send a Slack alert to the ops channel: 'Compliance Monitor returned no results for 3 days. Verify Airtable query and field IDs.' Do not suppress the alert.
All integrations
Unhandled exception or unexpected runtime error in any workflow step
Catch all unhandled exceptions at the workflow level. Log the full error stack, workflow run ID, step name, and input payload. Send a Slack alert to the ops channel immediately. Never fail silently. The workflow must terminate gracefully and the ops team must be notified within 5 minutes of the exception occurring.
For all Slack failure escalations, the fallback email address is support@gofullspec.com during the build and UAT phases. After go-live, the escalation target switches to the Operations Manager's email as configured in the runbook. Update the INVOICE_INBOX_EMAIL and escalation targets before switching to production credentials.
Integration and API SpecPage 3 of 3