Building Sheets Integrations
Go beyond the standard add-on with custom Google Sheets integrations using bkper-gs — build team-specific menus, automated import/export pipelines, scheduled balance reports, and two-way sync between spreadsheets and Bkper books.
The Bkper Add-on for Google Sheets lets users record transactions and fetch data with built-in functions. This page covers the next level: building custom Sheets integrations with bkper-gs — automated pipelines, custom menus, scheduled reports, and two-way sync.
See Apps Script Development first to set up bkper-gs and understand the fundamentals.
The boundary: add-on vs custom integrations
The built-in add-on covers the common cases well. Build a custom integration when:
- You need a custom menu tailored to your team’s workflow
- You want automated pipelines that run on a schedule without user interaction
- You’re building a specialized report that the standard functions don’t cover
- You need two-way sync between a spreadsheet and Bkper (data flowing both directions)
- You’re distributing a custom add-on to your domain or organization
Custom menu functions
Add a Bkper-powered menu to any Google Sheet. Users can trigger operations directly from the spreadsheet without opening Bkper.
function onOpen() { SpreadsheetApp.getUi() .createMenu('Bkper') .addItem('Import expenses from this sheet', 'importExpenses') .addItem('Fetch account balances', 'fetchBalances') .addSeparator() .addItem('Sync all', 'syncAll') .addToUi();}
function importExpenses() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Expenses'); const book = BkperApp.getBook(getBookId());
const rows = sheet.getDataRange().getValues().slice(1); // skip header const transactions = rows .filter(row => row[0] && row[1] && row[2]) // date, description, amount .map(row => `${row[1]} ${row[2]} ${row[0]}`); // "description amount date"
book.record(transactions); SpreadsheetApp.getUi().alert(`Imported ${transactions.length} transactions.`);}Automated data pipelines
Sheets → Bkper (import)
Pull structured data from a spreadsheet and create transactions in bulk. Useful for importing bank exports, expense reports, or any data that lives in Sheets first.
function importFromSheet() { const ss = SpreadsheetApp.openById('YOUR_SHEET_ID'); const sheet = ss.getSheetByName('Transactions'); const book = BkperApp.getBook('YOUR_BOOK_ID');
const rows = sheet.getDataRange().getValues(); const header = rows[0]; const dateCol = header.indexOf('Date'); const descCol = header.indexOf('Description'); const amountCol = header.indexOf('Amount'); const importedCol = header.indexOf('Imported');
const toImport = [];
for (let i = 1; i < rows.length; i++) { const row = rows[i]; if (row[importedCol]) continue; // skip already imported
const date = Utilities.formatDate(new Date(row[dateCol]), 'UTC', 'dd/MM/yyyy'); toImport.push({ row: i + 1, tx: `${row[descCol]} ${row[amountCol]} ${date}`, }); }
if (toImport.length === 0) return;
book.record(toImport.map(item => item.tx));
// Mark rows as imported for (const item of toImport) { sheet.getRange(item.row, importedCol + 1).setValue(true); }}Bkper → Sheets (export/reporting)
Write Bkper data into a spreadsheet for dashboards, analysis, or sharing with stakeholders who work in Sheets.
function exportBalancesToSheet() { const book = BkperApp.getBook('YOUR_BOOK_ID'); const sheet = SpreadsheetApp.getActiveSpreadsheet() .getSheetByName('Balances');
sheet.clearContents(); sheet.appendRow(['Account', 'Balance']);
const accounts = book.getAccounts(); for (const account of accounts) { if (account.isPermanent() && account.isActive()) { sheet.appendRow([account.getName(), account.getBalance()]); } }}Scheduled reporting
Use time-based triggers to run reports on a schedule — no user needs to be logged in.
function setupWeeklyReport() { // Run every Monday at 8am ScriptApp.newTrigger('generateWeeklyReport') .timeBased() .onWeekDay(ScriptApp.WeekDay.MONDAY) .atHour(8) .create();}
function generateWeeklyReport() { const book = BkperApp.getBook('YOUR_BOOK_ID'); const ss = SpreadsheetApp.openById('YOUR_REPORT_SHEET_ID'); const sheet = ss.getSheetByName('Weekly') || ss.insertSheet('Weekly');
const lastWeek = new Date(); lastWeek.setDate(lastWeek.getDate() - 7); const from = Utilities.formatDate(lastWeek, 'UTC', 'MM/dd/yyyy');
sheet.clearContents(); sheet.appendRow(['Description', 'Amount', 'Date', 'Account']);
const iterator = book.getTransactions(`after:${from}`); while (iterator.hasNext()) { const tx = iterator.next(); sheet.appendRow([ tx.getDescription(), tx.getAmount(), tx.getDateFormatted(), tx.getCreditAccount()?.getName(), ]); }}Working with Custom Properties
Custom Properties let you attach metadata to Bkper entities (accounts, transactions). Use them as a sync key between Sheets and Bkper to avoid duplicates and enable updates.
// Store a Sheets row ID on a transaction as a custom propertyfunction recordWithSheetId(book, txString, sheetRowId) { const transaction = book.newTransaction() .setDate(new Date()) .setAmount(100) .setDescription(txString) .setProperty('sheet_row_id', sheetRowId);
transaction.create();}
// Later, look up transactions by their sheet row IDfunction findBySheetId(book, sheetRowId) { const iterator = book.getTransactions(`properties.sheet_row_id:${sheetRowId}`); return iterator.hasNext() ? iterator.next() : null;}This pattern enables idempotent sync: check if a transaction already exists before creating it, and update rather than duplicate.
When to move beyond Sheets
Google Sheets is powerful, but it has limits. Consider a platform app when:
- You need real-time event handling — platform apps get webhook events pushed instantly; Sheets triggers have latency and quota limits
- You need a web UI outside of Sheets — platform apps get
{appId}.bkper.appwith full auth - Your automation needs to run at scale — Workers have no cold starts and higher execution limits than Apps Script
- You want to publish to all Bkper users — platform apps appear in the Bkper app listing; Sheets add-ons have a separate distribution model
Related
- Apps Script Development — Setting up
bkper-gs, BkperApp patterns, triggers - The Bkper Platform — When to build a full platform app
- Guides → Google Sheets Add-on — End-user guide for the built-in add-on