Small teams can run a payment reminder system off one shared Google Sheet. The sheet tracks who paid, what each person owes, when the money is due, and whether a reminder already went out. That's the whole machine.

Ever scrolled a group chat at midnight trying to remember who covered the grocery run? Use one row per person who owes money. A single line reading "$180 split three ways" sounds tidy, but it leaves partial payments and follow-ups to memory. Turns out, a plain tracker like this is often enough for roommates, travel groups, clubs, and committees with modest shared costs.

Set the split rule before building

Decide what "fair" means before you add formulas. A formula can repeat a rule. It can't settle an argument over that rule.

Split rule Record in the sheet Watch for
Equal split The same amount for each person Rounding differences
Usage-based Units such as nights, miles, or meals Agree on the units first
Reimbursement The actual amount each person owes the payer Keep the receipt or expense note
Income-based The agreed percentage or contribution rule Discuss privacy and consent before recording it

The tracker should record the decision, not quietly make one for you. Write the rule in the Notes column so a reminder doesn't restart the same argument three weeks later.

Build the shared expense tracker

Create a Google Sheet tab named Expenses. Give every row one amount due and one recipient.

Column What to enter
A. Expense date The date of the purchase or charge
B. Description Rent, utilities, cabin deposit, groceries, or another shared cost
C. Category Household, travel, event, club, or another useful label
D. Paid by The person who covered the expense
E. Owes The person who needs to reimburse the payer
F. Recipient email One email address for that person
G. Amount due That person's share, formatted as currency
H. Due date The agreed date for payment
I. Payment status Pending or Paid
J. Due flag A formula-driven status
K. Reminder stage Blank, Pre-due sent, or Follow-up sent
L. Last reminded The date an email was sent
M. Paid on The date the payment was confirmed
N. Receipt link A Drive or other file link
O. Notes Split rule, exception, dispute, or context

Example rows might look like this:

Expense date Description Paid by Owes Recipient email Amount due Due date Payment status
2026-05-01 Utilities Alex Sam [email protected] $60 2026-05-08 Pending
2026-05-01 Utilities Alex Priya [email protected] $60 2026-05-08 Paid

These are example entries only. For three people sharing one charge, repeat the expense across three rows and enter each person's actual share.

The row-per-person design feels fussy at first, honestly a little fussy, especially for a weekend trip where everyone remembers the same dinner anyway, but it earns its keep later because a paid row can close on its own while another row for the same expense stays open.

Format columns B, H, and M as dates. Format G as currency, and keep the amount as a plain number rather than gluing a note onto it. Add a dropdown to column I with Pending and Paid.

Share the sheet with the right access

Invite people by email, not a public link. Editors add expenses and update payment status. Most everyone else just needs to see what they owe.

Access choice Use it for
Editor People who enter expenses, confirm payments, or fix rows
Viewer People who only check what they owe
Restricted link Shared-money records that should stay limited to invited people

Notify collaborators when you share the file. Agree on who can mark a row as paid, and ask that person to enter the payment date and receipt link.

Keep sensitive information out of the tracker. No bank passwords, no full card numbers, no unrelated personal details. A shared expense sheet only needs the money facts.

Add formulas for overdue and outstanding amounts

Put the due-date formula in J2, then copy it down:

=IF(I2="Paid","Paid",IF(H2="","No due date",IF(TODAY()>H2,"Overdue",IF(TODAY()=H2,"Due today","Upcoming"))))

This keeps payment status in column I and the calculated timing in column J. That separation matters more than it sounds. People can update Paid without breaking the date logic.

Create a Summary tab for views the group doesn't need to touch. To show overdue rows, enter this in Summary!A2:

=IFNA(FILTER(Expenses!A2:O100,Expenses!J2:J100="Overdue"),"No overdue items")

For larger claims, park a review threshold in Summary!B20 and use:

=IFNA(FILTER(Expenses!A2:O100,Expenses!G2:G100>Summary!$B$20),"No items above threshold")

The threshold is a review choice, not a rule about whether someone owes money. It simply surfaces a trip deposit, moving cost, or other amount worth a closer look.

To total pending amounts per person, list names in Summary!E2:E and place this formula beside the first name:

=SUMIFS(Expenses!$G$2:$G$100,Expenses!$E$2:$E$100,E2,Expenses!$I$2:$I$100,"Pending")

Conditional formatting helps at a glance. Apply =$J2="Overdue" to the expense range with a fill you'll actually notice, then add a separate rule for =$J2="Due today".

Automate date-based email reminders

Automation works best after the rows, split rules, and permissions are settled. Thing is, a script should cut down repeated nudges, not decide whether an expense is valid.

Open Extensions > Apps Script from the spreadsheet and add this code. It assumes the columns above and sends one pre-due message plus one follow-up for each pending row.

const PRE_DUE_DAYS = 3;
const FOLLOW_UP_DAYS = 3;

