appscript.dev
Automation Beginner Gmail

Auto-forward receipts to your accountant

Route anything that looks like a receipt to a fixed address on a monthly schedule.

Published Jun 16, 2026

Receipts arrive all month — a software renewal here, a courier invoice there, a hardware order somewhere in between. If someone at Northwind forwards each one to the accountant as it lands, the accountant’s inbox fills with noise and the odd receipt still slips through. Batching them into a single monthly digest is calmer for everyone, but doing that by hand means trawling a month of mail.

This script does the trawl for you. Once a month it searches your inbox for threads that carry an attachment and look like a receipt, forwards each one to a fixed accountant address, and labels it so the next run never forwards it twice. It is a small automation, but it turns a recurring chore into something you never think about.

What you’ll need

  • A Gmail account that receives the receipts — this runs as you, so it must be the inbox the receipts land in.
  • Your accountant’s email address.
  • Nothing to set up in advance: the script creates the receipts/forwarded label itself on the first run.

The script

// Where every receipt gets forwarded.
const ACCOUNTANT = '[email protected]';

// Words in a subject line that suggest the message is a receipt. Tune this
// for the vendors you actually buy from — see "Watch out for".
const HINTS = /\b(receipt|invoice|order confirmation|payment)\b/i;

// The label that marks a thread as already sent, so it is never forwarded
// twice on the next run.
const FORWARDED_LABEL = 'receipts/forwarded';

/**
 * Finds recent threads with attachments that look like receipts, forwards
 * each one to the accountant, and labels it so it is not picked up again.
 * Designed to run once a month.
 */
function monthlyReceiptForward() {
  // 1. Search the last 35 days for threads that carry an attachment and
  //    have not already been forwarded. 35 days gives a little overlap so
  //    nothing falls between two monthly runs.
  const threads = GmailApp.search(
    'newer_than:35d has:attachment -label:' + FORWARDED_LABEL
  );

  if (!threads.length) {
    Logger.log('No candidate threads found — nothing to forward.');
    return;
  }

  // 2. Get the "forwarded" label, creating it the first time the script runs.
  const forwarded = GmailApp.getUserLabelByName(FORWARDED_LABEL)
    || GmailApp.createLabel(FORWARDED_LABEL);

  // 3. Walk each thread and forward the ones that look like receipts.
  let sent = 0;
  for (const thread of threads) {
    const msg = thread.getMessages()[0];

    // Skip anything whose subject does not match the receipt hints.
    if (!HINTS.test(msg.getSubject())) continue;

    // Forward the message (attachments included) and mark the thread done.
    msg.forward(ACCOUNTANT);
    thread.addLabel(forwarded);
    sent++;
  }

  Logger.log('Forwarded ' + sent + ' receipt(s) to ' + ACCOUNTANT + '.');
}

How it works

  1. monthlyReceiptForward searches your inbox for threads from the last 35 days that have an attachment and do not already carry the receipts/forwarded label. The 35-day window deliberately overlaps the month so nothing is missed between two scheduled runs.
  2. If the search returns nothing, it logs a message and stops.
  3. It looks up the receipts/forwarded label, creating it the first time the script runs so there is no manual setup.
  4. For each thread it takes the first message and tests its subject line against HINTS — a regular expression matching words like receipt, invoice and payment. Anything that does not match is skipped, which is what keeps personal mail out of the accountant’s inbox.
  5. Matching messages are forwarded with msg.forward, which carries the attachments along. The thread is then labelled so the next run ignores it.
  6. It logs how many receipts were sent.

Example run

Say the inbox picked up these four threads over the last month:

SubjectHas attachmentForwarded?
Your receipt from FigmaYesYes — matches “receipt”
Invoice #4471 — Couriers LtdYesYes — matches “invoice”
Lunch photosYesNo — subject does not match
Order confirmation: 2× monitorsYesYes — matches “order confirmation”

After the run, the accountant receives three forwarded messages — the Figma receipt, the courier invoice and the monitor order — each with its original attachment. All three threads gain the receipts/forwarded label, so next month’s run starts fresh and skips them.

Trigger it

Run this once a month so the accountant gets a single, predictable batch:

  1. In the Apps Script editor, open Triggers (the clock icon).
  2. Click Add Trigger.
  3. Choose the monthlyReceiptForward function, an event source of Time-driven, a Month timer, day 1 of month, and a time of 9am to 10am.
  4. Save and approve the authorisation prompt.

Watch out for

  • The filter is intentionally tight. HINTS only matches a handful of words, which avoids forwarding personal mail but also means a vendor with an unusual subject line (“Thanks for your order” with no other keyword) will be skipped. Add their wording to the regular expression as you spot gaps.
  • It checks the first message of each thread only. If a receipt arrives as a reply deep in an existing thread, the subject test runs against the original message, not the receipt — rare, but worth knowing.
  • Threads are matched on attachments. A receipt delivered as inline text with no attachment will not be picked up; this automation is built for the common case of a PDF or image attached.
  • Gmail caps how many messages you can send per day. Forwarding a normal month of receipts is well within the limit, but a first run over a large, unlabelled backlog could hit it — clear the backlog in smaller passes if so.

Related