Sheet rows to PDFs, in one script.
A short Apps Script turns any row into a PDF in Drive and writes the link back to the sheet — triggered from a custom menu or a form submission.
Prerequisites
- An ASHDOCS account — the free tier includes 100 monthly credits, no card required.
- An API key from the dashboard (Keys page). Sandbox keys (prefix ash_test_) run everything without consuming credits.
- Edit access to the spreadsheet (Extensions → Apps Script must be available).
Test the key before building anything:
curl https://www.ashdocs.com/api/v1/me -H "X-API-Key: ash_live_..."
Setup, step by step
- 1
Open the script editor
In your spreadsheet: Extensions → Apps Script. A bound project opens — no add-on, no deployment.
- 2
Paste the function
The API returns JSON with a signed URL; a second UrlFetchApp call downloads the PDF and files it in Drive.
var ASHDOCS_API = "https://www.ashdocs.com"; function generatePdfFromActiveRow() { var sheet = SpreadsheetApp.getActiveSheet(); var row = sheet.getActiveRange().getRow(); var values = sheet.getRange(row, 1, 1, 3).getValues()[0]; var invoiceNo = values[0], client = values[1], amount = values[2]; var res = UrlFetchApp.fetch(ASHDOCS_API + "/api/v1/tools/html-to-pdf", { method: "post", contentType: "application/json", headers: { "X-API-Key": "ash_live_..." }, payload: JSON.stringify({ html: "<h1>Invoice " + invoiceNo + "</h1><p>" + client + " — $" + amount + "</p>", options: { format: "A4", output: "url" } }), muteHttpExceptions: true }); if (res.getResponseCode() !== 200) throw new Error(res.getContentText()); var fileUrl = JSON.parse(res.getContentText()).file_url; var blob = UrlFetchApp.fetch(fileUrl).getBlob().setName("invoice-" + invoiceNo + ".pdf"); var file = DriveApp.createFile(blob); sheet.getRange(row, 4).setValue(file.getUrl()); } - 3
Authorize and run
Run the function once from the editor — approve the Drive and external-request scopes when prompted. Then wire it to a custom menu (onOpen) or an onFormSubmit trigger so non-technical teammates can use it.
Files in
Sheets holds values, not files — but any URL sitting in a cell (a Drive share link, a hosted upload) can be sent as “file_url” in the JSON payload to any file tool.
Files out
Request "output": "url", then UrlFetchApp.fetch(file_url).getBlob() downloads the PDF and DriveApp.createFile saves it. One line on quotas: UrlFetchApp has daily limits per Google account — high-volume runs belong on Make or n8n instead.
Prefer no code at all? The Make and Zapier pages cover the same “new Sheets row → PDF → Drive” flow with zero scripting.
Three ready-made recipes
Form submission → certificate PDF → Drive + link in row
- Attach an onFormSubmit trigger to the function (Triggers → Add Trigger).
- Build the HTML from the submitted row's values.
- The PDF lands in Drive and column D receives the link — automatically on every submission.
Custom menu: “Generate invoice” for the selected row
- Add an onOpen() that creates a menu: SpreadsheetApp.getUi().createMenu("ASHDOCS").addItem("Generate invoice", "generatePdfFromActiveRow").addToUi().
- Anyone with edit access selects a row and clicks the menu item — no script knowledge needed.
No-code alternative via Make or Zapierasync / webhook
- Trigger: new Sheets row (Make “Watch Rows” or Zapier “New Spreadsheet Row”).
- Call html-to-pdf as documented on the Make or Zapier page.
- Upload the result to Drive — same outcome, zero script, and the async webhook pattern is available there for slow jobs.
Troubleshooting
| Symptom | Fix |
|---|---|
| The authorization prompt loops | Approve both requested scopes — Drive access and external requests. Rejecting either makes Google re-prompt on the next run. |
| Blank PDF | Log the HTML string you built from the row values — an off-by-one column range produces empty variables and an empty-looking document. |
| “Service invoked too many times” | Google's daily UrlFetchApp quota is exhausted for the account. Batch less aggressively, or move volume runs to Make or n8n. |
| 401 Unauthorized | Check for trailing whitespace in the pasted key — Sheets cells and clipboards love adding it. |
Frequently asked questions
Do I need to install an add-on?
No. Apps Script is built into every Google Sheet under Extensions → Apps Script — the whole integration is the pasted function on this page.
Where do the PDFs go?
DriveApp.createFile saves them to the My Drive root of the account running the script; the file's link is written back into the row. Use DriveApp.getFolderById(...).createFile(blob) to target a specific folder.
Can non-technical teammates trigger it?
Yes — expose it as a custom menu item (recipe 2) or attach it to a form-submission trigger (recipe 1). After the one-time authorization, it's a click, not code.
Is the script safe to share?
Move the key out of the source first — a two-line change: store it once under Project Settings → Script Properties as ASHDOCS_KEY, then read it with PropertiesService.getScriptProperties().getProperty("ASHDOCS_KEY") instead of the literal string.