54 min readDeterministic automation

Implementation Guide: Auto-generate progress invoices based on project milestone completion

Step-by-step implementation guide for deploying AI to auto-generate progress invoices based on project milestone completion for Construction & Contractors clients.

Hardware Procurement

Field Tablet for Milestone Sign-Off

iPad 10th Generation (A2696) - 64GB Wi-Fi + Cellular

AppleA2696Qty: 3

$449 per unit MSP cost / $529 suggested resale (includes MDM enrollment and protective case)

Enables field superintendents and project managers to mark milestones complete on-site using the PM platform's mobile app (Procore, Buildertrend, or Knowify). Cellular connectivity ensures milestone updates push to the automation engine even at remote job sites without Wi-Fi.

Rugged Tablet Case

OtterBoxOtterBox Defender Series for iPad 10th Gen (77-89987)Qty: 3

$70 per unit MSP cost / $90 suggested resale

Protects field tablets from drops, dust, and moisture typical on construction job sites. Includes integrated stand for use during milestone review meetings.

Document Scanner for Lien Waivers

Fujitsu (Ricoh)ScanSnap iX1600 (PA03770-B615)Qty: 1

$395 MSP cost / $475 suggested resale

Digitizes signed lien waivers, change orders, and AIA pay application backup documentation. Scanned documents are auto-uploaded to the project folder in the PM platform or cloud storage, where they are attached to the corresponding invoice by the automation workflow.

Office Workstation for Invoice Review/Approval

Office Workstation for Invoice Review/Approval

DellDell OptiPlex 7020 Micro (Intel Core i5-14500T, 16GB RAM, 512GB SSD)Qty: 1

$899 MSP cost / $1,049 suggested resale

Dedicated workstation for the office manager or bookkeeper to review auto-generated draft invoices, approve them, and manage the billing queue in the accounting platform. Dual-monitor setup recommended for side-by-side SOV and invoice comparison.

Secondary Monitor

DellDell P2425H 24-inch IPS MonitorQty: 1

$199 MSP cost / $249 suggested resale

Second display for the invoice review workstation, allowing the bookkeeper to view the PM platform's milestone/SOV data alongside the accounting platform's invoice draft simultaneously.

Software Procurement

Knowify Advanced

KnowifySaaS - monthly subscription

$349/month (estimated mid-tier; confirm with Knowify sales for exact current pricing)

Primary construction project management platform for specialty and trade contractors. Provides milestone tracking, Schedule of Values management, AIA billing, change order tracking, and native QuickBooks Online bi-directional sync. The milestone completion events in Knowify serve as the trigger source for the entire automation.

QuickBooks Online Advanced

IntuitSaaS - monthly subscription (per-company)Qty: 1

$275/month (effective July 2025 pricing). MSP procures via QuickBooks Solution Provider Program or ProAdvisor discount at ~30% off ($192.50/month MSP cost).

Primary accounting and invoicing platform. Receives auto-generated draft invoices from the automation workflow. Handles accounts receivable, job costing, retainage tracking, payment processing, and financial reporting. Advanced tier required for API access, custom fields, and workflow approvals.

Make (formerly Integromat) - Pro Plan

Make (Celonis)Pro PlanQty: 10,000 operations/month

$16/month

Core automation and integration middleware. Orchestrates the milestone-to-invoice workflow: listens for milestone completion events via webhooks or polling, executes retainage calculations, maps SOV line items to invoice line items, creates draft invoices in QBO, attaches lien waiver documents, and sends notification emails. Make is preferred over Zapier for this project due to superior branching logic, data transformation capabilities, and lower per-operation cost.

License type: SaaS - monthly subscription (usage-based operations). Most mid-size contractors will use 2,000–5,000 operations/month.

PayAppPro

PayAppProSaaS / per-packageQty: per pay application package or unlimited subscription

$7.99–$19.99 per pay application package, or ~$49/month subscription for unlimited (contact vendor for current plans)

Generates AIA G702 (Application and Certificate for Payment) and G703 (Continuation Sheet) compliant PDF documents from invoice data. Required when the client's general contractor or project owner mandates AIA-format pay applications. Integrates with QuickBooks estimates and progress invoicing.

Microsoft 365 Business Standard

MicrosoftSaaS - per-user/month (procured via Microsoft CSP)Qty: 5–10 users depending on company size

$12.50/user/month (MSP cost via CSP ~$10.50/user/month)

Provides Exchange Online email for invoice delivery and notifications, SharePoint/OneDrive for document storage (lien waivers, signed pay apps, backup documentation), and Microsoft Teams for internal approval notifications. Azure AD provides SSO and MFA across all platforms.

Jamf Now (or Microsoft Intune)

Jamf / MicrosoftSaaS - per-device/month

Jamf Now: $4/device/month (Plus tier). If client is already on M365 Business Premium, Intune is included.

Mobile Device Management for the field iPads. Enforces passcode policies, remote wipe capability, app deployment (PM platform mobile app), and ensures tablets are compliant with company security policies before accessing project and financial data.

ScanSnap Home

Fujitsu (Ricoh)

$0 (bundled with scanner)

Scanner management software that enables one-touch scanning profiles. Configure profiles for 'Lien Waiver', 'Change Order', and 'Pay Application Backup' that automatically OCR, name, and route scanned documents to the correct project folder in SharePoint/OneDrive.

Prerequisites

  • Active QuickBooks Online Advanced subscription with API access enabled (Settings > Account and Settings > Advanced > verify API/OAuth enabled). The client must have a QBO company file with their Chart of Accounts, Items/Services list, and Customer list already configured.
  • Active construction project management platform subscription (Knowify, Buildertrend, or Procore) with at least 2–3 active projects that have a defined Schedule of Values (SOV) with milestones or phases.
  • Client must have a documented billing process: What triggers an invoice today? Who approves it? What format (standard invoice vs. AIA G702/G703)? What retainage percentage is used per contract? Gather this in the discovery phase.
  • Microsoft 365 Business Standard (or equivalent email/identity platform) deployed with MFA enabled for all users who will access the PM platform, accounting platform, or automation dashboards.
  • Stable internet connectivity at the main office (minimum 25 Mbps down / 10 Mbps up). Cellular data plans on field tablets (minimum 5GB/month per device).
  • Admin credentials for both the PM platform and QBO (or delegated API access via OAuth). The MSP will need these during automation configuration and must document them in the client's password manager.
  • At least one completed or near-completed project with known milestone values that can serve as the test case during implementation. The bookkeeper should have the corresponding manual invoices for validation comparison.
  • List of all active contracts with retainage terms (percentage, cap, release conditions) and any state-specific retainage laws that apply (e.g., 5% cap in Nevada, 10% until 50% completion in North Dakota).
  • If AIA billing is required: Copies of recent G702/G703 forms the client has submitted, so the automation output can be matched to the expected format.
  • Designated client-side project champion — typically the office manager or head bookkeeper — who will be available for 2–4 hours per week during the implementation for requirements validation, testing, and training.

Installation Steps

Step 1: Discovery & Current-State Workflow Mapping

Conduct a 2–3 hour on-site or video discovery session with the client's bookkeeper, project manager, and owner. Map the current invoicing workflow end-to-end: How are milestones currently tracked? Who determines when a milestone is complete? How is the invoice amount calculated (fixed milestone amount vs. percentage of completion vs. cost-to-cost)? What is the approval chain? How are invoices delivered (email, mail, portal)? What format (standard invoice vs. AIA pay app)? What retainage terms apply per contract? Document everything in a workflow diagram.

Note

Use a structured discovery questionnaire. Key questions: (1) How many active projects at any given time? (2) Average number of milestones per project? (3) How many invoices per month? (4) Do different project owners require different invoice formats? (5) What is the current average time from milestone completion to invoice sent? (6) What are the biggest pain points? This discovery output becomes the automation requirements document.

Step 2: Configure Construction PM Platform - Knowify Setup

If the client is not yet on a PM platform, provision Knowify Advanced. If already on Buildertrend or Procore, skip to the relevant variant of this step.

1
Create the company account
2
Import or create all active projects
3
For each project, define the Schedule of Values (SOV) with milestone line items matching the contract
4
Set up user accounts for field PMs and office staff
5
Configure project phases/milestones with completion percentage tracking
6
Enable the QuickBooks Online integration under Settings > Integrations > QuickBooks Online and complete the OAuth connection
1
Navigate to: Knowify > Settings > Integrations > QuickBooks Online
2
Click 'Connect to QuickBooks' and authorize via OAuth
3
Map Knowify customers to QBO customers
4
Map Knowify cost codes to QBO items/services
5
Enable automatic sync (bi-directional recommended)
Note

