[PR #7735] Add APIs to allow external sync tools easier places to tie in #65291

Open
opened 2026-05-11 22:27:58 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/actualbudget/actual/pull/7735
Author: @jcam
Created: 5/7/2026
Status: 🔄 Open

Base: masterHead: jcam/external-sync


📝 Commits (5)

  • 570ae0c Add account sync metadata APIs for external sync tools
  • 2cb5c05 External account sync get
  • f432ebf Add release note
  • 4314bd0 [autofix.ci] apply automated fixes
  • 76a21f8 changes for coderabbitai's suggestions

📊 Changes

14 files changed (+725 additions, -31 deletions)

View changed files

📝 packages/api/methods.ts (+22 -1)
📝 packages/desktop-client/src/components/accounts/Header.tsx (+28 -8)
📝 packages/desktop-client/src/components/banksync/AccountRow.tsx (+24 -12)
📝 packages/desktop-client/src/components/banksync/bankSyncUtils.test.ts (+15 -6)
📝 packages/desktop-client/src/components/banksync/bankSyncUtils.ts (+4 -1)
📝 packages/loot-core/src/server/accounts/app-bank-sync.test.ts (+29 -0)
packages/loot-core/src/server/accounts/app-external-sync.test.ts (+320 -0)
📝 packages/loot-core/src/server/accounts/app.ts (+162 -1)
📝 packages/loot-core/src/server/accounts/link.ts (+36 -0)
📝 packages/loot-core/src/server/api.ts (+20 -0)
📝 packages/loot-core/src/server/db/types/index.ts (+6 -1)
📝 packages/loot-core/src/types/api-handlers.ts (+48 -0)
📝 packages/loot-core/src/types/models/account.ts (+5 -1)
upcoming-release-notes/7735.md (+6 -0)

📄 Description

Description

I've had a lot of trouble with SimpleFin failing for my accounts, so I want to leverage some alternative sync gateways (plaid, teller.io, stripe, etc). Rather than going through the very deep process of building a first class sync that won't necessarily get a lot of use, or keep waiting for the plugin architecture to arrive, I decided to build a docker app that will sit next to my Actual container and handle scheduled sync from any number of providers.

I did use codex to help put this together, as my typescript was rusty, to say the least, but I then did a lot of manual trimming and review. I'll admit that I didn't really review the test cases that codex wrote.

The side app I'm building is here: https://github.com/jcam/actual-sync-hub (also heavily vibecoded), but these APIs should be usable by anyone who wants an easy way to plug in an external account.
My app supports Plaid, Teller.io, and Simplefin (primarily as a migration step, but also to have a unified automated sync)
I'll be adding a few more providers as well, if anyone else wants to use it... looking at SaltEdge and Stripe next

It creates an external sync source, wires in the various parts that touch that field to not explode, and allows the external tool to read and update more detailed account data, keep the 'last sync' updated, etc.

I don't know the conventions here, so I won't @ lelemm and youngcw, but I know you both have put a lot into sync, so would appreciate feedback on this!

I tried to keep it very minimal, so that once the big plugin system arrives, it can be torn back out with minimal fuss.

Relates to pretty much all of the Community Repos for external sync, #7697 comes to mind, but also especially that Plaid now has a free tier again: #898

Testing

I let codex build lots of test cases around it as I described what it should be doing. I also ran a full yarn build:api to generate an updated node module I could directly use in my app, as well as yarn build:server plus a docker build of the alpine variant of sync-server, so I would know that it worked end to end and wasn't relying on any local environment weirdness.

Checklist

  • [ X ] Release notes added (see link above)
  • [ X ] No obvious regressions in affected areas
  • [ X ] Self-review has been performed - I understand what each change in the code does and why it is needed

Bundle Stats

Bundle Files count Total bundle size % Changed
desktop-client 34 13.93 MB → 13.93 MB (-2.08 kB) -0.01%
loot-core 1 5.27 MB → 5.27 MB (+4.9 kB) +0.09%
api 2 3.89 MB → 3.9 MB (+5.12 kB) +0.13%
cli 1 7.97 MB 0%
crdt 1 41.83 kB 0%
View detailed bundle stats

desktop-client

Total

Files count Total bundle size % Changed
34 13.93 MB → 13.93 MB (-2.08 kB) -0.01%
Changeset
File Δ Size
src/components/banksync/AccountRow.tsx 📈 +484 B (+10.65%) 4.44 kB → 4.91 kB
src/components/banksync/bankSyncUtils.ts 📈 +52 B (+3.54%) 1.44 kB → 1.49 kB
src/components/accounts/Header.tsx 📈 +318 B (+1.11%) 28.01 kB → 28.33 kB
locale/es.json 📉 -162 B (-0.09%) 179.16 kB → 179 kB
locale/ca.json 📉 -172 B (-0.09%) 188.08 kB → 187.91 kB
locale/fr.json 📉 -176 B (-0.10%) 179.07 kB → 178.9 kB
locale/uk.json 📉 -212 B (-0.10%) 207.95 kB → 207.75 kB
locale/de.json 📉 -183 B (-0.10%) 170.73 kB → 170.55 kB
locale/it.json 📉 -187 B (-0.11%) 165.19 kB → 165.01 kB
locale/nb-NO.json 📉 -170 B (-0.11%) 148.48 kB → 148.31 kB
locale/zh-Hans.json 📉 -147 B (-0.12%) 118.05 kB → 117.91 kB
locale/en.json 📉 -261 B (-0.14%) 185.36 kB → 185.11 kB
locale/th.json 📉 -270 B (-0.15%) 175.02 kB → 174.76 kB
locale/nl.json 📉 -178 B (-0.16%) 106.65 kB → 106.47 kB
locale/da.json 📉 -186 B (-0.18%) 101.57 kB → 101.38 kB
locale/pl.json 📉 -161 B (-0.18%) 86.67 kB → 86.52 kB
locale/pt-BR.json 📉 -515 B (-0.26%) 190.09 kB → 189.58 kB
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger

Asset File Size % Changed
static/js/index.js 1.93 MB → 1.93 MB (+802 B) +0.04%
static/js/bankSyncUtils.js 54.11 kB → 54.16 kB (+52 B) +0.09%

Smaller

Asset File Size % Changed
static/js/pt-BR.js 190.09 kB → 189.58 kB (-515 B) -0.26%
static/js/th.js 175.02 kB → 174.76 kB (-270 B) -0.15%
static/js/en.js 185.36 kB → 185.11 kB (-261 B) -0.14%
static/js/uk.js 207.95 kB → 207.75 kB (-212 B) -0.10%
static/js/it.js 165.19 kB → 165.01 kB (-187 B) -0.11%
static/js/da.js 101.57 kB → 101.38 kB (-186 B) -0.18%
static/js/de.js 170.73 kB → 170.55 kB (-183 B) -0.10%
static/js/nl.js 106.65 kB → 106.47 kB (-178 B) -0.16%
static/js/fr.js 179.07 kB → 178.9 kB (-176 B) -0.10%
static/js/ca.js 188.08 kB → 187.91 kB (-172 B) -0.09%
static/js/nb-NO.js 148.48 kB → 148.31 kB (-170 B) -0.11%
static/js/es.js 179.16 kB → 179 kB (-162 B) -0.09%
static/js/pl.js 86.67 kB → 86.52 kB (-161 B) -0.18%
static/js/zh-Hans.js 118.05 kB → 117.91 kB (-147 B) -0.12%

Unchanged

Asset File Size % Changed
static/js/BackgroundImage.js 121.09 kB 0%
static/js/FormulaEditor.js 962.55 kB 0%
static/js/ReportRouter.js 1.22 MB 0%
static/js/ScheduleEditForm.js 145.68 kB 0%
static/js/TransactionEdit.js 189.54 kB 0%
static/js/TransactionList.js 85.81 kB 0%
static/js/Value.js 4.94 MB 0%
static/js/alerts.js 800.08 kB 0%
static/js/client.js 451.37 kB 0%
static/js/en-GB.js 8.2 kB 0%
static/js/extends.js 518.66 kB 0%
static/js/indexeddb-main-thread-worker-e59fee74.js 13.46 kB 0%
static/js/narrow.js 364.41 kB 0%
static/js/resize-observer.js 18.06 kB 0%
static/js/theme.js 31.67 kB 0%
static/js/useFormatList.js 4.96 kB 0%
static/js/wide.js 453 B 0%
static/js/workbox-window.prod.es5.js 7.33 kB 0%

loot-core

Total

Files count Total bundle size % Changed
1 5.27 MB → 5.27 MB (+4.9 kB) +0.09%
Changeset
File Δ Size
home/runner/work/actual/actual/packages/loot-core/src/server/accounts/link.ts 📈 +694 B (+164.45%) 422 B → 1.09 kB
home/runner/work/actual/actual/packages/loot-core/src/server/accounts/app.ts 📈 +3.71 kB (+16.86%) 22.02 kB → 25.73 kB
home/runner/work/actual/actual/packages/loot-core/src/server/api.ts 📈 +521 B (+2.25%) 22.65 kB → 23.16 kB
View detailed bundle breakdown

Added

Asset File Size % Changed
kcab.worker.RuExqU0o.js 0 B → 5.27 MB (+5.27 MB) -

Removed

Asset File Size % Changed
kcab.worker.DCbeRm_D.js 5.27 MB → 0 B (-5.27 MB) -100%

Bigger
No assets were bigger

Smaller
No assets were smaller

Unchanged
No assets were unchanged


api

Total

Files count Total bundle size % Changed
2 3.89 MB → 3.9 MB (+5.12 kB) +0.13%
Changeset
File Δ Size
home/runner/work/actual/actual/packages/loot-core/src/server/accounts/link.ts 📈 +678 B (+164.96%) 411 B → 1.06 kB
home/runner/work/actual/actual/packages/loot-core/src/server/accounts/app.ts 📈 +3.65 kB (+17.01%) 21.46 kB → 25.11 kB
methods.ts 📈 +318 B (+5.62%) 5.53 kB → 5.84 kB
home/runner/work/actual/actual/packages/loot-core/src/server/api.ts 📈 +506 B (+2.24%) 22.05 kB → 22.54 kB
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger

Asset File Size % Changed
index.js 3.89 MB → 3.9 MB (+5.12 kB) +0.13%

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
models.js 0 B 0%

cli

Total

Files count Total bundle size % Changed
1 7.97 MB 0%
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger
No assets were bigger

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
cli.js 7.97 MB 0%

crdt

Total

Files count Total bundle size % Changed
1 41.83 kB 0%
View detailed bundle breakdown

Added
No assets were added

Removed
No assets were removed

Bigger
No assets were bigger

Smaller
No assets were smaller

Unchanged

Asset File Size % Changed
index.js 41.83 kB 0%

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/actualbudget/actual/pull/7735 **Author:** [@jcam](https://github.com/jcam) **Created:** 5/7/2026 **Status:** 🔄 Open **Base:** `master` ← **Head:** `jcam/external-sync` --- ### 📝 Commits (5) - [`570ae0c`](https://github.com/actualbudget/actual/commit/570ae0c02f9f3f4161bcb38224d81574ae07e8a5) Add account sync metadata APIs for external sync tools - [`2cb5c05`](https://github.com/actualbudget/actual/commit/2cb5c052eb4929e36fc1d87671adbb8dbe01ebea) External account sync get - [`f432ebf`](https://github.com/actualbudget/actual/commit/f432ebfc7df90a11f2f0afa59a87c88a643563bd) Add release note - [`4314bd0`](https://github.com/actualbudget/actual/commit/4314bd033e7f6b7065aa389ceb792a8bf0b0f8ff) [autofix.ci] apply automated fixes - [`76a21f8`](https://github.com/actualbudget/actual/commit/76a21f84487f79933700b9820b6d7be7408bc8f4) changes for coderabbitai's suggestions ### 📊 Changes **14 files changed** (+725 additions, -31 deletions) <details> <summary>View changed files</summary> 📝 `packages/api/methods.ts` (+22 -1) 📝 `packages/desktop-client/src/components/accounts/Header.tsx` (+28 -8) 📝 `packages/desktop-client/src/components/banksync/AccountRow.tsx` (+24 -12) 📝 `packages/desktop-client/src/components/banksync/bankSyncUtils.test.ts` (+15 -6) 📝 `packages/desktop-client/src/components/banksync/bankSyncUtils.ts` (+4 -1) 📝 `packages/loot-core/src/server/accounts/app-bank-sync.test.ts` (+29 -0) ➕ `packages/loot-core/src/server/accounts/app-external-sync.test.ts` (+320 -0) 📝 `packages/loot-core/src/server/accounts/app.ts` (+162 -1) 📝 `packages/loot-core/src/server/accounts/link.ts` (+36 -0) 📝 `packages/loot-core/src/server/api.ts` (+20 -0) 📝 `packages/loot-core/src/server/db/types/index.ts` (+6 -1) 📝 `packages/loot-core/src/types/api-handlers.ts` (+48 -0) 📝 `packages/loot-core/src/types/models/account.ts` (+5 -1) ➕ `upcoming-release-notes/7735.md` (+6 -0) </details> ### 📄 Description ## Description I've had a *lot* of trouble with SimpleFin failing for my accounts, so I want to leverage some alternative sync gateways (plaid, teller.io, stripe, etc). Rather than going through the very deep process of building a first class sync that won't necessarily get a lot of use, or keep waiting for the plugin architecture to arrive, I decided to build a docker app that will sit next to my Actual container and handle scheduled sync from any number of providers. I did use **codex** to help put this together, as my typescript was rusty, to say the least, but I then did a lot of manual trimming and review. I'll admit that I didn't really review the test cases that codex wrote. The side app I'm building is here: https://github.com/jcam/actual-sync-hub (also heavily vibecoded), but these APIs should be usable by anyone who wants an easy way to plug in an external account. My app supports Plaid, Teller.io, and Simplefin (primarily as a migration step, but also to have a unified automated sync) I'll be adding a few more providers as well, if anyone else wants to use it... looking at SaltEdge and Stripe next It creates an `external` sync source, wires in the various parts that touch that field to not explode, and allows the external tool to read and update more detailed account data, keep the 'last sync' updated, etc. I don't know the conventions here, so I won't @ lelemm and youngcw, but I know you both have put a lot into sync, so would appreciate feedback on this! I tried to keep it very minimal, so that once the big plugin system arrives, it can be torn back out with minimal fuss. ## Related issue(s) Relates to pretty much all of the Community Repos for external sync, #7697 comes to mind, but also especially that Plaid now has a free tier again: #898 ## Testing I let codex build lots of test cases around it as I described what it should be doing. I also ran a full `yarn build:api` to generate an updated node module I could directly use in my app, as well as `yarn build:server` plus a docker build of the alpine variant of sync-server, so I would know that it worked end to end and wasn't relying on any local environment weirdness. ## Checklist - [ X ] Release notes added (see link above) - [ X ] No obvious regressions in affected areas - [ X ] Self-review has been performed - I understand what each change in the code does and why it is needed <!--- actual-bot-sections ---> <!--- bundlestats-action-comment key:combined start ---> ### Bundle Stats Bundle | Files count | Total bundle size | % Changed ------ | ----------- | ----------------- | --------- desktop-client | 34 | 13.93 MB → 13.93 MB (-2.08 kB) | -0.01% loot-core | 1 | 5.27 MB → 5.27 MB (+4.9 kB) | +0.09% api | 2 | 3.89 MB → 3.9 MB (+5.12 kB) | +0.13% cli | 1 | 7.97 MB | 0% crdt | 1 | 41.83 kB | 0% <details> <summary>View detailed bundle stats</summary> #### desktop-client **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 34 | 13.93 MB → 13.93 MB (-2.08 kB) | -0.01% <details> <summary>Changeset</summary> File | Δ | Size ---- | - | ---- `src/components/banksync/AccountRow.tsx` | 📈 +484 B (+10.65%) | 4.44 kB → 4.91 kB `src/components/banksync/bankSyncUtils.ts` | 📈 +52 B (+3.54%) | 1.44 kB → 1.49 kB `src/components/accounts/Header.tsx` | 📈 +318 B (+1.11%) | 28.01 kB → 28.33 kB `locale/es.json` | 📉 -162 B (-0.09%) | 179.16 kB → 179 kB `locale/ca.json` | 📉 -172 B (-0.09%) | 188.08 kB → 187.91 kB `locale/fr.json` | 📉 -176 B (-0.10%) | 179.07 kB → 178.9 kB `locale/uk.json` | 📉 -212 B (-0.10%) | 207.95 kB → 207.75 kB `locale/de.json` | 📉 -183 B (-0.10%) | 170.73 kB → 170.55 kB `locale/it.json` | 📉 -187 B (-0.11%) | 165.19 kB → 165.01 kB `locale/nb-NO.json` | 📉 -170 B (-0.11%) | 148.48 kB → 148.31 kB `locale/zh-Hans.json` | 📉 -147 B (-0.12%) | 118.05 kB → 117.91 kB `locale/en.json` | 📉 -261 B (-0.14%) | 185.36 kB → 185.11 kB `locale/th.json` | 📉 -270 B (-0.15%) | 175.02 kB → 174.76 kB `locale/nl.json` | 📉 -178 B (-0.16%) | 106.65 kB → 106.47 kB `locale/da.json` | 📉 -186 B (-0.18%) | 101.57 kB → 101.38 kB `locale/pl.json` | 📉 -161 B (-0.18%) | 86.67 kB → 86.52 kB `locale/pt-BR.json` | 📉 -515 B (-0.26%) | 190.09 kB → 189.58 kB </details> <details> <summary>View detailed bundle breakdown</summary> <div> **Added** No assets were added **Removed** No assets were removed **Bigger** Asset | File Size | % Changed ----- | --------- | --------- static/js/index.js | 1.93 MB → 1.93 MB (+802 B) | +0.04% static/js/bankSyncUtils.js | 54.11 kB → 54.16 kB (+52 B) | +0.09% **Smaller** Asset | File Size | % Changed ----- | --------- | --------- static/js/pt-BR.js | 190.09 kB → 189.58 kB (-515 B) | -0.26% static/js/th.js | 175.02 kB → 174.76 kB (-270 B) | -0.15% static/js/en.js | 185.36 kB → 185.11 kB (-261 B) | -0.14% static/js/uk.js | 207.95 kB → 207.75 kB (-212 B) | -0.10% static/js/it.js | 165.19 kB → 165.01 kB (-187 B) | -0.11% static/js/da.js | 101.57 kB → 101.38 kB (-186 B) | -0.18% static/js/de.js | 170.73 kB → 170.55 kB (-183 B) | -0.10% static/js/nl.js | 106.65 kB → 106.47 kB (-178 B) | -0.16% static/js/fr.js | 179.07 kB → 178.9 kB (-176 B) | -0.10% static/js/ca.js | 188.08 kB → 187.91 kB (-172 B) | -0.09% static/js/nb-NO.js | 148.48 kB → 148.31 kB (-170 B) | -0.11% static/js/es.js | 179.16 kB → 179 kB (-162 B) | -0.09% static/js/pl.js | 86.67 kB → 86.52 kB (-161 B) | -0.18% static/js/zh-Hans.js | 118.05 kB → 117.91 kB (-147 B) | -0.12% **Unchanged** Asset | File Size | % Changed ----- | --------- | --------- static/js/BackgroundImage.js | 121.09 kB | 0% static/js/FormulaEditor.js | 962.55 kB | 0% static/js/ReportRouter.js | 1.22 MB | 0% static/js/ScheduleEditForm.js | 145.68 kB | 0% static/js/TransactionEdit.js | 189.54 kB | 0% static/js/TransactionList.js | 85.81 kB | 0% static/js/Value.js | 4.94 MB | 0% static/js/alerts.js | 800.08 kB | 0% static/js/client.js | 451.37 kB | 0% static/js/en-GB.js | 8.2 kB | 0% static/js/extends.js | 518.66 kB | 0% static/js/indexeddb-main-thread-worker-e59fee74.js | 13.46 kB | 0% static/js/narrow.js | 364.41 kB | 0% static/js/resize-observer.js | 18.06 kB | 0% static/js/theme.js | 31.67 kB | 0% static/js/useFormatList.js | 4.96 kB | 0% static/js/wide.js | 453 B | 0% static/js/workbox-window.prod.es5.js | 7.33 kB | 0% </div> </details> --- #### loot-core **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 1 | 5.27 MB → 5.27 MB (+4.9 kB) | +0.09% <details> <summary>Changeset</summary> File | Δ | Size ---- | - | ---- `home/runner/work/actual/actual/packages/loot-core/src/server/accounts/link.ts` | 📈 +694 B (+164.45%) | 422 B → 1.09 kB `home/runner/work/actual/actual/packages/loot-core/src/server/accounts/app.ts` | 📈 +3.71 kB (+16.86%) | 22.02 kB → 25.73 kB `home/runner/work/actual/actual/packages/loot-core/src/server/api.ts` | 📈 +521 B (+2.25%) | 22.65 kB → 23.16 kB </details> <details> <summary>View detailed bundle breakdown</summary> <div> **Added** Asset | File Size | % Changed ----- | --------- | --------- kcab.worker.RuExqU0o.js | 0 B → 5.27 MB (+5.27 MB) | - **Removed** Asset | File Size | % Changed ----- | --------- | --------- kcab.worker.DCbeRm_D.js | 5.27 MB → 0 B (-5.27 MB) | -100% **Bigger** No assets were bigger **Smaller** No assets were smaller **Unchanged** No assets were unchanged </div> </details> --- #### api **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 2 | 3.89 MB → 3.9 MB (+5.12 kB) | +0.13% <details> <summary>Changeset</summary> File | Δ | Size ---- | - | ---- `home/runner/work/actual/actual/packages/loot-core/src/server/accounts/link.ts` | 📈 +678 B (+164.96%) | 411 B → 1.06 kB `home/runner/work/actual/actual/packages/loot-core/src/server/accounts/app.ts` | 📈 +3.65 kB (+17.01%) | 21.46 kB → 25.11 kB `methods.ts` | 📈 +318 B (+5.62%) | 5.53 kB → 5.84 kB `home/runner/work/actual/actual/packages/loot-core/src/server/api.ts` | 📈 +506 B (+2.24%) | 22.05 kB → 22.54 kB </details> <details> <summary>View detailed bundle breakdown</summary> <div> **Added** No assets were added **Removed** No assets were removed **Bigger** Asset | File Size | % Changed ----- | --------- | --------- index.js | 3.89 MB → 3.9 MB (+5.12 kB) | +0.13% **Smaller** No assets were smaller **Unchanged** Asset | File Size | % Changed ----- | --------- | --------- models.js | 0 B | 0% </div> </details> --- #### cli **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 1 | 7.97 MB | 0% <details> <summary>View detailed bundle breakdown</summary> <div> **Added** No assets were added **Removed** No assets were removed **Bigger** No assets were bigger **Smaller** No assets were smaller **Unchanged** Asset | File Size | % Changed ----- | --------- | --------- cli.js | 7.97 MB | 0% </div> </details> --- #### crdt **Total** Files count | Total bundle size | % Changed ----------- | ----------------- | --------- 1 | 41.83 kB | 0% <details> <summary>View detailed bundle breakdown</summary> <div> **Added** No assets were added **Removed** No assets were removed **Bigger** No assets were bigger **Smaller** No assets were smaller **Unchanged** Asset | File Size | % Changed ----- | --------- | --------- index.js | 41.83 kB | 0% </div> </details> </details> <!--- bundlestats-action-comment key:combined end ---> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-11 22:27:58 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#65291