FS-DOC-05Technical
Integration and API Spec
Tax Preparation Workflow
[YourCompany.com] · Finance Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the Tax Preparation Workflow automation. It covers tool authentication, exact OAuth scopes, webhook configuration, field mappings between agents, credential-store layout, and defined error-handling behaviour at every integration boundary. FullSpec engineers use this specification during build, QA, and post-launch monitoring. Nothing in this document requires action from the process owner; all configuration is handled by the FullSpec team.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Orchestration layer
Workflow orchestration, scheduling, credential store, retry logic
Internal service account
N/A (platform choice TBC)
All agents
QuickBooks Online
Source of transaction data: P&L, balance sheet, general ledger export
OAuth 2.0 (PKCE)
QuickBooks Online Essentials or above
Agent 1, Agent 2
Hubdoc
OCR receipt capture, field extraction, transaction matching
API key (Bearer token)
Hubdoc Standard ($25/mo)
Agent 2
Gmail
Personalised receipt request emails and reply monitoring
OAuth 2.0 (service account with domain delegation)
Google Workspace Business Starter or above
Agent 2
Google Drive
Document storage, checklist sheet, package folder assembly
OAuth 2.0 (service account)
Google Workspace Business Starter or above
Agent 1, Agent 3
Slack
Exception alerts and package-ready notifications to bookkeeper
OAuth 2.0 (bot token, Slack app)
Slack Free or above (webhooks require Pro for full history)
Agent 2, Agent 3
DocuSign
Digital sign-off envelope dispatch and completion tracking
OAuth 2.0 (JWT grant)
DocuSign Standard ($25/mo) or above
Agent 3
Before you connect anything: create sandbox or developer accounts for every tool listed above and validate all credentials, scopes, and webhook endpoints in those sandbox environments first. Never paste production credentials into the orchestration layer until sandbox end-to-end tests pass for every integration. QuickBooks sandbox, DocuSign developer account, and Hubdoc staging must all be used during the build phase.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
QuickBooks Online
Provides the transaction export (P&L, balance sheet, general ledger) that seeds the Document Gap Detector and is referenced by the Receipt Collection Coordinator for transaction matching. Used by Agents 1 and 2.
Auth method
OAuth 2.0 with PKCE flow. Redirect URI registered in the Intuit Developer Portal. Access token lifetime: 60 minutes. Refresh token lifetime: 100 days. Tokens stored in the credential store, never hardcoded.
Required scopes
com.intuit.quickbooks.accounting (read access to reports, transactions, and accounts). No write scope is required for this automation.
Webhook / trigger
No inbound webhook from QuickBooks is used. The orchestration layer polls the QuickBooks Reports API on a scheduled trigger at the start of each preparation window (see Section 04). Polling is preferred over webhooks here because report generation is batch-oriented, not event-driven.
Required configuration
Realm ID (company ID) stored in credential store. Report date range parameters derived from preparation window dates stored in workflow config variables, not hardcoded. Report types required: ProfitAndLoss, BalanceSheet, GeneralLedger. Accounting method: Accrual (confirm with bookkeeper before go-live).
Rate limits
500 requests/minute per company; 5,000 requests/day. At current volume (~4 cycles/year, each generating 3 report API calls and up to 50 transaction detail calls) peak usage is well under 100 requests per run. Throttling is not required at this volume. Implement exponential backoff for 429 responses as a precaution.
Constraints
QuickBooks Desktop and Enterprise are not supported by the REST API used here. If the business runs Desktop, a third-party connector (e.g. QODBC or Intuit's Web Connector) must be evaluated separately. OAuth tokens must be refreshed before expiry; token refresh logic is handled by the orchestration layer.
// API base URL
https://quickbooks.api.intuit.com/v3/company/{realmId}/
// Report endpoint example
GET /reports/ProfitAndLoss?start_date={period_start}&end_date={period_end}&accounting_method=Accrual
// Transaction query endpoint
POST /query?query=SELECT * FROM Transaction WHERE TxnDate >= '{period_start}' AND TxnDate <= '{period_end}'
// Output fields consumed downstream
transaction.Id, transaction.TxnDate, transaction.Amount, transaction.AccountRef.name,
transaction.EntityRef.name, transaction.PrivateNote, transaction.Attachments[]Hubdoc
Receives uploaded receipt documents, runs OCR extraction, and returns structured field data for matching against QuickBooks transactions. Used exclusively by Agent 2 (Receipt Collection Coordinator).
Auth method
API key passed as Bearer token in the Authorization header. Key generated from the Hubdoc account settings panel and stored in the credential store.
Required scopes
Hubdoc uses a single API key with full account access. There is no scope granularity. Restrict key exposure to the orchestration layer credential store only.
Webhook / trigger
Hubdoc supports outbound webhooks on document.processed events. Configure the webhook to POST to the orchestration layer's inbound endpoint for the Receipt Collection Coordinator. Validate the X-Hubdoc-Signature header (HMAC-SHA256) on every inbound request. Webhook secret stored in the credential store.
Required configuration
Hubdoc account must have auto-fetch disabled for sources not in scope to avoid false positives. Folder structure in Hubdoc should mirror the Google Drive package folders (by employee name or vendor). Matching confidence threshold stored as a workflow config variable (default: 0.85); receipts below threshold are flagged as exceptions rather than auto-matched.
Rate limits
Hubdoc does not publish a hard rate limit in its public documentation. Based on standard usage patterns, treat as 60 requests/minute. At current volume (estimated 200 to 400 receipts per cycle across 4 cycles/year) there is no throttling concern. Insert a 500ms delay between consecutive document upload calls as a courtesy limit.
Constraints
Handwritten or low-quality image receipts will produce low-confidence extractions and must route to the manual exception Slack channel. Hubdoc's OCR does not support all currencies; confirm that all expense currencies in scope are supported before go-live. PDF receipts with embedded text extract more reliably than image-only PDFs.
// Inbound: upload document
POST /documents
Content-Type: multipart/form-data
Body: { file: <binary>, filename: <string>, folder_id: <string> }
// Outbound webhook payload (document.processed)
{
"event": "document.processed",
"document": {
"id": "<hubdoc_doc_id>",
"vendor": "<extracted_vendor_name>",
"date": "<extracted_date_YYYY-MM-DD>",
"total": <extracted_amount_float>,
"currency": "<ISO_4217_code>",
"confidence": <0.0_to_1.0>,
"folder_id": "<string>"
}
}Gmail
Sends personalised receipt request emails to individual employees and monitors replies and attachment uploads. Used by Agent 2 (Receipt Collection Coordinator).
Auth method
OAuth 2.0 with service account and domain-wide delegation. The service account impersonates the bookkeeper's address (e.g. bookkeeper@[YourCompany.com]) to send mail on their behalf. Service account JSON key stored in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send (send messages); https://www.googleapis.com/auth/gmail.readonly (read replies and attachments); https://www.googleapis.com/auth/gmail.labels (apply processing labels to handled threads).
Webhook / trigger
Use the Gmail Push Notification API (Google Cloud Pub/Sub topic subscription) to receive near-real-time notifications when new messages arrive in the monitored inbox. The orchestration layer subscribes to the Pub/Sub topic and processes only messages with the label TAX_PREP_REPLY applied by the automation. Alternatively, poll the Gmail API every 5 minutes for the label if Pub/Sub is not available in the chosen orchestration platform.
Required configuration
Gmail labels to create before go-live: TAX_PREP_OUTBOUND, TAX_PREP_REPLY, TAX_PREP_PROCESSED. These label IDs (not names) are stored in the credential store. Email template ID stored as a config variable; template contains placeholders {{employee_name}}, {{transaction_list}}, {{upload_link}}, {{deadline_date}}. Sender display name configured as 'Finance Team' to maintain professional context.
Rate limits
Gmail API quota: 250 quota units/second per user; sending limit: 2,000 messages/day for Google Workspace accounts. At current volume (estimated 10 to 30 receipt request emails per cycle, 4 cycles/year) there is no risk of hitting limits. No throttling required.
Constraints
Domain-wide delegation must be approved by the Google Workspace administrator before build begins. Email threading logic must use the In-Reply-To header to correlate replies to the original request. Attachments larger than 25MB cannot be received via Gmail; employees with large files should use the Google Drive upload link provided in the email instead.
// Send receipt request
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
Body: { raw: <base64url_encoded_MIME_message> }
// MIME message key headers
To: {{employee_email}}
Subject: Action required: receipts needed for {{period_label}}
In-Reply-To: <not set on initial send>
X-TAX-PREP-TOKEN: {{unique_request_token}} // for reply correlation
// List messages with label
GET /gmail/v1/users/me/messages?labelIds=TAX_PREP_REPLY&q=is:unread
// Output fields consumed downstream
message.id, message.threadId, message.payload.headers[From],
message.payload.parts[].filename, message.payload.parts[].body.attachmentIdGoogle Drive
Stores the preparation checklist (Google Sheet), incoming documents, and the final assembled tax package. Used by Agents 1 and 3.
Auth method
OAuth 2.0 with service account. The service account is granted Editor access to the specific tax preparation folder only, not the entire shared drive. Service account JSON key stored in the credential store.
Required scopes
https://www.googleapis.com/auth/drive.file (create and modify files the app created); https://www.googleapis.com/auth/spreadsheets (read and write the checklist Google Sheet); https://www.googleapis.com/auth/drive.readonly (list folder contents for gap detection).
Webhook / trigger
No inbound webhook from Google Drive. The Document Gap Detector reads the checklist sheet via the Sheets API on a scheduled poll at workflow start. Agent 3 writes to Drive via the Drive API when assembling the package.
Required configuration
Root folder ID for tax preparation stored in the credential store. Checklist Google Sheet ID stored in the credential store. Sheet tab names and column structure must match the field mapping in Section 03. Subfolder naming convention: TAX_{PERIOD_YEAR}_{Q|ANNUAL}_{ENTITY_CODE} (e.g. TAX_2024_Q2_MAIN). Folder IDs for each period stored in workflow config variables updated each cycle.
Rate limits
Google Drive API: 1,000 requests/100 seconds per user. Sheets API: 300 requests/minute per project. At current volume (under 500 file operations per cycle) no throttling is required. Implement a 200ms delay between batch file move operations in Agent 3 to stay within quotas comfortably.
Constraints
The service account must be added as an explicit Editor to the shared drive or folder; broad domain sharing is not sufficient. File uploads via the Drive API must use resumable upload sessions for files larger than 5MB to avoid timeout errors. Google Sheets API has a 10MB response size limit per read; if the checklist sheet exceeds this, pagination or splitting into multiple sheets is required.
// Read checklist sheet
GET https://sheets.googleapis.com/v4/spreadsheets/{checklist_sheet_id}/values/{sheet_tab}!A:H
// Update checklist row status
PUT /v4/spreadsheets/{checklist_sheet_id}/values/{sheet_tab}!G{row}
Body: { values: [["MATCHED"]] }
// Upload file to package folder
POST https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable
Body: { name: <filename>, parents: [<package_folder_id>] }
// Output: file metadata returned after upload
file.id, file.name, file.mimeType, file.webViewLink, file.createdTimeSlack
Posts exception alerts to the bookkeeper when receipts cannot be auto-matched, and sends a package-ready notification to the finance manager when Agent 3 completes assembly. Used by Agents 2 and 3.
Auth method
OAuth 2.0 bot token (xoxb- prefix) issued through a dedicated Slack app installed in the workspace. Bot token stored in the credential store. Incoming webhook URLs stored separately per channel in the credential store as a fallback for simple notifications.
Required scopes
chat:write (post messages); chat:write.public (post to channels the bot has not joined); files:write (upload exception summary files if needed); channels:read (resolve channel IDs by name); users:read (look up employee Slack user IDs for direct messages); users:read.email (map employee email addresses to Slack user IDs).
Webhook / trigger
Slack is used as an output channel only; no inbound events from Slack are consumed by this automation. Outbound messages are sent via the chat.postMessage API method. Incoming webhook URLs are configured per channel as a simpler fallback if the bot token approach is not available.
Required configuration
Exception channel ID (e.g. #tax-prep-exceptions) stored in the credential store. Finance manager notification channel ID (e.g. #finance-ops) stored in the credential store. Bookkeeper Slack user ID stored in the credential store for direct-message escalation. Message block templates defined as JSON Block Kit payloads in workflow config; not hardcoded in step logic.
Rate limits
Slack API tier 3: 50+ requests/minute for chat.postMessage. At current volume (under 40 Slack messages per cycle across 4 cycles) there is no rate limit concern. No throttling required. Retry once with a 2-second delay on HTTP 429 responses.
Constraints
The Slack app must be installed in the workspace by a Workspace Admin before go-live. The bot must be invited to each channel it posts to (#tax-prep-exceptions, #finance-ops) before production runs. Slack message formatting uses Block Kit JSON; plain text fallback must be included in every payload for notification-only clients.
// Post exception alert
POST https://slack.com/api/chat.postMessage
Headers: Authorization: Bearer {slack_bot_token}
Body: {
channel: "{exception_channel_id}",
blocks: [
{ type: "header", text: { type: "plain_text", text: "Receipt exceptions: {period_label}" } },
{ type: "section", text: { type: "mrkdwn",
text: "*{exception_count}* items require manual review.\n<{drive_folder_link}|Open exception folder>" } }
],
text: "{exception_count} receipt exceptions require manual review for {period_label}."
}
// Post package-ready notification
Body: {
channel: "{finance_ops_channel_id}",
text: "Tax package for {period_label} is ready for sign-off. DocuSign request sent to {approver_name}."
}DocuSign
Dispatches the sign-off envelope to the business owner or director for final approval on the assembled tax package. Used by Agent 3 (Package Builder and Dispatcher).
Auth method
OAuth 2.0 JWT grant. The integration key and RSA private key are stored in the credential store. The service account user (a DocuSign admin) grants consent once. Access tokens are obtained programmatically by the orchestration layer before each envelope send.
Required scopes
signature (send envelopes and act on behalf of users); impersonation (required for JWT grant to act as the sending user). These scopes are requested at the time of admin consent grant.
Webhook / trigger
DocuSign Connect (outbound webhook) must be configured in the DocuSign Admin console to POST envelope status events (envelope-completed, envelope-declined, envelope-voided) to the orchestration layer's inbound endpoint. Validate the X-DocuSign-Signature-1 header (HMAC-SHA256) using the Connect secret stored in the credential store. On envelope-completed, the orchestration layer updates the checklist sheet status and posts a Slack confirmation.
Required configuration
Account ID stored in the credential store. Envelope template ID (created once per tax entity in the DocuSign admin panel) stored in the credential store. Template must include signer role 'Director', signature tab mapped to the sign-off page, and date-signed tab. Approver signer email and name passed as dynamic envelope recipients at send time, not hardcoded in the template. DocuSign Connect webhook URL and secret stored in the credential store.
Rate limits
DocuSign Standard plan: 100 envelopes/month included; additional envelopes billed. At 4 preparation cycles/year, envelope consumption is minimal (1 envelope per cycle). API rate limit: 1,000 requests/hour. No throttling required at this volume.
Constraints
DocuSign envelope templates must be reviewed and approved by the external accountant before go-live to confirm signature field placement matches the return format. JWT grant requires a one-time admin consent step that must be completed by the DocuSign account owner before the first production run. Voided or declined envelopes must trigger an alert to the finance manager, not silently fail.
// Obtain JWT access token
POST https://account.docusign.com/oauth/token
Body: { grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion: <signed_JWT> }
// Send envelope from template
POST https://na4.docusign.net/restapi/v2.1/accounts/{account_id}/envelopes
Body: {
templateId: "{envelope_template_id}",
status: "sent",
templateRoles: [{
roleName: "Director",
email: "{approver_email}",
name: "{approver_name}"
}],
emailSubject: "Action required: sign off on {period_label} tax package",
emailBlurb: "The tax document package for {period_label} is ready for your review and signature."
}
// Connect webhook inbound payload (envelope-completed)
{
"event": "envelope-completed",
"data": {
"envelopeId": "<guid>",
"envelopeSummary": { "status": "completed", "completedDateTime": "<ISO8601>" }
}
}Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The tables below document every field that moves between tools at each agent handoff. Monospace field names reflect the exact property path used in API responses and the orchestration layer's data model. Where a field is transformed or derived, the transformation is noted in the Destination field column.
Handoff A: QuickBooks to Document Gap Detector (Agent 1 internal)
Source tool
Source field
Destination tool
Destination field
QuickBooks
`Transaction.Id`
Gap Detector logic
`gap_item.transaction_id`
QuickBooks
`Transaction.TxnDate`
Gap Detector logic
`gap_item.transaction_date`
QuickBooks
`Transaction.Amount`
Gap Detector logic
`gap_item.amount`
QuickBooks
`Transaction.AccountRef.name`
Gap Detector logic
`gap_item.account_name`
QuickBooks
`Transaction.EntityRef.name`
Gap Detector logic
`gap_item.employee_or_vendor`
QuickBooks
`Transaction.PrivateNote`
Gap Detector logic
`gap_item.description`
QuickBooks
`Transaction.Attachments[]`
Gap Detector logic
`gap_item.has_attachment` (boolean, derived: length > 0)
Google Drive (Checklist Sheet)
`sheet.row.B` (employee_name)
Gap Detector logic
`gap_item.assigned_to`
Google Drive (Checklist Sheet)
`sheet.row.C` (document_type)
Gap Detector logic
`gap_item.expected_document_type`
Google Drive (Checklist Sheet)
`sheet.row.G` (status)
Gap Detector logic
`gap_item.checklist_status` (filter: status != 'COMPLETE')
Handoff B: Document Gap Detector output to Receipt Collection Coordinator (Agent 1 to Agent 2)
Source tool
Source field
Destination tool
Destination field
Gap Detector output
`gap_item.transaction_id`
Gmail (email body)
`{{transaction_list}}` row: transaction reference
Gap Detector output
`gap_item.employee_or_vendor`
Gmail (email recipient)
`To:` header (resolved via employee email lookup table)
Gap Detector output
`gap_item.transaction_date`
Gmail (email body)
`{{transaction_list}}` row: date column
Gap Detector output
`gap_item.amount`
Gmail (email body)
`{{transaction_list}}` row: amount column
Gap Detector output
`gap_item.expected_document_type`
Gmail (email body)
`{{transaction_list}}` row: document type requested
Gap Detector output
`gap_item.transaction_id`
QuickBooks (match reference)
`receipt_match.qb_transaction_id`
Gap Detector output
`gap_item.assigned_to`
Hubdoc (folder routing)
`hubdoc.folder_id` (resolved from employee-to-folder mapping in config)
Handoff C: Hubdoc OCR output matched against QuickBooks (Agent 2 internal)
Source tool
Source field
Destination tool
Destination field
Hubdoc webhook
`document.vendor`
QuickBooks match logic
`receipt_match.vendor_name` (fuzzy-matched to `Transaction.EntityRef.name`)
Hubdoc webhook
`document.date`
QuickBooks match logic
`receipt_match.receipt_date` (matched within +/- 3 calendar days of `Transaction.TxnDate`)
Hubdoc webhook
`document.total`
QuickBooks match logic
`receipt_match.amount` (matched within $0.01 tolerance of `Transaction.Amount`)
Hubdoc webhook
`document.confidence`
Exception routing logic
`receipt_match.confidence_score` (threshold: 0.85; below = exception)
Hubdoc webhook
`document.id`
Google Drive (file reference)
`package_item.hubdoc_doc_id` (stored for audit trail)
Hubdoc webhook
`document.currency`
QuickBooks match logic
`receipt_match.currency` (must equal `Transaction.CurrencyRef.value`)
Handoff D: Receipt Collection Coordinator to Slack and checklist update (Agent 2 to Slack and Google Drive)
Source tool
Source field
Destination tool
Destination field
Match logic output
`receipt_match.qb_transaction_id`
Google Drive (Checklist Sheet)
`sheet.row.G` (status) = 'MATCHED'
Match logic output
`receipt_match.hubdoc_doc_id`
Google Drive (Checklist Sheet)
`sheet.row.H` (document_reference)
Exception list
`exception.transaction_id`
Slack Block Kit payload
`blocks[].text` (exception line item)
Exception list
`exception.employee_or_vendor`
Slack Block Kit payload
`blocks[].text` (assignee field)
Exception list
`exception.amount`
Slack Block Kit payload
`blocks[].text` (amount field)
Exception list
`exception.reason`
Slack Block Kit payload
`blocks[].text` (reason: low_confidence | no_match | currency_mismatch)
Handoff E: Package Builder and Dispatcher (Agent 3) output to DocuSign and Slack
Source tool
Source field
Destination tool
Destination field
Google Drive (package folder)
`file.webViewLink`
DocuSign email body
`emailBlurb` (package link for reference)
Workflow config
`config.approver_email`
DocuSign envelope recipient
`templateRoles[0].email`
Workflow config
`config.approver_name`
DocuSign envelope recipient
`templateRoles[0].name`
Workflow config
`config.period_label`
DocuSign email subject
`emailSubject` (period reference string)
DocuSign webhook
`envelopeSummary.status`
Google Drive (Checklist Sheet)
`sheet.row.G` (status) = 'SIGNED' or 'DECLINED'
DocuSign webhook
`data.envelopeId`
Google Drive (Checklist Sheet)
`sheet.row.H` (document_reference) = envelope ID
DocuSign webhook
`envelopeSummary.completedDateTime`
Slack notification
`blocks[].text` (completion timestamp)
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent: (1) Document Gap Detector workflow, (2) Receipt Collection Coordinator workflow, (3) Package Builder and Dispatcher workflow. All three workflows share a single credential store within the orchestration platform. Inter-workflow data is passed via a shared internal data store (key-value or lightweight database table) keyed on period_run_id, a unique identifier generated at workflow start.
Agent 1 trigger: Document Gap Detector
Scheduled poll trigger. Fires at 08:00 local time on the first business day of each preparation window (dates stored as workflow config variables: Q1 = Jan 5, Q2 = Apr 5, Q3 = Jul 5, Q4 = Oct 5, Annual = Jan 20). Can also be manually triggered by the finance manager via a Slack slash command or a simple webhook call to the orchestration layer's manual trigger endpoint. No signature validation required for the scheduled trigger; the manual webhook endpoint requires a shared secret header (X-Manual-Trigger-Secret) stored in the credential store.
Agent 2 trigger: Receipt Collection Coordinator
Event-driven trigger. Fires when Agent 1 writes the gap list to the shared data store and sets the period_run_id status to GAP_COMPLETE. The Receipt Collection Coordinator workflow polls the shared data store every 2 minutes for this status flag (webhook-native trigger preferred if the orchestration platform supports internal event emitting). Hubdoc document.processed webhook events are processed continuously by a sub-workflow within Agent 2's workflow context.
Agent 3 trigger: Package Builder and Dispatcher
Event-driven trigger. Fires when the bookkeeper posts a specific Slack message (e.g. /taxprep-ready {period_run_id}) to the exceptions channel, or when the automated exception count reaches zero and the shared data store status is set to EXCEPTIONS_RESOLVED. The Slack event trigger uses the Slack Events API (event_callback for message.channels) with request URL verification (URL verification challenge must be handled at setup). The orchestration layer validates the Slack signing secret (X-Slack-Signature header, HMAC-SHA256) on every inbound Slack event.
Credential store structure
See code block below. All credentials are stored as encrypted key-value entries in the orchestration platform's native credential store. No credential value appears in workflow node configuration fields; all references use the credential key name.
Shared data store schema
Key: period_run_id (format: TAX_{YEAR}_{PERIOD_CODE}, e.g. TAX_2024_Q2). Fields: status (STARTED | GAP_COMPLETE | CHASING | EXCEPTIONS_RESOLVED | PACKAGED | SIGNED), gap_item_count (int), matched_count (int), exception_count (int), package_folder_id (string), docusign_envelope_id (string), created_at (ISO8601), updated_at (ISO8601).
Logging
Every workflow execution logs: period_run_id, step name, timestamp, input payload hash (not full payload for PII reasons), output status, and any error codes. Logs are retained for 90 days in the orchestration platform's built-in log store and exported to a dedicated Google Drive log folder monthly.
All values are encrypted at rest in the orchestration platform credential store. Rotate OAuth refresh tokens and API keys at least every 180 days.
// ── CREDENTIAL STORE CONTENTS ──────────────────────────────────────
// One entry per line: KEY_NAME = <type> | <source>
// QuickBooks Online
QBO_CLIENT_ID = OAuth2 client ID | Intuit Developer Portal
QBO_CLIENT_SECRET = OAuth2 client secret | Intuit Developer Portal
QBO_REFRESH_TOKEN = OAuth2 refresh token | Generated at consent grant
QBO_REALM_ID = Company ID string | QuickBooks account settings
// Hubdoc
HUBDOC_API_KEY = Bearer token string | Hubdoc account settings
HUBDOC_WEBHOOK_SECRET = HMAC-SHA256 secret | Hubdoc webhook config panel
HUBDOC_CONFIDENCE_THRESHOLD= Config float (0.85) | Workflow config variable
// Gmail (Google Workspace service account)
GMAIL_SA_JSON = Service account JSON | Google Cloud Console
GMAIL_IMPERSONATE_EMAIL = String (bookkeeper@) | Workflow config variable
GMAIL_LABEL_OUTBOUND_ID = Gmail label ID | Gmail API (created at setup)
GMAIL_LABEL_REPLY_ID = Gmail label ID | Gmail API (created at setup)
GMAIL_LABEL_PROCESSED_ID = Gmail label ID | Gmail API (created at setup)
GMAIL_PUBSUB_TOPIC = Pub/Sub topic name | Google Cloud Console
// Google Drive
GDRIVE_SA_JSON = Service account JSON | Google Cloud Console
GDRIVE_ROOT_FOLDER_ID = Drive folder ID | Google Drive (shared folder)
GDRIVE_CHECKLIST_SHEET_ID = Spreadsheet ID | Google Sheets URL
// Slack
SLACK_BOT_TOKEN = xoxb- token string | Slack app credentials page
SLACK_SIGNING_SECRET = HMAC signing secret | Slack app credentials page
SLACK_EXCEPTION_CHANNEL_ID = Channel ID string | Slack channel settings
SLACK_FINANCE_CHANNEL_ID = Channel ID string | Slack channel settings
SLACK_BOOKKEEPER_USER_ID = Slack user ID string | Slack workspace admin
SLACK_MANUAL_TRIGGER_SECRET= Shared secret string | Generated at setup
// DocuSign
DOCUSIGN_INTEGRATION_KEY = OAuth2 client ID | DocuSign admin console
DOCUSIGN_RSA_PRIVATE_KEY = RSA PEM string | DocuSign admin console
DOCUSIGN_ACCOUNT_ID = Account GUID | DocuSign account settings
DOCUSIGN_TEMPLATE_ID = Envelope template ID | DocuSign template library
DOCUSIGN_CONNECT_SECRET = HMAC-SHA256 secret | DocuSign Connect config
DOCUSIGN_APPROVER_EMAIL = String (approver@) | Workflow config variable
DOCUSIGN_APPROVER_NAME = String | Workflow config variable
// Shared / cross-workflow
PERIOD_RUN_STORE_TABLE = DB table / KV prefix | Orchestration platform
CURRENT_PERIOD_LABEL = String (e.g. Q2 2024)| Updated each cycle
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 the period_run_id, the failing step, the error code or message, and the timestamp. Where a retry policy is defined, exponential backoff is used: wait 30s before retry 1, 2 minutes before retry 2, 10 minutes before retry 3, then escalate.
Integration
Scenario
Required behaviour
QuickBooks API
401 Unauthorized (token expired)
Orchestration layer automatically refreshes the access token using QBO_REFRESH_TOKEN before retrying the failed request once. If refresh also returns 401, alert the bookkeeper via Slack (direct message to SLACK_BOOKKEEPER_USER_ID) and halt the workflow. Log the event with full error response.
QuickBooks API
429 Too Many Requests
Apply exponential backoff (30s, 2min, 10min). If the third retry fails, pause the workflow for 1 hour and retry once more. If still failing, send a Slack alert to #finance-ops and log the failure. At current volume this scenario is very unlikely.
QuickBooks API
Report returns zero transactions
Treat as a data anomaly, not a successful run. Do not proceed to Agent 2. Send a Slack alert to the bookkeeper and finance manager: 'QuickBooks report returned zero transactions for {period_label}. Manual check required.' Log and halt the workflow run.
Hubdoc webhook
Webhook signature validation fails (X-Hubdoc-Signature mismatch)
Reject the request immediately with HTTP 400. Log the inbound IP, timestamp, and payload hash. Do not process the document. If three consecutive invalid signatures are received within 5 minutes, send a Slack alert to the bookkeeper and pause Hubdoc inbound processing for manual review.
Hubdoc OCR
confidence score below 0.85 threshold
Do not auto-match the receipt. Route the document to the exception list. Post the exception to #tax-prep-exceptions via Slack with vendor name, amount, date, confidence score, and a link to the Hubdoc document. Update the checklist sheet row status to EXCEPTION. Await bookkeeper resolution.
Gmail send
Delivery failure (bounce or 5xx from Gmail API)
Retry the send once after 60 seconds. If the second attempt fails, log the failed recipient address and the error response. Post a Slack alert to the bookkeeper listing the employee name and email address that could not be reached, with instructions to contact them directly.
Gmail reply monitoring
Pub/Sub subscription loses connection or returns no events for 30 minutes during an active preparation window
Fall back to polling the Gmail API every 5 minutes using the GMAIL_LABEL_REPLY_ID filter. Log the fallback activation. Attempt to re-establish the Pub/Sub subscription every 15 minutes. Alert the FullSpec team at support@gofullspec.com if the subscription cannot be restored within 2 hours.
Google Drive
File upload fails (timeout or 5xx)
Retry using a resumable upload session (the Drive API returns a session URI on the first request; resume from the last committed byte). Retry up to 3 times with exponential backoff. If all retries fail, log the failed file name and period_run_id, post a Slack alert to the bookkeeper, and halt package assembly until the issue is resolved.
Google Drive (Checklist Sheet)
Spreadsheet write returns 403 Forbidden
Check that the service account (GDRIVE_SA_JSON) still has Editor access to the sheet. If access has been revoked, alert the FullSpec team at support@gofullspec.com and post a Slack alert to the finance manager. Do not silently skip the write; stale checklist data will corrupt subsequent runs.
Slack
Bot token revoked or 401 Unauthorized on chat.postMessage
Log the failure. Attempt to post to the incoming webhook URL (SLACK_EXCEPTION_CHANNEL_ID webhook fallback) instead. If the fallback also fails, send an email from the Gmail service account to GMAIL_IMPERSONATE_EMAIL and the finance manager address as a last-resort alert. Never suppress an exception notification.
DocuSign
JWT token request fails or returns 401
Retry the token request once after 30 seconds. If the retry fails, do not attempt to send the envelope. Log the failure and post a Slack alert to the finance manager: 'DocuSign sign-off could not be sent for {period_label}. Manual envelope dispatch required.' Provide the template ID and approver details in the Slack message.
DocuSign Connect webhook
Envelope status event not received within 48 hours of send
Poll the DocuSign envelopes/{envelope_id} endpoint every 6 hours to check status. If the envelope shows as 'sent' (not completed) after 48 hours, send a Slack reminder to the finance manager and a direct Slack message to the approver. If no response after 72 hours, post a Slack escalation to the finance manager requesting manual follow-up.
Unhandled exceptions must never fail silently. Every error path in the orchestration layer must log to the platform's execution log AND dispatch a Slack notification to at least one human recipient. If Slack is unavailable, the fallback is email via Gmail. There is no scenario in which an error is swallowed without a human being informed. Contact support@gofullspec.com if any integration produces an error pattern not covered in this table.
Integration and API SpecPage 4 of 4