If the client already uses Knowify, audit the existing SOV structure to ensure milestones are granular enough for billing. Each billable milestone must be a discrete line item in the SOV. If the SOV is too coarse (e.g., 'Framing - $150,000' with no sub-milestones), work with the PM to break it down into billable phases (e.g., 'First Floor Framing - $60,000', 'Second Floor Framing - $55,000', 'Roof Framing - $35,000').

Step 3: Configure QuickBooks Online Advanced for Construction Billing

Ensure QBO Advanced is properly configured for construction progress invoicing:

1
Enable progress invoicing: Settings > Account and Settings > Sales > Progress Invoicing > toggle ON
2
Create custom fields for 'Project Name', 'Milestone ID', 'Retainage %', and 'Contract Number' under Settings > Custom Fields
3
Create a 'Retainage Payable' liability account in the Chart of Accounts (Account Type: Other Current Liability)
4
Create invoice templates: one standard template and one for AIA-style billing if needed
5
Set up Sales Tax settings per applicable state jurisdiction
6
Create or verify Customer records match the PM platform's project/client records
1
Navigate to: Settings (gear icon) > Account and Settings > Sales
2
Scroll to 'Progress Invoicing' section
3
Toggle to 'On'
4
Save
5
Navigate to: Settings > Custom Fields > Add Field
6
Add Field 1: Name='Project Name', Type='Text', Apply to='Invoice'
7
Add Field 2: Name='Milestone ID', Type='Text', Apply to='Invoice'
8
Add Field 3: Name='Retainage Pct', Type='Number', Apply to='Invoice'
9
Add Field 4: Name='Contract Number', Type='Text', Apply to='Invoice'
Note

Progress Invoicing in QBO works by creating an Estimate first, then invoicing against it in increments. The automation will create the Estimate (representing the full contract SOV) and then create Invoice records against it as milestones complete. Verify that the client's QBO subscription is Advanced ($275/mo as of July 2025) — the Plus plan supports progress invoicing but lacks custom fields and advanced API features we need.

Step 4: Provision and Configure Make.com Automation Account