function sendPaymentReminders() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Expenses');
  if (!sheet) throw new Error('Create a sheet named Expenses first.');

  const values = sheet.getDataRange().getValues();
  if (values.length === 1) return;

  const timeZone = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
  const todayKey = dateKey(new Date(), timeZone);
  if (!todayKey) return;

  for (let i = 1; i !== values.length; i++) {
    const row = values[i];
    const email = String(row[5] || '').trim();
    const status = String(row[8] || '').trim().toLowerCase();
    const dueKey = dateKey(row[7], timeZone);
    const amount = Number(row[6]);

    if (!email || !dueKey || status !== 'pending' || !amount || isNaN(amount)) {
      continue;
    }

    const daysUntilDue =
      Math.round((keyToUtc(dueKey) - keyToUtc(todayKey)) / 86400000);
    const stage = String(row[10] || '').trim().toLowerCase();
    let newStage = '';

    if (!newStage) {
      if (stage !== 'pre-due sent') {
        if (stage !== 'follow-up sent') {
          if (PRE_DUE_DAYS >= daysUntilDue) {
            if (daysUntilDue >= 1) {
              newStage = 'Pre-due sent';
            }
          }
        }
      }
    }

    if (!newStage) {
      if (stage !== 'follow-up sent') {
        if (-FOLLOW_UP_DAYS >= daysUntilDue) {
          newStage = 'Follow-up sent';
        }
      }
    }

    if (!newStage) continue;

    const name = row[4] || 'there';
    const description = row[1] || 'shared expense';
    const subject = newStage === 'Pre-due sent'
      ? 'Upcoming shared expense reminder'
      : 'Follow-up on shared expense';

    const body = [
      'Hi ' + name + ',',
      '',
      'This is a reminder that $' + amount.toFixed(2) + ' is due for ' +
        description + ' on ' + dueKey + '.',
      'Please update the shared sheet after you pay.',
      '',
      'If the amount or due date looks wrong, reply before sending payment.'
    ].join('\n');

    MailApp.sendEmail(email, subject, body);
    sheet.getRange(i + 1, 11).setValue(newStage);
    sheet.getRange(i + 1, 12).setValue(new Date());
  }
}

function dateKey(value, timeZone) {
  if (!(value instanceof Date) || isNaN(value.getTime())) return '';
  return Utilities.formatDate(value, timeZone, 'yyyy-MM-dd');
}

function keyToUtc(key) {
  const parts = key.split('-').map(Number);
  return Date.UTC(parts[0], parts[1] - 1, parts[2]);
}

The date helper converts both the due date and today's date to the spreadsheet's time zone before comparing them, which sidesteps a problem that bites a lot of groups: the trigger runs in one time zone, the dates were entered in another, and suddenly a bill shows up a day early or a day late for no obvious reason.

The email call uses Google's MailApp reference. The first run asks for permission because the script sends messages on your behalf. Google's authorization guidance explains that approval step.

Set up the trigger as follows:

  1. Save the script.
  2. Run sendPaymentReminders once from Apps Script.
  3. Review the authorization request and approve it if the group accepts the workflow.
  4. Open the Triggers panel and add a time-driven trigger for sendPaymentReminders.
  5. Choose a daily schedule.
  6. Test with your own email and a copied sheet before adding the rest of the group.

Test a row with a due date three days away, then inspect columns K and L after the script runs. The script sends email only. It does not collect money, verify a transfer, or resolve a disputed amount.

Write reminders that people can act on

A reminder should state the facts and give the recipient one simple next step. Plenty of groups send a pre-due message two to seven days before payment and a follow-up three to five days after, but write down whatever timing fits your group.

Timing Include Update
Before the due date Description, amount, due date, and how to flag an error Set Pre-due sent and record the date
After the due date The same facts, a status request, and a neutral tone Set Follow-up sent and record the date

A pre-due message can be short:

Hi Jordan, the sheet shows $120 for the cabin deposit, due Friday. If you've already paid, mark the row Paid and add the payment date. If the amount or date looks wrong, reply before sending payment.

A follow-up should stay factual:

Hi Jordan, following up on the $120 cabin deposit listed in the shared sheet. Please update the row when you pay, or reply if we need to correct the amount or due date.

Pause reminders for a disputed row. Repeating the same email won't fix a missing receipt or an unclear split.

Keep the record current

With fewer than five people and the occasional expense, a weekly scan is usually manageable. A daily trigger makes more sense when due dates are scattered across the month or someone routinely misses manual checks.

Cadence Check Record
Weekly Filter for Overdue, Due today, and unusually large amounts Correct status and confirm the responsible person
After payment Check the confirmation or receipt Set Paid, add Paid on, and paste the receipt link
Monthly Review open balances, old rows, and sharing access Export or archive a copy if the group needs a stable record

Keep receipt photos in a folder the right collaborators can access, then paste each file link into column N. Use column O for exceptions such as a canceled booking, a partial payment, or an agreed adjustment.

To be honest, the spreadsheet is only as reliable as its last update. If people stop marking payments, or the same rows keep getting disputed, pause the script and fix the record before sending more messages.

Know when a spreadsheet no longer fits

A sheet fits a lightweight record with clear rules. Consider another workflow once you need formal approvals, complex adjustments, many simultaneous events, or audit detail the group can't maintain by hand.

For recurring rent, club dues, deposits, or large trip costs, a written agreement may matter more than automation. The spreadsheet documents what the group recorded. It doesn't decide whether a debt is legally enforceable; local rules and the group's own agreement can change that question.

FAQ

Can Google Sheets send automatic payment reminders?

Not from formulas alone. A bound Apps Script and a time-driven trigger can send date-based emails, subject to the script's permissions and the sending account's limits.

Should several people share one expense row?

No. Use one row per person who owes money. Separate rows make individual status, email address, reminder stage, and payment date easier to track.

How do I handle uneven splits?

Enter the actual amount due for each person and describe the rule in Notes. For usage-based or nights-stayed splits, record the agreed units or calculation so everyone can review it.

Does the script confirm that someone paid?

It doesn't. The script sends reminders and records that a reminder was sent. A person or designated editor must confirm payment and update the row.

When should we stop using a spreadsheet?

When the group can no longer keep balances, receipts, permissions, or disputes organized. There is no universal transaction count that makes a spreadsheet unsuitable.

Create the Expenses tab, add two test rows, put your own email on one of them, and verify the Due flag before inviting the group. Then run the script once and inspect the reminder stage and date. Small test now, fewer surprises later.