Template Beginner Sheets
Client retainer tracker
A Sheets + Apps Script kit that tracks monthly retainers, flags clients who are over budget, and posts a weekly summary to Slack.
Published Jan 20, 2026
A starter kit for tracking client retainers the way Northwind Studios does: one Clients sheet, a script that totals logged hours against each retainer, and a weekly Slack summary so nothing slips.
What’s in the kit
- A
Clientssheet — columns:name,email,monthlyRetainer,hoursLogged,status. - An
onOpenmenu to recalculate usage on demand. - A time-based trigger that posts a weekly summary to
#alerts.
Set up the sheet
- Create a sheet named
Clientswith the columns above. - Open Extensions → Apps Script and paste the code below.
- Add your Slack webhook under Project Settings → Script Properties as
SLACK_WEBHOOK.
The script
const CLIENTS_SHEET = 'Clients';
/** Add a Northwind menu when the sheet opens. */
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Northwind')
.addItem('Post retainer summary', 'postRetainerSummary')
.addToUi();
}
/** Total each client's usage and post a summary to Slack. */
function postRetainerSummary() {
const sheet = SpreadsheetApp.getActive().getSheetByName(CLIENTS_SHEET);
const rows = sheet.getDataRange().getValues().slice(1); // drop header row
const overBudget = rows
.filter(([, , retainer, hours]) => Number(hours) * 100 > Number(retainer))
.map(([name]) => `• ${name}`);
const text = overBudget.length
? `Clients over retainer this week:\n${overBudget.join('\n')}`
: 'All Northwind clients are within retainer this week. 🎉';
const url = PropertiesService.getScriptProperties().getProperty('SLACK_WEBHOOK');
UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({ text }),
});
}
Schedule it
Add a weekly trigger so the summary posts itself:
function createWeeklyTrigger() {
ScriptApp.newTrigger('postRetainerSummary')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
}
Run createWeeklyTrigger once and every Monday at 9am the summary lands in #alerts.