Create a Make.com account for the client (or under the MSP's team account if managing centrally). Set up the organization, team, and initial scenario structure: (1) Create organization named '[ClientName]-Automation', (2) Create a team for the client, (3) Create a folder structure: 'Invoice Automation', 'Notifications', 'Document Management', (4) Install required app connections: QuickBooks Online (via OAuth 2.0), Knowify (via API key or webhook), Email (SMTP or Microsoft 365), SharePoint/OneDrive (via OAuth).

1
Navigate to https://www.make.com/en/register
2
Create account with MSP admin email
3
Upgrade to Pro Plan ($16/month)
4
Create Organization: Settings > Organizations > Create
5
Add client team: Organizations > Teams > Create Team
1
Connect QuickBooks Online: Scenarios > Create Connection > QuickBooks Online — OAuth 2.0 flow, authorize with client QBO admin credentials. Scopes needed: com.intuit.quickbooks.accounting (read/write)
2
Connect Microsoft 365 (for email/SharePoint): Scenarios > Create Connection > Microsoft 365 — OAuth 2.0 flow, authorize with client M365 admin. Scopes: Mail.Send, Files.ReadWrite, Sites.ReadWrite.All
Note

Make.com is preferred over Zapier for this implementation because: (1) Make supports complex branching and conditional logic needed for retainage calculations that vary by contract, (2) Make's router module allows a single trigger to produce multiple outputs (invoice + lien waiver + notification), (3) Make's pricing is per-operation rather than per-task, making it 3–5x more cost-effective for multi-step workflows. If the client has strong preference for Microsoft ecosystem, Power Automate is a viable alternative (see Alternatives section).

Step 5: Build Core Automation Scenario - Milestone Trigger Configuration

Create the primary Make.com scenario that watches for milestone completion events. The trigger mechanism depends on the PM platform: For Knowify, use a polling trigger via Knowify API or a custom webhook. For Buildertrend or Procore, use their native webhook capabilities. Configure the trigger to fire when a milestone/phase status changes to 'Complete' or when percentage-of-completion reaches a billing threshold.

1
Create a custom webhook in Make: Webhooks > Custom Webhook
2
Name the webhook: 'knowify-milestone-complete'
3
Copy the webhook URL
4
In Knowify (or via Knowify API): Configure a notification or API polling to POST to the webhook URL when a milestone status changes

Make.com Scenario: 'Milestone-to-Invoice Generator' — Module 1: Webhook Trigger (Custom Webhook). Webhook Payload Expected Structure:

Expected webhook payload structure for milestone completion events
json
{
  "event": "milestone.completed",
  "project_id": "PRJ-2025-042",
  "project_name": "Riverside Office Renovation",
  "milestone_id": "MS-007",
  "milestone_name": "Electrical Rough-In Complete",
  "milestone_amount": 45000.00,
  "contract_total": 380000.00,
  "completed_to_date": 185000.00,
  "retainage_pct": 10,
  "customer_name": "Riverside Development LLC",
  "customer_email": "ap@riversidedev.com",
  "completed_by": "John Smith",
  "completed_date": "2025-07-15T14:30:00Z",
  "sov_line_items": [
    {
      "line_id": "SOV-007",
      "description": "Division 26 - Electrical Rough-In",
      "scheduled_value": 45000.00,
      "previous_billed": 0,
      "this_period": 45000.00,
      "retainage": 4500.00
    }
  ]
}
Note

If Knowify does not support native webhooks (check current API docs), implement a polling approach: use Make's Schedule module to check the Knowify API every 15 minutes for milestones whose status changed to 'Complete' since the last poll. Store the last-checked timestamp in a Make data store. For Procore, use the Webhooks API (POST /rest/v1.0/webhooks) to register the Make webhook URL with trigger event 'budget_line_items.update' or 'schedule.tasks.update'. For Buildertrend, check for API/webhook availability or use Zapier as an intermediary trigger that calls the Make webhook.

Procore: Register Make webhook URL via the Webhooks API
http
POST /rest/v1.0/webhooks

Step 6: Build Invoice Calculation & Retainage Logic Module

Add data transformation modules to the Make scenario that calculate the invoice amounts including retainage deductions. This is the core business logic: (1) For each SOV line item in the milestone, calculate: This Period Amount = milestone_amount, Retainage This Period = milestone_amount × retainage_pct, Net Billable This Period = milestone_amount - retainage, (2) Calculate running totals: Total Completed to Date, Total Retainage to Date, Total Previously Billed, (3) Handle edge cases: milestone amounts that differ from SOV scheduled values (change orders), final milestone where retainage is released, stored materials billing.

1
Module 2: Iterator — Source: {{webhook.sov_line_items}} — Iterates over each SOV line item in the milestone
2
Module 3: Set Variable — Calculate Line Item Values
3
Module 4: Router (Conditional Paths) — Path A: Standard milestone (retainage_pct > 0 AND milestone is not final); Path B: Final milestone (milestone triggers retainage release); Path C: Stored materials only (no milestone completion, just material delivery)
4
Module 5: Aggregator — Aggregates all line items back into a single invoice payload
Make.com Scenario Modules 3–5: Retainage calculation variables, router paths, and aggregator configuration
text
# Module 3: Set Variable - Calculate Line Item Values
#   - scheduled_value: {{iterator.scheduled_value}}
#   - previous_billed: {{iterator.previous_billed}}
#   - this_period: {{iterator.this_period}}
#   - retainage_pct: {{webhook.retainage_pct}} / 100
#   - retainage_amount: {{iterator.this_period}} * ({{webhook.retainage_pct}} / 100)
#   - net_billable: {{iterator.this_period}} - ({{iterator.this_period}} * ({{webhook.retainage_pct}} / 100))
#   - completed_to_date: {{iterator.previous_billed}} + {{iterator.this_period}}
#   - pct_complete: ({{iterator.previous_billed}} + {{iterator.this_period}}) / {{iterator.scheduled_value}} * 100

# Module 4: Router (Conditional Paths)
#   Path A: Standard milestone (retainage_pct > 0 AND milestone is not final)
#     -> Continue to invoice creation with retainage deduction
#   Path B: Final milestone (milestone triggers retainage release)
#     -> Create invoice for milestone amount PLUS accumulated retainage
#   Path C: Stored materials only (no milestone completion, just material delivery)
#     -> Create invoice for stored materials value only

# Module 5: Aggregator
#   - Aggregates all line items back into a single invoice payload
#   - Sums: total_this_period, total_retainage, total_net_billable
Note

Retainage logic is critical and must be configurable per contract. Some contracts use 10% retainage until 50% complete, then 5% thereafter (common in North Dakota and other states). Store retainage rules in a Make data store keyed by project_id so each project can have its own retainage schedule. The final milestone path must include logic to release all accumulated retainage — verify with the client whether retainage release requires a separate invoice or is included in the final progress invoice.

Step 7: Build QuickBooks Online Invoice Creation Module

Configure the Make scenario to create a draft invoice in QuickBooks Online using the calculated values from the previous module. The invoice is created as a Draft (not sent) so the bookkeeper can review before sending. Map the aggregated line items to QBO invoice line items, populate custom fields, and set the correct customer, terms, and due date.

1
Make.com Module 6: QuickBooks Online - Create Invoice
2
Connection: [Client QBO OAuth Connection]
3
Customer: Map from {{webhook.customer_name}} to QBO Customer ID (Use a Make data store or QBO API lookup to resolve name -> ID)
4
Invoice Configuration: TxnDate: {{webhook.completed_date}}, DueDate: {{addDays(webhook.completed_date, 30)}} (Net 30 default), DocNumber: Auto-generate or use pattern: 'PI-{{webhook.project_id}}-{{sequenceNumber}}', PrivateNote: 'Auto-generated from milestone: {{webhook.milestone_name}}'
5
Line Items (from Aggregator output) — For each SOV line item: DetailType: 'SalesItemLineDetail', Description: '{{sov_line.description}} - Progress Billing Period {{currentMonth}}', Amount: {{sov_line.net_billable}}, SalesItemLineDetail.ItemRef: Map to QBO Service item, SalesItemLineDetail.Qty: 1, SalesItemLineDetail.UnitPrice: {{sov_line.net_billable}}
6
Retainage Line Item (negative line or separate tracking): Description: 'Retainage Withheld ({{webhook.retainage_pct}}%)', Amount: -{{total_retainage}} (Negative amount), SalesItemLineDetail.ItemRef: 'Retainage Withheld' service item
7
Custom Fields: Project Name: {{webhook.project_name}}, Milestone ID: {{webhook.milestone_id}}, Retainage Pct: {{webhook.retainage_pct}}, Contract Number: {{webhook.contract_number}}
QBO API call for custom HTTP module
http
# creates invoice with mapped line items, customer reference, and custom
# fields

POST https://quickbooks.api.intuit.com/v3/company/{companyId}/invoice
Content-Type: application/json
Authorization: Bearer {access_token}
Body: {
  "Line": [...mapped line items...],
  "CustomerRef": {"value": "{qbo_customer_id}"},
  "TxnDate": "2025-07-15",
  "DueDate": "2025-08-14",
  "DocNumber": "PI-PRJ2025042-007",
  "CustomField": [...],
  "PrivateNote": "Auto-generated..."
}
Note

QBO creates invoices in 'Unsent' status by default, which acts as our 'Draft' state. The bookkeeper will see these in the Invoice list with status 'Unsent' and can review, edit if needed, then send. Important: QBO's progress invoicing feature works against Estimates. If the client wants to use QBO's native progress invoicing (billing against an Estimate), the automation must first create or reference the Estimate for the full contract, then create partial invoices against it. This is more complex but provides better tracking in QBO. Alternatively, create standalone invoices with project tracking via custom fields — simpler to automate but less integrated with QBO's built-in progress invoicing reports.

Step 8: Build Notification & Approval Workflow Module

Add modules to the Make scenario that notify the appropriate people when a draft invoice is generated and route it for approval: (1) Send email to the bookkeeper/office manager with invoice summary and a direct link to the draft invoice in QBO, (2) Send a Microsoft Teams message to the #billing channel with invoice details, (3) If the invoice amount exceeds a configurable threshold (e.g., $25,000), send an additional approval request to the company owner/CFO via email with approve/reject links, (4) Log the invoice generation event to a tracking spreadsheet or data store for audit purposes.

1
Configure Make.com Module 7: Email Notification (Microsoft 365 - Send Email)
2
Configure Module 8: Microsoft Teams - Post Message (optional)
3
Configure Module 9: Google Sheets / Data Store - Log Entry
Module 7: Email Notification
plaintext
# To, CC, Subject, and HTML Body fields

To: {{bookkeeper_email}}
CC: {{pm_email}}
Subject: '[Action Required] Draft Invoice Generated - {{webhook.project_name}} - {{webhook.milestone_name}}'
Body (HTML):
<h2>Progress Invoice Ready for Review</h2>
<table>
  <tr><td><b>Project:</b></td><td>{{webhook.project_name}}</td></tr>
  <tr><td><b>Milestone:</b></td><td>{{webhook.milestone_name}}</td></tr>
  <tr><td><b>Gross Amount:</b></td><td>${{total_this_period}}</td></tr>
  <tr><td><b>Retainage ({{webhook.retainage_pct}}%):</b></td><td>${{total_retainage}}</td></tr>
  <tr><td><b>Net Invoice Amount:</b></td><td>${{total_net_billable}}</td></tr>
  <tr><td><b>Completed By:</b></td><td>{{webhook.completed_by}}</td></tr>
  <tr><td><b>Completed Date:</b></td><td>{{webhook.completed_date}}</td></tr>
</table>
<p><a href='https://app.qbo.intuit.com/app/invoice/{{qbo_invoice_id}}'>Review Invoice in QuickBooks</a></p>
<p><i>This invoice was auto-generated. Please review all line items and amounts before sending to the customer.</i></p>
Module 8: Microsoft Teams
plaintext
# Post message to #billing-notifications channel

Channel: #billing-notifications
Message: 'New draft invoice: {{webhook.project_name}} - {{webhook.milestone_name}} - ${{total_net_billable}}'
Module 9: Google Sheets / Data Store
plaintext
# Audit log row structure

Row: [timestamp, project_id, milestone_id, gross_amount, retainage, net_amount, qbo_invoice_id, status='draft']
Note

The approval threshold should be configurable — store it in a Make data store variable. Some clients may want all invoices auto-sent without approval (rare in construction but possible for small recurring service work). Others may want two-tier approval (PM approves scope accuracy, then bookkeeper approves financial accuracy). Design the notification flow to be adjustable without rebuilding the scenario. Consider adding a 48-hour reminder: if the draft invoice hasn't been sent within 48 hours, send a follow-up reminder email to the bookkeeper.

Step 9: Build Lien Waiver Document Generation Module

Add a module that generates a conditional lien waiver document to accompany each progress invoice. In many states, general contractors require subcontractors to submit a conditional lien waiver with every pay application. The automation generates a pre-filled waiver from a template and attaches it to the invoice notification email and/or uploads it to the project document folder.

Option A: Use Make's Document Generator (Google Docs Template)

1
Create a Google Docs template for Conditional Lien Waiver with template variables: {{project_name}}, {{owner_name}}, {{contractor_name}}, {{through_date}}, {{invoice_amount}}, {{job_address}}
2
Add Make Module: Google Docs > Fill a Template — set Template ID to your Google Doc template ID and fill variables from webhook + calculated data
3
Add Make Module: Google Drive > Export as PDF
4
Add Make Module: Microsoft 365 > Upload to SharePoint — Path: /Projects/{{webhook.project_id}}/Lien Waivers/, Filename: Conditional_Lien_Waiver_{{webhook.milestone_id}}_{{date}}.pdf

Option B: Use WebMerge/Formstack Documents for PDF Generation

Formstack Documents API call for PDF generation
http
POST to Formstack Documents API with merge fields
Note

Twelve states require specific statutory lien waiver forms — Arizona, California, Georgia, Florida, Michigan, Mississippi, Missouri, Nevada, Texas, Utah, Wisconsin, and Wyoming. Mississippi and Wyoming require notarization. During discovery, identify which states the client operates in and obtain the correct statutory forms. Create separate templates for each required state form. Store the state-to-template mapping in a Make data store. If the client operates in a state without statutory forms, a standard conditional lien waiver template is sufficient.

Step 10: Configure AIA G702/G703 Pay Application Generation (If Required)

If the client's contracts require AIA-format pay applications (common for commercial GC/owner billing), integrate PayAppPro or configure the PM platform's native AIA billing feature. This step maps the automation's invoice data to the G702 (Application and Certificate for Payment) and G703 (Continuation Sheet) format.

Option A: PayAppPro Integration

1
Purchase PayAppPro subscription at https://www.payapppro.com
2
Set up company profile, project data, and SOV in PayAppPro
3
Export SOV template from PayAppPro
4
Map Make scenario output to PayAppPro import format
5
Generate G702/G703 PDFs

Option B: Knowify Native AIA Billing

1
Navigate to: Project > Billing > AIA Billing
2
Import or create the Schedule of Values
3
Knowify generates G702/G703 forms natively
4
The Make scenario triggers Knowify to create the pay app rather than creating a QBO invoice directly

Option C: Custom PDF Generation via Make + Google Docs

1
Create G702 and G703 templates in Google Docs matching the official AIA form layout
2
Use Make's Google Docs module to fill the template

Key G702 Fields to Map

  • Application No., Application Date, Period To
  • Contract For, Project Name, Owner, Architect
  • Original Contract Sum, Net Change by Change Orders
  • Contract Sum to Date, Total Completed & Stored to Date
  • Retainage (a% of Completed Work, b% of Stored Material)
  • Total Earned Less Retainage
  • Less Previous Certificates for Payment
  • Current Payment Due

G703 Continuation Sheet: One Row Per SOV Line Item

  • Item No.
  • Description of Work
  • Scheduled Value
  • Work Completed (Previous, This Period)
  • Materials Stored
  • Total Completed & Stored
  • % Complete
  • Balance to Finish
  • Retainage
Note

AIA G702/G703 forms are copyrighted by the American Institute of Architects. If using custom PDF generation (Option C), be aware that reproducing the exact AIA form layout may require an AIA license. PayAppPro and Knowify have licensed the forms. For clients who only occasionally need AIA format, PayAppPro's per-package pricing ($7.99) is most cost-effective. For clients who bill every project in AIA format, Knowify's built-in AIA billing is the better path. Confirm with the client which projects require AIA format vs. standard invoicing.

Step 11: Deploy Field Tablets and Configure Mobile Access

Provision the iPads for field staff, enroll them in MDM, install the PM platform's mobile app, and configure the milestone sign-off workflow:

1
Unbox and perform initial iPad setup
2
Enroll in Jamf Now MDM
3
Push PM platform mobile app (Knowify, Buildertrend, or Procore)
4
Configure Wi-Fi profiles for office and common job sites
5
Set up user accounts on each device
6
Test milestone completion workflow from field to office
1
Log into Jamf Now at https://manage.jamfnow.com
2
Create enrollment blueprint: 'Construction Field Tablet' — Passcode policy: 6-digit minimum, auto-lock 5 minutes; Restrictions: No app installation without MDM approval; Wi-Fi profiles: Office SSID, common job site SSIDs; Required apps: Knowify (or Buildertrend/Procore), Microsoft Outlook, OneDrive
3
Generate enrollment link or QR code
4
On each iPad: Settings > General > VPN & Device Management > Enroll
5
Verify enrollment and app deployment
1
On iPad, open Knowify app > Navigate to active project
2
Select a test milestone > Mark as 'Complete'
3
Add completion photo (optional)
4
Submit
5
Verify: Make.com scenario receives the webhook/trigger within 5 minutes
Note

iPads are recommended over Android tablets for this deployment due to better enterprise MDM support, longer OS update lifecycle, and superior construction app availability. If the client already has Android devices, Samsung Galaxy Tab A9+ with Samsung Knox MDM is an acceptable alternative. Configure the tablets with a shared Apple ID managed by the MSP for app purchases, or use Apple Business Manager for volume app licensing. Each user should have their own PM platform login.

Step 12: Configure Document Scanner and Scan Profiles

Set up the Fujitsu ScanSnap iX1600 at the client's office with pre-configured scan profiles for construction document types. Each profile automatically names, OCRs, and routes the scanned document to the correct SharePoint folder.

1
Install ScanSnap Home on the office workstation
2
Connect scanner via USB or Wi-Fi
3
Create Scan Profiles:
4
Test each profile with sample documents
5
Label scanner buttons or create a quick-reference card
  • Profile 1: 'Lien Waiver' — Scan settings: Color, 300dpi, Auto-detect size | File format: Searchable PDF | Naming: 'LienWaiver_[DateStamp]_[Counter]' | Save to: OneDrive/SharePoint > Projects > _Lien Waivers (Inbox)
  • Profile 2: 'Signed Pay Application' — Scan settings: Color, 300dpi, Letter size | File format: Searchable PDF | Naming: 'PayApp_[DateStamp]_[Counter]' | Save to: OneDrive/SharePoint > Projects > _Pay Applications (Inbox)
  • Profile 3: 'Change Order' — Scan settings: Color, 300dpi, Auto-detect size | File format: Searchable PDF | Naming: 'ChangeOrder_[DateStamp]_[Counter]' | Save to: OneDrive/SharePoint > Projects > _Change Orders (Inbox)
Note

The ScanSnap iX1600 supports up to 30 custom profiles selectable via its touchscreen. Create a laminated quick-reference card showing which profile to select for each document type and place it next to the scanner. The scanned documents in the SharePoint inbox folders can optionally trigger a secondary Make scenario that files them into the correct project subfolder based on OCR content or manual tagging.

Step 13: End-to-End Integration Testing

Conduct comprehensive testing of the full automation pipeline using a real project's data. This is the most critical step — run the automation against known historical invoices and compare output to the manually-created originals. Test at least 3 scenarios: (1) standard milestone completion with retainage, (2) final milestone with retainage release, (3) milestone with a change order affecting the SOV.

Test Scenario 1: Standard Milestone with 10% Retainage

  • Input: Milestone 'Foundation Complete' on Project 'Riverside Renovation'
  • SOV Line: 'Division 3 - Concrete', Scheduled Value: $75,000
  • Previous Billed: $0
  • This Period: $75,000
  • Retainage: 10%

Expected Output — Scenario 1

  • QBO Draft Invoice: $67,500 ($75,000 - $7,500 retainage)
  • Retainage line item: -$7,500
  • Email notification sent to bookkeeper
  • Conditional lien waiver PDF generated

Test Scenario 2: Final Milestone with Retainage Release

  • Input: Milestone 'Final Punch List Complete' on Project 'Oak Street Build'
  • Final milestone flag: true
  • This Period: $15,000
  • Accumulated Retainage to Release: $38,000

Expected Output — Scenario 2

  • QBO Draft Invoice: $53,000 ($15,000 + $38,000 retainage release)
  • Retainage release line item: +$38,000

Test Scenario 3: Milestone with Change Order

  • Input: Milestone 'Electrical Rough-In' with approved CO #3
  • Original SOV: $45,000
  • CO #3 adds: $8,500
  • Revised SOV: $53,500

Expected Output — Scenario 3

  • Invoice reflects revised amount: $53,500 less retainage
  • CO reference noted in invoice description

Validation Checklist

Note

Run these tests on a QBO Sandbox environment first if possible (available via QBO Developer Portal). If not, create the test invoices in QBO Production and immediately void/delete them after validation. Have the client's bookkeeper sit alongside during testing to validate that the output matches their expectations and manual process. Document any discrepancies and adjust the automation logic before go-live. Keep a test log spreadsheet with expected vs. actual results for each test case.

Step 14: Go-Live Cutover and Parallel Run

Deploy the automation in production with a 2-week parallel run period where both the manual process and automated process operate simultaneously. During this period, the bookkeeper creates invoices manually as usual AND the automation generates draft invoices. Compare outputs daily to catch any discrepancies before fully transitioning to automated invoicing.

Go-Live Checklist

Parallel Run Procedure (2 weeks)

1
Bookkeeper continues manual invoicing as normal
2
Automation also generates draft invoices
3
Daily: Compare automated draft to manual invoice
4
Log any discrepancies in shared spreadsheet
5
MSP adjusts automation logic as needed
6
End of Week 1: Review accuracy metrics
7
End of Week 2: If >98% accuracy, transition to automated-only
8
Bookkeeper shifts to review-and-approve role
Note

The parallel run is essential for building client confidence and catching edge cases. Common issues that surface during parallel run: (1) milestone names don't exactly match SOV descriptions (fuzzy matching needed), (2) change orders were approved but not yet reflected in the PM platform's SOV, (3) tax calculations differ between manual and automated methods, (4) retainage terms changed mid-project and weren't updated in the data store. Budget 4–8 hours of MSP time during the parallel run for adjustments.

Custom AI Components

Milestone-to-Invoice Automation Workflow

Type: workflow

The primary Make.com scenario that orchestrates the entire milestone completion to draft invoice generation pipeline. This is a multi-module, branching workflow that handles webhook reception, data transformation, retainage calculation, QBO invoice creation, lien waiver generation, and notification distribution. It is the core deliverable of this project.

Implementation:

SCENARIO ARCHITECTURE

Module 1: Custom Webhook (Trigger)

  • Type: webhooks/CustomWebHook
  • Name: 'knowify-milestone-complete'
  • Webhook URL: [auto-generated by Make]

Data Structure Definition:

Module 1 Webhook Data Structure
json
{
  "event": "string",
  "project_id": "string",
  "project_name": "string",
  "milestone_id": "string",
  "milestone_name": "string",
  "milestone_amount": "number",
  "contract_total": "number",
  "completed_to_date": "number",
  "retainage_pct": "number",
  "customer_name": "string",
  "customer_email": "string",
  "customer_qbo_id": "string",
  "completed_by": "string",
  "completed_date": "string",
  "is_final_milestone": "boolean",
  "accumulated_retainage": "number",
  "state_code": "string",
  "contract_number": "string",
  "sov_line_items": [
    {
      "line_id": "string",
      "description": "string",
      "scheduled_value": "number",
      "previous_billed": "number",
      "this_period": "number",
      "stored_materials": "number"
    }
  ]
}

Module 2: Data Store Lookup - Retainage Rules

  • Type: datastore/GetRecord
  • Data Store: 'project-retainage-rules'
  • Key: {{1.project_id}}
  • Returns: retainage_pct, retainage_cap_pct_complete, retainage_after_cap, retainage_release_trigger
  • Fallback: Use {{1.retainage_pct}} from webhook if no override exists

Module 3: Set Variable - Calculate Retainage

  • Type: tools/SetVariable

Variables:

Module 3 Variable Definitions
text
effective_retainage_pct:
  IF({{1.completed_to_date}} / {{1.contract_total}} * 100 >= {{2.retainage_cap_pct_complete}},
     {{2.retainage_after_cap}},
     {{2.retainage_pct}})

is_final: {{1.is_final_milestone}}

Module 4: Iterator - SOV Line Items

  • Type: flow/Iterator
  • Source Array: {{1.sov_line_items}}

Module 5: Set Variable - Per Line Calculations

  • Type: tools/SetVariable

For each SOV line item:

Module 5 Per-Line Calculation Expressions
text
gross_this_period:    {{4.this_period}} + {{4.stored_materials}}
retainage_this_period: ({{4.this_period}} * {{3.effective_retainage_pct}} / 100)
net_this_period:      {{4.this_period}} + {{4.stored_materials}} - ({{4.this_period}} * {{3.effective_retainage_pct}} / 100)
total_completed:      {{4.previous_billed}} + {{4.this_period}} + {{4.stored_materials}}
pct_complete:         ROUND(({{4.previous_billed}} + {{4.this_period}}) / {{4.scheduled_value}} * 100, 1)
balance_to_finish:    {{4.scheduled_value}} - {{4.previous_billed}} - {{4.this_period}}

Module 6: Array Aggregator

  • Type: flow/ArrayAggregator
  • Source Module: Module 5
  • Aggregated Fields: All calculated line item values

Also calculate totals:

Module 6 Aggregated Totals
text
total_gross:     SUM(gross_this_period)
total_retainage: SUM(retainage_this_period)
total_net:       SUM(net_this_period)

Module 7: Router (3 paths)

  • Type: flow/Router

Path A: Standard Progress Invoice (is_final = false)

Module 8A: QBO - Create Invoice

  • Type: quickbooks/CreateInvoice
  • Connection: [QBO OAuth Connection]
  • CustomerRef: {{1.customer_qbo_id}}
  • TxnDate: {{formatDate(1.completed_date, 'YYYY-MM-DD')}}
  • DueDate: {{addDays(formatDate(1.completed_date, 'YYYY-MM-DD'), 30)}}
  • DocNumber: CONCAT('PI-', {{1.project_id}}, '-', {{formatDate(now, 'YYYYMM')}})

Line Items — for each aggregated line item:

Module 8A — SOV Line Item Payload
json
{
  "DetailType": "SalesItemLineDetail",
  "Amount": {{line.net_this_period}},
  "Description": CONCAT({{line.description}}, ' | Period: ', {{formatDate(1.completed_date, 'MMM YYYY')}}, ' | ', {{line.pct_complete}}, '% Complete'),
  "SalesItemLineDetail": {
    "ItemRef": {"value": "[mapped_qbo_item_id]"},
    "Qty": 1,
    "UnitPrice": {{line.net_this_period}}
  }
}

Plus retainage tracking line:

Module 8A — Retainage Withholding Line Item Payload
json
{
  "DetailType": "SalesItemLineDetail",
  "Amount": -{{6.total_retainage}},
  "Description": CONCAT('Retainage Withheld (', {{3.effective_retainage_pct}}, '%)'),
  "SalesItemLineDetail": {
    "ItemRef": {"value": "[retainage_item_id]"},
    "Qty": 1,
    "UnitPrice": -{{6.total_retainage}}
  }
}

Custom fields and private note:

Module 8A — Custom Fields and Private Note
json
"CustomField": [
  {"Name": "Project Name",    "StringValue": {{1.project_name}}},
  {"Name": "Milestone ID",    "StringValue": {{1.milestone_id}}},
  {"Name": "Contract Number", "StringValue": {{1.contract_number}}}
]

"PrivateNote": CONCAT('Auto-generated from milestone: ', {{1.milestone_name}}, '. Gross: $', {{6.total_gross}}, ', Retainage: $', {{6.total_retainage}}, ', Net: $', {{6.total_net}})

Path B: Final Invoice with Retainage Release (is_final = true)

Module 8B: QBO - Create Invoice (Final + Retainage) — Same as 8A except add the following additional line item for retainage release, and update PrivateNote accordingly:

Module 8B — Retainage Release Line Item and Private Note
json
{
  "DetailType": "SalesItemLineDetail",
  "Amount": {{1.accumulated_retainage}},
  "Description": "Retainage Release - All Prior Periods",
  "SalesItemLineDetail": {
    "ItemRef": {"value": "[retainage_release_item_id]"},
    "Qty": 1,
    "UnitPrice": {{1.accumulated_retainage}}
  }
}

"PrivateNote": CONCAT('FINAL INVOICE with retainage release. ', ...)

Path C: Continue to shared post-invoice modules

Module 9: Data Store - Log Invoice

  • Type: datastore/AddRecord
  • Data Store: 'invoice-audit-log'
Module 9 — Audit Log Record Payload
json
{
  "timestamp":          {{now}},
  "project_id":         {{1.project_id}},
  "milestone_id":       {{1.milestone_id}},
  "milestone_name":     {{1.milestone_name}},
  "gross_amount":       {{6.total_gross}},
  "retainage_amount":   {{6.total_retainage}},
  "net_invoice_amount": {{6.total_net}},
  "qbo_invoice_id":     {{8.id}},
  "qbo_doc_number":     {{8.DocNumber}},
  "status":             "draft",
  "is_final":           {{3.is_final}}
}

Module 10: Email Notification

  • Type: microsoft365/SendEmail
  • To: [bookkeeper_email from data store]
  • CC: [pm_email from webhook or data store]
  • Subject: CONCAT('[Review] Draft Invoice ', {{8.DocNumber}}, ' - ', {{1.project_name}})
  • HTML Body: (see Step 8 commands for template)

Error Handling

  • Each module has a Break error handler that logs the error to the 'invoice-error-log' data store, sends an alert email to the MSP monitoring address, and sends an alert email to the bookkeeper with details
  • Retry policy: 3 retries with exponential backoff for API timeouts

Data Stores Required

1
'project-retainage-rules' — Key: project_id, Fields: retainage_pct, retainage_cap_pct_complete, retainage_after_cap
2
'invoice-audit-log' — Key: auto-increment, Fields: timestamp, project_id, milestone_id, amounts, qbo_invoice_id, status
3
'invoice-error-log' — Key: auto-increment, Fields: timestamp, error_module, error_message, webhook_payload
4
'qbo-customer-map' — Key: pm_customer_name, Fields: qbo_customer_id (for name-to-ID resolution)
5
'config' — Key: setting_name, Fields: setting_value (stores thresholds, email addresses, defaults)

PM Platform Webhook Relay

Type: integration A lightweight middleware component that bridges the construction PM platform's event system with the Make.com webhook. For platforms like Knowify that may not have native webhook support, this relay polls the PM platform's API on a schedule and translates status changes into webhook calls to the main automation scenario. For platforms like Procore that have native webhooks, this component configures and manages the webhook subscription.

Implementation:

Option A: Polling Relay for Knowify (Make.com Scenario)

  • Scenario Name: 'PM Platform Poller'
  • Schedule: Every 15 minutes

Module 1: Data Store - Get Last Poll Timestamp

  • Type: datastore/GetRecord
  • Data Store: 'config'
  • Key: 'last_poll_timestamp'
  • Returns: {{timestamp_value}}

Module 2: HTTP Request - Knowify API

  • Type: http/MakeRequest
  • URL: https://api.knowify.com/v1/projects/milestones (Note: Verify exact Knowify API endpoint - may need to use /projects/{id}/tasks or /projects/{id}/schedule depending on API version)
  • Method: GET
  • Headers: - Authorization: Bearer {{knowify_api_key}} - Content-Type: application/json
  • Query Parameters: - updated_since: {{1.timestamp_value}} - status: completed

Module 3: Iterator

  • Source: {{2.data.milestones}} (or equivalent response array)
  • Filter: Only milestones where status_changed_to = 'completed' AND milestone_id NOT IN [already_processed_ids from data store]

Module 4: HTTP Request - Call Main Webhook

  • Type: http/MakeRequest
  • URL: [Make.com webhook URL from main scenario]
  • Method: POST

Body (JSON): Transform Knowify milestone data to webhook payload format:

Knowify milestone webhook payload shape
json
# map Make.com module 3 fields to these keys

{
  "event": "milestone.completed",
  "project_id": "{{3.project_id}}",
  "project_name": "{{3.project_name}}",
  "milestone_id": "{{3.milestone_id}}",
  "milestone_name": "{{3.milestone_name}}",
  "milestone_amount": {{3.amount}},
  "contract_total": {{3.contract_total}},
  "completed_to_date": {{3.billed_to_date + 3.amount}},
  "retainage_pct": {{3.retainage_percentage}},
  "customer_name": "{{3.client_name}}",
  "customer_email": "{{3.client_email}}",
  "customer_qbo_id": "[lookup from qbo-customer-map data store]",
  "completed_by": "{{3.completed_by_user}}",
  "completed_date": "{{3.completion_date}}",
  "is_final_milestone": {{3.is_final}},
  "accumulated_retainage": {{3.total_retainage_held}},
  "state_code": "{{3.project_state}}",
  "contract_number": "{{3.contract_number}}",
  "sov_line_items": {{3.schedule_of_values_items}}
}

Module 5: Data Store - Update Last Poll Timestamp

  • Type: datastore/UpdateRecord
  • Data Store: 'config'
  • Key: 'last_poll_timestamp'
  • Value: {{now}}

Module 6: Data Store - Record Processed Milestone

  • Type: datastore/AddRecord
  • Data Store: 'processed-milestones'
  • Record: {milestone_id: {{3.milestone_id}}, processed_at: {{now}}}

Option B: Procore Native Webhook Configuration

Use Procore's REST API to register webhook subscriptions directly.

1
Create Webhook Hook
2
Create Webhook Trigger for Schedule Tasks
Step 1: Register the webhook hook with Procore
bash
curl -X POST 'https://api.procore.com/rest/v1.0/companies/{company_id}/webhooks/hooks' \
  -H 'Authorization: Bearer {procore_access_token}' \
  -H 'Content-Type: application/json' \
  -d '{
    "hook": {
      "api_version": "v2",
      "destination_url": "{make_webhook_url}",
      "namespace": "company"
    }
  }'
