Background Mobile

An Invoice-Filing Agent Built in Google Apps Script

artificial intelligence/
September 17, 2026
An Invoice-Filing Agent Built in Google Apps Script

A practical walkthrough of building an invoice-filing agent inside Google Apps Script — what works, what breaks at scale, and where the approach hits a ceiling.

What the Agent Actually Does

The agent sits inside a Google Workspace environment. It watches a Gmail label, pulls attachments that look like invoices, extracts the relevant fields, and writes rows into a Google Sheet that feeds an approval workflow. No third-party SaaS, no webhook infrastructure to maintain, no additional IAM roles to configure.

The trigger is a time-based Apps Script trigger running every 15 minutes. That cadence is deliberate — Google's quota for GmailApp.search() calls is 20,000 per day across the workspace, so polling too aggressively on a shared account will hit that ceiling faster than you'd expect.

The extraction step is where the actual intelligence lives. For structured PDFs (generated invoices from accounting software), a regex pass over the raw text pulled via DriveApp and a PDF-to-text utility covers most cases. For scanned invoices, the agent calls the Google Cloud Vision API's DOCUMENT_TEXT_DETECTION feature, which handles printed and near-printed text at acceptable accuracy without needing a fine-tuned model.

Fields extracted per invoice: vendor name, invoice number, invoice date, due date, line items (description, quantity, unit price), subtotal, tax amount, and total. Missing fields trigger a flag column in the Sheet rather than a silent failure.

How the Extraction Pipeline Is Wired Together

Parsing structured PDFs

Apps Script cannot parse a PDF natively. The workaround is converting the PDF to a Google Doc via the Drive API's convert=true parameter on upload, then reading the Doc body as plain text. This works reliably for single-column, text-layer PDFs. Multi-column layouts confuse the conversion; the text order comes out scrambled.

For those edge cases, Vision API is the fallback. The agent tries the Doc conversion first, checks whether it got at least four of the eight expected fields, and if not, re-routes to Vision.

Calling Vision API from Apps Script

Apps Script's UrlFetchApp handles the Vision API call. The image (or PDF page, converted to PNG via a small Cloud Run function) is base64-encoded and sent as a JSON payload to https://vision.googleapis.com/v1/images:annotate. Response parsing is straightforward JSON traversal.

The Cloud Run function is the only external dependency. It does one thing: accept a Drive file ID, export the first page as a 300 DPI PNG, return the base64 string. Keeping it stateless means cold starts are the only latency concern, and at invoice volumes below a few hundred per day that's fine.

Writing to Sheets and triggering approval

Once fields are extracted, the agent appends a row to a named sheet using SpreadsheetApp. A separate onEdit trigger watches for a "Status" column change from "Pending" to "Approved" or "Rejected" and sends a notification email via GmailApp.sendEmail(). That's the entirety of the approval workflow — no third-party BPM tool required for teams processing under 200 invoices a month.

/// Not sure where to start?

Get the architecture before you commit

Tell us what you're building and we'll map the technical approach, stack, and rough timeline. No cost, no obligation, no sales call required.

Where Does This Approach Break Down?

Scale is the honest answer. Google Apps Script has hard execution limits: 6 minutes per execution for consumer accounts, 30 minutes for Workspace Business and Enterprise. A batch of 50 scanned invoices, each requiring a Vision API round-trip and a Cloud Run PNG conversion, can consume 8 to 12 minutes. You either break the batch into smaller triggered runs or move the heavy processing off-script entirely.

