Back to Client Deposit & Payment Tracking

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Client Deposit & Payment Tracking

[YourCompany.com] · Finance Department · Prepared by FullSpec · [Today's Date]

This document gives the FullSpec build team everything needed to implement, wire, and deploy the three-agent Client Deposit and Payment Tracking automation. It covers the current-state step map with bottleneck flags, full agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. All configuration decisions, field names, matching logic, and exception handling rules are defined here. The process owner does not need to read this document; it is written for the person doing the build.

01Process snapshot

Step
Name
Description
1
Raise Invoice and Record Deposit Terms
Bookkeeper creates the invoice in Xero and notes the deposit amount and due date, sometimes duplicated into a spreadsheet row. (10 min)
2
Send Invoice to Client
Invoice emailed to the client manually from Xero or forwarded via Gmail with a covering note about the deposit requirement. (8 min)
3 BOTTLENECK
Monitor Bank Feed for Deposit Receipt
Bookkeeper checks Xero bank feed or Stripe dashboard daily, cross-referencing client name and amount to detect whether the deposit has arrived. (15 min)
4
Update Payment Tracker Spreadsheet
Once a deposit is spotted, the bookkeeper manually updates the shared Google Sheet with receipt date, amount received, and any shortfall. (10 min)
5
Send Deposit Receipt Confirmation to Client
Confirmation email drafted and sent to the client to acknowledge the deposit, often copied from a saved template edited each time. (8 min)
6 BOTTLENECK
Chase Overdue Deposits Manually
For deposits not received by the due date, a staff member drafts and sends a chaser email, sometimes forgetting or sending it twice. (20 min)
7
Log Follow-up Activity in HubSpot
Account manager manually logs the chaser in HubSpot against the client record; this step is frequently skipped when staff are busy. (7 min)
8
Reconcile Deposit Against Invoice in Xero
Bookkeeper applies the deposit payment to the correct invoice in Xero, adjusting the outstanding balance and marking the deposit line as settled. (10 min)
9
Track Final Balance Due and Due Date
After reconciliation, the bookkeeper updates the spreadsheet again with remaining balance and the final payment due date. (8 min)
10
Send Final Payment Reminder
Before the final balance due date, a reminder is manually drafted and sent to the client repeating the amount and payment details. (10 min)
11
Confirm Full Settlement and Close Invoice
Once the final payment clears, the bookkeeper marks the invoice as fully paid in Xero and updates the spreadsheet to show the account as settled. (8 min)
12
Notify Internal Team of Payment Cleared
A manual Slack message is sent to the project team confirming the client has paid in full so work can proceed without financial hold. (5 min)
Time cost summary: Each cycle through this process costs 119 minutes of manual work. At approximately 60 invoices per month, the process runs roughly 248 times per month, producing around 6 hours of manual effort per week and an annual staff cost of $9,300 at the assumed $30/hour rate. Steps 1, 3, 4, 8, 9, and 11 are replaced by the Deposit Monitor Agent. Steps 2, 5, 6, 7, and 10 are replaced by the Client Communication Agent. Step 12 is replaced by the Internal Notification Agent. The only step retained with human involvement is the exception review for unmatched payments, which sits between the Deposit Monitor Agent output and the Xero reconciliation action.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Deposit Monitor Agent

The Deposit Monitor Agent is the entry point of the automation. It listens for new invoices created in Xero that carry deposit terms, writes a structured record to the Google Sheets payment tracker, and then polls the Stripe payment feed on a daily schedule to detect incoming payments. Matching logic compares the incoming Stripe charge amount and client reference against open deposit records in Google Sheets. Confirmed matches pass downstream for reconciliation and confirmation. Unmatched payments, including partial payments, misreferenced transfers, and split payments, are flagged for bookkeeper review before any reconciliation action is taken. This agent replaces the daily manual bank feed check (step 3), the spreadsheet update (step 4), the deposit reconciliation in Xero (step 8), the final balance spreadsheet update (step 9), and the full settlement closure in Xero (step 11), as well as the initial invoice logging in step 1.

Trigger
Xero webhook fires on invoice.created event where the invoice contains a deposit line item or a deposit due date field. Polling fallback runs every 24 hours for any invoices missed by the webhook.
Tools
Xero, Stripe, Google Sheets
Replaces steps
1, 3, 4, 8, 9, 11
Estimated build
14 hours, Moderate complexity
// Input
xero_invoice_id: string           // e.g. 'INV-0042'
xero_client_name: string          // matched against Stripe metadata.client_name
xero_client_email: string
deposit_amount_usd: float         // from Xero invoice deposit line
deposit_due_date: date            // ISO 8601
invoice_total_usd: float
invoice_currency: string          // must be 'USD'; flag others for review

// Stripe poll payload (daily)
stripe_charge_id: string
stripe_amount_cents: int          // divide by 100 to compare
stripe_metadata.invoice_ref: string
stripe_metadata.client_name: string
stripe_payment_date: date

// Output (on match)
match_status: 'confirmed' | 'unmatched' | 'partial'
matched_charge_id: string
matched_amount_usd: float
sheets_row_id: string             // row reference in payment tracker
xero_reconciliation_status: 'applied' | 'pending_review'

// Output (on no match by due date)
overdue_flag: true
days_overdue: int
sheets_row_id: string
  • Xero API: requires the xero-accounting OAuth 2.0 scope. Use the Xero webhook endpoint for invoice.created events; register the webhook URL in the Xero developer portal under the connected app. Webhook secret must be stored in the shared credential store and validated on every incoming request.
  • Stripe API: use the /v1/charges/search or /v1/payment_intents/list endpoint with a date range filter. The Stripe account must be on at least the standard plan to access metadata fields on charges. Confirm metadata key naming convention (invoice_ref, client_name) with the client before build.
  • Google Sheets: the payment tracker sheet must be created before build begins. Confirm the sheet ID and tab name. The agent writes one row per invoice on creation and updates columns for match status, matched amount, and reconciliation date. Use a header row lock to prevent accidental column shifts.
  • Matching logic: primary match on stripe_amount_cents equals deposit_amount_usd multiplied by 100, AND stripe_metadata.invoice_ref equals xero_invoice_id. Secondary fallback: fuzzy match on stripe_metadata.client_name against xero_client_name using normalised lowercase comparison. If neither condition is met, flag as unmatched.
  • Partial payment handling: if stripe_amount_cents is greater than zero but less than deposit_amount_usd multiplied by 100, set match_status to 'partial' and route to bookkeeper review queue. Do not auto-reconcile.
  • Dedupe: store processed stripe_charge_id values in a Google Sheets lookup column or a lightweight key-value store in the orchestration layer. On each poll, skip any charge_id already present. This prevents double-reconciliation.
  • Xero reconciliation: only trigger the Xero payment application call after match_status is 'confirmed'. For 'pending_review' or 'partial', write to Google Sheets only. The bookkeeper review step must complete before the reconciliation branch executes.
  • Must confirm before build: exact deposit term field name used on Xero invoices (custom field or line item label), Stripe account live/test mode, Google Sheets file ID and tab name, and whether clients are currently instructed to quote the invoice number as payment reference.
Client Communication Agent

The Client Communication Agent handles all outbound email to the client throughout the deposit and payment lifecycle. It receives triggers from the Deposit Monitor Agent, either an overdue flag or a confirmed match, and sends the appropriate email via Gmail. Overdue chasers are sent on a pre-set schedule with duplicate-send prevention. Deposit confirmations and final payment reminders are sent once per qualifying event. Every outbound email is logged automatically as a contact activity in HubSpot against the matching client record, including the date sent and the outstanding amount. This agent replaces manual steps 2, 5, 6, 7, and 10.

Trigger
Overdue flag raised by Deposit Monitor Agent (for chaser), or confirmed payment match returned by Deposit Monitor Agent (for confirmation and final reminder).
Tools
Gmail, HubSpot, Google Sheets
Replaces steps
2, 5, 6, 7, 10
Estimated build
11 hours, Moderate complexity
// Input (chaser branch)
xero_client_email: string
xero_client_name: string
xero_invoice_id: string
deposit_amount_usd: float
deposit_due_date: date
days_overdue: int
overdue_flag: true
sheets_row_id: string

// Input (confirmation branch)
xero_client_email: string
xero_client_name: string
xero_invoice_id: string
matched_amount_usd: float
invoice_total_usd: float
remaining_balance_usd: float      // invoice_total_usd minus matched_amount_usd
final_payment_due_date: date
match_status: 'confirmed'

// Output
gmail_message_id: string          // stored for dedupe reference
email_type: 'chaser' | 'confirmation' | 'final_reminder'
hubspot_activity_id: string       // created engagement record ID
hubspot_contact_id: string
send_timestamp: datetime
sheets_row_updated: true
  • Gmail API: requires the gmail.send OAuth scope. Authenticate as the sending address used by the business (e.g. accounts@[YourCompany.com]). Do not use a personal Gmail account. Confirm the sending address before build.
  • Email templates: build three templates as parameterised strings within the orchestration layer: (1) overdue chaser referencing invoice_id, deposit_amount_usd, and days_overdue; (2) deposit confirmation referencing matched_amount_usd and remaining_balance_usd; (3) final payment reminder referencing remaining_balance_usd and final_payment_due_date. Store template bodies in the workflow configuration, not hardcoded in the script.
  • Duplicate-send prevention: before sending any chaser, query the Gmail sent folder for messages with the subject line containing xero_invoice_id sent within the previous 5 days. If a match exists, skip and log to Google Sheets. Also store gmail_message_id in the payment tracker row after each send.
  • Chaser schedule: do not send on Saturday or Sunday. Add a day-of-week check before the Gmail send action. The first chaser fires on day 1 overdue; subsequent chasers fire every 3 days. Maximum 4 chasers before escalation flag is written to Google Sheets for manual review.
  • HubSpot activity logging: use the HubSpot Engagements API (POST /crm/v3/objects/emails) to create an email engagement. Match the client to a HubSpot contact using xero_client_email. If no matching contact is found, log a warning to the error table and skip the HubSpot write (do not fail the whole workflow). HubSpot tier must be at least Starter for API access.
  • HubSpot contact name alignment: client names in Xero and HubSpot must match before go-live. A one-off data cleanup aligning names across both tools is required. Flag this as a pre-build dependency.
  • Google Sheets update: after each email send, write email_type, send_timestamp, and gmail_message_id to the corresponding sheets_row_id. This maintains a full audit trail without relying solely on HubSpot.
  • Must confirm before build: sending Gmail address and OAuth consent completed, HubSpot account tier and API key, chaser timing preferences (days overdue before first chaser, interval between chasers), and whether the client team wants a CC or BCC on any outbound email.
Internal Notification Agent

The Internal Notification Agent is the lightest of the three agents. It fires a single Slack message to the designated project channel when the Deposit Monitor Agent confirms that an invoice is fully paid and closed in Xero. The message includes the client name, invoice reference, and cleared amount so the project team can proceed without logging into any finance tool. This agent replaces manual step 12.

Trigger
Deposit Monitor Agent confirms invoice fully paid and xero_reconciliation_status is 'applied' for the final balance. Fires once per invoice lifecycle.
Tools
Slack, Xero
Replaces steps
12
Estimated build
3 hours, Simple complexity
// Input
xero_invoice_id: string
xero_client_name: string
invoice_total_usd: float
cleared_date: date
slack_channel_id: string          // set in config, not dynamic

// Output
slack_message_ts: string          // Slack message timestamp, used as idempotency key
slack_channel_id: string
notification_logged: true         // written back to Google Sheets payment tracker row
  • Slack API: use the chat.postMessage method with the bot token scope chat:write. The bot must be added to the target channel before deployment. Confirm the channel ID (not the display name) before build.
  • Message format: use Slack Block Kit with a header block ('Payment Cleared'), a section block containing client name, invoice reference, and cleared amount, and a context block with the cleared date. Keep the message under 200 characters in the plain-text fallback field.
  • Idempotency: store slack_message_ts in the Google Sheets payment tracker row after posting. Before posting, check whether a non-null value already exists in that column for the same invoice. If yes, skip. This prevents duplicate Slack messages if the workflow reruns.
  • Xero read: a lightweight Xero GET /Invoices/{InvoiceID} call is used to confirm the invoice status is 'PAID' before posting. Do not rely solely on the upstream agent output as the source of truth for this check.
  • Must confirm before build: Slack channel ID for the project team, whether multiple channels need notification, and Slack bot installation and permissions completed in the workspace.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow: Client Deposit and Payment Tracking
// ────────────────────────────────────────────────────────────────��
// TRIGGER: Xero webhook fires on invoice.created
// ─────────────────────────────────────────────────────────────────
xero_webhook_payload {
  xero_invoice_id: 'INV-0042'
  xero_client_name: 'Acme Pty Ltd'
  xero_client_email: 'finance@acme.example'
  deposit_amount_usd: 1500.00
  deposit_due_date: '2025-05-10'
  invoice_total_usd: 6000.00
  invoice_currency: 'USD'
}

// ─────────────────────────────────────────────────────────────────
// AGENT HANDOFF 1: Deposit Monitor Agent receives webhook payload
// ─────────────────────────────────────────────────────────────────

// ACTION: Write initial row to Google Sheets payment tracker
sheets.appendRow('PaymentTracker', {
  invoice_id: xero_invoice_id,             // 'INV-0042'
  client_name: xero_client_name,           // 'Acme Pty Ltd'
  client_email: xero_client_email,
  deposit_due: deposit_due_date,           // '2025-05-10'
  deposit_amount: deposit_amount_usd,      // 1500.00
  invoice_total: invoice_total_usd,        // 6000.00
  match_status: 'pending',
  chaser_count: 0
})
// Returns: sheets_row_id -> 'row_47'

// ACTION: Daily Stripe poll (cron, every 24h)
stripe.charges.list({
  created_gte: deposit_due_date - 7_days,
  limit: 100
})
// Per charge, evaluate:
//   PRIMARY MATCH: stripe_amount_cents == deposit_amount_usd * 100
//                  AND stripe_metadata.invoice_ref == xero_invoice_id
//   SECONDARY MATCH: normalise(stripe_metadata.client_name)
//                     == normalise(xero_client_name)
//   PARTIAL: 0 < stripe_amount_cents < deposit_amount_usd * 100
//   UNMATCHED: no condition met

// ─────────────────────────────────────────────────────────────────
// BRANCH A: match_status == 'confirmed'
// ─────────────────────────────────────────────────────────────────
match_result {
  match_status: 'confirmed'
  matched_charge_id: 'ch_3PxT7A2eZvKYlo2C0abc1234'
  matched_amount_usd: 1500.00
  sheets_row_id: 'row_47'
  xero_reconciliation_status: 'applied'
}

// ACTION: Apply payment to Xero invoice
xero.payments.create({
  InvoiceID: xero_invoice_id,              // 'INV-0042'
  Date: stripe_payment_date,
  Amount: matched_amount_usd,              // 1500.00
  Reference: matched_charge_id
})

// ACTION: Update Google Sheets row
sheets.updateRow('row_47', {
  match_status: 'confirmed',
  matched_amount: 1500.00,
  reconciled_date: today,
  remaining_balance: 4500.00               // invoice_total - matched_amount
})

// ─────────────────────────────────────────────────────────────────
// AGENT HANDOFF 2: Deposit Monitor Agent -> Client Communication Agent
// ─────────────────────────────────────────────────────────────────
communication_trigger_payload {
  email_type: 'confirmation'
  xero_client_email: 'finance@acme.example'
  xero_client_name: 'Acme Pty Ltd'
  xero_invoice_id: 'INV-0042'
  matched_amount_usd: 1500.00
  remaining_balance_usd: 4500.00
  final_payment_due_date: '2025-06-10'
  sheets_row_id: 'row_47'
}

// ACTION: Send deposit confirmation via Gmail
gmail.send({
  to: xero_client_email,
  subject: 'Deposit Received: INV-0042',
  body: template('confirmation', {
    client_name: xero_client_name,
    matched_amount: matched_amount_usd,
    remaining_balance: remaining_balance_usd,
    due_date: final_payment_due_date
  })
})
// Returns: gmail_message_id -> 'msg_abc9876'

// ACTION: Log activity in HubSpot
hubspot.engagements.create({
  type: 'EMAIL',
  contact_id: hubspot_contact_id,          // resolved via xero_client_email
  metadata: {
    subject: 'Deposit Received: INV-0042',
    sent_at: send_timestamp,
    amount_outstanding: remaining_balance_usd
  }
})
// Returns: hubspot_activity_id -> 'eng_112233'

// ─────────────────────────────────────────────────────────────────
// BRANCH B: overdue_flag == true (no match by deposit_due_date)
// ─────────────────────────────────────────────────────────────────
overdue_payload {
  email_type: 'chaser'
  xero_client_email: 'finance@acme.example'
  xero_invoice_id: 'INV-0042'
  deposit_amount_usd: 1500.00
  days_overdue: 1
  sheets_row_id: 'row_47'
}
// Day-of-week check: skip Saturday (6) and Sunday (0)
// Dedupe check: query Gmail sent for subject containing 'INV-0042' within last 5 days
// If no duplicate found, send chaser and increment sheets[row_47].chaser_count
// Max chasers: 4. On chaser_count == 4, set sheets[row_47].escalation_flag = true

// ─────────────────────────────────────────────────────────────────
// BRANCH C: match_status == 'partial' or 'unmatched' -> manual review
// ─────────────────────────────────────────────────────────────────
// sheets.updateRow('row_47', { match_status: 'pending_review' })
// No Xero reconciliation call. No Gmail send.
// Bookkeeper reviews Google Sheets flag and confirms allocation manually.
// On bookkeeper confirmation, workflow resumes from BRANCH A.

// ─────────────────────────────────────────────────────────────────
// AGENT HANDOFF 3: Full settlement confirmed -> Internal Notification Agent
// ─────────────────────────────────────────────────────────────────
settlement_payload {
  xero_invoice_id: 'INV-0042'
  xero_client_name: 'Acme Pty Ltd'
  invoice_total_usd: 6000.00
  cleared_date: '2025-06-11'
  slack_channel_id: 'C04XXXXABCD'          // set in config
}

// Pre-post check: GET /Invoices/INV-0042 -> Status == 'PAID'
// Idempotency check: sheets[row_47].slack_message_ts is null

// ACTION: Post to Slack
slack.chat.postMessage({
  channel: slack_channel_id,
  blocks: [
    { type: 'header', text: 'Payment Cleared' },
    { type: 'section', text: 'Acme Pty Ltd | INV-0042 | $6,000.00' },
    { type: 'context', text: 'Cleared: 2025-06-11' }
  ],
  text: 'Payment cleared: Acme Pty Ltd INV-0042 $6,000.00'
})
// Returns: slack_message_ts -> '1749000000.123456'

// ACTION: Write slack_message_ts to Google Sheets
sheets.updateRow('row_47', {
  slack_notified: true,
  slack_message_ts: '1749000000.123456'
})
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool configured with one workflow per agent (three workflows total): Deposit Monitor Agent workflow, Client Communication Agent workflow, and Internal Notification Agent workflow. All three workflows share a single credential store so that Xero, Stripe, Gmail, HubSpot, Google Sheets, and Slack tokens are managed centrally and rotated in one place. Each workflow is independently triggerable and testable without affecting the others.
Webhook configuration
Xero webhook: register the invoice.created event in the Xero developer portal under the connected OAuth app. The webhook URL is generated by the orchestration layer and must be verified using Xero's intent-to-receive handshake before the workflow goes live. The HMAC-SHA256 webhook secret must be stored in the credential store and validated on every inbound request. Stripe polling does not use a webhook; a time-based cron trigger fires the Stripe charges list call every 24 hours between 06:00 and 08:00 in the client's local timezone to avoid late-night reconciliation writes.
Templating approach
All three email bodies (overdue chaser, deposit confirmation, final payment reminder) are stored as parameterised template strings in the workflow configuration layer, not hardcoded in expressions. Field placeholders: {{client_name}}, {{invoice_id}}, {{deposit_amount}}, {{days_overdue}}, {{remaining_balance}}, {{final_due_date}}. The Slack Block Kit message structure is defined as a JSON template in the Internal Notification Agent workflow config. Templates must be reviewed and approved by the process owner before go-live, as they constitute client-facing communications.
Error logging
All workflow errors (Xero API timeout, Stripe rate limit exceeded, Gmail send failure, HubSpot contact not found, Slack post failure) are written to a dedicated error log table in a Supabase project. Each error row includes: timestamp, agent_name, error_code, error_message, payload_snapshot (JSON), and retry_count. A Slack alert fires to a separate internal ops channel (distinct from the client payment cleared channel) whenever error_count for a given agent exceeds 3 within a 1-hour window. The Supabase table is also used as the idempotency store for processed stripe_charge_id values.
Testing approach
All three agent workflows are built and verified against sandbox or test-mode credentials first. Xero sandbox environment is used for invoice creation tests. Stripe test mode (with test card payments and test charge IDs) is used for matching logic validation. Gmail sends during QA use an internal FullSpec test inbox, not the live client email address. HubSpot sandbox account is used for engagement logging tests. Google Sheets testing uses a duplicate sheet tab named 'TEST_PaymentTracker'. Slack testing posts to a private test channel. Live credentials are substituted only after all test cases in the QA plan have passed.
Estimated total build time
Deposit Monitor Agent: 14 hours. Client Communication Agent: 11 hours. Internal Notification Agent: 3 hours. End-to-end integration testing and credential handover: 4 hours. Total: 32 hours, consistent with the confirmed build effort in the project snapshot.
Pre-build blockers: the following must be confirmed before any build work begins. (1) Xero OAuth app created and invoice.created webhook registered. (2) Stripe account mode confirmed (live or test) and metadata key names agreed with the client. (3) Google Sheets payment tracker file created, sheet ID confirmed, and tab named 'PaymentTracker'. (4) Gmail sending address confirmed and OAuth consent screen completed. (5) HubSpot account tier confirmed as Starter or above for API access. (6) Slack bot installed in the workspace and added to the project channel; channel ID confirmed. (7) One-off data cleanup completed to align client names across Xero and HubSpot. Contact support@gofullspec.com to confirm access checklist status before kicking off the build.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Client Deposit & Payment Tracking.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View