Step 2: Attach a trigger to fire on Schedule Task updates
bash
curl -X POST 'https://api.procore.com/rest/v1.0/companies/{company_id}/webhooks/hooks/{hook_id}/triggers' \
  -H 'Authorization: Bearer {procore_access_token}' \
  -H 'Content-Type: application/json' \
  -d '{
    "trigger": {
      "resource_name": "Schedule Tasks",
      "event_type": "update"
    }
  }'
Note

The Make.com main scenario's webhook module will need a filter to only process events where the task status changed to 'completed' and the task is tagged as a 'billing milestone' in Procore.

Retainage Calculation Engine

Type: skill A configurable retainage calculation module that supports multiple retainage schemes used across different states and contract types. This component is called by the main workflow for every invoice line item and returns the correct retainage amount based on the project's rules, the current completion percentage, and applicable state law.

Implementation:

Make.com Custom JavaScript Module (Module type: tools/Custom)

Retainage Calculation Engine
javascript
// Make.com Custom JavaScript Module

// Retainage Calculation Engine
// Input variables from previous modules:
//   scheduled_value: Total scheduled value of the line item
//   previous_billed: Amount previously billed for this line item
//   this_period: Amount being billed this period
//   stored_materials: Materials presently stored (not yet installed)
//   project_pct_complete: Overall project completion percentage
//   retainage_rules: Object from data store lookup
//     {
//       base_pct: 10,                    // Base retainage percentage
//       cap_at_project_pct: 50,          // Reduce retainage after project is X% complete
//       reduced_pct: 5,                  // Reduced retainage percentage after cap
//       max_retainage_amount: null,      // Optional dollar cap on total retainage
//       materials_retainage_pct: 0,      // Retainage on stored materials (often 0%)
//       release_at_substantial: true,    // Release retainage at substantial completion
//       state_code: 'NV'                 // State for regulatory compliance
//     }
//   is_final_milestone: boolean
//   accumulated_retainage: Total retainage held to date across all prior periods

