Ops ask we hear a lot: after the nightly sync, dump “status = done” (or last 24h) rows into Exports/2026-09-18.csv without opening Sheets. Filter → values → CSV → Drive folder (create-if-missing), then hang it on a daily trigger.
TL;DR
- Read the sheet once; filter in memory (status column or timestamp window).
- Reuse
valuesToCsv/escapeCsvCellfor correct quoting. writeValuesAsCsvToDriveinto anExportsfolder; filename =yyyy-MM-dd.csvin the script timezone.- Installable time-driven trigger after the sync job (or at the end of the same handler).
- Soft tip: search NitroGAS for CSV / Drive helpers, or let Co-Pilot narrate the export for your ops teammate.
Using the Apps Script editor a lot? NitroGAS drops free themes & snippets right into script.google.com — optional Co-Pilot when you want a boost.
Pipeline
function exportFilteredRowsToDrive() {
var ss = openConfiguredSpreadsheet(); // or SpreadsheetApp.getActive()
var sheet = ss.getSheetByName('Queue');
var values = sheet.getDataRange().getDisplayValues();
if (values.length < 2) {
console.log('No data rows');
return null;
}
var headers = values[0];
var statusCol = headers.indexOf('status');
if (statusCol < 0) throw new Error('Missing status column');
var body = values.slice(1).filter(function (row) {
return String(row[statusCol]).toLowerCase() === 'done';
});
var out = [headers].concat(body);
var root = DriveApp.getRootFolder(); // or a fixed parent folder id
var folder = getOrCreateFolder(root, 'Exports');
var tz = ss.getSpreadsheetTimeZone() || Session.getScriptTimeZone();
var name = Utilities.formatDate(new Date(), tz, 'yyyy-MM-dd') + '.csv';
var file = writeValuesAsCsvToDrive(out, folder, name);
console.log('Wrote', body.length, 'rows →', file.getUrl());
return file;
}
Wire getOrCreateFolder, valuesToCsv, and writeValuesAsCsvToDrive from the snippets library (or paste them above this function).
Last-24-hours variant
If you filter on a date column instead of status:
var updatedCol = headers.indexOf('updated_at');
var cutoff = new Date().getTime() - 24 * 60 * 60 * 1000;
var body = values.slice(1).filter(function (row) {
var t = new Date(row[updatedCol]).getTime();
return !isNaN(t) && t >= cutoff;
});
Prefer getValues() for real Dates; use getDisplayValues() when the CSV should match what humans see in the grid.
Trigger hook
function installExportTrigger() {
deleteTriggersByHandler('exportFilteredRowsToDrive'); // snippet
ScriptApp.newTrigger('exportFilteredRowsToDrive')
.timeBased()
.everyDays(1)
.atHour(6) // after your nightly sync — adjust
.inTimezone(Session.getScriptTimeZone())
.create();
}
Or call exportFilteredRowsToDrive() at the end of the sync handler so one failure story stays in one execution log.
Checklist
- Headers include the filter column (
status/updated_at) - Timezone for the filename matches how ops name files
- Exports folder shared with whoever needs the CSVs
- Dry Run / pause flags still respected if this sits inside the sync job
- Overwrite-same-name vs dated archive — pick one and document it
Soft CTA
Search NitroGAS for the CSV and Drive helpers, then let Co-Pilot add a short comment block so the next person knows why 6am exists. Happy Coding.


