Example: a Plaid budget

A complete plugin that connects banks and credit cards through Plaid, plus a sample budget built on it with plain formulas. Download it, read every line, and adapt it.

This example connects DreamSheets to your banks and credit cards through Plaid, then builds a budget on top. It's split the way we'd suggest splitting any integration:

  • The plugin is only a connector. It fetches your accounts and transactions and writes them into two tables. That takes about 500 lines of plain JavaScript with no dependencies and no build step.
  • The budget is only a document. Categories, account groups, monthly limits and charts are ordinary formulas and one SQL query, so you can change any of them without touching code.

Downloads

FileWhat it is
Plaid-Budget.dsheetThe sample budget, with three months of made-up transactions. Works before you connect anything.
plaid-budget-plugin.zipThe plugin, ready to install.
main.js · plugin.jsonThe same source, readable in your browser. The zip contains exactly these files plus a README.

A plugin runs inside the app with access to your open document, so read main.js before you install it. It's written to be read.

1. Explore the sample (no Plaid needed)

Download Plaid-Budget.dsheet above and open it in the app. The Budget tab shows this month's spending against your limits, income, net worth, and spending by month per account group. Every number is a formula you can click into:

  • Budget → Spent is SUMIFS(Transactions.Amount, Transactions.'Budget Category', @Category, Transactions.'In Budget', TRUE, …), limited to the Budget Month tile. It follows today's date, so the budget always shows the current month. Replace its formula with a date, like DATE(2026, 8, 1), to look at another month. The sample transactions cover July to September 2026.
  • Transactions carries four calculated columns the plugin knows nothing about: - Budget Category: the first that applies of your My Category on that row, a Merchant Rules entry for its merchant, or the Category Map. - Merchant Rule: the category Merchant Rules gives this merchant, if any. - Group: the account's group, looked up by Account ID in Accounts. - In Budget: whether the account counts toward the budget.
  • Category Map decides how Plaid's categories become yours. Map everything to one category if you don't budget by category, or to Needs / Wants / Savings for a 50/30/20 budget.
  • Merchant Rules sends everything from one merchant to one category, for example Netflix and Spotify to Subscriptions, or Blue Bottle to Coffee. It beats the Category Map.
  • Accounts → Group and Leave Out are yours. Group cards by person or purpose, and tick Leave Out for savings or a business card.
  • Card payments and transfers map to Transfers, so paying off a card from checking isn't counted as spending twice.

2. Connect Plaid

  1. Get keys. Sign up at dashboard.plaid.com and copy your client_id and Sandbox secret from Developers → Keys.
  2. Install the plugin. Tools → Plugin Manager… → Install from Zip…, then pick plaid-budget-plugin.zip. The Plugin Manager shows the only hosts it can reach: sandbox.plaid.com and production.plaid.com.
  3. Add your keys. Tools → Plaid: API Keys…, choose Sandbox, paste both keys, and click Verify & save. The plugin checks them with Plaid before saving anything, and account linking stays greyed out until it has.
  4. Link test banks. Tools → Plaid: Linked Accounts…, pick a test bank, and click Add test bank. Add a second one to see several institutions side by side. Each comes with Plaid's realistic test data: checking, a credit card and loans.
  5. Clear the sample rows. In the sample document, open the Edit Table panel for Transactions and for Accounts, and set Rows to 0.
  6. Sync. Tools → Plaid: Sync Transactions. Your accounts and transactions land in the same tables, and the whole Budget tab updates.

Your real accounts. Plaid's free Trial plan covers personal use: up to 10 institutions, most US and Canadian banks. Apply from the Plaid dashboard. Then remove your sandbox links, switch the plugin to Production with your production secret, and use Link a bank or card…. That opens Plaid's own sign-in page in your browser, so your bank password never passes through DreamSheets or the plugin. If a bank later needs you to sign in again, the Linked Accounts panel flags it with a Reconnect… button.

3. How the plugin works

The manifest: what it's allowed to do

{
  "id": "com.dreamsheets.plaid-budget",
  "apiVersion": 4,
  "permissions": { "network": ["sandbox.plaid.com", "production.plaid.com"] },
  "contributes": { "commands": [ … three Tools-menu commands … ] }
}

permissions.network is enforced by the app itself, outside the plugin: a request to any other host fails. apiVersion: 4 is needed for api.secrets.

Secrets stay out of the document

The Plaid keys and each bank's access token go into the operating system's credential store through `api.secrets`:

await api.secrets.set(`token:${item_id}`, access_token);

None of it is ever written to the .dsheet. Share the document and you share the numbers, not access to the bank. Removing the plugin deletes its secrets.

Calling Plaid

Every Plaid call is one `api.http.fetch`:

const res = await api.http.fetch({
  url: `https://${env}.plaid.com${path}`,
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ client_id: clientId, secret, ...body }),
});

Plaid's own JavaScript sign-in widget can't load inside DreamSheets. The app blocks outside scripts. So linking uses Plaid's Hosted Link: the plugin opens Plaid's page in your browser and checks /link/token/get every few seconds until you finish.

Writing to the document

Sync reads each bank's full history with /transactions/sync. It compares that with the tables by id and writes the difference as one batch of actions:

await api.document.applyActions([
  { action: "append_row", params: { tableId, values: { "Transaction ID": "…", Amount: 12.5, … } } },
  { action: "set_cell", params: { tableId, rowId, columnName: "Balance", value: 1042.18 } },
]);

A batch is atomic and is one undo step, so Ctrl+Z takes back a whole sync. append_row fills columns by name, which is why your own columns and formulas in those tables are safe: the plugin never overwrites a row it already wrote, except for an account's Balance.

Several banks at once

Each linked institution syncs on its own. If one fails, the others still sync: its rows are left alone and the status bar names it. Typical failures are an expired sign-in or a bank outage. Every row carries its Account ID, so two institutions' "Checking" accounts never collide, and your formulas can join on it.

What it deliberately doesn't do

  • Pending transactions are skipped until they post, because Plaid gives a pending transaction a new id when it posts.
  • Withdrawn transactions: plugins can't delete rows yet, so a transaction Plaid withdraws is set to 0 and marked (removed).
  • No budget logic. That's the document's job, which is the point of the example.

Build your own

The same shape works for any service with an HTTP API:

  • keys in api.secrets;
  • requests through api.http.fetch, with the host in permissions.network;
  • results written through applyActions into tables that formulas can build on.

Start with Building a plugin and the Plugin API reference.