const STATE_RETAINAGE_CAPS = {
  'NV': { max_pct: 5, notes: 'Nevada caps retainage at 5%' },
  'ND': { max_pct: 10, reduce_at_50_pct: true, notes: 'ND: 10% until 50% complete, then 0%' },
  'CO': { max_pct: 5, notes: 'Colorado: typically 5% max' },
  'CA': { max_pct: 5, notes: 'California: 5% on public works' },
  'TX': { max_pct: 10, notes: 'Texas: 10% standard' },
  'FL': { max_pct: 10, notes: 'Florida: 10% standard, 5% after 50%' },
  // Add additional states as needed per client requirements
};

function calculateRetainage(input) {
  const rules = input.retainage_rules;
  const stateRules = STATE_RETAINAGE_CAPS[rules.state_code] || {};
  
  // Start with base retainage percentage from contract
  let effectivePct = rules.base_pct;
  
  // Apply project completion reduction if applicable
  if (rules.cap_at_project_pct && input.project_pct_complete >= rules.cap_at_project_pct) {
    effectivePct = rules.reduced_pct || 0;
  }
  
  // Apply state-specific caps (state law overrides contract if lower)
  if (stateRules.max_pct && effectivePct > stateRules.max_pct) {
    effectivePct = stateRules.max_pct;
  }
  
  // North Dakota special rule: no retainage after 50% complete
  if (rules.state_code === 'ND' && input.project_pct_complete >= 50) {
    effectivePct = 0;
  }
  
  // Calculate retainage on work performed
  const workRetainage = input.this_period * (effectivePct / 100);
  
  // Calculate retainage on stored materials (often 0%)
  const materialsRetainagePct = rules.materials_retainage_pct || 0;
  const materialsRetainage = input.stored_materials * (materialsRetainagePct / 100);
  
  const totalRetainage = workRetainage + materialsRetainage;
  
  // Apply dollar cap if specified
  let cappedRetainage = totalRetainage;
  if (rules.max_retainage_amount) {
    const remainingCap = rules.max_retainage_amount - input.accumulated_retainage;
    cappedRetainage = Math.min(totalRetainage, Math.max(0, remainingCap));
  }
  
  // Final milestone: release all accumulated retainage
  let retainageRelease = 0;
  if (input.is_final_milestone) {
    retainageRelease = input.accumulated_retainage;
  }
  
  return {
    effective_retainage_pct: effectivePct,
    retainage_on_work: workRetainage,
    retainage_on_materials: materialsRetainage,
    total_retainage_this_period: cappedRetainage,
    retainage_release: retainageRelease,
    net_billable: (input.this_period + input.stored_materials) - cappedRetainage + retainageRelease,
    gross_billable: input.this_period + input.stored_materials,
    new_accumulated_retainage: input.accumulated_retainage + cappedRetainage - retainageRelease,
    state_compliance_note: stateRules.notes || 'No state-specific retainage cap applied',
    calculation_details: `Base: ${rules.base_pct}%, Effective: ${effectivePct}%, State: ${rules.state_code}, Project Complete: ${input.project_pct_complete}%`
  };
}