The other ceiling is concurrency. Apps Script is single-threaded per execution. If two triggers fire simultaneously (which happens when Google's scheduler drifts), you can get duplicate rows. A lock via LockService.getScriptLock() with a timeout of 10 seconds handles this, but it means one execution will simply skip its run rather than queue behind the other.

For organisations processing more than 500 invoices a month with meaningful variance in format, the right architecture is a dedicated extraction service (a Python microservice using pdfplumber or camelot for structured PDFs, plus a Vision or Textract call for scans) with Apps Script acting only as the UI and notification layer. That separation keeps each component within its competence.

Volume Format Variance Recommended Approach
< 200/month Low (mostly structured PDFs) Pure Apps Script + Vision fallback
200–500/month Medium Apps Script orchestrator + Cloud Run extractor
> 500/month High (mixed scanned/structured) Dedicated Python service, Apps Script for notifications only

Is Google Apps Script the Right Runtime for AI-Adjacent Workflows?

For narrow, well-defined tasks operating on Google Workspace data, yes. The deployment overhead is zero — no infrastructure to provision, no container registry, no CI pipeline needed to ship a change. A senior engineer can build and deploy the agent described here in two to three days.

The trade-off is runtime capability. You cannot install npm packages, you cannot use Python libraries, and you cannot run anything that needs more than ~50 MB of memory comfortably. The UrlFetchApp interface to external APIs compensates for most of that, but there is latency on every external call and you are subject to Google's URL Fetch daily quota (20,000 calls per day on Workspace accounts).

Apps Script also has no built-in secret manager. Storing API keys in PropertiesService is the conventional approach — it keeps keys out of source but offers no rotation, no audit trail, and no access scoping beyond the script owner. If your security posture requires key rotation on a schedule or audit logs of API key usage, you need something else.

Structuring the Code for Maintainability

One file per concern. The Gmail watcher, the extraction router, the Vision caller, the Sheets writer, and the approval notifier each live in separate .gs files. Apps Script concatenates them at runtime, so there's no import system to manage, but keeping files scoped to a single responsibility makes testing and modification straightforward.

Testing is manual by default. The script editor's debugger is usable but limited. For anything beyond simple runs, writing test functions that operate on fixture data (a Drive folder of sample invoices) and logging output to a dedicated Sheet is the most practical approach available in the environment.

Version control is the biggest operational gap. The built-in version history in Apps Script is coarse. Connecting the project to a GitHub repository via clasp (the command-line Apps Script tool) gives you proper Git history, code review via pull requests, and the ability to deploy from a CI pipeline if the project grows to warrant it.

Conclusion

An invoice-filing agent built in Apps Script is a legitimate production tool for the right volume and format range. The architecture is not exotic — a Gmail watcher, a Vision API call, a Sheets writer, and a lock to handle concurrency. The limits are real and documented above. If your invoicing operation is inside those limits, this is a fast and low-overhead way to automate a repetitive process without buying another SaaS subscription or provisioning cloud infrastructure.

The clear next step: instrument your current invoice volume and format breakdown before building. If more than 20% of your invoices are multi-column scans or if volume is already near 300 per month, start with the Cloud Run extractor rather than retrofitting it later.

FAQ

Can Apps Script read password-protected PDFs? No. The Drive API conversion and Vision API both require an unencrypted file. You'd need to decrypt the PDF before it enters the pipeline — either at the email receipt stage using a Cloud Function, or by enforcing a policy with vendors to send unprotected files. There is no workaround inside Apps Script itself.

How accurate is Google Vision API for invoice extraction compared to a dedicated model? Vision's DOCUMENT_TEXT_DETECTION achieves high character-level accuracy on clean printed invoices, typically above 98% for standard fonts. Accuracy drops on low-resolution scans, handwritten annotations, and non-Latin scripts. A fine-tuned Document AI processor handles those cases better, at higher cost per page (around $0.065 per page vs $0.0015 for Vision at standard volume).

What happens if an invoice email has multiple attachments? The agent iterates over all attachments and applies a MIME type filter — application/pdf and image/* pass through, everything else is skipped. Each qualifying attachment gets its own row in the Sheet, with the same email metadata (sender, subject, received timestamp) stamped on each row. Duplicate invoice detection is a separate step based on matching invoice number and vendor name.

Is this approach compliant with data residency requirements? That depends on your Workspace region configuration and how you've configured Vision API. Google Cloud Vision processes data in Google's infrastructure; you can specify a regional endpoint (e.g. eu-vision.googleapis.com) if EU data residency is required. Apps Script itself stores data in Google's infrastructure under your Workspace agreement. Verify your specific Workspace data region setting before processing sensitive financial documents.

Can the agent handle invoices received as email body text rather than attachments? Yes, with a separate parsing branch. GmailApp.getMessages()[n].getPlainBody() retrieves the email body. You'd apply the same regex field extraction against that string. The challenge is that invoice-in-body formatting is far less consistent than PDF attachments, so expect a higher rate of flagged-for-review rows from that source.

Have a project in mind? Contact Sodio Technologies to discuss your requirements and explore the right technology solution for your business.

/// Work with us

Talk to the engineers who'd build it

You'll get a technical scope, timeline and cost estimate from the people doing the work, not an account manager. In-house team, no subcontracting, since 2016.

Contact Us