Short answer: To automate a proposal from Google Sheets, store each proposal in one row, map its columns to placeholders in a Google Docs or Slides template, and use Google Apps Script or a dedicated publishing tool to create the document. Review the result, apply appropriate sharing permissions, and record the final URL and status in the sheet.
Plan the Google Sheets Proposal Workflow
Treat Google Sheets as the structured data source, not the client deliverable. The sheet can hold calculations, approvals, internal notes, and records for multiple clients, while the generated Docs or Slides file provides a controlled presentation.
Choose Docs for narrative proposals with substantial text, flowing pages, and tables. Choose Slides when fixed positioning, large images, or page-by-page visual design matters. Then define the stages: data entry, approval, generation, review, sharing, sending, and follow-up. Require essential values such as proposal ID, client name, approved scope, total price, expiry date, and owner before generation.
Structure Proposal Data in Google Sheets
Use one row per proposal and one stable heading per field. Keep numbers, dates, and text in separate columns when they require different formatting.
| Group | Example columns |
|---|---|
| Client |
client_name, contact_name, contact_email
|
| Proposal |
proposal_id, proposal_title, proposal_date, expiry_date
|
| Scope |
project_summary, project_scope, deliverables
|
| Pricing |
subtotal, discount, tax, total_price, currency
|
| Terms |
payment_terms, assumptions
|
| Operations |
owner, review_status, generated_at, output_url
|
A sample row might contain:
| proposal_id | client_name | proposal_title | project_scope | total_price | expiry_date | output_url |
|---|---|---|---|---|---|---|
| P-1042 | Northwind Studio | Website Redesign | Discovery, design, and implementation | $18,500.00 | 15 March 2026 | blank until generated |
Use data validation for controlled fields such as status, currency, owner, and template version. Keep quantity, unit price, discount, tax, and total separate so calculations remain auditable.
For a fixed number of services, columns such as service_1_name and service_1_price can work. For variable-length pricing, create a second sheet with one line item per row and a proposal_id linking it to the main record.
Map Sheet Fields to the Proposal Template
Create a Docs or Slides master template and add distinctive placeholders:
{{client_name}}{{proposal_title}}{{project_scope}}{{total_price}}{{payment_terms}}{{expiry_date}}
Keep fixed introductions, headings, brand elements, and disclaimers in the template. Keep client-specific values in Sheets. A configuration tab can document the mapping:
| Sheet column | Template placeholder | Format | Required |
|---|---|---|---|
| client_name | {{client_name}} |
Text | Yes |
| project_scope | {{project_scope}} |
Multiline text | Yes |
| total_price | {{total_price}} |
Currency | Yes |
| expiry_date | {{expiry_date}} |
Display date | Yes |
Simple replacement has limits. It does not automatically create repeating line-item rows or remove an entire conditional section cleanly. Multiline text may need paragraph handling, and raw date values can expose timestamps unless formatted first. Docs supports flowing text and document tables; Slides uses independently positioned page elements, so table insertion, overflow handling, and slide duplication require different APIs and logic.
Choose a Google Workspace Generation Method
Google Apps Script is suitable when the mapping is predictable and someone can maintain the code, permissions, and template changes. Choose a dedicated publishing layer when client-facing links, controlled presentation, reusable publishing workflows, or verified engagement signals matter. Evaluate tools by supported source formats, permission model, output experience, maintenance burden, view tracking, and whether their documented capabilities match your workflow. This guide to automated proposal software provides additional selection context.
Google Docs template
The following runnable example copies a Docs template, replaces placeholders using the selected sheet row, saves the file in a Drive folder, applies link-based view-only sharing, and writes its URL back to output_url.
Set up the workflow as follows:
- Create a Docs template containing the placeholders shown above.
- Copy the template ID from the URL between
/d/and/edit. - Create the destination Drive folder and copy its ID from the folder URL.
- In Sheets, add headers matching the script’s column names, including
output_urlandgenerated_at. - Open Extensions > Apps Script, paste the code, and replace both ID values.
- Confirm the
PLACEHOLDERSmapping, save, and select a populated proposal row. - Run
generateProposal. Approve the requested Sheets, Docs, and Drive permissions. - Inspect the generated file and test its view-only URL in a private browser session.
const TEMPLATE_ID = 'PASTE_TEMPLATE_ID';
const FOLDER_ID = 'PASTE_FOLDER_ID';
const PLACEHOLDERS = {
client_name: '{{client_name}}',
proposal_title: '{{proposal_title}}',
project_scope: '{{project_scope}}',
total_price: '{{total_price}}',
payment_terms: '{{payment_terms}}',
expiry_date: '{{expiry_date}}'
};
function generateProposal() {
const sheet = SpreadsheetApp.getActiveSheet();
const row = sheet.getActiveRange().getRow();
if (row < 2) throw new Error('Select a proposal data row.');
const lastColumn = sheet.getLastColumn();
const headers = sheet.getRange(1, 1, 1, lastColumn).getValues()[0];
const values = sheet.getRange(row, 1, 1, lastColumn).getDisplayValues()[0];
const data = Object.fromEntries(headers.map((header, i) => [header, values[i]]));
['proposal_id', 'client_name', 'proposal_title'].forEach(field => {
if (!data[field]) throw new Error('Missing required field: ' + field);
});
const folder = DriveApp.getFolderById(FOLDER_ID);
const name = `${data.proposal_id} - ${data.client_name} - ${data.proposal_title}`;
const file = DriveApp.getFileById(TEMPLATE_ID).makeCopy(name, folder);
const document = DocumentApp.openById(file.getId());
const body = document.getBody();
Object.entries(PLACEHOLDERS).forEach(([column, placeholder]) => {
body.replaceText(escapeRegex_(placeholder), data[column] || '');
});
document.saveAndClose();
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
writeValue_(sheet, row, headers, 'output_url', file.getUrl());
writeValue_(sheet, row, headers, 'generated_at', new Date());
}
function escapeRegex_(text) {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function writeValue_(sheet, row, headers, heading, value) {
const column = headers.indexOf(heading) + 1;
if (!column) throw new Error('Missing column: ' + heading);
sheet.getRange(row, column).setValue(value);
}
Using getDisplayValues() preserves the displayed currency and date formatting from Sheets. It also returns multiline cell content, although complex paragraph styling will not carry into Docs. The sample output would be named P-1042 - Northwind Studio - Website Redesign, contain the selected row’s formatted values, and place its Drive URL in output_url.
Google Slides template
Slides is appropriate for tightly controlled visual layouts, but the example above uses DocumentApp and cannot generate Slides unchanged. A Slides implementation must open the copied presentation with SlidesApp, replace text in page elements, and explicitly handle overflowing text, duplicated slides, tables, and images.
Apps Script or a compatible platform
Custom code offers control but creates maintenance responsibilities. A suitable platform should clearly support your existing source files, client-facing publication, required permission controls, and any engagement reporting you intend to use. Do not assume support for signatures, payments, CRM connections, or automated follow-up unless the provider verifies those features.
Test the Generation Process
Test with fictional data and a duplicate template. Confirm that placeholders disappear, totals match the approved row, dates and currencies are formatted correctly, multiline scope remains readable, and the output reaches the intended folder. Also test blank optional values, special characters, unusually long text, and duplicate client names.
Require a human review of scope, deliverables, pricing, taxes, terms, dates, branding, links, and permissions. Automation transfers data; it does not validate the commercial decision behind that data.
Publish the Proposal as a Client-Facing Link
Share the generated file rather than the working spreadsheet. Apply view-only access and test the link while signed out. Google Workspace administrator policies may prohibit public or link-based sharing, so ANYONE_WITH_LINK can fail or remain unavailable. In that case, share the file directly with the client’s Google account or use an approved publishing method.
Record the exact final URL, generation time, and template version in the proposal row. Link creation and email delivery do not prove that the recipient opened the proposal.
Reuse the Workflow for Future Clients
Protect master templates and configuration tabs, and assign stable versions such as Consulting Proposal v3. For each client, complete the row, secure approval, select the correct template, generate the file, review it, apply sharing, and record follow-up dates. Turn recurring exceptions into approved fields or template variants rather than repeatedly editing generated files by hand.
Track Proposal Status, Views, and Engagement
Sheets can track owner, status, sent date, next action, decision, and notes. Update those fields manually unless a verified automation supplies the event. Standard Drive links do not necessarily report client views. If engagement matters, evaluate a publishing product that explicitly provides it; this overview of proposal software with analytics explains the requirement. Verified signals can guide a human response using an appropriate follow-up email example.
Use Templify With a Google Sheets Proposal Workflow
A dedicated layer becomes useful when a team wants to keep Workspace files as proposal sources while publishing client-ready links and observing proposal views. Templify uses existing Google Docs, Slides, and Sheets as sources, publishes client-facing proposal links, and tracks views and engagement that can inform manual follow-up timing. See the product page for details.
Troubleshoot Common Problems
- Unreplaced placeholders: Check spelling, braces, column headings, and whether the placeholder was split across differently formatted text runs.
- Authorization failure: Run the function from Apps Script, approve the requested scopes, and ask an administrator whether Drive or external-sharing policies block it.
- Inaccessible output link: Test while signed out and verify the file’s sharing setting. Organization policies may require direct recipient access.
- Incorrect dates or numbers: Format the source cells deliberately; the sample uses displayed values rather than raw serial values.
-
Duplicated files: Each run creates a copy. Check
output_urlbefore rerunning, or add a status guard if accidental reruns are common.
Frequently Asked Questions
Can Google Sheets generate a proposal automatically?
Yes. Apps Script can read a selected row, copy a template, replace placeholders, save the output, and return its URL. Review the file before sharing it.
Should I use a Google Docs or Google Slides template?
Use Docs for flowing text and tables. Use Slides for fixed visual layouts, while allowing for extra overflow and page-element logic.
How should multiple services or pricing items be stored?
Fixed items can use separate columns. Variable lists are better stored on a line-items sheet with one row per item and a shared proposal ID.
How can I share the proposal without exposing the spreadsheet?
Share only the generated file with view-only permission or an approved publishing link. Test access independently and follow your administrator’s sharing restrictions.
Can I track whether a client viewed the proposal?
Not from link creation alone. View and engagement tracking requires a publishing product that explicitly provides those signals.