// Execute
const result = calculateRetainage({
  scheduled_value: scheduled_value,
  previous_billed: previous_billed,
  this_period: this_period,
  stored_materials: stored_materials,
  project_pct_complete: project_pct_complete,
  retainage_rules: retainage_rules,
  is_final_milestone: is_final_milestone,
  accumulated_retainage: accumulated_retainage
});

return result;

Data Store Schema: 'project-retainage-rules'

  • Key: project_id (string)
  • base_pct (number): Default retainage percentage for this contract
  • cap_at_project_pct (number): Project completion % at which retainage reduces
  • reduced_pct (number): Retainage % after the cap threshold
  • max_retainage_amount (number, nullable): Dollar cap on total retainage
  • materials_retainage_pct (number): Retainage % on stored materials
  • release_at_substantial (boolean): Whether to release at substantial completion
  • state_code (string): Two-letter state code for regulatory compliance

Setup Instructions

1
Create the data store in Make.com: Data Stores > Create > name 'project-retainage-rules'
2
Define the schema per above
3
For each active project, add a record with the contract's retainage terms
4
The main workflow's Module 2 looks up these rules before calculating

Lien Waiver Document Generator

Type: skill Generates state-appropriate conditional lien waiver PDF documents pre-filled with invoice data. Uses Google Docs templates with merge fields, with different templates for states that require statutory forms (AZ, CA, FL, GA, MI, MO, MS, NV, TX, UT, WI, WY) versus generic conditional waivers for other states.

Implementation

Setup: Google Docs Templates

Template 1: Generic Conditional Waiver (Non-Statutory States)

Create a Google Doc with the following content and merge fields:

Generic Conditional Waiver template content with merge fields
text
CONDITIONAL WAIVER AND RELEASE ON PROGRESS PAYMENT

