FS-DOC-05Technical
Integration and API Spec
End of Month Close Automation
[YourCompany.com] · Finance Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the End of Month Close automation. It covers tool authentication, exact OAuth scopes, webhook configuration, field-level mappings between agents, orchestration layout, credential storage conventions, and failure handling. The FullSpec team uses this specification to build and maintain the automation. It is a developer-facing document and assumes familiarity with REST APIs, OAuth 2.0 flows, and webhook signature validation.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Xero
Source of record: transactions, bank feeds, journal posting, report data
OAuth 2.0 (PKCE)
Xero Standard or higher (API access included)
Agent 2, Agent 3
Google Sheets
Accruals schedule input, exceptions output, management accounts template
OAuth 2.0 (service account)
Google Workspace Business Starter or any account with Sheets API enabled
Agent 2, Agent 3
Gmail
Outbound chase emails, report distribution, 24-hour reminder messages
OAuth 2.0 (service account with domain delegation)
Google Workspace (same account as Sheets)
Agent 1, Agent 3
Slack
Internal notifications and close-status alerts
OAuth 2.0 (Bot Token via Slack App)
Slack Pro or higher (webhook posting on free is deprecated)
Agent 1
Hubdoc
Supplier document fetch: invoices and receipts linked to Xero transactions
API key (Bearer token)
Hubdoc Standard (included with Xero Partner plans)
Agent 2
Dext
Secondary document source: expense receipts and supplier bills
API key (Bearer token, per-workspace)
Dext Prepare Standard
Agent 2
Automation platform
Workflow orchestration, scheduling, credential store, retry logic, logging
Internal (platform manages own auth)
Platform subscription ($99/month as confirmed)
All agents
Before you connect anything: provision sandbox or test accounts for every tool listed above and validate each connection end-to-end in the sandbox environment before any production credentials are entered. Xero provides a Demo Company for this purpose. Gmail and Sheets service accounts should be created in a non-production Google Workspace project first. Never enter live Xero OAuth tokens or Hubdoc/Dext API keys into the automation platform until sandbox validation passes.
02Per-tool integration detail
Xero
Used by Agent 2 (Reconciliation) and Agent 3 (Accruals and Reporting). Reads transactions, bank statements, and coded rules; writes journal entries; reads aged reports and final period figures for the management pack.
Auth method
OAuth 2.0 with PKCE. Register a Xero app at developer.xero.com. Store client_id and client_secret in the credential store. The access token expires after 30 minutes; refresh tokens are valid for 60 days. The automation platform must silently refresh on 401 responses.
Required scopes
openid profile email offline_access accounting.transactions accounting.transactions.read accounting.reports.read accounting.journals.read accounting.settings.read accounting.settings
Webhook / trigger
No inbound webhook is used from Xero. All Xero calls are outbound REST (polling). Agent 2 polls the GET /BankTransactions and GET /BankStatements endpoints after the chase window closes. Agent 3 polls GET /Reports/TrialBalance, GET /Reports/ProfitAndLoss, GET /Reports/BalanceSheet, and GET /Reports/AgedPayablesByContact after bookkeeper sign-off.
Required configuration
XERO_TENANT_ID must be stored in the credential store (not hardcoded). The correct tenant ID is obtained from GET /Connections after OAuth grant. Chart of accounts code mapping (account code to category label) must be stored as a JSON lookup table in the credential store under XERO_ACCOUNT_MAP. Bank account IDs for each reconciled account must be stored as XERO_BANK_ACCOUNT_IDS (array).
Rate limits
60 API calls/minute per app; 5,000 calls/day per tenant. At 200-600 transactions/month-end cycle the automation will issue roughly 40-80 Xero calls per full run. No throttling logic is strictly required, but all Xero request loops must include a 1-second delay between iterations as a precaution and must respect 429 responses with exponential backoff.
Constraints
Journal entries (POST /Journals) require the JournalLines array to balance to zero. Xero rejects unbalanced journal posts with a 400. The automation must validate the sum of DebitAmount minus CreditAmount equals 0 before posting. The Xero Demo Company resets every 28 days; do not use it for long-running credential tests.
// Reads (Agent 2)
GET /api.xro/2.0/BankTransactions?where=Status=="AUTHORISED"&fromDate={period_start}&toDate={period_end}
GET /api.xro/2.0/BankStatements/{bankAccountID}
GET /api.xro/2.0/Items // coding rules reference
// Writes (Agent 3)
POST /api.xro/2.0/ManualJournals // accruals and prepayments
// Reads (Agent 3)
GET /api.xro/2.0/Reports/TrialBalance?date={period_end}
GET /api.xro/2.0/Reports/ProfitAndLoss?fromDate={period_start}&toDate={period_end}
GET /api.xro/2.0/Reports/BalanceSheet?date={period_end}
GET /api.xro/2.0/Reports/AgedPayablesByContactGoogle Sheets
Used by Agent 2 (writes exceptions list) and Agent 3 (reads accruals schedule, writes management accounts pack). The automation platform authenticates via a dedicated service account with domain delegation so no user OAuth consent is required at runtime.
Auth method
OAuth 2.0 service account. Create a service account in Google Cloud Console, download the JSON key file, and store the entire JSON payload as GOOGLE_SERVICE_ACCOUNT_JSON in the credential store. Grant the service account editor access to the two target spreadsheets by sharing them to the service account email address.
Required scopes
https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive.file
Webhook / trigger
No inbound webhook from Sheets. Agent 3 polls the exceptions sheet for a bookkeeper_approved flag (cell B1 of the Exceptions tab) on a 15-minute interval after the reconciliation run completes. When the flag value equals TRUE the Accruals and Reporting Agent is triggered.
Required configuration
SHEETS_EXCEPTIONS_ID: the spreadsheet ID for the exceptions output sheet. SHEETS_ACCRUALS_ID: the spreadsheet ID for the accruals schedule. SHEETS_MGMT_PACK_ID: the spreadsheet ID for the management accounts template. Tab names must be fixed: Exceptions, Accruals_Schedule, PL, BalanceSheet, CashFlow, Commentary. Column headers in the accruals schedule must match exactly: period, account_code, account_name, description, debit_amount, credit_amount, reversal_flag. Store all three spreadsheet IDs in the credential store, never hardcoded.
Rate limits
Google Sheets API allows 300 requests/minute per project and 60 requests/minute per user. At current volume (one run per month with approximately 600 rows maximum) this limit is not at risk. No throttling is required but batch reads using batchGet should be preferred over individual cell reads.
Constraints
The Accruals_Schedule tab must be in a consistent column-ordered format before the agent reads it. If the format is inconsistent (merged cells, irregular headers) the agent will fail at the read step. FullSpec standardises this sheet during the Discovery stage before build begins.
// Agent 2 writes
POST /v4/spreadsheets/{SHEETS_EXCEPTIONS_ID}/values/{range}:append
// body: { values: [[transaction_id, date, amount, description, status, note]] }
// Agent 3 polls
GET /v4/spreadsheets/{SHEETS_EXCEPTIONS_ID}/values/Exceptions!B1
// Agent 3 reads accruals
GET /v4/spreadsheets/{SHEETS_ACCRUALS_ID}/values/Accruals_Schedule!A:G
// Agent 3 writes pack
PUT /v4/spreadsheets/{SHEETS_MGMT_PACK_ID}/values/{range}:updateGmail
Used by Agent 1 (chase emails and 24-hour reminders) and Agent 3 (final report distribution). Sends via the Gmail API using a service account with domain-wide delegation so mail is sent as the bookkeeper or controller address rather than a generic service address.
Auth method
OAuth 2.0 service account with domain-wide delegation. In Google Workspace Admin, grant the service account the delegation scope for Gmail. Store the service account JSON as GOOGLE_SERVICE_ACCOUNT_JSON (shared with Sheets). The impersonated sender address is stored as GMAIL_SENDER_ADDRESS in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/gmail.readonly
Webhook / trigger
Gmail push notifications (via Google Cloud Pub/Sub) are used to detect reply receipt during the chase window. Agent 1 registers a watch on the inbox of GMAIL_SENDER_ADDRESS using POST /users/me/watch with labelIds: [INBOX]. The Pub/Sub topic name is stored as GMAIL_PUBSUB_TOPIC. The watch expires every 7 days and must be renewed by the automation platform on a scheduled 6-day renewal job. Incoming notifications carry a historyId; Agent 1 calls GET /users/me/history to diff new messages.
Required configuration
GMAIL_SENDER_ADDRESS: the impersonated From address. GMAIL_CHASE_TEMPLATE_ID: the ID of the chase email template (stored as a Sheets cell reference or inline in the credential store as a string). GMAIL_REMINDER_TEMPLATE_ID: 24-hour reminder template. GMAIL_REPORT_TEMPLATE_ID: report distribution template. GMAIL_DISTRIBUTION_LIST: JSON array of stakeholder email addresses for report send. GMAIL_PUBSUB_TOPIC: Pub/Sub topic ARN for reply monitoring. All values stored in the credential store.
Rate limits
Gmail API: 250 quota units/user/second; sending 1 million messages/day project-wide. Chase emails per month-end are typically 5-20 recipients plus reminders: well within limits. No throttling required. The automation must handle 429 responses with a 5-second retry.
Constraints
Domain-wide delegation must be explicitly enabled by a Google Workspace Super Admin. If the client's Workspace admin has not done this, Gmail sending will fail with a 403 PERMISSION_DENIED. Confirm this is in place during the Discovery stage before build begins.
// Agent 1: send chase
POST /gmail/v1/users/me/messages/send
// body: RFC 2822 MIME message, base64url-encoded
// Agent 1: monitor replies
GET /gmail/v1/users/me/history?startHistoryId={historyId}&historyTypes=messageAdded
// Agent 3: send distribution
POST /gmail/v1/users/me/messages/send // with PDF attachment, base64url-encodedSlack
Used by Agent 1 for internal close-status notifications and parallel alerts to the chase emails. A dedicated Slack App is created for the automation with a Bot Token scoped to the minimum required permissions.
Auth method
OAuth 2.0 Bot Token (xoxb- prefix). Create a Slack App at api.slack.com, install it to the workspace, and store the Bot Token as SLACK_BOT_TOKEN in the credential store. Do not use Incoming Webhooks (deprecated on free plans); use the chat.postMessage API method.
Required scopes
chat:write channels:read groups:read im:read users:read
Webhook / trigger
No inbound webhook from Slack is required. All Slack calls are outbound. Agent 1 posts to channel IDs resolved at build time and stored in the credential store. Channel IDs are preferred over channel names to avoid breakage if a channel is renamed.
Required configuration
SLACK_BOT_TOKEN: xoxb- bot token. SLACK_CHANNEL_FINANCE: channel ID for the finance team notifications (e.g. C04XXFINANCE). SLACK_CHANNEL_ALERTS: channel ID for exception alerts. SLACK_USER_MAP: JSON object mapping department_head_name to their Slack user_id for @mention in chase notifications. All stored in the credential store.
Rate limits
Slack API Tier 3: 50 requests/minute for chat.postMessage. At current volume (fewer than 30 messages per month-end run) this limit presents no risk. No throttling required. Handle 429 responses by reading the Retry-After header and waiting accordingly.
Constraints
The bot must be invited to each channel it posts to before deployment, or posts will return channel_not_found or not_in_channel errors. Confirm channel membership during the sandbox test phase.
// Agent 1: post notification
POST https://slack.com/api/chat.postMessage
// body: { channel: SLACK_CHANNEL_FINANCE, text: '...', blocks: [...] }
// Authorization: Bearer {SLACK_BOT_TOKEN}Hubdoc
Used by Agent 2 (Reconciliation). Fetches published supplier documents (invoices and receipts) that have been pushed from Hubdoc to Xero. The integration reads document metadata to enrich the reconciliation matching logic.
Auth method
API key as Bearer token. Generate an API key from Hubdoc account settings under Integrations. Store the key as HUBDOC_API_KEY in the credential store. The key does not expire but should be rotated quarterly.
Required scopes
Hubdoc uses a single API key with full account access. There are no granular scopes. Access is limited to the documents and accounts belonging to the authenticated Hubdoc organisation.
Webhook / trigger
Hubdoc does not provide a native webhook. Agent 2 polls GET /documents?status=published&limit=100&page={n} after the chase window closes, iterating pages until all published documents for the period are retrieved. The period filter is applied using created_at date range parameters.
Required configuration
HUBDOC_API_KEY: Bearer token. HUBDOC_ORG_ID: the organisation identifier returned by GET /account (store at setup time). No other Hubdoc-side configuration is needed; document routing to Xero is handled within Hubdoc by the client's existing publishing rules.
Rate limits
Hubdoc API does not publish a formal rate limit but exhibits throttling above approximately 100 requests/minute in testing. At current volume (200-600 documents/month) Agent 2 will issue 3-6 paginated calls per run. A 500ms inter-request delay must be applied. Handle 429 with a 10-second backoff and one retry.
Constraints
Hubdoc only exposes documents that have been published (i.e. pushed to Xero or marked as exported). Documents still in the inbox (unpublished) are not returned by the API. If a supplier document is not yet published at the time Agent 2 runs, it will not be matched and will appear in the exceptions list.
// Agent 2: fetch published documents
GET https://api.hubdoc.com/v1/documents?status=published&created_after={period_start}&created_before={period_end}&limit=100&page={n}
// Authorization: Bearer {HUBDOC_API_KEY}
// Response fields used: id, supplier_name, total_amount, currency, date, xero_transaction_idDext
Used by Agent 2 (Reconciliation) as a secondary document source. Dext (formerly Receipt Bank) provides expense receipts and supplier bills submitted by staff. The integration reads extracted document data to supplement Hubdoc coverage.
Auth method
API key as Bearer token. Generate from Dext Prepare workspace settings under API Access. Store as DEXT_API_KEY in the credential store. Rotate quarterly.
Required scopes
Dext uses a single workspace-scoped API key. No granular OAuth scopes exist. Access covers all items and suppliers within the authenticated workspace.
Webhook / trigger
Dext supports outbound webhooks for item state changes. Register a webhook endpoint on the automation platform for the event type item.published. Store the webhook secret as DEXT_WEBHOOK_SECRET and validate the X-Dext-Signature-256 HMAC-SHA256 header on every inbound request. If the signature does not match, reject the request with 401 and log the failure.
Required configuration
DEXT_API_KEY: Bearer token. DEXT_WORKSPACE_ID: the workspace identifier from GET /v1/workspaces. DEXT_WEBHOOK_SECRET: HMAC secret for signature validation. DEXT_WEBHOOK_ENDPOINT: the public URL of the automation platform's inbound webhook receiver, registered in Dext settings.
Rate limits
Dext API: 100 requests/minute. At current volume Agent 2 will issue fewer than 20 calls per run. No throttling required. Handle 429 with a 10-second backoff.
Constraints
Dext items must have status=published to appear in the API. Items awaiting review or still processing will not be returned. Items submitted after the chase cut-off will not be included in the current cycle run and will appear as unmatched in the exceptions list.
// Agent 2: fetch published items (polling fallback)
GET https://api.dext.com/v1/items?status=published&date_from={period_start}&date_to={period_end}
// Authorization: Bearer {DEXT_API_KEY}
// Webhook: inbound event
POST {DEXT_WEBHOOK_ENDPOINT}
// Header: X-Dext-Signature-256: sha256=<hmac>
// Body fields used: item_id, supplier_name, total, currency, date, category, xero_bill_idIntegration and API SpecPage 1 of 3
FS-DOC-05Technical
03Field mappings between tools
The tables below define the exact field-level mappings for each agent handoff. All field names are written in the format used by the respective tool API response or sheet column header. Transformation notes are included where a direct 1:1 mapping does not apply.
Agent 1 handoff: Chase and Notification Agent output to Gmail and Slack
Source tool
Source field
Destination tool
Destination field
Transform
Automation platform schedule
`period_end_date`
Gmail
`subject` (template var `{{period}}`)
Format: MMM YYYY e.g. Jun 2025
Automation platform config
`GMAIL_DISTRIBUTION_LIST[n].email`
Gmail
`to`
Direct
Automation platform config
`GMAIL_DISTRIBUTION_LIST[n].name`
Gmail
`body` (template var `{{recipient_name}}`)
Direct
Automation platform config
`GMAIL_CHASE_TEMPLATE_ID`
Gmail
`body` (rendered HTML)
Template merge
Automation platform config
`SLACK_USER_MAP[name].user_id`
Slack
`blocks[].elements[].user_id` (@mention)
Direct
Automation platform config
`SLACK_CHANNEL_FINANCE`
Slack
`channel`
Direct
Automation platform schedule
`period_end_date`
Slack
`blocks[].text` (template var `{{period}}`)
Format: MMM YYYY
Agent 2 handoff: Xero, Hubdoc, and Dext to Google Sheets exceptions list
Source tool
Source field
Destination tool
Destination field
Transform
Xero
`BankTransaction.BankTransactionID`
Google Sheets
`Exceptions!A:A` (transaction_id)
Direct
Xero
`BankTransaction.DateString`
Google Sheets
`Exceptions!B:B` (date)
ISO 8601 to DD/MM/YYYY
Xero
`BankTransaction.Total`
Google Sheets
`Exceptions!C:C` (amount)
Absolute value; sign preserved
Xero
`BankTransaction.Reference`
Google Sheets
`Exceptions!D:D` (description)
Direct; truncate at 200 chars
Xero
`BankTransaction.Status`
Google Sheets
`Exceptions!E:E` (xero_status)
Direct
Hubdoc
`id`
Google Sheets
`Exceptions!F:F` (hubdoc_doc_id)
Direct; empty string if no match
Hubdoc
`supplier_name`
Google Sheets
`Exceptions!G:G` (supplier)
Direct; Dext `supplier_name` takes precedence if both present
Hubdoc
`total_amount`
Google Sheets
`Exceptions!H:H` (doc_amount)
Numeric; currency symbol stripped
Dext
`item_id`
Google Sheets
`Exceptions!I:I` (dext_item_id)
Direct; empty string if no match
Dext
`total`
Google Sheets
`Exceptions!H:H` (doc_amount)
Overwrites Hubdoc value if Dext match is more recent
Automation platform
`match_confidence_score`
Google Sheets
`Exceptions!J:J` (confidence)
0.00 to 1.00 float; flag <0.80 as exception
Automation platform
`exception_reason`
Google Sheets
`Exceptions!K:K` (reason)
Enum: UNMATCHED, AMOUNT_MISMATCH, DUPLICATE, UNCATEGORISED
Agent 3 handoff: Xero reports and Google Sheets accruals to management accounts pack
Source tool
Source field
Destination tool
Destination field
Transform
Google Sheets
`Accruals_Schedule!A` (period)
Xero
`ManualJournal.Date`
Match to period_end_date
Google Sheets
`Accruals_Schedule!B` (account_code)
Xero
`JournalLine.AccountCode`
Direct; validate against XERO_ACCOUNT_MAP
Google Sheets
`Accruals_Schedule!C` (account_name)
Xero
`JournalLine.Description`
Direct
Google Sheets
`Accruals_Schedule!E` (debit_amount)
Xero
`JournalLine.LineAmount` (positive)
Direct; 2 decimal places
Google Sheets
`Accruals_Schedule!F` (credit_amount)
Xero
`JournalLine.LineAmount` (negative)
Negated; 2 decimal places
Xero
`Report.Rows[].Cells[].Value` (P&L)
Google Sheets
`PL!C:C` (actuals column)
Numeric; strip currency formatting
Xero
`Report.Rows[].Cells[].Value` (Balance Sheet)
Google Sheets
`BalanceSheet!C:C` (actuals column)
Numeric; strip currency formatting
Xero
`Report.Rows[].Cells[].Value` (Trial Balance)
Google Sheets
`CashFlow!C:C` (closing balances)
Numeric
Google Sheets
`PL!D:D` (prior_period)
Google Sheets
`Commentary!B2` (variance_pct)
Calculate: (current-prior)/prior*100
Automation platform
`variance_commentary_draft`
Google Sheets
`Commentary!C:C` (commentary)
LLM-generated text; Finance Manager edits before approve
Agent 3 handoff: approved management accounts pack to Gmail distribution
Source tool
Source field
Destination tool
Destination field
Transform
Google Sheets
`SHEETS_MGMT_PACK_ID` (full spreadsheet)
Gmail
`attachment` (PDF)
Export via Sheets export API as application/pdf
Automation platform config
`GMAIL_DISTRIBUTION_LIST[n].email`
Gmail
`to`
Iterate; one send per recipient or BCC array
Automation platform schedule
`period_end_date`
Gmail
`subject` (template var `{{period}}`)
Format: MMM YYYY
Automation platform config
`GMAIL_REPORT_TEMPLATE_ID`
Gmail
`body`
Template merge
Automation platform config
`SLACK_CHANNEL_FINANCE`
Slack
`channel`
Confirmation post after all sends complete
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent. Each workflow is independently deployable and independently restartable. Shared state is passed via the credential store and a lightweight run-state record (stored as a JSON object in a dedicated Sheets tab named _RunState). Workflows do not call each other directly; Agent 2 and Agent 3 are triggered by state flags written to _RunState or Exceptions!B1 rather than direct invocations.
Agent 1 trigger
Scheduled trigger. The automation platform evaluates the current date at 07:00 each working day. If the date matches the last business day of the month (calculated using a business-calendar lookup excluding weekends and public holidays defined in BUSINESS_CALENDAR_REGION), the Chase and Notification workflow fires. No inbound webhook.
Agent 2 trigger
Scheduled poll. After Agent 1 fires, Agent 2 begins polling on a 15-minute interval starting at T+2 hours (allowing the chase window to open). The reconciliation run executes once the _RunState field chase_window_closed equals true, which is set either by Agent 1 at the 24-hour reminder mark or manually by the bookkeeper via a Sheets cell flag.
Agent 3 trigger
State-poll trigger. Agent 3 polls Exceptions!B1 every 15 minutes. When the value equals TRUE (set manually by the bookkeeper after reviewing exceptions), Agent 3 fires. An additional safety check confirms _RunState.reconciliation_complete equals true before proceeding, to prevent premature triggering.
Signature validation
Dext inbound webhook: HMAC-SHA256 validation on X-Dext-Signature-256 header using DEXT_WEBHOOK_SECRET. Gmail Pub/Sub: messages are authenticated via the Google Cloud service account JWT; no additional signature validation required. Slack does not send inbound events in this automation.
Credential store
All secrets are stored in the automation platform's built-in encrypted credential store. No secret values appear in workflow configuration fields, code blocks, logs, or Sheets cells. Credential keys are referenced by name only.
All credential keys referenced by name in workflow configuration. No values hardcoded anywhere in the build.
// Credential store contents (key names only - values stored encrypted)
// Xero
XERO_CLIENT_ID
XERO_CLIENT_SECRET
XERO_TENANT_ID
XERO_BANK_ACCOUNT_IDS // JSON array of account GUIDs
XERO_ACCOUNT_MAP // JSON object: { "account_code": "category_label" }
// Google (Sheets + Gmail shared service account)
GOOGLE_SERVICE_ACCOUNT_JSON // Full JSON key file contents
GMAIL_SENDER_ADDRESS // e.g. bookkeeper@[YourCompany.com]
GMAIL_CHASE_TEMPLATE_ID // Template string or Sheets reference
GMAIL_REMINDER_TEMPLATE_ID
GMAIL_REPORT_TEMPLATE_ID
GMAIL_DISTRIBUTION_LIST // JSON array: [{ name, email }, ...]
GMAIL_PUBSUB_TOPIC // projects/{project}/topics/{topic}
// Google Sheets
SHEETS_EXCEPTIONS_ID // Spreadsheet ID
SHEETS_ACCRUALS_ID // Spreadsheet ID
SHEETS_MGMT_PACK_ID // Spreadsheet ID
// Slack
SLACK_BOT_TOKEN // xoxb-...
SLACK_CHANNEL_FINANCE // Channel ID e.g. C04XXFINANCE
SLACK_CHANNEL_ALERTS // Channel ID
SLACK_USER_MAP // JSON object: { "Full Name": "UXXXXXXXX" }
// Hubdoc
HUBDOC_API_KEY
HUBDOC_ORG_ID
// Dext
DEXT_API_KEY
DEXT_WORKSPACE_ID
DEXT_WEBHOOK_SECRET
DEXT_WEBHOOK_ENDPOINT // Public URL of platform webhook receiver
// Platform
BUSINESS_CALENDAR_REGION // e.g. US, GB, AU - for last-business-day calc
MATCH_CONFIDENCE_THRESHOLD // Default: 0.80Integration and API SpecPage 2 of 3
FS-DOC-05Technical
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration point has a defined failure path. If a scenario is not covered by the table below, the default behaviour is: log the full error payload to the platform's execution log, post an alert to SLACK_CHANNEL_ALERTS with the workflow name, step name, error code, and timestamp, and halt the current run without retrying. The FullSpec team reviews silent-failure alerts within one business day. Contact support@gofullspec.com for urgent escalation.
Integration
Scenario
Required behaviour
Xero OAuth
Access token expired (401 on any Xero call)
Automatically refresh using stored refresh token. If refresh also returns 401 or 400, halt run, post alert to SLACK_CHANNEL_ALERTS with message 'Xero token refresh failed', and email GMAIL_SENDER_ADDRESS. Do not retry the original call until a valid token is confirmed. Manual re-authorisation required.
Xero API
Rate limit hit (429 Too Many Requests)
Read Retry-After header value. Wait the specified number of seconds (minimum 60). Retry the failed request once. If the retry also returns 429, halt the current step, log the error, and resume from the failed step at the next scheduled poll interval.
Xero journal POST
Unbalanced journal entry (400 ValidationException)
Do not retry. Log the full JournalLines payload that caused the error. Post alert to SLACK_CHANNEL_ALERTS. Write a row to Exceptions sheet with reason JOURNAL_BALANCE_ERROR and the account codes involved. Halt the accruals posting step only; report generation continues with a warning banner in the pack.
Google Sheets API
Spreadsheet not found (404) or permission denied (403)
Halt the affected agent immediately. Post alert to SLACK_CHANNEL_ALERTS. Do not retry. This indicates a configuration error (wrong SHEETS_*_ID or service account not granted access). Requires manual correction by the FullSpec team before the run can resume.
Google Sheets API
Quota exceeded (429) during pack write
Apply exponential backoff: wait 10 seconds, retry; wait 30 seconds, retry; wait 60 seconds, retry. If all three retries fail, log the error and post alert. The pack write is idempotent so a clean retry on the next manual trigger is safe.
Gmail send
Recipient address invalid or mailbox full (5xx SMTP error via API)
Log the failed recipient address and the error code. Skip that recipient and continue sending to remaining addresses. After the run completes, post a Slack alert listing all failed recipients. Do not retry failed sends automatically. The bookkeeper must send manually to failed recipients.
Gmail Pub/Sub
Watch expires (no reply notifications received after 7 days)
The platform runs a scheduled renewal job every 6 days to call POST /users/me/watch. If the renewal call fails, post alert to SLACK_CHANNEL_ALERTS. Agent 1 falls back to polling GET /users/me/messages?q=in:inbox&after={timestamp} on a 30-minute interval for the duration of the chase window.
Slack chat.postMessage
Channel not found (channel_not_found or not_in_channel error)
Log the error. Do not retry. Post a fallback alert via Gmail to GMAIL_SENDER_ADDRESS listing the failed Slack message content. The automation continues; Slack alerts are non-blocking.
Hubdoc API
Timeout or 5xx server error on document fetch
Retry once after 10 seconds. If the retry fails, log the error and continue the reconciliation run without Hubdoc documents for this cycle. Write a row to Exceptions with reason HUBDOC_FETCH_FAILED. Post alert to SLACK_CHANNEL_ALERTS. The bookkeeper must manually check Hubdoc for the affected period.
Dext webhook
Signature validation failure (HMAC mismatch on X-Dext-Signature-256)
Reject the request immediately with HTTP 401. Log the source IP and timestamp. Do not process the payload. Post alert to SLACK_CHANNEL_ALERTS. If more than 3 signature failures occur within one hour, disable the Dext webhook receiver and switch to polling mode using GET /v1/items. Notify support@gofullspec.com.
Dext API
Rate limit (429) during polling fallback
Wait 10 seconds and retry once. If the retry fails, log the error and continue without Dext items for this cycle. Write a row to Exceptions with reason DEXT_FETCH_FAILED. Post alert to SLACK_CHANNEL_ALERTS.
Google Sheets (Exceptions!B1 poll)
Bookkeeper approval flag not set within 48 hours of reconciliation completing
Post a reminder to SLACK_CHANNEL_FINANCE and send a Gmail reminder to GMAIL_SENDER_ADDRESS after 24 hours. Post a second reminder at 48 hours. If the flag is still not set after 72 hours, halt Agent 3 and post an escalation alert to SLACK_CHANNEL_ALERTS. Do not auto-approve. A human must set the flag.
Integration and API SpecPage 3 of 3