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:

  1. An account executive moved a HubSpot deal to a proposal-related stage.
  2. The account executive opened a shared Google Sheets price list and looked up service prices.
  3. Quantities, term adjustments, costs, discounts, and margins were copied into a separate working spreadsheet.
  4. Customer and commercial information was copied from HubSpot into a Word proposal.
  5. The account executive edited scope and executive-summary text, often starting from an older proposal.
  6. If the discount appeared unusual, the Word file and pricing spreadsheet were emailed to finance.
  7. Finance checked the calculation, replied with approval or requested changes, and sometimes received a second version containing different values.
  8. The account executive converted the approved Word file to PDF and emailed it to the buyer.
  9. After acceptance, the account executive uploaded the final file to HubSpot and updated the deal stage.
  10. 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.

Business and technical requirements
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

Implementation approaches considered by Oakline Systems Advisory
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.

Selected tools and responsibilities
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.

  1. Request submission: A sales representative completes the HubSpot quote fields and changes quote_request_status to Requested. The deal ID and revision form the idempotency key.
  2. 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.
  3. 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.
  4. Pricing lookup: Zapier looks up each requested SKU in the protected Google Sheets catalog. It validates active dates, quantities, currency, and product availability.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. Status synchronization: PandaDoc status events update HubSpot and the audit sheet. Rejected documents become Changes Required. Completed documents become Accepted.
  10. Failure path: Validation, API, authentication, or notification failures set quote_automation_status to Error, 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

Core HubSpot quote 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 Email Yes HubSpot owner mapping Receives operational updates.
customer_signer_email 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.

Google Sheets catalog fields
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.

Important quote audit fields
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

Quote workflow statuses
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

  1. Confirm that HubSpot supports the required custom deal properties, owner access, saved views, and Zapier connection.
  2. 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.
  3. 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.
  4. Create a Zapier workspace folder named Quote Automation. Limit editor access to sales operations and the automation administrator.
  5. 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.
  6. Create three PandaDoc templates: standard, finance review, and exception review. Record their template UUIDs using placeholders during development.
  7. 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.
  8. 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.
  9. Create test identities for a sales representative, finance approver, sales director, and customer signer. Use controlled test email addresses.
  10. 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.

Sales intake fields and validation
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: 1
  • quote_request_status: Draft
  • discount_approval_status: Pending
  • automation_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.

Integration connections and mappings
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.ID
  • Deal.Name
  • Customer.Company
  • Contract.Term
  • Quote.ValidUntil
  • Quote.ListSubtotal
  • Quote.DiscountPercent
  • Quote.NetTotal
  • Scope.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_status equals 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:

  1. Receive the HubSpot deal update.
  2. Filter out records whose request status is not Requested.
  3. Create automation_key as the deal ID followed by the quote revision.
  4. Search Quote_Audit for that key.
  5. If a row with a PandaDoc document ID exists, stop and mark the duplicate event as ignored.
  6. Update the HubSpot request status to Processing.
  7. Validate customer, signer, term, currency, discount, validity date, first SKU, and first quantity.
  8. Count populated SKU fields and route to a one-line, two-line, or three-line Zapier Path.
  9. Look up only the populated SKUs in the Catalog sheet.
  10. Validate each catalog row, including active and effective dates.
  11. Pass the request and catalog data to the calculation code.
  12. Append the returned values to Quote_Audit.
  13. Update the HubSpot calculated fields.
  14. 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.

Normalized PandaDoc status mapping
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:

  1. PandaDoc sends the initial approval request.
  2. A scheduled Zap identifies records still awaiting approval after one business day and sends a reminder to the current approver.
  3. After two business days, Zapier notifies the backup approver and sales operations.
  4. 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

  1. Build the properties, workbook, templates, and Zaps in the test environment.
  2. Create at least 20 sample deals covering one, two, and three line items, every approval route, duplicate revisions, and invalid input.
  3. Use test recipient addresses controlled by the implementation team.
  4. Run unit tests for the calculation code using known prices and manually verified totals.
  5. Run integration tests from HubSpot request through PandaDoc completion.
  6. Have sales, finance, and sales operations perform user acceptance testing.
  7. Compare every generated proposal against its HubSpot fields and Google Sheets audit row.
  8. Pilot the process with two account executives for two weeks or a representative number of transactions.
  9. Keep the former process available as a documented rollback path, but require any fallback proposal to be recorded in HubSpot.
  10. Activate the Zaps in sequence: intake, document creation, status synchronization, then reminders.
  11. Monitor every run during the pilot and daily during the first production week.
  12. Publish a short operating guide covering submission, rejection, revision, error recovery, and superseding a document.
  13. 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_UUID
  • YOUR_FINANCE_TEMPLATE_UUID
  • YOUR_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_body as 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 handling and recovery
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].

Example quote input
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.

Representative one-time implementation cost
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
Representative recurring monthly allowances
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:

Time and value 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 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_status to 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.

Representative AI value assumptions
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.

End-to-end testing checklist
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.

Maintenance schedule
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.

You need a similar solution?

Get a FREE
Proof of Concept
& Consultation

No Cost, No Commitment!