Project: {{project_name}}
Job Location: {{project_address}}
Owner: {{owner_name}}
Through Date: {{billing_period_end}}

On receipt of payment in the sum of ${{invoice_amount}}, the undersigned waives and releases any mechanic's lien, stop payment notice, or bond right claims for labor, services, equipment, or material furnished to the above-referenced project through the date shown above.

This waiver and release is CONDITIONED on receipt of payment. If the maker of the payment fails to issue a check, or if the check is dishonored, this waiver and release shall be null and void.

Claimant: {{contractor_name}}
Claimant Address: {{contractor_address}}
Date: {{current_date}}
Signature: _________________________
Title: _________________________

Template 2: California Statutory Form (Civil Code § 8132)

Use exact statutory language from California Civil Code Section 8132.

Template 3: Texas Statutory Form

Use exact statutory language from Texas Property Code Chapter 53.

Template 4: Florida Statutory Form

Use Florida Statute § 713.20 language.

Additional templates required for: AZ (A.R.S. § 33-1008), GA, MI, MO, MS, NV, UT, WI, WY.

Make.com Sub-Scenario: 'Generate Lien Waiver'

Module 1: Data Store Lookup — State Template Map

  • Data Store: 'lien-waiver-templates'
  • Key: {{state_code}}
  • Returns: google_doc_template_id, requires_notarization (boolean)

Module 2: Google Docs — Create Document from Template

Make.com Module 2 configuration
text
# Google Docs CreateDocumentFromTemplate

Type: googledocs/CreateDocumentFromTemplate
Template Document ID: {{1.google_doc_template_id}}
New Document Title: CONCAT('Conditional_Lien_Waiver_', {{project_id}}, '_', {{milestone_id}}, '_', {{formatDate(now, 'YYYY-MM-DD')}})
Save to Folder: [Google Drive folder ID for staging]
Merge Fields:
  project_name: {{project_name}}
  project_address: {{project_address}}
  owner_name: {{owner_name}}
  billing_period_end: {{formatDate(completed_date, 'MMMM DD, YYYY')}}
  invoice_amount: {{formatNumber(net_invoice_amount, 2)}}
  contractor_name: {{contractor_name}}
  contractor_address: {{contractor_address}}
  current_date: {{formatDate(now, 'MMMM DD, YYYY')}}

Module 3: Google Drive — Download File as PDF

Make.com Module 3 configuration — Google Drive DownloadFile
text
Type: googledrive/DownloadFile
File ID: {{2.documentId}}
Format: PDF

Module 4: SharePoint — Upload PDF

Make.com Module 4 configuration — SharePoint UploadFile
text
Type: microsoft365/UploadFile
Site: [Client SharePoint site]
Path: /Projects/{{project_id}}/Lien Waivers/
Filename: {{2.title}}.pdf
Content: {{3.data}}

Module 5: Return PDF Data

Return the PDF binary data and SharePoint URL for attachment to the notification email in the parent scenario.

Data Store Schema: 'lien-waiver-templates'

  • Key: state_code (string, 2-letter)
  • google_doc_template_id (string): Google Doc ID for that state's form
  • form_type (string): 'statutory' or 'generic'
  • requires_notarization (boolean): true for MS, WY
  • notes (string): Any state-specific instructions
Note

Populate the data store for each state where the client operates. Default fallback to the generic template if state_code is not found in the data store.

Invoice Audit & Reconciliation Dashboard

Type: workflow

A secondary Make.com scenario that runs weekly (or on-demand) to reconcile the automation's invoice log against QBO's actual invoice data. Detects discrepancies, identifies draft invoices that haven't been sent within the expected timeframe, and generates a summary report for the bookkeeper and MSP.

Implementation:

