Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
Developer Handover Pack
Tax Preparation Workflow
[YourCompany.com] · Finance Department · Prepared by FullSpec · [Today's Date]
This pack gives the FullSpec build team everything needed to construct, connect, and validate the Tax Preparation Workflow automation. It covers the current-state process map with bottlenecks identified, full agent specifications with IO contracts, an end-to-end data flow trace, and the recommended build stack. The process owner's role is to confirm access credentials and resolve exception items flagged during testing. All build, integration, and deployment work is handled by FullSpec. Reach the FullSpec team at support@gofullspec.com for any pre-build queries.
01Process snapshot
02Agent specifications
Compares the QuickBooks transaction export against the expected document checklist and identifies every missing receipt, uncategorised entry, or unmatched statement. Builds a structured request list broken down by employee or vendor. This agent fires at the start of each preparation window and produces the input list that drives all downstream agents. It must handle QuickBooks Online OAuth token refresh transparently and must not re-flag transactions already resolved in a prior partial run. Complexity: Moderate. Estimated build time: 10 hours.
// Input
quickbooks.export.profit_loss -> { period_start, period_end, line_items[] }
quickbooks.export.general_ledger -> { transactions[]: { txn_id, date, vendor, amount, category, receipt_attached } }
google_drive.checklist_sheet -> { required_docs[]: { doc_type, employee_or_vendor, due_date } }
// Output
gap_list: {
missing_receipts[]: { txn_id, employee, amount, date, category }
uncategorised_txns[]: { txn_id, vendor, amount, date }
unmatched_statements[]:{ account_id, period, expected_closing_balance }
generated_at: ISO8601 timestamp
preparation_period: { start, end }
}- QuickBooks Online (not Desktop or Enterprise) is assumed. If the client runs Desktop/Enterprise, a middleware connector such as a local sync agent must be confirmed before build starts. Confirm the active QuickBooks edition before writing any API calls.
- OAuth 2.0 credentials (client_id, client_secret, refresh_token) must be provisioned by the QuickBooks account administrator and stored in the shared credential store before this agent can be built or tested.
- The expected document checklist is stored as a Google Sheet with a fixed schema: columns doc_type, employee_or_vendor, period, and status. The sheet ID and tab name must be confirmed and locked before build. Any structural change post-build requires a mapping update.
- Deduplication: the agent must check the gap_list output from the previous run (stored in the error-log table) and exclude any txn_id already marked resolved. This prevents repeat emails to employees for receipts already submitted.
- Fallback behaviour: if the QuickBooks API returns a rate-limit error (429), the agent must back off with exponential retry (3 attempts, 30/60/120 second intervals) and post a Slack alert if all retries fail.
- Confirm with the process owner: what percentage of transactions are from employees versus direct vendor invoices? This affects how the gap list is segmented for the next agent.
Sends personalised receipt request emails to employees via Gmail, monitors replies and uploads, processes incoming documents through Hubdoc, and matches each receipt to its corresponding QuickBooks transaction. Any item that cannot be matched automatically is escalated to the bookkeeper via a dedicated Slack channel with full transaction context. This agent handles steps 4, 5, and 6 of the manual process and is the highest-volume agent in the workflow. Complexity: Complex. Estimated build time: 18 hours.
// Input
gap_list.missing_receipts[]: { txn_id, employee_email, amount, date, category, vendor }
gmail.inbound_reply: { from_email, thread_id, attachments[]: { filename, mime_type, file_data } }
hubdoc.extraction_result: { doc_id, vendor_name, date, total_amount, currency, confidence_score }
// Output
receipt_log: {
matched[]: { txn_id, hubdoc_doc_id, qb_attachment_id, confidence_score, matched_at }
unmatched[]: { txn_id, employee_email, reason_code, escalated_at }
}
// On exception (unmatched item)
slack.post to #tax-exceptions: {
txn_id, employee, amount, date, category,
reason_code: 'LOW_CONFIDENCE' | 'NO_REPLY' | 'WRONG_AMOUNT' | 'DUPLICATE',
action_required: 'Bookkeeper to resolve manually'
}- Gmail API requires the gmail.send and gmail.readonly OAuth scopes. A dedicated service account or delegated credentials for the bookkeeper's Gmail address must be confirmed before build. Do not use a personal OAuth token that may expire without a refresh mechanism.
- Gmail watch (push notifications) should be configured on the bookkeeper's inbox with a Pub/Sub topic to capture inbound replies without polling. Confirm the Google Cloud project and Pub/Sub topic name before configuring the watch subscription.
- Hubdoc API: documents are uploaded via POST /documents with multipart/form-data. The extraction result is retrieved via GET /documents/{doc_id} polling until status is 'processed'. Set a polling timeout of 120 seconds before marking as failed.
- Matching logic: match on (amount within $0.10 tolerance) AND (date within 3 calendar days) AND (vendor name fuzzy match, Levenshtein distance threshold of 2). Any match scoring below the threshold is flagged as LOW_CONFIDENCE and sent to Slack.
- Hubdoc OCR accuracy drops on low-resolution or handwritten receipts. The matching threshold must be calibrated with a real sample of the client's receipts during the sandbox test phase before going live.
- Dedupe: if the same attachment filename and amount combination has already been matched to a txn_id in the current period, skip silently and log. Do not create duplicate QuickBooks attachments.
- Follow-up emails: if no reply is received within 3 business days, send one automated follow-up. After a second 3-day window with no reply, escalate to the #tax-exceptions Slack channel and stop emailing. Confirm the follow-up cadence with the process owner before build.
- Confirm with the process owner: who sends the Gmail emails, the bookkeeper's address or a shared finance@ alias? The OAuth credentials must match the sending address.
Assembles the verified reports, matched receipts, and reconciled statements into a structured document package in Google Drive, then dispatches the DocuSign sign-off request to the appropriate approver. Posts a Slack notification to the Finance Manager confirming the package is ready for the accountant. This agent is triggered only after the bookkeeper has confirmed all manual exception items are resolved. Complexity: Moderate. Estimated build time: 10 hours.
// Input
receipt_log.matched[]: { txn_id, hubdoc_doc_id, qb_attachment_id }
quickbooks.reports[]: { report_type, file_id, period }
google_drive.checklist_sheet: { all items status = 'resolved' }
slack.trigger_event: { user_id, action: 'confirm_exceptions_resolved', period }
// Output
google_drive.package_folder: {
folder_id,
folder_path: '/Tax Packages/{YYYY-QQ}/',
contents[]: { file_name, file_id, category }
}
docusign.envelope: {
envelope_id,
signer_email,
signer_name,
status: 'sent',
sent_at: ISO8601 timestamp
}
// On approval (DocuSign webhook callback)
docusign.envelope.status = 'completed' ->
slack.post to #finance-ops: {
message: 'Tax package signed off. Ready for accountant.',
drive_folder_link, envelope_id, completed_at
}- Google Drive folder schema must be agreed and locked before build. Recommended path: /Tax Packages/{YYYY}/{QQ}/ with subfolders /Reports, /Receipts, /Statements, /Signed. Confirm the root Drive folder ID with the process owner.
- DocuSign envelope templates must be created and reviewed by the accountant before build. One template per entity or filing type is required. Confirm the templateId values for each entity. Do not generate ad-hoc envelopes without a template as this breaks audit trail consistency.
- DocuSign webhook (Connect): configure a Connect listener to receive envelope status callbacks. The automation platform endpoint URL must be registered in the DocuSign account settings. Status transitions to monitor: sent, delivered, completed, declined.
- If DocuSign returns a declined status, post an alert to #tax-exceptions and halt the workflow. Do not auto-resend. Bookkeeper must investigate.
- The Slack trigger (bookkeeper confirmation) must be implemented as a Slack interactive message button or a slash command, not a free-text keyword match, to prevent accidental triggers.
- Confirm the signer routing with the process owner: is it always a single director, or does it route conditionally by entity or filing type? Routing logic must be mapped before the DocuSign template is configured.
03End-to-end data flow
// ── TRIGGER ──────────────────────────────────────────────────────────────
// Scheduled cron OR manual trigger from Finance Manager
TRIGGER: preparation_window_open
-> payload: { period_start: DATE, period_end: DATE, entity_id: STRING }
// ── STEP 1: QuickBooks report export ─────────────────────────────────────
// Automation platform calls QuickBooks Reports API
GET /v3/company/{realmId}/reports/ProfitAndLoss
params: { start_date, end_date, accounting_method: 'Accrual' }
-> profit_loss: { rows[]: { account, amount } }
GET /v3/company/{realmId}/reports/GeneralLedger
params: { start_date, end_date }
-> general_ledger: { transactions[]: { txn_id, date, vendor, amount, category, receipt_attached: BOOL } }
// ── HANDOFF 1: To Document Gap Detector ─────────────────────────────────
AGENT_IN (Document Gap Detector):
general_ledger.transactions[]
google_drive.checklist_sheet -> required_docs[]: { doc_type, employee_or_vendor, status }
AGENT_LOGIC:
FOR EACH txn IN general_ledger.transactions:
IF txn.receipt_attached == FALSE OR txn.category == NULL:
append to gap_list.missing_receipts { txn_id, employee, amount, date, category }
FOR EACH doc IN required_docs WHERE doc.status != 'received':
append to gap_list.unmatched_statements { account_id, period }
AGENT_OUT (Document Gap Detector):
gap_list: { missing_receipts[], uncategorised_txns[], unmatched_statements[], generated_at }
-> written to: error_log_table { run_id, period, gap_list_json, created_at }
// ── HANDOFF 2: To Receipt Collection Coordinator ─────────────────────────
AGENT_IN (Receipt Collection Coordinator):
gap_list.missing_receipts[]: { txn_id, employee_email, amount, date, category, vendor }
AGENT_LOGIC (per missing_receipt):
gmail.send(
to: employee_email,
template: 'receipt_request_v1',
vars: { employee_name, txn_date, amount, vendor, upload_link }
)
-> thread_id stored against txn_id in run state
ON inbound reply (gmail.watch Pub/Sub event):
attachment -> POST /hubdoc/documents (multipart/form-data)
POLL GET /hubdoc/documents/{doc_id} until status = 'processed'
hubdoc_result: { vendor_name, date, total_amount, confidence_score }
MATCH hubdoc_result TO general_ledger.txn:
IF amount_delta <= 0.10 AND date_delta <= 3 days AND vendor_fuzzy <= 2:
-> POST /v3/company/{realmId}/attachments { txn_id, doc_id } // attach to QB
-> receipt_log.matched[]: { txn_id, hubdoc_doc_id, qb_attachment_id, confidence_score }
ELSE:
-> receipt_log.unmatched[]: { txn_id, reason_code }
-> slack.post #tax-exceptions { txn_id, employee, amount, reason_code }
IF no reply after 3 business days:
gmail.send follow_up (once)
IF no reply after further 3 days:
slack.post #tax-exceptions { action: 'ESCALATE', txn_id, employee_email }
AGENT_OUT (Receipt Collection Coordinator):
receipt_log: { matched[], unmatched[] }
-> written to: error_log_table { run_id, receipt_log_json, updated_at }
// ── MANUAL STEP: Bookkeeper resolves exceptions ───────────────────────────
// Bookkeeper reviews #tax-exceptions, resolves items manually (<30 min/cycle)
// When done, triggers Package Builder via Slack button or /resolve-exceptions slash command
SLACK_EVENT: { user_id, action: 'confirm_exceptions_resolved', period }
// ── HANDOFF 3: To Package Builder and Dispatcher ─────────────────────────
AGENT_IN (Package Builder and Dispatcher):
receipt_log.matched[]
quickbooks.reports[]: { report_type, file_id, period }
google_drive.checklist_sheet (all statuses confirmed = 'resolved')
AGENT_LOGIC:
CREATE Drive folder: /Tax Packages/{YYYY}/{QQ}/
COPY reports -> /Tax Packages/{YYYY}/{QQ}/Reports/
COPY receipts -> /Tax Packages/{YYYY}/{QQ}/Receipts/
COPY statements -> /Tax Packages/{YYYY}/{QQ}/Statements/
UPDATE checklist_sheet: all rows status = 'packaged'
POST /docusign/envelopes {
templateId: entity_template_id,
signers[]: [{ email: director_email, name: director_name, role: 'Director' }],
emailSubject: 'Tax Return Sign-Off Required - {period}'
}
-> envelope_id stored in run state
ON DocuSign Connect webhook { envelope_id, status: 'completed' }:
slack.post #finance-ops {
message: 'Package signed. Ready for accountant.',
drive_folder_link, envelope_id, completed_at
}
ON DocuSign Connect webhook { envelope_id, status: 'declined' }:
slack.post #tax-exceptions { alert: 'Sign-off declined. Investigate.' }
-> HALT workflow
// ── END ──────────────────────────────────────────────────────────────────
// Final state: signed package in Drive, Slack confirmation sent,
// run record closed in error_log_table { run_id, status: 'complete', completed_at }04Recommended build stack
More documents for this process
Every document generated for Tax Preparation Workflow.