Table of Contents
- 1 The Business Situation
- 2 The Existing Process
- 2.1 Operational Problems
- 2.2 Business Effects
- 3 What the New System Needed to Do
- 4 Implementation Approaches Considered
- 4.1 Improving the Existing Manual Process
- 4.2 Google Workspace with Apps Script
- 4.3 Microsoft 365 with Power Automate
- 4.4 Airtable as a Quote Application
- 4.5 HubSpot, Google Sheets, Zapier, and PandaDoc
- 5 The Selected Solution
- 6 System Architecture and Data Flow
- 7 Data Structure
- 7.1 HubSpot Deal Properties
- 7.2 Google Sheets Product Catalog
- 7.3 Google Sheets Quote Audit
- 8 Workflow Statuses and Ownership
- 9 Step-by-Step Implementation
- 9.1 Step 1: Prepare the Accounts and Permissions
- 9.2 Step 2: Build the Intake
- 9.3 Step 3: Create the System of Record
- 9.4 Step 4: Connect the Tools
- 9.5 Step 5: Build the Core Automation
- 9.6 Step 6: Add Approvals, Reminders, and Escalations
- 9.7 Step 7: Add Documents and File Management
- 9.8 Step 8: Add Reporting and Operational Views
- 9.9 Step 9: Add Security and Governance Controls
- 9.10 Step 10: Deploy and Test
- 10 Code and Configuration
- 10.1 Code by Zapier Pricing and Payload Step
- 10.2 PandaDoc Document Creation Request
- 10.3 Send Request
- 10.4 Deployment and Troubleshooting
- 11 Failure Handling and Operational Reliability
- 12 A Complete Example
- 13 Implementation Cost
- 14 Estimated Time and Cost Savings
- 14.1 Actual Calculation
- 15 Adding AI to the Automation
- 15.1 The Recommended AI Enhancement
- 15.2 Benefits of the AI Enhancement
- 15.3 What Remains Rule-Based or Human-Controlled
- 15.4 Estimating the Additional Value of AI
- 16 Testing Checklist
- 17 Ongoing Maintenance
- 18 When to Move to Dedicated Software
- 19 Implementation Checklist
The Business Situation
Oakline Systems Advisory is a fictional 45-person managed IT and cybersecurity services firm. Its sales team includes eight account executives, a sales operations coordinator, a sales director, and a finance manager responsible for pricing and discount review.
The company creates approximately 55 new or revised proposals each month. HubSpot holds opportunities and contact information, but account executives previously assembled commercial proposals in Microsoft Word. Pricing came from a Google Sheets workbook, approvals took place over email, and completed PDFs were manually attached to CRM records.
A standard proposal could include up to three service packages, a contract term, implementation services, an approved scope narrative, and a requested discount. Account executives copied customer details from HubSpot, selected prices from the spreadsheet, calculated discounts, edited a Word template, converted it to PDF, and emailed it to finance when approval was required.
The process worked at low volume, but it created version errors, inconsistent pricing, slow approvals, and limited visibility into whether a proposal was being drafted, reviewed, sent, viewed, or signed. Finance also lacked a reliable audit record showing which price list and discount rule had been used.
The business wanted a structured process before adding any artificial intelligence. The core requirement was deterministic automation: required fields, governed prices, exact calculations, approval thresholds, controlled document templates, and human authorization for material discounts.
Note: This case study is provided as a representative example of the types of AI integration and digital transformation solutions Intelligex designs and delivers. Actual engagements are tailored to each client’s goals, constraints, existing systems, timeline, and available resources, so the approach, tools, and outcomes may vary.
The Existing Process
The original process followed these steps:
- An account executive moved a HubSpot deal to a proposal-related stage.
- The account executive opened a shared Google Sheets price list and looked up service prices.
- Quantities, term adjustments, costs, discounts, and margins were copied into a separate working spreadsheet.
- Customer and commercial information was copied from HubSpot into a Word proposal.
- The account executive edited scope and executive-summary text, often starting from an older proposal.
- If the discount appeared unusual, the Word file and pricing spreadsheet were emailed to finance.
- Finance checked the calculation, replied with approval or requested changes, and sometimes received a second version containing different values.
- The account executive converted the approved Word file to PDF and emailed it to the buyer.
- After acceptance, the account executive uploaded the final file to HubSpot and updated the deal stage.
- Sales operations periodically reconciled email, HubSpot, and shared files to identify outstanding proposals.
Operational Problems
- Customer and pricing data were entered more than once.
- Old Word files could contain outdated wording or terms.
- Spreadsheet formulas could be overwritten.
- Finance approval was buried in email.
- No consistent quote identifier connected the CRM record, calculation, and document.
- Sales operations could not reliably identify stalled work.
Business Effects
- Account executives spent time assembling documents instead of progressing opportunities.
- Version confusion increased review effort.
- Customers occasionally received revised PDFs after errors were found.
- Discount decisions depended on who noticed the request.
- Management reporting required manual reconciliation.
- Knowledge of the complete process was concentrated in one coordinator.
The most important risk was not document formatting. It was the absence of a controlled handoff between opportunity data, pricing, discount authorization, document creation, signature, and CRM status.
What the New System Needed to Do
Sales and finance agreed on the requirements before selecting the implementation method.
| Requirement | Expected behavior | Control owner |
|---|---|---|
| Structured intake | Collect customer, contact, package, quantity, term, discount, and narrative fields in HubSpot. | Sales operations |
| Validation | Stop requests with missing contacts, inactive SKUs, invalid quantities, or unsupported currencies. | Automation |
| Unique identification | Assign one quote ID to each deal revision. | Automation |
| Governed pricing | Read active prices, costs, and allowed discounts from a protected Google Sheets catalog. | Finance |
| Deterministic calculation | Calculate list value, discounted value, estimated cost, and gross margin without AI. | Automation |
| Discount routing | Apply exact thresholds and select the required PandaDoc approval workflow. | Finance |
| Document generation | Create a proposal from an approved PandaDoc template using the calculated values. | Automation |
| Human approval | Require finance or sequential finance and sales director approval for defined exceptions. | Named approvers |
| Signature | Send the approved document to the designated customer signer. | PandaDoc workflow |
| Status synchronization | Reflect draft, approval, sent, completed, declined, expired, and error states in HubSpot. | Automation |
| Document storage | Retain the final signed document and audit history in PandaDoc, with its identifier and link in HubSpot. | Sales operations |
| Reporting | Show waiting approvals, unsigned proposals, completed quotes, processing time, and failures. | Sales operations |
| Exception handling | Place failed or incomplete requests into a visible recovery queue. | Sales operations |
| Manual override | Allow authorized staff to cancel, supersede, or regenerate a revision without deleting history. | Sales director |
| Audit evidence | Record inputs, outputs, approvers, timestamps, document IDs, and error details. | Finance |
The design deliberately separated rules from judgment. Required fields, calculations, thresholds, and status changes would be automated. Finance would retain control over discount exceptions, and the customer would remain responsible for signing the proposal.
Implementation Approaches Considered
| Approach | Connected tools | Effort | Customization | Main limitation |
|---|---|---|---|---|
| Improve the manual process | HubSpot, Word, Google Sheets, email | Low | Low | Still depends on copying, file versions, and email evidence. |
| Google Workspace workflow | Google Forms, Sheets, Docs, Drive, Apps Script | Medium | High | Would duplicate CRM data and require custom signature integration. |
| Microsoft 365 workflow | Microsoft Forms, SharePoint, Word, Power Automate, Approvals | Medium | High | Introduces a second productivity environment and still requires CRM and signature connections. |
| Airtable quote application | Airtable, automation platform, document service | Medium | High | Creates another system of record beside HubSpot. |
| CRM-led document automation | HubSpot, Google Sheets, Zapier, PandaDoc | Medium | High | Requires careful field governance, template management, and automation monitoring. |
Improving the Existing Manual Process
A locked Word template, protected pricing sheet, file-naming rules, and approval checklist would reduce some mistakes. It would not eliminate repeated entry or provide reliable status synchronization. This approach was useful as an interim control but did not address the central integration problem.
Google Workspace with Apps Script
Google Forms or a structured Sheet could collect inputs, Apps Script could calculate prices, and Google Docs could create PDFs. This offered significant flexibility. However, the business already used HubSpot as the opportunity record and needed electronic signatures, recipient reminders, and a document audit trail. Recreating those capabilities would add code and operational ownership.
Microsoft 365 with Power Automate
Microsoft Forms, SharePoint, Word templates, Power Automate, and approval actions could support a controlled workflow. It was technically viable, especially for an organization standardized on Microsoft 365. Oakline Systems Advisory used Google Workspace for daily collaboration, so this approach would add identities, permissions, storage locations, and administrative overhead.
Airtable as a Quote Application
Airtable could provide an accessible relational interface for quotes, quote lines, approvals, and reports. The disadvantage was architectural duplication. Sales representatives would need to work in both HubSpot and Airtable, and the company would have to decide which system controlled ownership, stage, and customer details.
HubSpot, Google Sheets, Zapier, and PandaDoc
This option retained HubSpot as the sales system of record, used Google Sheets for a finance-controlled product catalog and calculation audit, used Zapier for orchestration, and used PandaDoc for templates, internal approvals, signatures, and final document storage. It connected existing tools without asking sales representatives to adopt a separate quote application.
The Selected Solution
Oakline Systems Advisory selected a CRM-led workflow connecting HubSpot, Google Sheets, Zapier, and PandaDoc. Gmail notifications and HubSpot tasks were used for operational alerts, while business reporting remained in HubSpot with supporting audit views in Google Sheets.
| Tool | Responsibility | Authoritative data |
|---|---|---|
| HubSpot | Quote intake, deal ownership, customer data, workflow status, document reference, and commercial reporting. | Current sales and quote status |
| Google Sheets | Protected SKU catalog, cost assumptions, discount limits, calculation snapshots, and automation error log. | Approved pricing inputs and calculation audit |
| Zapier | Triggers, validation, branching, calculations, API requests, record updates, reminders, and error routing. | Automation configuration and run history |
| PandaDoc | Proposal templates, internal approval workflows, customer signature, document status, final document, and audit trail. | Executed document and signing evidence |
| Gmail | Operational failure and escalation notices that are not handled by PandaDoc. | Notification delivery only |
| HubSpot dashboards and Google Sheets views | Pipeline reporting, aging, exceptions, and reconciliation. | Operational reporting |
Three PandaDoc templates were created from one controlled proposal design:
- Standard template: No internal approval. The document proceeds to customer signature after generation and validation.
- Finance-review template: A finance approver must authorize the document before it proceeds to the customer.
- Exception template: Finance approval is followed by sales director approval before customer delivery.
Using separate templates avoided dependence on dynamically changing an approval workflow at runtime. Each template used the same customer role, variables, pricing-table name, terms, and visual layout. Only the internal approval configuration differed.
Word proposal assembly and email-based discount approval were removed. Human control remained over scope wording, finance exceptions, high-discount authorization, document rejection, customer signature, and any decision to supersede an issued proposal.
System Architecture and Data Flow
The architecture starts with a sales representative requesting a quote from a HubSpot deal. Zapier validates the request, reads governed pricing from Google Sheets, calculates the commercial result, selects a PandaDoc approval template, creates the document, and writes the returned document ID to HubSpot and the calculation audit.
PandaDoc then manages internal approval and customer signature. PandaDoc status events trigger a second Zap that updates HubSpot. A scheduled Zap identifies overdue approvals, unsigned documents, and failed records.
- Request submission: A sales representative completes the HubSpot quote fields and changes
quote_request_statustoRequested. The deal ID and revision form the idempotency key. - Initial validation: Zapier confirms that required customer, contact, line-item, term, narrative, and discount fields are present. Invalid requests are changed to
Incomplete, with a specific validation message. - Duplicate check: Zapier searches the Google Sheets quote audit for the same automation key. If an active or completed row already has a PandaDoc ID, document creation stops.
- Pricing lookup: Zapier looks up each requested SKU in the protected Google Sheets catalog. It validates active dates, quantities, currency, and product availability.
- Calculation: A Code by Zapier step calculates list value, approved unit prices, net value, estimated cost, gross margin, and the required approval route. AI is not used.
- Audit snapshot: Zapier appends the request, source prices, calculation, approval route, and timestamps to Google Sheets. The values are stored as a snapshot rather than formulas linked to a changing catalog.
- Document creation: Zapier sends a template-based document request to PandaDoc. PandaDoc returns a document ID and asynchronous processing status. Both are written to HubSpot and Google Sheets.
- Approval or signature: When the document is ready, Zapier sends it. The selected template either routes it to internal approvers or directly to the customer signer.
- Status synchronization: PandaDoc status events update HubSpot and the audit sheet. Rejected documents become
Changes Required. Completed documents becomeAccepted. - Failure path: Validation, API, authentication, or notification failures set
quote_automation_statustoError, increment the retry count, write an error message, and notify sales operations.
- Intake: HubSpot deal properties completed by the deal owner.
- System of record: HubSpot for current commercial status and PandaDoc for the executed document.
- Automation layer: Zapier, including Paths, Code, Webhooks, filters, delays, and scheduled checks.
- Document storage: PandaDoc retains the final signed proposal and audit trail.
- Notifications: PandaDoc approval and signature notifications, HubSpot tasks, and Gmail exception alerts.
- Reporting: HubSpot dashboards supported by Google Sheets calculation and failure views.
- AI layer: Optional narrative drafting after the deterministic workflow is stable. AI never calculates or approves pricing.
Data Structure
HubSpot remained the current system of record for each quote. Google Sheets stored the governed product catalog and an immutable snapshot of every calculation. PandaDoc stored document content, signatures, approval history, and final status.
HubSpot Deal Properties
| Field | Type | Required | Source | Purpose |
|---|---|---|---|---|
quote_revision |
Integer | Yes | Sales operations | Distinguishes revised quotes for the same deal. |
quote_id |
Text | Generated | Zapier | Uses the format Q-{dealId}-R{revision}. |
quote_request_status |
Enumeration | Yes | Sales and automation | Controls request processing and recovery. |
quote_owner_email |
Yes | HubSpot owner mapping | Receives operational updates. | |
customer_signer_email |
Yes | Sales | Identifies the PandaDoc recipient. | |
customer_signer_first_name |
Text | Yes | Sales | Populates the recipient record. |
customer_signer_last_name |
Text | Yes | Sales | Populates the recipient record. |
contract_term_months |
Enumeration | Yes | Sales | Allowed values are 12, 24, and 36. |
quote_currency |
Enumeration | Yes | Sales | The initial implementation allows USD only. |
requested_discount_pct |
Number | Yes | Sales | Entered as a whole percentage from 0 through 30. |
quote_sku_1 |
Text or enumeration | Yes | Sales | Primary service package. |
quote_quantity_1 |
Number | Yes | Sales | Quantity for the primary package. |
quote_sku_2 |
Text or enumeration | No | Sales | Optional second package. |
quote_quantity_2 |
Number | Conditional | Sales | Required when the second SKU is present. |
quote_sku_3 |
Text or enumeration | No | Sales | Optional third package. |
quote_quantity_3 |
Number | Conditional | Sales | Required when the third SKU is present. |
approved_scope_narrative |
Multiline text | Yes | Sales, after review | Only approved narrative is copied into PandaDoc. |
quote_valid_until |
Date | Yes | Sales | Controls proposal validity. |
list_subtotal |
Currency | Generated | Zapier | List value before discount. |
net_quote_total |
Currency | Generated | Zapier | Approved commercial value. |
estimated_gross_margin_pct |
Percentage | Generated | Zapier | Supports approval routing. |
discount_approval_route |
Enumeration | Generated | Zapier | Auto, Finance, or Finance plus Sales Director. |
discount_approval_status |
Enumeration | Generated | PandaDoc and Zapier | Pending, Approved, Rejected, or Not Required. |
pandadoc_document_id |
Text | Generated | PandaDoc | External system identifier used for synchronization. |
pandadoc_status |
Text | Generated | PandaDoc | Stores the latest normalized document state. |
pandadoc_document_link |
URL | Generated | PandaDoc connector | Opens the document when the connector returns a usable URL. |
last_automation_run |
Date and time | Generated | Zapier | Supports monitoring and reconciliation. |
automation_retry_count |
Integer | Generated | Zapier | Tracks automatic and manual attempts. |
automation_error_message |
Multiline text | Generated | Zapier | Records the latest actionable failure detail. |
quote_notes |
Multiline text | No | Sales and finance | Records non-sensitive processing notes. |
Google Sheets Product Catalog
The Catalog sheet contained one row per sellable SKU. Finance owned this tab and protected all pricing columns.
| Column | Type | Validation | Example |
|---|---|---|---|
sku |
Text | Required and unique | MSS-50-12 |
item_name |
Text | Required | Managed Security Package |
description |
Text | Required | Approved pricing-table description |
unit_price |
Decimal | Greater than or equal to zero | 24000.00 |
unit_cost |
Decimal | Greater than or equal to zero | 14000.00 |
minimum_quantity |
Decimal | Greater than zero | 1 |
maximum_discount_pct |
Decimal | From 0 through 1 | 0.15 |
active |
Boolean | TRUE or FALSE | TRUE |
effective_from |
Date | Required | 2026-01-01 |
effective_to |
Date | Blank or later than effective date | 2026-12-31 |
Google Sheets Quote Audit
The Quote_Audit tab held one immutable row per quote revision. Source prices and calculated outputs were written as values, so a later catalog change could not alter historical evidence.
| Field | Purpose |
|---|---|
automation_key |
Unique combination of deal ID and quote revision. |
quote_id |
Human-readable quote identifier. |
created_at |
Timestamp of the first processing attempt. |
last_updated |
Timestamp of the latest status update. |
requester |
HubSpot owner or requesting user. |
sku_1 through sku_3 |
Requested products. |
quantity_1 through quantity_3 |
Requested quantities. |
source_unit_price_1 through source_unit_price_3 |
Catalog prices used at calculation time. |
source_unit_cost_1 through source_unit_cost_3 |
Cost assumptions used at calculation time. |
requested_discount_pct |
Requested decimal discount. |
list_subtotal |
Calculated list total. |
net_total |
Calculated discounted total. |
estimated_cost |
Calculated cost total. |
gross_margin_pct |
Calculated gross margin percentage. |
approval_route |
Selected approval path. |
approval_status |
Current approval result. |
pandadoc_document_id |
Returned PandaDoc identifier. |
pandadoc_status |
Latest normalized document status. |
document_link |
Connector-provided document link, when available. |
automation_status |
Processing, Complete, Error, or Manual Review. |
retry_count |
Number of recovery attempts. |
error_message |
Latest failure detail. |
notes |
Manual recovery and reconciliation notes. |
The relationship was one HubSpot deal to many quote revisions, with each quote revision linked to one PandaDoc document. A superseded revision was retained rather than overwritten.
Workflow Statuses and Ownership
| Status | Meaning | Owner | Exit condition | Reminder or escalation |
|---|---|---|---|---|
| Draft | Sales is preparing required fields. | Deal owner | Status changed to Requested. | None |
| Requested | A new quote revision is waiting for automation. | Automation | Validation begins. | Alert if unchanged for 30 minutes. |
| Processing | Pricing and document creation are in progress. | Automation | Document ID returned or an error occurs. | Alert if unchanged for 30 minutes. |
| Incomplete | Required or valid information is missing. | Deal owner | Sales corrects the fields and resubmits. | Daily reminder after one business day. |
| Awaiting Approval | PandaDoc is waiting for an internal approver. | Finance or sales director | Approved or rejected. | Reminder after one business day; escalation after two. |
| Changes Required | An approver rejected or returned the document. | Deal owner | A revised request is submitted with a new revision. | Daily reminder to owner. |
| Awaiting Signature | The customer has received the approved proposal. | Deal owner and customer signer | Completed, declined, expired, or superseded. | PandaDoc recipient reminders and sales follow-up. |
| Accepted | All required signatures are complete. | Sales operations | Closed after downstream handoff. | Completion notice. |
| Declined | The customer declined the proposal. | Deal owner | Closed or replaced with a new revision. | Sales task for follow-up. |
| Expired | The document passed its validity period. | Deal owner | Closed or regenerated. | Sales task before and after expiry. |
| Error | Automation could not complete a required action. | Sales operations | Recovered, retried, or moved to Manual Review. | Immediate error notification. |
| Superseded | A newer quote revision replaces this document. | Sales operations | Terminal status. | None |
A record moves backward when an approver rejects it, validation fails, or sales intentionally creates a new revision. Sales cannot move a document from Awaiting Approval to Approved. That transition must originate from PandaDoc or an authorized recovery action supported by approval evidence.
Step-by-Step Implementation
Step 1: Prepare the Accounts and Permissions
- Confirm that HubSpot supports the required custom deal properties, owner access, saved views, and Zapier connection.
- Confirm that the PandaDoc subscription supports templates, the required approval workflows, electronic signatures, API access, and status events. Feature availability should be checked against the current subscription rather than inferred from a plan name.
- Create or identify a Google Workspace shared drive or controlled folder for the pricing workbook. Give finance edit access and sales read access only where necessary.
- Create a Zapier workspace folder named
Quote Automation. Limit editor access to sales operations and the automation administrator. - Create a dedicated PandaDoc integration credential. Where OAuth is available for the selected connection, use OAuth. If an API key is required for Webhooks by Zapier, store it only in restricted Zap steps and rotate it according to company policy.
- Create three PandaDoc templates: standard, finance review, and exception review. Record their template UUIDs using placeholders during development.
- Create an internal finance approval group or named primary and backup approvers in PandaDoc. Configure the exception template for sequential finance and sales director review.
- Create a test HubSpot pipeline or clearly marked test deals, a test Google workbook, and test PandaDoc templates. Do not send development documents to real customers.
- Create test identities for a sales representative, finance approver, sales director, and customer signer. Use controlled test email addresses.
- Assign a primary automation owner and a backup owner. Both need access to Zapier run history, HubSpot properties, the pricing workbook, and PandaDoc administration.
The HubSpot connection should be authorized by an account with access to the required deal and company records, but it should not receive unrelated administrative permissions. The Google Sheets connection needs edit access only to the catalog workbook. The PandaDoc connection needs document creation, send, status, and template access.
Step 2: Build the Intake
The intake was implemented as a grouped set of custom properties on the HubSpot deal record. Sales representatives did not complete a separate form, which reduced duplicate customer entry.
| Field | Input control | Validation |
|---|---|---|
| Customer signer | First name, last name, and email | All required; email must have a valid basic format. |
| Contract term | Dropdown | 12, 24, or 36 months. |
| Currency | Dropdown | USD in the initial implementation. |
| Requested discount | Number | Whole number from 0 through 30. |
| SKU 1 | Dropdown or controlled text | Required and checked against the active catalog. |
| Quantity 1 | Number | Greater than zero and no less than the catalog minimum. |
| SKU 2 and SKU 3 | Dropdown or controlled text | Optional; each requires a corresponding positive quantity. |
| Approved scope narrative | Multiline text | Required before quote generation. |
| Validity date | Date | Must be later than the request date. |
| Quote revision | Integer | Starts at 1 and increases for each regenerated commercial document. |
| Request status | Dropdown | Sales selects Requested only after checking the other fields. |
HubSpot field requirements should be used where the current subscription supports them. Zapier still performs server-side validation because interface requirements can be bypassed by imports, integrations, or older records.
The duplicate key is created from the HubSpot deal ID and revision. A second request with the same pair does not create another document if a PandaDoc ID already exists. If sales needs a revised proposal, the revision must be increased before resubmission.
Attachments were not accepted through this workflow. If a scope document was required, sales linked an approved internal source document in a separate HubSpot property. The proposal itself contained only approved narrative copied into a PandaDoc variable.
Step 3: Create the System of Record
Create a HubSpot property group named Quote Automation and add the fields defined in the data structure. Use stable internal names because Zapier mappings depend on them.
Configure these defaults:
quote_revision: 1quote_request_status: Draftdiscount_approval_status: Pendingautomation_retry_count: 0
Create HubSpot saved views for Draft, Requested, Incomplete, Awaiting Approval, Awaiting Signature, Error, and recently Accepted records. Keep the deal stage separate from the quote workflow status. A deal can remain in the same pipeline stage while its quote moves through several document states.
In Google Sheets, create these tabs:
- Catalog: Protected product, price, cost, discount, and effective-date data.
- Quote_Audit: Immutable calculation and document snapshots.
- Automation_Errors: One row per failed run, including the Zap run URL when available.
- Reference_Values: Allowed currencies, status mappings, reminder intervals, and rule thresholds.
Freeze the header row, turn on filters, protect calculated and finance-maintained ranges, and use ISO date formats. The catalog SKU should be treated as unique. Google Sheets does not enforce database constraints, so finance should use a duplicate-check formula and a protected duplicate view.
=COUNTIF($A$2:$A,A2)>1
Apply this as a conditional formatting rule to flag repeated SKUs. A duplicate catalog SKU causes the automation to enter manual review rather than selecting an arbitrary row.
Step 4: Connect the Tools
Interface labels may change as the platforms update. The important configuration is the source event, authentication, data mapping, destination action, and returned identifier.
| Source | Destination | Trigger or action | Authentication | Returned value |
|---|---|---|---|---|
| HubSpot | Zapier | Deal created or updated, filtered to Requested | HubSpot OAuth connection | Deal ID and quote fields |
| Zapier | Google Sheets | Find catalog row and append audit row | Google OAuth connection | Catalog values and audit row number |
| Zapier | PandaDoc | Create document from template through API request | Restricted API credential | PandaDoc document ID and processing status |
| PandaDoc | Zapier | Document status event | PandaDoc app connection or configured webhook | Document ID and new status |
| Zapier | HubSpot | Update deal properties and create tasks | HubSpot OAuth connection | Updated deal record |
| Zapier | Gmail | Send operational error or escalation notice | Google OAuth connection | Message identifier when available |
Map HubSpot discount values entered as whole percentages to decimal percentages before calculation. For example, an input of 12 becomes 0.12. Currency values should be passed as numbers, not formatted strings containing commas or symbols.
For PandaDoc, create variables with names such as:
Quote.IDDeal.NameCustomer.CompanyContract.TermQuote.ValidUntilQuote.ListSubtotalQuote.DiscountPercentQuote.NetTotalScope.ApprovedNarrative
Create a recipient role named Customer in all three templates. Create a pricing table named Services. The role and pricing-table names must match the API payload exactly.
Step 5: Build the Core Automation
Automation A: Validate and Price the Quote
- Trigger: A HubSpot deal is created or updated.
- Conditions:
quote_request_statusequals Requested and the deal is not marked as a test exclusion. - Actions: Set Processing, build the quote key, search for duplicates, validate fields, find catalog rows, calculate totals, and append the audit snapshot.
- Fields updated: Quote ID, list subtotal, net total, margin, approval route, last automation run, and error fields.
- Notification: The deal owner is notified only if the request is incomplete or fails.
- Exception: Missing, inactive, duplicated, or out-of-date catalog products move the record to Manual Review or Incomplete.
The exact action order is:
- Receive the HubSpot deal update.
- Filter out records whose request status is not Requested.
- Create
automation_keyas the deal ID followed by the quote revision. - Search
Quote_Auditfor that key. - If a row with a PandaDoc document ID exists, stop and mark the duplicate event as ignored.
- Update the HubSpot request status to Processing.
- Validate customer, signer, term, currency, discount, validity date, first SKU, and first quantity.
- Count populated SKU fields and route to a one-line, two-line, or three-line Zapier Path.
- Look up only the populated SKUs in the Catalog sheet.
- Validate each catalog row, including active and effective dates.
- Pass the request and catalog data to the calculation code.
- Append the returned values to Quote_Audit.
- Update the HubSpot calculated fields.
- Continue to PandaDoc creation.
Automation B: Create the PandaDoc Document
- Trigger: Successful output from the calculation step.
- Conditions: Validation status is Valid and no PandaDoc ID exists for the automation key.
- Actions: Select the template UUID, build the request body, create the PandaDoc document, and store the returned ID.
- Fields updated: PandaDoc ID, PandaDoc status, automation status, request status, and document link when returned.
- Notification: None until the document is ready or an error occurs.
- Exception: A timeout after document creation is reconciled by document ID before any retry creates another document.
PandaDoc document creation is asynchronous. The initial response may indicate that the document is still being processed. Do not immediately assume it is ready to send.
Automation C: Send for Approval or Signature
- Trigger: PandaDoc reports that the new document is ready, or a delayed status check confirms the draft state.
- Conditions: The document ID matches an active audit record and the quote has not been superseded.
- Actions: Send the document using the template’s configured workflow.
- Fields updated: Awaiting Approval for review templates or Awaiting Signature for the standard template.
- Notification: PandaDoc notifies the required internal approver or customer signer.
- Exception: If the document remains unready, add it to the retry queue instead of creating another document.
Automation D: Synchronize PandaDoc Status
- Trigger: A PandaDoc document status changes.
- Conditions: The document ID is present in Quote_Audit.
- Actions: Normalize the source status, update the audit row, update HubSpot, and create any required follow-up task.
- Fields updated: PandaDoc status, quote request status, approval status, final document link, and last automation run.
- Notification: Deal owner receives completion, decline, expiry, or rejection information.
- Exception: An unknown status is recorded without changing the deal to an incorrect final state.
Use a controlled mapping rather than copying raw status text directly into business logic. The tested tenant may expose status labels differently through the native connector and API.
| Source event category | HubSpot quote status | Approval status |
|---|---|---|
| Document being generated | Processing | Pending |
| Draft ready | Processing | Pending |
| Waiting for internal approval | Awaiting Approval | Pending |
| Internal approval rejected | Changes Required | Rejected |
| Sent or viewed by recipient | Awaiting Signature | Approved or Not Required |
| Completed | Accepted | Approved or Not Required |
| Declined | Declined | Approved or Not Required |
| Expired | Expired | Approved or Not Required |
| Voided or superseded | Superseded | Not Applicable |
| Error or unknown terminal state | Error | Pending |
Step 6: Add Approvals, Reminders, and Escalations
The approval rules were deterministic:
- Discount at or below 10 percent, gross margin at or above 35 percent, and no product-level exception: no internal approval.
- Discount above 10 percent and at or below 20 percent, or gross margin below 35 percent but at or above 25 percent: finance approval.
- Discount above 20 percent, gross margin below 25 percent, or requested discount above a product’s maximum: sequential finance and sales director approval.
These thresholds are representative assumptions. A business should replace them with approved commercial policy.
The finance template was configured with one internal approval step. The exception template was configured with finance first and the sales director second. The customer recipient did not receive the document until all configured internal approvals were complete.
Approval reminders followed this schedule:
- PandaDoc sends the initial approval request.
- A scheduled Zap identifies records still awaiting approval after one business day and sends a reminder to the current approver.
- After two business days, Zapier notifies the backup approver and sales operations.
- After three business days, a HubSpot task is created for the sales director to resolve the delay.
Unavailable approvers were handled through a named backup approver or an approval group supported by the selected PandaDoc configuration. An administrator could reassign the approval without changing the document’s commercial values.
A rejected document changed the HubSpot status to Changes Required. Sales corrected the deal, increased the quote revision, and submitted a new request. The rejected document and its comments remained part of the audit history.
Step 7: Add Documents and File Management
PandaDoc was the authoritative document repository for generated and signed proposals. The naming convention was:
Q-{HubSpot Deal ID}-R{Two-Digit Revision} - {Customer Company}
For example:
Q-84621057-R02 - Example Customer
The quote ID and HubSpot deal ID were also added as PandaDoc metadata. This allowed reconciliation even when the document name was edited by an administrator.
The file-management rules were:
- Templates were editable only by sales operations and designated administrators.
- Sales representatives could view generated documents but could not change approved pricing logic.
- A completed PandaDoc document was not replaced. A revised commercial proposal received a new quote revision and document ID.
- Old active proposals were voided or marked superseded when a replacement was issued.
- HubSpot stored the PandaDoc document ID and connector-provided link.
- Retention followed the company’s contract and customer-record policy.
- Failed document creation did not result in a manually named PDF outside the workflow unless the recovery was documented.
If an additional archive in Google Drive is required, it should be implemented as a separate completed-document export after confirming that the current PandaDoc connection can return a downloadable file. PandaDoc remained the final repository in this implementation, avoiding an untested duplicate-file process.
Step 8: Add Reporting and Operational Views
HubSpot saved views and dashboards were created for:
- New requests
- Processing requests older than 30 minutes
- Incomplete requests
- Awaiting finance approval
- Awaiting sales director approval
- Unsigned proposals
- Proposals expiring within seven days
- Rejected or declined proposals
- Accepted proposals in the last 30 days
- Automation errors
- Quotes by owner
- Average time from request to send
- Volume by approval route
- Manual-review queue
The Google Sheets audit view supplied calculation-level reporting. Useful calculated columns included processing minutes, approval hours, signature days, age, and exception category.
Processing minutes:
=IF(OR(created_at="",sent_at=""),"",ROUND((sent_at-created_at)*1440,1))
Signature days:
=IF(OR(sent_at="",completed_at=""),"",ROUND(completed_at-sent_at,2))
Overdue approval:
=IF(AND(status="Awaiting Approval",NOW()-approval_requested_at>1),"Yes","No")
HubSpot was refreshed when status events arrived. Google Sheets views were updated by each Zap run. A dashboard owner reviewed exception counts weekly rather than relying on a dashboard with no assigned operational owner.
Step 9: Add Security and Governance Controls
- Use least-privilege connections for HubSpot, Google Sheets, Gmail, and PandaDoc.
- Restrict the Zapier folder to administrators and the designated process owner.
- Protect Google Sheets cost, price, and discount columns from sales edits.
- Limit PandaDoc template editing to sales operations and administrators.
- Do not place API keys in HubSpot properties, spreadsheets, prompts, or email notifications.
- Restrict shared links and avoid public access to pricing workbooks or signed documents.
- Remove former employees from all four systems as part of offboarding.
- Retain Zapier run history, PandaDoc audit evidence, HubSpot history, and calculation snapshots according to policy.
- Review whether proposal data contains personal, regulated, confidential, or export-controlled information before adding AI.
- Do not send unit costs or internal margin calculations to the customer document or an AI service.
- Require human approval for discount exceptions and template changes.
- Document the owner of each threshold and obtain finance authorization before changing it.
Sensitive margin and cost fields should not be inserted into PandaDoc variables visible to the recipient. They remain in the protected audit and restricted HubSpot properties.
Step 10: Deploy and Test
- Build the properties, workbook, templates, and Zaps in the test environment.
- Create at least 20 sample deals covering one, two, and three line items, every approval route, duplicate revisions, and invalid input.
- Use test recipient addresses controlled by the implementation team.
- Run unit tests for the calculation code using known prices and manually verified totals.
- Run integration tests from HubSpot request through PandaDoc completion.
- Have sales, finance, and sales operations perform user acceptance testing.
- Compare every generated proposal against its HubSpot fields and Google Sheets audit row.
- Pilot the process with two account executives for two weeks or a representative number of transactions.
- Keep the former process available as a documented rollback path, but require any fallback proposal to be recorded in HubSpot.
- Activate the Zaps in sequence: intake, document creation, status synchronization, then reminders.
- Monitor every run during the pilot and daily during the first production week.
- Publish a short operating guide covering submission, rejection, revision, error recovery, and superseding a document.
- Communicate the exact production start time so sales does not submit the same request through both processes.
Rollback means disabling the quote-request trigger, not deleting Zaps, fields, templates, or audit rows. Existing PandaDoc documents should continue through approval and signature even if new quote creation is paused.
Code and Configuration
The implementation uses native HubSpot, Google Sheets, Gmail, and PandaDoc events where suitable. A complete JavaScript step is used to validate pricing, calculate totals, select the approval template, and build the PandaDoc API payload. This avoids spreadsheet rounding differences and keeps commercial decisions deterministic.
Code by Zapier Pricing and Payload Step
Place the following code in a JavaScript Code by Zapier action after the catalog lookup actions. Code by Zapier supplies the runtime, so no external package import is required. Map every inputData value from HubSpot, Google Sheets, or Zap configuration.
/*
* Code by Zapier
* Validates quote inputs, calculates pricing, selects an approval route,
* and creates a PandaDoc document request body.
*
* No external dependencies are required.
*/
function requiredString(value, fieldName) {
const result = String(value || "").trim();
if (!result) {
throw new Error(`Missing required field: ${fieldName}`);
}
return result;
}
function optionalString(value) {
return String(value || "").trim();
}
function numberValue(value, fieldName, minimum) {
const parsed = Number(value);
if (!Number.isFinite(parsed)) {
throw new Error(`Invalid number for ${fieldName}`);
}
if (parsed < minimum) {
throw new Error(`${fieldName} must be at least ${minimum}`);
}
return parsed;
}
function roundCurrency(value) {
return Math.round((value + Number.EPSILON) * 100) / 100;
}
function roundRate(value) {
return Math.round((value + Number.EPSILON) * 10000) / 10000;
}
function booleanValue(value) {
const normalized = String(value || "").trim().toLowerCase();
return normalized === "true" || normalized === "yes" || normalized === "1";
}
function validEmail(value) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
function parseDate(value, fieldName) {
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
throw new Error(`Invalid date for ${fieldName}`);
}
return date;
}
function isoDate(value) {
return new Date(value).toISOString().slice(0, 10);
}
const quoteId = requiredString(inputData.quote_id, "quote_id");
const dealId = requiredString(inputData.deal_id, "deal_id");
const dealName = requiredString(inputData.deal_name, "deal_name");
const companyName = requiredString(inputData.company_name, "company_name");
const firstName = requiredString(
inputData.customer_first_name,
"customer_first_name"
);
const lastName = requiredString(
inputData.customer_last_name,
"customer_last_name"
);
const customerEmail = requiredString(
inputData.customer_email,
"customer_email"
);
const currency = requiredString(inputData.currency, "currency").toUpperCase();
const approvedNarrative = requiredString(
inputData.approved_scope_narrative,
"approved_scope_narrative"
);
const validUntil = parseDate(inputData.valid_until, "valid_until");
const calculationDate = parseDate(
inputData.calculation_date || new Date().toISOString(),
"calculation_date"
);
if (!validEmail(customerEmail)) {
throw new Error("customer_email does not have a valid format");
}
if (currency !== "USD") {
throw new Error(`Unsupported currency: ${currency}`);
}
if (validUntil.getTime() <= calculationDate.getTime()) {
throw new Error("valid_until must be later than the calculation date");
}
const enteredDiscount = numberValue(
inputData.requested_discount_pct,
"requested_discount_pct",
0
);
if (enteredDiscount > 30) {
throw new Error("requested_discount_pct cannot exceed 30");
}
const discountRate = enteredDiscount / 100;
const templateStandard = requiredString(
inputData.template_standard_uuid,
"template_standard_uuid"
);
const templateFinance = requiredString(
inputData.template_finance_uuid,
"template_finance_uuid"
);
const templateException = requiredString(
inputData.template_exception_uuid,
"template_exception_uuid"
);
const pricingTableName =
optionalString(inputData.pricing_table_name) || "Services";
const rawLines = [
{
sku: optionalString(inputData.sku_1),
quantity: inputData.quantity_1,
name: optionalString(inputData.item_name_1),
description: optionalString(inputData.description_1),
unitPrice: inputData.unit_price_1,
unitCost: inputData.unit_cost_1,
minimumQuantity: inputData.minimum_quantity_1,
maximumDiscount: inputData.maximum_discount_1,
active: inputData.active_1,
effectiveFrom: inputData.effective_from_1,
effectiveTo: inputData.effective_to_1
},
{
sku: optionalString(inputData.sku_2),
quantity: inputData.quantity_2,
name: optionalString(inputData.item_name_2),
description: optionalString(inputData.description_2),
unitPrice: inputData.unit_price_2,
unitCost: inputData.unit_cost_2,
minimumQuantity: inputData.minimum_quantity_2,
maximumDiscount: inputData.maximum_discount_2,
active: inputData.active_2,
effectiveFrom: inputData.effective_from_2,
effectiveTo: inputData.effective_to_2
},
{
sku: optionalString(inputData.sku_3),
quantity: inputData.quantity_3,
name: optionalString(inputData.item_name_3),
description: optionalString(inputData.description_3),
unitPrice: inputData.unit_price_3,
unitCost: inputData.unit_cost_3,
minimumQuantity: inputData.minimum_quantity_3,
maximumDiscount: inputData.maximum_discount_3,
active: inputData.active_3,
effectiveFrom: inputData.effective_from_3,
effectiveTo: inputData.effective_to_3
}
];
const populatedLines = rawLines.filter((line) => line.sku);
if (populatedLines.length === 0) {
throw new Error("At least one quote line is required");
}
const lines = populatedLines.map((line, index) => {
const lineNumber = index + 1;
const quantity = numberValue(
line.quantity,
`quantity_${lineNumber}`,
0.000001
);
const itemName = requiredString(
line.name,
`item_name_${lineNumber}`
);
const description = requiredString(
line.description,
`description_${lineNumber}`
);
const unitPrice = numberValue(
line.unitPrice,
`unit_price_${lineNumber}`,
0
);
const unitCost = numberValue(
line.unitCost,
`unit_cost_${lineNumber}`,
0
);
const minimumQuantity = numberValue(
line.minimumQuantity || 1,
`minimum_quantity_${lineNumber}`,
0.000001
);
const maximumDiscount = numberValue(
line.maximumDiscount,
`maximum_discount_${lineNumber}`,
0
);
if (maximumDiscount > 1) {
throw new Error(
`maximum_discount_${lineNumber} must be stored as a decimal`
);
}
if (!booleanValue(line.active)) {
throw new Error(`Inactive catalog SKU: ${line.sku}`);
}
if (quantity < minimumQuantity) {
throw new Error(
`Quantity for ${line.sku} is below the catalog minimum`
);
}
const effectiveFrom = parseDate(
line.effectiveFrom,
`effective_from_${lineNumber}`
);
if (calculationDate.getTime() < effectiveFrom.getTime()) {
throw new Error(`Catalog SKU is not yet effective: ${line.sku}`);
}
if (optionalString(line.effectiveTo)) {
const effectiveTo = parseDate(
line.effectiveTo,
`effective_to_${lineNumber}`
);
effectiveTo.setUTCHours(23, 59, 59, 999);
if (calculationDate.getTime() > effectiveTo.getTime()) {
throw new Error(`Catalog SKU has expired: ${line.sku}`);
}
}
const listTotal = roundCurrency(quantity * unitPrice);
const discountedUnitPrice = roundCurrency(
unitPrice * (1 - discountRate)
);
const netTotal = roundCurrency(quantity * discountedUnitPrice);
const estimatedCost = roundCurrency(quantity * unitCost);
return {
sku: line.sku,
name: itemName,
description,
quantity,
unitPrice,
unitCost,
maximumDiscount,
listTotal,
discountedUnitPrice,
netTotal,
estimatedCost
};
});
const listSubtotal = roundCurrency(
lines.reduce((sum, line) => sum + line.listTotal, 0)
);
const netTotal = roundCurrency(
lines.reduce((sum, line) => sum + line.netTotal, 0)
);
const estimatedCost = roundCurrency(
lines.reduce((sum, line) => sum + line.estimatedCost, 0)
);
if (listSubtotal <= 0 || netTotal <= 0) {
throw new Error("Calculated quote total must be greater than zero");
}
const grossMarginRate = roundRate(
(netTotal - estimatedCost) / netTotal
);
const lowestProductDiscount = Math.min(
...lines.map((line) => line.maximumDiscount)
);
let approvalRoute = "Auto";
let approvalStatus = "Not Required";
let templateUuid = templateStandard;
if (
discountRate > 0.20 ||
grossMarginRate < 0.25 ||
discountRate > lowestProductDiscount
) {
approvalRoute = "Finance + Sales Director";
approvalStatus = "Pending";
templateUuid = templateException;
} else if (discountRate > 0.10 || grossMarginRate < 0.35) {
approvalRoute = "Finance";
approvalStatus = "Pending";
templateUuid = templateFinance;
}
const pandaDocRows = lines.map((line) => ({
data: {
Name: line.name,
Description: line.description,
Price: line.discountedUnitPrice,
QTY: line.quantity,
SKU: line.sku
}
}));
const payload = {
name: `${quoteId} - ${companyName}`,
template_uuid: templateUuid,
recipients: [
{
email: customerEmail,
first_name: firstName,
last_name: lastName,
role: "Customer"
}
],
tokens: [
{ name: "Quote.ID", value: quoteId },
{ name: "Deal.Name", value: dealName },
{ name: "Customer.Company", value: companyName },
{
name: "Contract.Term",
value: requiredString(inputData.contract_term, "contract_term")
},
{
name: "Quote.ValidUntil",
value: isoDate(validUntil)
},
{
name: "Quote.ListSubtotal",
value: listSubtotal.toFixed(2)
},
{
name: "Quote.DiscountPercent",
value: enteredDiscount.toFixed(2)
},
{
name: "Quote.NetTotal",
value: netTotal.toFixed(2)
},
{
name: "Scope.ApprovedNarrative",
value: approvedNarrative
}
],
metadata: {
hubspot_deal_id: dealId,
quote_id: quoteId,
approval_route: approvalRoute
},
tags: ["hubspot-quote"],
pricing_tables: [
{
name: pricingTableName,
data_merge: true,
sections: [
{
title: "Services",
default: true,
rows: pandaDocRows
}
]
}
]
};
console.log(
JSON.stringify({
quoteId,
dealId,
lineCount: lines.length,
approvalRoute
})
);
return {
validation_status: "Valid",
quote_id: quoteId,
automation_key: `${dealId}:${inputData.quote_revision}`,
line_count: lines.length,
list_subtotal: listSubtotal.toFixed(2),
net_total: netTotal.toFixed(2),
estimated_cost: estimatedCost.toFixed(2),
gross_margin_pct: (grossMarginRate * 100).toFixed(2),
requested_discount_decimal: discountRate.toFixed(4),
lowest_product_discount_decimal:
lowestProductDiscount.toFixed(4),
approval_route: approvalRoute,
approval_status: approvalStatus,
selected_template_uuid: templateUuid,
request_body: JSON.stringify(payload),
line_snapshot_json: JSON.stringify(lines)
};
Replace these mapped configuration values:
YOUR_STANDARD_TEMPLATE_UUIDYOUR_FINANCE_TEMPLATE_UUIDYOUR_EXCEPTION_TEMPLATE_UUID- The actual pricing-table name if it is not
Services - The exact template role name if it is not
Customer
Test the code with one, two, and three populated product lines. Confirm the returned totals manually. Likely errors include a missing catalog lookup, maximum discounts stored as whole percentages instead of decimals, inactive products, invalid dates, and mismatched PandaDoc role or pricing-table names.
PandaDoc Document Creation Request
Add a Webhooks by Zapier custom request after the code step:
- Method: POST
- URL:
https://api.pandadoc.com/public/v1/documents - Authorization header:
API-Key YOUR_API_KEY - Content-Type header:
application/json - Body: Map the code output named
request_bodyas the raw JSON body.
{
"name": "Q-84621057-R02 - Example Customer",
"template_uuid": "YOUR_FINANCE_TEMPLATE_UUID",
"recipients": [
{
"email": "[email protected]",
"first_name": "Alex",
"last_name": "Morgan",
"role": "Customer"
}
],
"tokens": [
{
"name": "Quote.ID",
"value": "Q-84621057-R02"
}
],
"metadata": {
"hubspot_deal_id": "84621057",
"quote_id": "Q-84621057-R02",
"approval_route": "Finance"
},
"pricing_tables": [
{
"name": "Services",
"data_merge": true,
"sections": [
{
"title": "Services",
"default": true,
"rows": [
{
"data": {
"Name": "Managed Security Package",
"Description": "Approved service description",
"Price": 21120,
"QTY": 1,
"SKU": "MSS-50-12"
}
}
]
}
]
}
]
}
The expected response contains a PandaDoc document ID and processing status. Document generation is asynchronous, so the initial status is not treated as permission to send.
{
"id": "PDOC_EXAMPLE_7F31",
"status": "document.uploaded",
"name": "Q-84621057-R02 - Example Customer",
"date_created": "2026-07-15T14:20:00Z",
"date_modified": "2026-07-15T14:20:00Z"
}
The example identifier and timestamps are illustrative. Map the actual returned id immediately to HubSpot and Google Sheets. If the webhook step times out after PandaDoc created the document, search or reconcile by the stored quote metadata before retrying creation.
Send Request
When PandaDoc reports that the document is ready, send it using the PandaDoc connector action that accepts an existing document ID. If the current connector does not expose that operation, use the supported API send operation for the document ID. Test the operation with every template because the review templates should enter internal approval while the standard template should proceed to the customer.
Do not place a fixed delay in front of an unconditional send request and assume the document is ready. Use a ready-status event or a delayed status check with a retry queue.
Deployment and Troubleshooting
- Keep development template UUIDs separate from production UUIDs.
- Use Zapier test data that does not contain real customer information.
- Review Code by Zapier logs for validation exceptions.
- Review Webhooks by Zapier response status and response body for API errors.
- Confirm that the PandaDoc role and pricing-table names match exactly.
- Confirm that the API credential has the required document permissions.
- For rate limits or temporary server errors, retry the same document operation only after checking whether a document ID was returned or recorded.
- For authentication errors, pause affected Zaps until the credential is corrected.
Failure Handling and Operational Reliability
Reliability depended on idempotency, visible errors, and separation between creation retries and status retries. A quote was not considered successfully created until its PandaDoc ID had been written to both HubSpot and the audit sheet.
| Failure | Automated response | Manual recovery | Owner |
|---|---|---|---|
| Missing required field | Set status to Incomplete and record the field name. | Correct the deal and resubmit the same revision if no document exists. | Deal owner |
| Invalid SKU | Stop before calculation and mark Manual Review. | Correct the SKU or have finance add an approved catalog item. | Sales operations and finance |
| Duplicate catalog SKU | Stop rather than choosing a row. | Finance removes or dates the duplicate entry. | Finance |
| Duplicate HubSpot event | Find the existing automation key and ignore the event. | Confirm the existing document is correct. | Automation |
| Duplicate quote request | Stop if a PandaDoc ID exists for the same revision. | Increase the revision only when a new document is required. | Deal owner |
| Partial completion before document ID | Set Error and retain the calculation snapshot. | Retry document creation after checking PandaDoc by quote metadata. | Sales operations |
| Partial completion after document ID | Do not create another document; retry the failed record update. | Copy the existing ID into HubSpot if necessary, then resume status processing. | Sales operations |
| PandaDoc API rate limit | Queue a delayed retry with the same quote key. | Review repeated limits and reduce burst volume. | Automation owner |
| PandaDoc timeout | Mark the run uncertain and reconcile before retry. | Search for a document with the quote ID or metadata. | Automation owner |
| Authentication expiry | Pause processing and send an administrator alert. | Reconnect OAuth or rotate the API credential. | System administrator |
| Approver unavailable | Escalate to the configured backup. | Administrator reassigns the approval. | Finance manager |
| Document creation failure | Set Error and store the API response. | Correct template, role, variable, or pricing-table configuration and retry. | Automation owner |
| Invalid signer email | Stop before PandaDoc creation. | Correct the contact email and resubmit. | Deal owner |
| Notification failure | Record the failure without changing the commercial status. | Create a HubSpot task or contact the approver directly. | Sales operations |
| Unknown PandaDoc status | Store the raw status and leave the previous business status unchanged. | Review the event and extend the controlled mapping. | Automation owner |
| Failed audit-sheet write | Stop before document creation where possible. | Restore access and rerun the calculation. | Automation owner |
The Automation_Errors tab acted as a lightweight dead-letter queue. It contained the quote ID, deal ID, failed step, timestamp, retry count, error category, error detail, and recovery owner.
Automatic retries were limited to temporary failures such as rate limits and server errors. Validation failures, authentication failures, approval rejection, and unknown status values required human review.
A daily reconciliation Zap compared active HubSpot quote records with Google Sheets audit rows and PandaDoc IDs. Missing identifiers, conflicting final statuses, and records unchanged beyond their expected processing window were added to the manual-review queue.
A Complete Example
An account executive at Oakline Systems Advisory prepared revision 2 for HubSpot deal 84621057. The prospect was a 60-user engineering consultancy, and the designated test-style signer address was [email protected].
| Input | Value |
|---|---|
| Deal ID | 84621057 |
| Revision | 2 |
| Contract term | 12 months |
| Currency | USD |
| Requested discount | 12 percent |
| SKU 1 | MSS-50-12, quantity 1 |
| SKU 2 | ONB-STD, quantity 1 |
| SKU 3 | AWR-100, quantity 1 |
| Validity | 30 days after request |
| Request status | Requested |
Zapier generated the quote ID Q-84621057-R02 and the automation key 84621057:2. It found no audit row with an existing PandaDoc ID.
The Google Sheets catalog returned these representative values:
MSS-50-12: list price $24,000, estimated cost $14,000, maximum discount 15 percent.ONB-STD: list price $4,000, estimated cost $2,000, maximum discount 20 percent.AWR-100: list price $3,000, estimated cost $2,000, maximum discount 15 percent.
The calculation produced:
- List subtotal: $31,000
- Discount: 12 percent
- Net total: $27,280
- Estimated cost: $18,000
- Estimated gross margin: 34.02 percent
The requested discount exceeded 10 percent, and the margin was below 35 percent but above 25 percent. It did not exceed any product-level maximum. The rule therefore selected the Finance approval route and the finance-review PandaDoc template.
Zapier created a calculation snapshot, generated the PandaDoc proposal, and received the illustrative document ID PDOC_EXAMPLE_7F31. The ID was written to HubSpot and Google Sheets before any send attempt.
When the document became ready, Zapier sent it. Because the finance-review template contained an internal approval step, PandaDoc notified the finance approver rather than the customer.
Finance reviewed the exact document, pricing table, discount, and narrative, then approved it. PandaDoc proceeded to the customer signer. Zapier changed the HubSpot status from Awaiting Approval to Awaiting Signature.
The customer completed the signature. PandaDoc retained the signed proposal and audit evidence. The status synchronization Zap changed HubSpot to Accepted, stored the completion timestamp, and created a sales operations handoff task.
If finance had rejected the document, the status would have changed to Changes Required. The account executive would have corrected the inputs, increased the revision to 3, and generated a separate document without deleting revision 2.
Implementation Cost
All amounts below are representative planning assumptions, not verified client costs or current vendor prices. Vendor subscriptions, API access, usage limits, taxes, currencies, and implementation rates must be confirmed for each business.
| Activity | Hours | Assumed rate | Estimated cost |
|---|---|---|---|
| Requirements and process design | 8 | $75 per hour | $600 |
| HubSpot fields, Google Sheets catalog, and PandaDoc templates | 36 | $75 per hour | $2,700 |
| Zapier automation and integration configuration | 28 | $75 per hour | $2,100 |
| Testing and user acceptance support | 16 | $75 per hour | $1,200 |
| Training | 6 | $75 per hour | $450 |
| Documentation | 6 | $75 per hour | $450 |
| Total internal implementation | 100 | $7,500 |
| Item | Assumption | Monthly allowance |
|---|---|---|
| HubSpot | Existing subscription; no incremental allowance in this model | $0 incremental |
| Google Workspace | Existing subscription; no incremental allowance in this model | $0 incremental |
| PandaDoc | Budget allowance for required users and workflow features | $300 |
| Zapier | Budget allowance for required tasks, Paths, Code, and Webhooks | $100 |
| Core AI usage | No AI in the core workflow | $0 |
| Internal maintenance labor | 4 hours at $55 per hour | $220 |
An optional professional implementation allowance could range from $12,000 to $20,000 for discovery, configuration, integration, testing, training, and deployment support. That range is a representative planning assumption and is not included in the modeled payback calculation.
Estimated Time and Cost Savings
The savings model used these representative assumptions:
| Assumption | Value |
|---|---|
| Monthly quote volume | 55 |
| Current handling time | 55 minutes per quote |
| New routine handling time | 14 minutes per quote |
| Exception rate | 12 percent |
| Exception review time | 20 minutes |
| Monthly maintenance | 4 hours |
| Loaded labor cost | $42 per hour |
| Recurring software allowance | $400 per month |
| One-time implementation cost | $7,500 |
Current monthly labour hours: Monthly volume × current minutes per record ÷ 60
New monthly labour hours: Monthly volume × new minutes per record ÷ 60, plus exception handling and maintenance
Monthly hours recovered: Current monthly labour hours minus new monthly labour hours
Estimated monthly labour value: Monthly hours recovered × loaded hourly labour cost
Net estimated monthly value: Monthly labour value minus recurring tool costs
Estimated payback period: One-time implementation cost ÷ net estimated monthly value
Actual Calculation
Current monthly labour hours: 55 × 55 ÷ 60 = 50.42 hours
New routine labour: 55 × 14 ÷ 60 = 12.83 hours
Exception labour: 55 × 12% × 20 ÷ 60 = 2.20 hours
New monthly labour hours: 12.83 + 2.20 + 4.00 = 19.03 hours
Monthly hours recovered: 50.42 − 19.03 = 31.39 hours
Estimated monthly labour value: 31.39 × $42 = $1,318.38
Net estimated monthly value: $1,318.38 − $400 = $918.38
Estimated payback period: $7,500 ÷ $918.38 = 8.17 months
Recovered time does not automatically reduce payroll. It may create additional sales capacity, quicker response, less overtime, fewer administrative tasks, and lower dependence on the sales operations coordinator.
Non-financial benefits include clearer ownership, fewer incomplete requests, consistent pricing, documented approvals, better reporting, easier reconciliation, and a more consistent customer signing experience.
Readers should replace the quote volume, handling time, exception rate, labor cost, subscription allowance, implementation cost, and maintenance requirement with their own figures.
Adding AI to the Automation
AI should be added only after the pricing, approval, document, and status workflow operates reliably. The quote process contains several tasks that are better handled without AI:
- Required-field validation
- SKU matching
- Price lookup
- Discount calculation
- Margin calculation
- Approval thresholds
- Recipient permissions
- Final approval and signature
Potential AI applications include drafting an executive summary, reorganizing approved scope notes, identifying missing narrative information, classifying customer objectives, and summarizing an approved discovery document.
The core automation already removes copying, applies pricing rules, selects approval paths, generates documents, and synchronizes statuses. AI does not create those benefits. Its additional role is limited to preparing narrative text from approved facts.
The Recommended AI Enhancement
The recommended enhancement drafts the executive summary and scope narrative after required deal and product data has been collected. The output is stored in a separate HubSpot property named ai_draft_narrative. It is never inserted directly into PandaDoc.
- Trigger: Sales changes
narrative_draft_statusto Requested. - AI input: Approved customer objectives, selected service descriptions, contract term, scope notes, and explicit exclusions.
- System instruction: Draft only from supplied facts and return structured JSON.
- Record update: Save the draft, missing-information list, and confidence indicator to restricted HubSpot properties.
- Human review: The deal owner reviews, edits, and copies accepted text into
approved_scope_narrative. - Low confidence: Route to manual drafting when confidence is below 0.75 or missing information is material.
- Prohibited data: Unit costs, margins, credentials, regulated personal data, security secrets, and unrelated customer records.
- Logging: Record model, prompt version, timestamp, requester, result status, and human disposition.
- Failure behavior: Leave the approved narrative unchanged and assign manual drafting.
Reusable System Instruction
You draft business proposal narrative using only the approved facts supplied by the user.
Do not calculate, alter, recommend, or mention prices, discounts, costs, margins, approval status, legal conclusions, or contract commitments.
Do not invent capabilities, customer facts, outcomes, timelines, integrations, certifications, staffing, service levels, or guarantees.
If required information is absent, list it in missing_information rather than guessing.
Use clear business language. Avoid exaggerated claims. Return only JSON matching the supplied schema.
Reusable User Prompt
Draft an executive summary and scope narrative for a customer proposal.
Approved customer objectives:
{{approved_customer_objectives}}
Selected services:
{{approved_service_descriptions}}
Contract term:
{{contract_term_months}} months
Approved scope notes:
{{approved_scope_notes}}
Explicit exclusions:
{{approved_exclusions}}
Required output:
1. executive_summary of 90 to 140 words
2. scope_narrative of 150 to 250 words
3. assumptions as a short array
4. missing_information as an array
5. confidence from 0 to 1
6. evidence_refs identifying which supplied section supports each material statement
Do not include pricing, discounts, costs, margins, approval commentary, legal advice, or unsupported claims.
Structured Output Schema
{
"type": "object",
"additionalProperties": false,
"required": [
"executive_summary",
"scope_narrative",
"assumptions",
"missing_information",
"confidence",
"evidence_refs"
],
"properties": {
"executive_summary": {
"type": "string"
},
"scope_narrative": {
"type": "string"
},
"assumptions": {
"type": "array",
"items": {
"type": "string"
}
},
"missing_information": {
"type": "array",
"items": {
"type": "string"
}
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"evidence_refs": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
Zapier should validate that all required keys are present, confidence is numeric, and both narrative fields remain within configured length limits. Malformed output is sent to manual review. The original approved notes remain unchanged.
Benefits of the AI Enhancement
- Less time spent converting structured discovery notes into first-draft prose.
- More consistent organization across executive summaries.
- Earlier identification of missing narrative information.
- Faster preparation of a draft for human review.
- Better separation between source facts and unsupported assumptions.
These benefits are specific to unstructured narrative. AI does not improve the accuracy of the price lookup, calculation, threshold, approval, signature, or status synchronization because those tasks are already controlled by deterministic rules.
What Remains Rule-Based or Human-Controlled
- Price lookup: Uses the finance-controlled catalog because exact values are required.
- Discount and margin calculations: Use deterministic code so the same inputs always produce the same result.
- Approval routing: Uses approved thresholds rather than model interpretation.
- Finance approval: Remains human-controlled because it authorizes a commercial exception.
- Sales director approval: Remains human-controlled for high discounts and low margins.
- Final narrative approval: Sales reviews every AI draft for accuracy, commitments, and customer context.
- Contract and legal language: Comes from controlled templates and authorized reviewers.
- Customer signature: Remains an explicit action by the designated recipient.
- Final risk acceptance: Cannot be delegated to the AI system.
Estimating the Additional Value of AI
The AI estimate assumes that 70 percent of the 55 monthly proposals require a customized narrative. That equals 38.5 applicable proposals per month.
| Measure | Assumption |
|---|---|
| Applicable proposals | 38.5 per month |
| Manual narrative time without AI | 8 minutes each |
| Initial review time with AI | 3 minutes each |
| Correction rate | 20 percent |
| Additional correction time | 4 minutes |
| AI failure rate | 3 percent |
| Manual fallback time | 8 minutes |
| AI usage allowance | $25 per month |
Without AI: 38.5 × 8 ÷ 60 = 5.13 hours
AI review: 38.5 × 3 ÷ 60 = 1.93 hours
Corrections: 38.5 × 20% × 4 ÷ 60 = 0.51 hours
Failure fallback: 38.5 × 3% × 8 ÷ 60 = 0.15 hours
Total with AI: 1.93 + 0.51 + 0.15 = 2.59 hours
Additional monthly capacity: 5.13 − 2.59 = 2.54 hours
Additional labor value: 2.54 × $42 = $106.68
Net additional modeled value: $106.68 − $25 = $81.68 per month
This estimate does not assume that AI eliminates review or errors. A business should measure actual acceptance, correction, and failure rates during a controlled pilot.
Testing Checklist
Use sample data and controlled email addresses before processing real customer information.
| Test | Expected result |
|---|---|
| Normal standard submission | Document is created and proceeds to customer signature without internal approval. |
| Finance-route submission | Document waits for finance approval before customer delivery. |
| Exception-route submission | Finance and sales director approve sequentially. |
| Missing required field | Request becomes Incomplete and identifies the missing field. |
| Invalid quantity | Automation stops before document creation. |
| Invalid SKU | Record enters Manual Review. |
| Inactive or expired SKU | Calculation is rejected. |
| Duplicate submission | No second PandaDoc document is created for the same revision. |
| Duplicate trigger event | Existing automation key is recognized and ignored. |
| Failed authentication | Run fails visibly and an administrator is alerted. |
| Expired credential | New processing is paused until reconnection. |
| Failed API request | Error detail and response status are recorded. |
| API timeout after creation | Reconciliation occurs before any creation retry. |
| Unavailable approver | Backup or reassignment procedure is used. |
| Approval rejection | HubSpot changes to Changes Required. |
| Approver reassignment | Approval evidence remains intact. |
| Overdue approval | Reminder and escalation occur at the configured times. |
| Overdue signature | Recipient reminder and sales task are created. |
| Failed document creation | No document ID is recorded and the quote enters Error. |
| Failed file or final-document access | Completion is not treated as fully reconciled until access is restored. |
| Failed notification | Commercial status remains correct and a backup task is created. |
| Unauthorized user | User cannot edit protected prices, costs, templates, or approval settings. |
| Malformed AI output | Draft is rejected and manual review is assigned. |
| Inaccurate AI output | Human reviewer rejects or edits it before approval. |
| AI service failure | Core quote workflow remains available with manual narrative drafting. |
| Successful completion | HubSpot becomes Accepted and references the completed PandaDoc document. |
| Correct reporting | Views and dashboards reflect the latest workflow state. |
| Correct audit record | Inputs, calculations, IDs, statuses, and timestamps are complete. |
| Correct retry behavior | Temporary failures retry without creating duplicate documents. |
Ongoing Maintenance
The sales operations coordinator was assigned as the primary process owner. The automation administrator was the backup owner, and finance owned catalog and threshold changes.
| Frequency | Task | Owner |
|---|---|---|
| Daily | Review failed Zap runs and records unchanged beyond expected processing windows. | Sales operations |
| Weekly | Reconcile active HubSpot quotes against PandaDoc IDs and statuses. | Sales operations |
| Weekly | Review manual exceptions, rejected approvals, and repeated validation failures. | Sales operations and finance |
| Monthly | Review Zapier task consumption, PandaDoc usage, and optional AI cost. | Automation owner |
| Monthly | Sample completed documents against their calculation snapshots. | Finance |
| Quarterly | Review user permissions, shared links, approver assignments, and former-user removal. | System administrator |
| Quarterly | Test credentials, status mappings, API requests, and reminder paths. | Automation owner |
| Quarterly | Review catalog effective dates and retire obsolete SKUs. | Finance |
| Every six months | Run the full testing checklist using sample data. | Process owners |
| After any change | Update configuration documentation, field mappings, and rollback instructions. | Change owner |
Credential rotation should be planned so that the replacement credential is tested before the old one is disabled. Template changes require regression testing because deleted variables, renamed roles, or renamed pricing tables can break document creation.
If AI is enabled, the process owner should sample accepted and rejected drafts, monitor missing-information behavior, review prompt changes, and confirm that prohibited data is not included.
When to Move to Dedicated Software
The implementation is appropriate for a controlled catalog, up to three standard line items, moderate monthly volume, and relatively simple discount routing. It should not be replaced only because a dedicated platform exists.
Signs that a configure-price-quote, contract lifecycle management, or dedicated revenue platform should be evaluated include:
- Quotes regularly require more than three line items or complex product bundles.
- Pricing depends on usage tiers, geographic rules, channel agreements, or multiple currencies.
- Sales needs guided configuration with compatibility and dependency rules.
- Formal regulatory controls require stronger separation of duties or immutable database audit logs.
- Multiple subsidiaries need distinct catalogs, taxes, currencies, and legal templates.
- Approval paths include many parallel or conditional reviewers.
- Spreadsheet catalog performance or governance becomes unreliable.
- Exception rates continue increasing.
- Automation maintenance requires excessive specialist time.
- Customers need a portal for quote configuration, negotiation, or document history.
- Offline or advanced mobile quoting is required.
- Revenue recognition, billing, provisioning, or renewal workflows need tightly controlled integration.
- Formal vendor support and service commitments become mandatory.
- Security risk exceeds what can be managed through the connected-tool architecture.
A dedicated platform should be evaluated against the actual process, volume, controls, and integration requirements. The existing workflow can remain suitable when it is monitored, documented, and kept within its intended operating boundaries.
Implementation Checklist
- Document quote requirements, approval thresholds, and exception rules.
- Confirm HubSpot, Google Sheets, Zapier, and PandaDoc capabilities.
- Confirm required subscriptions and API access.
- Assign business, technical, finance, and backup owners.
- Create test users and a test environment.
- Create HubSpot quote properties and status values.
- Build and protect the Google Sheets product catalog.
- Create the immutable quote audit and error queue.
- Build the HubSpot intake fields and validation rules.
- Create the standard, finance, and exception PandaDoc templates.
- Confirm recipient roles, variables, pricing-table names, and approval workflows.
- Configure tool connections and least-privilege permissions.
- Document every field mapping and percentage conversion.
- Build duplicate checks and the quote revision rule.
- Install and test the pricing and payload code.
- Configure document creation, readiness checking, and sending.
- Configure finance and sales director approvals.
- Configure reminders, escalations, rejection, and reassignment.
- Configure PandaDoc-to-HubSpot status synchronization.
- Configure final document references and retention controls.
- Create operational views, dashboards, and reconciliation reports.
- Protect costs, margins, credentials, templates, and signed documents.
- Test normal, invalid, duplicate, rejected, failed, and completed paths.
- Run user acceptance testing with sales and finance.
- Complete pilot deployment and rollback documentation.
- Validate representative software, labor, and maintenance costs.
- Replace savings assumptions with actual volume and time measurements.
- Add AI only after the core workflow is reliable.
- Require human approval for every AI-generated narrative.
- Assign ongoing maintenance and failed-run review owners.
- Define the volume, complexity, security, and governance criteria for evaluating dedicated software.
Department/Function: Finance & AccountingSales & Business Development
Get a FREE
Proof of Concept
& Consultation
No Cost, No Commitment!