1
Module 1: Scheduler — Type: flow/Scheduler, Run: Weekly, Monday 08:00
2
Module 2: Data Store - Get All Records from Last 7 Days
3
Module 3: Iterator - For Each Logged Invoice
4
Module 4: QBO - Get Invoice by ID
5
Module 5: Set Variable - Compare & Flag Issues
6
Module 6: Filter - Only Flagged Items
7
Module 7: Array Aggregator — Collect all flagged items into a summary array
8
Module 8: Email - Send Reconciliation Report
9
Module 9: Data Store - Update Reconciliation Log
Make.com Scenario: Weekly Invoice Reconciliation — Schedule: Every Monday at 8:00 AM (client's local time)
plaintext
## Module 1: Scheduler
- Type: flow/Scheduler
- Run: Weekly, Monday 08:00

## Module 2: Data Store - Get All Records from Last 7 Days
- Type: datastore/SearchRecords
- Data Store: 'invoice-audit-log'
- Filter: timestamp >= {{addDays(now, -7)}}
- Returns: Array of all logged invoices from past week

## Module 3: Iterator - For Each Logged Invoice
- Type: flow/Iterator
- Source: {{2.records}}

## Module 4: QBO - Get Invoice by ID
- Type: quickbooks/GetInvoice
- Invoice ID: {{3.qbo_invoice_id}}
- Returns: QBO invoice record with current status, amount, etc.

## Module 5: Set Variable - Compare & Flag Issues
- Type: tools/SetVariable
- Variables:
  - amount_match: {{3.net_invoice_amount}} == {{4.TotalAmt}} ? 'OK' : 'MISMATCH'
  - status: {{4.EmailStatus}} (Sent, NotSent, etc.)
  - days_since_created: dateDiff({{3.timestamp}}, now, 'days')
  - is_stale: {{days_since_created}} > 2 AND {{status}} == 'NotSent' ? true : false
  - flag: IF(amount_match != 'OK', 'AMOUNT_MISMATCH',
           IF(is_stale, 'STALE_DRAFT', 'OK'))

## Module 6: Filter - Only Flagged Items
- Condition: {{5.flag}} != 'OK'

## Module 7: Array Aggregator
- Collect all flagged items into a summary array

## Module 8: Email - Send Reconciliation Report
- Type: microsoft365/SendEmail
- To: [bookkeeper_email], [msp_monitoring_email]
- Subject: CONCAT('Weekly Invoice Reconciliation Report - ', formatDate(now, 'MMM DD, YYYY'))
- Body (HTML):
  <h2>Invoice Automation - Weekly Reconciliation</h2>
  <p>Period: {{formatDate(addDays(now, -7), 'MMM DD')}} - {{formatDate(now, 'MMM DD, YYYY')}}</p>
  <p>Total Invoices Generated: {{length(2.records)}}</p>
  <p>Issues Found: {{length(7.array)}}</p>
  
  IF issues found:
  <table border='1'>
    <tr><th>Invoice #</th><th>Project</th><th>Issue</th><th>Expected</th><th>Actual</th></tr>
    FOR EACH flagged item:
    <tr>
      <td>{{item.qbo_doc_number}}</td>
      <td>{{item.project_name}}</td>
      <td>{{item.flag}}</td>
      <td>${{item.expected_amount}}</td>
      <td>${{item.actual_amount}}</td>
    </tr>
  </table>
  
  IF no issues: <p style='color:green'>✓ All invoices reconciled successfully.</p>

## Module 9: Data Store - Update Reconciliation Log
- Record reconciliation run timestamp and results
Warning

Error Handling: If QBO API returns 404 for an invoice (deleted), flag as 'INVOICE_DELETED'. If QBO API is unreachable, retry 3x then send alert email to MSP.

Testing & Validation

1
TEST 1 - Webhook Connectivity: Manually send a test JSON payload to the Make.com webhook URL using Postman or curl. Verify the scenario triggers and the webhook data is parsed correctly. Expected: Make.com scenario execution log shows successful trigger with all fields populated.
2
TEST 2 - Retainage Calculation Accuracy (Standard): Create a test milestone with SOV line item $100,000, retainage 10%, no prior billing. Expected output: Gross = $100,000, Retainage = $10,000, Net Invoice = $90,000.
3
TEST 3 - Retainage Calculation Accuracy (State Cap): Create a test milestone for a Nevada project (5% cap) with contract retainage set to 10%. Expected: Automation applies 5% cap per NV law, Retainage = $5,000 on $100,000 milestone.
4
TEST 4 - Retainage Reduction at Threshold: Create a test scenario where project is 52% complete with rules set to reduce from 10% to 5% at 50% completion. Expected: Retainage calculates at 5% for this milestone.
5
TEST 5 - Final Milestone Retainage Release: Create a test final milestone ($25,000) with $45,000 accumulated retainage. Expected: Invoice amount = $25,000 milestone + $45,000 retainage release - current retainage = total per contract terms.
6
TEST 6 - QBO Invoice Creation: Verify the draft invoice appears in QuickBooks Online with correct customer, line items, amounts, custom fields (Project Name, Milestone ID, Contract Number), and 'Unsent' status.
7
TEST 7 - Multi-Line SOV Invoice: Create a milestone that triggers billing for 3 SOV line items simultaneously. Expected: Single QBO invoice with 3 line items plus 1 retainage line, all amounts correct.
8
TEST 8 - Email Notification Delivery: Verify bookkeeper receives email within 5 minutes of milestone completion with correct invoice summary, amounts, and clickable QBO link.
9
TEST 9 - Lien Waiver Generation: Trigger a milestone for a California project. Expected: Conditional lien waiver PDF generated using CA statutory template (Civil Code § 8132), correct amounts populated, uploaded to SharePoint in correct project folder.
10
TEST 10 - Lien Waiver State Fallback: Trigger a milestone for a state without statutory forms (e.g., Ohio). Expected: Generic conditional waiver template used.
11
TEST 11 - Duplicate Prevention: Mark the same milestone complete twice in the PM platform. Expected: Second trigger is rejected/ignored (checked against processed-milestones data store). No duplicate invoice created.
12
TEST 12 - Change Order Handling: Modify an SOV line item amount via change order in the PM platform, then complete the milestone. Expected: Invoice reflects the revised (post-CO) scheduled value, not the original.
13
TEST 13 - Field Tablet Workflow: On an iPad in the field (using cellular data), open the PM platform app, navigate to an active project, mark a milestone complete, and verify the automation fires and draft invoice appears in QBO within 15 minutes.
14
TEST 14 - Error Handling: Temporarily invalidate the QBO OAuth token (revoke access). Trigger a milestone. Expected: Error is caught, logged in error data store, alert email sent to MSP and bookkeeper, no partial/corrupt invoice created.
15
TEST 15 - Reconciliation Report: After processing 5+ test invoices, manually edit one invoice amount in QBO. Run the reconciliation scenario. Expected: Report flags the edited invoice as 'AMOUNT_MISMATCH' with expected and actual amounts.
16
TEST 16 - Historical Comparison: Run the automation against 3 real historical milestones and compare the auto-generated draft invoices against the manually-created originals. Expected: Amounts match within $0.01 (rounding). Any discrepancies must be investigated and resolved before go-live.
17
TEST 17 - AIA G702/G703 Output (if applicable): Generate an AIA pay application for a test project with 10+ SOV line items, 2 change orders, and stored materials. Compare output against a manually prepared G702/G703. Expected: All fields match — Original Contract Sum, Net Change by COs, Total Completed & Stored to Date, Retainage, Current Payment Due.
18
TEST 18 - Performance/Load Test: Trigger 10 milestone completions within a 5-minute window (simulating end-of-month billing rush). Expected: All 10 invoices generated correctly within 30 minutes with no errors or dropped webhooks.

Client Handoff

The client handoff should be structured as a half-day (4-hour) session with two audiences: office staff (bookkeeper, office manager, owner) and field staff (project managers, superintendents). Cover the following topics:

For Office Staff (2.5 hours)

1
Overview of the automation — what triggers it, what it produces, and what it does NOT do (it creates drafts, not final invoices; human review is always required)
2
Live walkthrough: completing a milestone on the PM platform and watching the draft invoice appear in QBO within minutes
3
How to review and approve draft invoices in QBO — what to check (amounts, customer, retainage, line item descriptions)
4
How to handle exceptions: what to do if an invoice amount is wrong, how to void and regenerate, how to manually create an invoice if the automation fails
5
How to update retainage rules when a new contract starts (adding records to the Make data store — provide a simple admin form or spreadsheet-to-data-store sync)
6
How to read the weekly reconciliation report and what each flag means
7
Where to find lien waiver documents in SharePoint
8
Escalation procedure: when to call the MSP helpdesk vs. when to handle internally

For Field Staff (1.5 hours)

1
How to properly mark a milestone complete in the PM platform's mobile app
2
What information to include (completion notes, photos if required by contract)
3
The importance of accurate milestone completion — explain that their action directly triggers billing
4
What happens after they mark a milestone (the office receives a draft invoice for review)
5
Hands-on practice on their iPads with a test project

Documentation to Leave Behind

  • Quick Reference Card (laminated, for each desk/tablet): Step-by-step for marking milestones complete and reviewing draft invoices
  • Full Process Documentation (PDF + SharePoint): Detailed guide with screenshots for every step
  • Retainage Rules Reference: Table showing each active project's retainage terms and the state law that applies
  • Error Handling Playbook: What to do for each type of automation failure
  • Contact Card: MSP helpdesk phone, email, portal URL, and SLA response times
  • Make.com Admin Guide: How to access the automation dashboard, view execution logs, and pause/resume scenarios (for the designated client admin only)

Success Criteria to Review Together

Maintenance

Ongoing MSP Responsibilities:

Weekly (30 minutes):

  • Review Make.com scenario execution logs for errors or warnings
  • Check the invoice-error-log data store for any new entries
  • Review the automated weekly reconciliation report
  • Verify webhook connectivity (check last successful trigger timestamp)

Monthly (1-2 hours):

  • Review Make.com operations usage against plan limits (Pro plan: 10,000 ops)
  • Verify QBO OAuth token is still valid (tokens expire; refresh tokens are used automatically but can fail)
  • Review and rotate any API keys if security policy requires it
  • Check for PM platform or QBO updates that may affect API compatibility
  • Add retainage rules for any new projects started during the month
  • Review client's invoice accuracy metrics and address any recurring issues

Quarterly (2-4 hours):

  • Comprehensive system health review with the client bookkeeper
  • Update state retainage rules if any legislative changes occurred
  • Update lien waiver templates if state forms have been revised
  • Review and optimize Make.com scenarios for performance (check execution times, consolidate modules if possible)
  • Assess whether additional automation opportunities exist (e.g., automated payment reminders, AR aging alerts)
  • Update documentation with any process changes

Annually:

  • Full platform version review — check for major updates to Knowify/Buildertrend/Procore, QBO, and Make.com
  • Renew software subscriptions and renegotiate pricing if possible
  • Re-validate AIA form compliance (AIA periodically updates form versions)
  • Review field tablet hardware — replace batteries or devices if degraded
  • Re-train any new staff members

SLA Considerations:

  • Target 4-hour response time for automation failures during business hours (invoice generation is time-sensitive due to Prompt Payment Act deadlines)
  • Target 1-business-day resolution for non-critical issues (formatting, notification delivery)
  • Target same-business-day resolution for critical issues (invoices not generating, wrong amounts)
  • Provide after-hours emergency contact for month-end billing periods (contractors often do heavy billing on the last 2-3 days of the month)

Escalation Path:

1
Client bookkeeper attempts resolution using Error Handling Playbook
2
If unresolved, client contacts MSP helpdesk (phone/email/portal)
3
MSP Tier 1 checks Make.com logs, restarts scenarios, verifies API connections
4
MSP Tier 2 (automation engineer) investigates workflow logic issues, data mapping errors
5
MSP escalates to vendor support (Knowify, QBO, Make.com) for platform-level bugs

Monitoring Alerts (configure in Make.com):

  • Scenario execution failure → immediate email to MSP monitoring inbox
  • Scenario execution time > 5 minutes → warning alert
  • Operations usage > 80% of monthly limit → alert to MSP for plan upgrade consideration
  • No scenario executions for 5+ business days → alert (may indicate webhook disconnection or no milestones being completed, which should be verified with client)

Alternatives

Zapier-Based Implementation (Instead of Make.com)

Replace Make.com with Zapier as the automation middleware. Zapier has pre-built connectors for Procore, QuickBooks Online, and many construction platforms. The workflow is built using Zapier's Zap editor with multi-step Zaps, Paths for conditional logic, and Formatter actions for calculations.

Microsoft Power Automate Implementation

Use Microsoft Power Automate as the middleware instead of Make.com. This is ideal when the client is already invested in the Microsoft 365 ecosystem (SharePoint, Teams, Outlook). Power Automate's premium connectors include QuickBooks Online and can connect to Procore/Knowify via custom HTTP connectors.

Native PM Platform Billing (No Middleware)

Skip the external automation layer entirely and use the construction PM platform's built-in billing features. Knowify, Buildertrend, and Procore all have native progress invoicing and QBO sync capabilities. The workflow becomes: PM marks milestone complete → uses the platform's billing module to generate the invoice → native sync pushes to QBO.

Procore + Sage Intacct Enterprise Stack

For large general contractors ($20M+ annual revenue), replace the Knowify/QBO stack with Procore for project management and Sage Intacct Construction for accounting. Both platforms have native integration capabilities and enterprise-grade features for multi-entity, multi-project progress billing.

Custom API Integration (Node.js/Python)

Instead of using a no-code/low-code middleware like Make.com, build a custom integration application using Node.js or Python that directly interfaces with the PM platform API and QBO API. Host on a small cloud VM (AWS EC2 or Azure VM) or serverless functions (AWS Lambda / Azure Functions).

Want early access to the full toolkit?