diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx
index b78573b3c644e04d956063611544322b693a2f57..ebec57e5e4be0da398a90781dfe9fba5507bc7a6 100644
--- a/packages/desktop-client/src/components/Modals.tsx
+++ b/packages/desktop-client/src/components/Modals.tsx
@@ -32,6 +32,7 @@ import { PlaidExternalMsg } from './modals/PlaidExternalMsg';
 import { ReportBudgetSummary } from './modals/ReportBudgetSummary';
 import { RolloverBudgetSummary } from './modals/RolloverBudgetSummary';
 import { SelectLinkedAccounts } from './modals/SelectLinkedAccounts';
+import { SimpleFinInitialise } from './modals/SimpleFinInitialise';
 import { SingleInput } from './modals/SingleInput';
 import { SwitchBudgetType } from './modals/SwitchBudgetType';
 import { DiscoverSchedules } from './schedules/DiscoverSchedules';
@@ -80,6 +81,7 @@ export function Modals() {
             <CreateAccount
               modalProps={modalProps}
               syncServerStatus={syncServerStatus}
+              upgradingAccountId={options?.upgradingAccountId}
             />
           );
 
@@ -109,6 +111,7 @@ export function Modals() {
               requisitionId={options.requisitionId}
               localAccounts={accounts.filter(acct => acct.closed === 0)}
               actions={actions}
+              syncSource={options.syncSource}
             />
           );
 
@@ -196,6 +199,14 @@ export function Modals() {
             />
           );
 
+        case 'simplefin-init':
+          return (
+            <SimpleFinInitialise
+              modalProps={modalProps}
+              onSuccess={options.onSuccess}
+            />
+          );
+
         case 'gocardless-external-msg':
           return (
             <GoCardlessExternalMsg
diff --git a/packages/desktop-client/src/components/accounts/Account.jsx b/packages/desktop-client/src/components/accounts/Account.jsx
index e61c46c68ad08befe6a900f0e2219e07662f5aa7..37653a025b81d6de294311e56c2be47945431422 100644
--- a/packages/desktop-client/src/components/accounts/Account.jsx
+++ b/packages/desktop-client/src/components/accounts/Account.jsx
@@ -26,7 +26,6 @@ import {
 } from 'loot-core/src/shared/transactions';
 import { applyChanges, groupById } from 'loot-core/src/shared/util';
 
-import { authorizeBank } from '../../gocardless';
 import { useCategories } from '../../hooks/useCategories';
 import { SelectedProviderWithItems } from '../../hooks/useSelected';
 import { styles, theme } from '../../style';
@@ -589,7 +588,9 @@ class AccountInternal extends PureComponent {
 
     switch (item) {
       case 'link':
-        authorizeBank(this.props.pushModal, { upgradingAccountId: accountId });
+        this.props.pushModal('add-account', {
+          upgradingAccountId: accountId,
+        });
         break;
       case 'unlink':
         this.props.unlinkAccount(accountId);
diff --git a/packages/desktop-client/src/components/modals/CreateAccount.tsx b/packages/desktop-client/src/components/modals/CreateAccount.tsx
index fab820fb99a9226e0612e1d25ba58db7e9aca109..c577ab7f8d220c0d968857f2df070df1306a4aba 100644
--- a/packages/desktop-client/src/components/modals/CreateAccount.tsx
+++ b/packages/desktop-client/src/components/modals/CreateAccount.tsx
@@ -1,9 +1,13 @@
 // @ts-strict-ignore
 import React, { useEffect, useState } from 'react';
 
+import { send } from 'loot-core/src/platform/client/fetch';
+
 import { authorizeBank } from '../../gocardless';
 import { useActions } from '../../hooks/useActions';
+import { useFeatureFlag } from '../../hooks/useFeatureFlag';
 import { useGoCardlessStatus } from '../../hooks/useGoCardlessStatus';
+import { useSimpleFinStatus } from '../../hooks/useSimpleFinStatus';
 import { type SyncServerStatus } from '../../hooks/useSyncServerStatus';
 import { theme } from '../../style';
 import { type CommonModalProps } from '../../types/modals';
@@ -17,23 +21,75 @@ import { View } from '../common/View';
 type CreateAccountProps = {
   modalProps: CommonModalProps;
   syncServerStatus: SyncServerStatus;
+  upgradingAccountId?: string;
 };
 
 export function CreateAccount({
   modalProps,
   syncServerStatus,
+  upgradingAccountId,
 }: CreateAccountProps) {
   const actions = useActions();
   const [isGoCardlessSetupComplete, setIsGoCardlessSetupComplete] =
     useState(null);
+  const [isSimpleFinSetupComplete, setIsSimpleFinSetupComplete] =
+    useState(null);
 
-  const onConnect = () => {
+  const onConnectGoCardless = () => {
     if (!isGoCardlessSetupComplete) {
       onGoCardlessInit();
       return;
     }
 
-    authorizeBank(actions.pushModal);
+    if (upgradingAccountId == null) {
+      authorizeBank(actions.pushModal);
+    } else {
+      authorizeBank(actions.pushModal, {
+        upgradingAccountId,
+      });
+    }
+  };
+
+  const onConnectSimpleFin = async () => {
+    if (!isSimpleFinSetupComplete) {
+      onSimpleFinInit();
+      return;
+    }
+
+    if (loadingSimpleFinAccounts) {
+      return;
+    }
+
+    setLoadingSimpleFinAccounts(true);
+
+    const results = await send('simplefin-accounts');
+
+    const newAccounts = [];
+
+    type NormalizedAccount = {
+      account_id: string;
+      name: string;
+      institution: string;
+      orgDomain: string;
+    };
+
+    for (const oldAccount of results.accounts) {
+      const newAccount: NormalizedAccount = {
+        account_id: oldAccount.id,
+        name: oldAccount.name,
+        institution: oldAccount.org.name,
+        orgDomain: oldAccount.org.domain,
+      };
+
+      newAccounts.push(newAccount);
+    }
+
+    actions.pushModal('select-linked-accounts', {
+      accounts: newAccounts,
+      syncSource: 'simpleFin',
+    });
+
+    setLoadingSimpleFinAccounts(false);
   };
 
   const onGoCardlessInit = () => {
@@ -42,45 +98,68 @@ export function CreateAccount({
     });
   };
 
+  const onSimpleFinInit = () => {
+    actions.pushModal('simplefin-init', {
+      onSuccess: () => setIsSimpleFinSetupComplete(true),
+    });
+  };
+
   const onCreateLocalAccount = () => {
     actions.pushModal('add-local-account');
   };
 
-  const { configured } = useGoCardlessStatus();
+  const { configuredGoCardless } = useGoCardlessStatus();
   useEffect(() => {
-    setIsGoCardlessSetupComplete(configured);
-  }, [configured]);
+    setIsGoCardlessSetupComplete(configuredGoCardless);
+  }, [configuredGoCardless]);
+
+  const { configuredSimpleFin } = useSimpleFinStatus();
+  useEffect(() => {
+    setIsSimpleFinSetupComplete(configuredSimpleFin);
+  }, [configuredSimpleFin]);
+
+  let title = 'Add Account';
+  const [loadingSimpleFinAccounts, setLoadingSimpleFinAccounts] =
+    useState(false);
+
+  if (upgradingAccountId != null) {
+    title = 'Link Account';
+  }
+
+  const simpleFinSyncFeatureFlag = useFeatureFlag('simpleFinSync');
 
   return (
-    <Modal title="Add Account" {...modalProps}>
+    <Modal title={title} {...modalProps}>
       {() => (
         <View style={{ maxWidth: 500, gap: 30, color: theme.pageText }}>
-          <View style={{ gap: 10 }}>
-            <Button
-              type="primary"
-              style={{
-                padding: '10px 0',
-                fontSize: 15,
-                fontWeight: 600,
-              }}
-              onClick={onCreateLocalAccount}
-            >
-              Create local account
-            </Button>
-            <View style={{ lineHeight: '1.4em', fontSize: 15 }}>
-              <Text>
-                <strong>Create a local account</strong> if you want to add
-                transactions manually. You can also{' '}
-                <ExternalLink
-                  to="https://actualbudget.org/docs/transactions/importing"
-                  linkColor="muted"
-                >
-                  import QIF/OFX/QFX files into a local account
-                </ExternalLink>
-                .
-              </Text>
+          {upgradingAccountId == null && (
+            <View style={{ gap: 10 }}>
+              <Button
+                type="primary"
+                style={{
+                  padding: '10px 0',
+                  fontSize: 15,
+                  fontWeight: 600,
+                }}
+                onClick={onCreateLocalAccount}
+              >
+                Create local account
+              </Button>
+              <View style={{ lineHeight: '1.4em', fontSize: 15 }}>
+                <Text>
+                  <strong>Create a local account</strong> if you want to add
+                  transactions manually. You can also{' '}
+                  <ExternalLink
+                    to="https://actualbudget.org/docs/transactions/importing"
+                    linkColor="muted"
+                  >
+                    import QIF/OFX/QFX files into a local account
+                  </ExternalLink>
+                  .
+                </Text>
+              </View>
             </View>
-          </View>
+          )}
           <View style={{ gap: 10 }}>
             {syncServerStatus === 'online' ? (
               <>
@@ -92,17 +171,46 @@ export function CreateAccount({
                     fontWeight: 600,
                     flex: 1,
                   }}
-                  onClick={onConnect}
+                  onClick={onConnectGoCardless}
                 >
                   {isGoCardlessSetupComplete
                     ? 'Link bank account with GoCardless'
                     : 'Set up GoCardless for bank sync'}
                 </ButtonWithLoading>
                 <Text style={{ lineHeight: '1.4em', fontSize: 15 }}>
-                  <strong>Link a bank account</strong> to automatically download
-                  transactions. GoCardless provides reliable, up-to-date
-                  information from hundreds of banks.
+                  <strong>
+                    Link a <u>European</u> bank account
+                  </strong>{' '}
+                  to automatically download transactions. GoCardless provides
+                  reliable, up-to-date information from hundreds of banks.
                 </Text>
+                {simpleFinSyncFeatureFlag === true && (
+                  <>
+                    <ButtonWithLoading
+                      disabled={syncServerStatus !== 'online'}
+                      loading={loadingSimpleFinAccounts}
+                      style={{
+                        marginTop: '18px',
+                        padding: '10px 0',
+                        fontSize: 15,
+                        fontWeight: 600,
+                        flex: 1,
+                      }}
+                      onClick={onConnectSimpleFin}
+                    >
+                      {isSimpleFinSetupComplete
+                        ? 'Link bank account with SimpleFIN'
+                        : 'Set up SimpleFIN for bank sync'}
+                    </ButtonWithLoading>
+                    <Text style={{ lineHeight: '1.4em', fontSize: 15 }}>
+                      <strong>
+                        Link a <u>North American</u> bank account
+                      </strong>{' '}
+                      to automatically download transactions. SimpleFIN provides
+                      reliable, up-to-date information from hundreds of banks.
+                    </Text>
+                  </>
+                )}
               </>
             ) : (
               <>
@@ -114,7 +222,7 @@ export function CreateAccount({
                     fontWeight: 600,
                   }}
                 >
-                  Set up GoCardless for bank sync
+                  Set up bank sync
                 </Button>
                 <Paragraph style={{ fontSize: 15 }}>
                   Connect to an Actual server to set up{' '}
@@ -122,7 +230,7 @@ export function CreateAccount({
                     to="https://actualbudget.org/docs/advanced/bank-sync"
                     linkColor="muted"
                   >
-                    automatic syncing with GoCardless
+                    automatic syncing.
                   </ExternalLink>
                   .
                 </Paragraph>
diff --git a/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.tsx b/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.tsx
index dbb74a9562bd0fdff7fe03b69067df3706b7db51..9e3b4cfc9b11098c22f68d3d92d18e9d07fae7f9 100644
--- a/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.tsx
+++ b/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.tsx
@@ -110,8 +110,10 @@ export function GoCardlessExternalMsg({
     isLoading: isBankOptionsLoading,
     isError: isBankOptionError,
   } = useAvailableBanks(country);
-  const { configured: isConfigured, isLoading: isConfigurationLoading } =
-    useGoCardlessStatus();
+  const {
+    configuredGoCardless: isConfigured,
+    isLoading: isConfigurationLoading,
+  } = useGoCardlessStatus();
 
   async function onJump() {
     setError(null);
diff --git a/packages/desktop-client/src/components/modals/SelectLinkedAccounts.jsx b/packages/desktop-client/src/components/modals/SelectLinkedAccounts.jsx
index a5a2aadcd1edb38ec411c22eb18db203edbfcc76..8d8d82475bc1e5f7b4b448eb0a3b1a3e3350647c 100644
--- a/packages/desktop-client/src/components/modals/SelectLinkedAccounts.jsx
+++ b/packages/desktop-client/src/components/modals/SelectLinkedAccounts.jsx
@@ -16,6 +16,7 @@ export function SelectLinkedAccounts({
   externalAccounts,
   localAccounts,
   actions,
+  syncSource,
 }) {
   const [chosenAccounts, setChosenAccounts] = useState(() => {
     return Object.fromEntries(
@@ -49,13 +50,22 @@ export function SelectLinkedAccounts({
         }
 
         // Finally link the matched account
-        actions.linkAccount(
-          requisitionId,
-          externalAccount,
-          chosenLocalAccountId !== addAccountOption.id
-            ? chosenLocalAccountId
-            : undefined,
-        );
+        if (syncSource === 'simpleFin') {
+          actions.linkAccountSimpleFin(
+            externalAccount,
+            chosenLocalAccountId !== addAccountOption.id
+              ? chosenLocalAccountId
+              : undefined,
+          );
+        } else {
+          actions.linkAccount(
+            requisitionId,
+            externalAccount,
+            chosenLocalAccountId !== addAccountOption.id
+              ? chosenLocalAccountId
+              : undefined,
+          );
+        }
       },
     );
 
diff --git a/packages/desktop-client/src/components/modals/SimpleFinInitialise.tsx b/packages/desktop-client/src/components/modals/SimpleFinInitialise.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4db5bff8822bc9291ad3989384d479ef0d5c5eca
--- /dev/null
+++ b/packages/desktop-client/src/components/modals/SimpleFinInitialise.tsx
@@ -0,0 +1,88 @@
+// @ts-strict-ignore
+import React, { useState } from 'react';
+
+import { send } from 'loot-core/src/platform/client/fetch';
+
+import { Error } from '../alerts';
+import { ButtonWithLoading } from '../common/Button';
+import { ExternalLink } from '../common/ExternalLink';
+import { Input } from '../common/Input';
+import { Modal, ModalButtons } from '../common/Modal';
+import type { ModalProps } from '../common/Modal';
+import { Text } from '../common/Text';
+import { View } from '../common/View';
+import { FormField, FormLabel } from '../forms';
+
+type SimpleFinInitialiseProps = {
+  modalProps?: Partial<ModalProps>;
+  onSuccess: () => void;
+};
+
+export const SimpleFinInitialise = ({
+  modalProps,
+  onSuccess,
+}: SimpleFinInitialiseProps) => {
+  const [token, setToken] = useState('');
+  const [isValid, setIsValid] = useState(true);
+  const [isLoading, setIsLoading] = useState(false);
+
+  const onSubmit = async () => {
+    if (!token) {
+      setIsValid(false);
+      return;
+    }
+
+    setIsLoading(true);
+
+    await send('secret-set', {
+      name: 'simplefin_token',
+      value: token,
+    });
+
+    onSuccess();
+    modalProps.onClose();
+    setIsLoading(false);
+  };
+
+  return (
+    <Modal title="Set-up SimpleFIN" size={{ width: 300 }} {...modalProps}>
+      <View style={{ display: 'flex', gap: 10 }}>
+        <Text>
+          In order to enable bank-sync via SimpleFIN (only for North American
+          banks) you will need to create a token. This can be done by creating
+          an account with{' '}
+          <ExternalLink
+            to="https://beta-bridge.simplefin.org/"
+            linkColor="purple"
+          >
+            SimpleFIN
+          </ExternalLink>
+          .
+        </Text>
+
+        <FormField>
+          <FormLabel title="Token:" htmlFor="token-field" />
+          <Input
+            id="token-field"
+            type="password"
+            value={token}
+            onUpdate={setToken}
+            onChange={() => setIsValid(true)}
+          />
+        </FormField>
+
+        {!isValid && <Error>It is required to provide a token.</Error>}
+      </View>
+
+      <ModalButtons>
+        <ButtonWithLoading
+          type="primary"
+          loading={isLoading}
+          onClick={onSubmit}
+        >
+          Save and continue
+        </ButtonWithLoading>
+      </ModalButtons>
+    </Modal>
+  );
+};
diff --git a/packages/desktop-client/src/components/settings/Experimental.tsx b/packages/desktop-client/src/components/settings/Experimental.tsx
index 28ed21722375b7ab5bb64cec703ad2fa17ca40df..6a67a406e1cd1abcc006b7f8c751c65c76cae478 100644
--- a/packages/desktop-client/src/components/settings/Experimental.tsx
+++ b/packages/desktop-client/src/components/settings/Experimental.tsx
@@ -94,6 +94,7 @@ export function ExperimentalFeatures() {
             <FeatureToggle flag="goalTemplatesEnabled">
               Goal templates
             </FeatureToggle>
+            <FeatureToggle flag="simpleFinSync">SimpleFIN sync</FeatureToggle>
           </View>
         ) : (
           <LinkButton
diff --git a/packages/desktop-client/src/gocardless.ts b/packages/desktop-client/src/gocardless.ts
index 6f5e27794ed55c58787a8dba25e0b1cc5c4daf65..809c9f86c6edbc0b386ef6304a23b5834137a52f 100644
--- a/packages/desktop-client/src/gocardless.ts
+++ b/packages/desktop-client/src/gocardless.ts
@@ -47,6 +47,7 @@ export async function authorizeBank(
         accounts: data.accounts,
         requisitionId: data.id,
         upgradingAccountId,
+        syncSource: 'goCardless',
       });
     },
   });
diff --git a/packages/desktop-client/src/hooks/useFeatureFlag.ts b/packages/desktop-client/src/hooks/useFeatureFlag.ts
index 5b506dc4d58b1cdea66323748a4df93286f1e2e8..08338bb3c0a7fb82b6ba715dcefc1f189fa1e4f8 100644
--- a/packages/desktop-client/src/hooks/useFeatureFlag.ts
+++ b/packages/desktop-client/src/hooks/useFeatureFlag.ts
@@ -9,6 +9,7 @@ const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = {
   reportBudget: false,
   goalTemplatesEnabled: false,
   customReports: false,
+  simpleFinSync: false,
 };
 
 export function useFeatureFlag(name: FeatureFlag): boolean {
diff --git a/packages/desktop-client/src/hooks/useGoCardlessStatus.ts b/packages/desktop-client/src/hooks/useGoCardlessStatus.ts
index 22927722e2a9eff5ff0d85a15890f25e933cc006..9b65cd43494b7191ef9e0e7317c78fa4ab3f7bd5 100644
--- a/packages/desktop-client/src/hooks/useGoCardlessStatus.ts
+++ b/packages/desktop-client/src/hooks/useGoCardlessStatus.ts
@@ -5,7 +5,9 @@ import { send } from 'loot-core/src/platform/client/fetch';
 import { useSyncServerStatus } from './useSyncServerStatus';
 
 export function useGoCardlessStatus() {
-  const [configured, setConfigured] = useState<boolean | null>(null);
+  const [configuredGoCardless, setConfiguredGoCardless] = useState<
+    boolean | null
+  >(null);
   const [isLoading, setIsLoading] = useState(false);
   const status = useSyncServerStatus();
 
@@ -15,7 +17,7 @@ export function useGoCardlessStatus() {
 
       const results = await send('gocardless-status');
 
-      setConfigured(results.configured || false);
+      setConfiguredGoCardless(results.configured || false);
       setIsLoading(false);
     }
 
@@ -25,7 +27,7 @@ export function useGoCardlessStatus() {
   }, [status]);
 
   return {
-    configured,
+    configuredGoCardless,
     isLoading,
   };
 }
diff --git a/packages/desktop-client/src/hooks/useSimpleFinStatus.ts b/packages/desktop-client/src/hooks/useSimpleFinStatus.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3f4a06fae185dc052464e7bb9d4e6178df37790
--- /dev/null
+++ b/packages/desktop-client/src/hooks/useSimpleFinStatus.ts
@@ -0,0 +1,33 @@
+import { useEffect, useState } from 'react';
+
+import { send } from 'loot-core/src/platform/client/fetch';
+
+import { useSyncServerStatus } from './useSyncServerStatus';
+
+export function useSimpleFinStatus() {
+  const [configuredSimpleFin, setConfiguredSimpleFin] = useState<
+    boolean | null
+  >(null);
+  const [isLoading, setIsLoading] = useState(false);
+  const status = useSyncServerStatus();
+
+  useEffect(() => {
+    async function fetch() {
+      setIsLoading(true);
+
+      const results = await send('simplefin-status');
+
+      setConfiguredSimpleFin(results.configured || false);
+      setIsLoading(false);
+    }
+
+    if (status === 'online') {
+      fetch();
+    }
+  }, [status]);
+
+  return {
+    configuredSimpleFin,
+    isLoading,
+  };
+}
diff --git a/packages/loot-core/migrations/1704572023730_add_account_sync_source.sql b/packages/loot-core/migrations/1704572023730_add_account_sync_source.sql
new file mode 100644
index 0000000000000000000000000000000000000000..3aae8da909ef0f62ce3f63cdd5a4f1487001b8b4
--- /dev/null
+++ b/packages/loot-core/migrations/1704572023730_add_account_sync_source.sql
@@ -0,0 +1,11 @@
+BEGIN TRANSACTION;
+
+ALTER TABLE accounts ADD COLUMN account_sync_source TEXT;
+
+UPDATE accounts SET
+  account_sync_source = CASE
+    WHEN account_id THEN 'goCardless'
+    ELSE NULL
+  END;
+
+COMMIT;
diff --git a/packages/loot-core/src/client/actions/account.ts b/packages/loot-core/src/client/actions/account.ts
index 3591f96a8661b25fe9af1f809c817472bb582d31..c8349d84c578607b951c5dedb941ca14274a4bf7 100644
--- a/packages/loot-core/src/client/actions/account.ts
+++ b/packages/loot-core/src/client/actions/account.ts
@@ -76,6 +76,17 @@ export function linkAccount(requisitionId, account, upgradingId) {
   };
 }
 
+export function linkAccountSimpleFin(externalAccount, upgradingId) {
+  return async (dispatch: Dispatch) => {
+    await send('simplefin-accounts-link', {
+      externalAccount,
+      upgradingId,
+    });
+    await dispatch(getPayees());
+    await dispatch(getAccounts());
+  };
+}
+
 // TODO: type correctly or remove (unused)
 export function connectAccounts(
   institution,
diff --git a/packages/loot-core/src/client/state-types/modals.d.ts b/packages/loot-core/src/client/state-types/modals.d.ts
index c1d3a9ee339a71fc268183e421804d6a88baf57f..e25e38feb327a3ee438d2714547abcecc13d6115 100644
--- a/packages/loot-core/src/client/state-types/modals.d.ts
+++ b/packages/loot-core/src/client/state-types/modals.d.ts
@@ -39,6 +39,7 @@ type FinanceModals = {
     accounts: unknown[];
     requisitionId: string;
     upgradingAccountId?: string;
+    syncSource?: AccountSyncSource;
   };
 
   'confirm-category-delete': { onDelete: () => void } & (
@@ -67,6 +68,10 @@ type FinanceModals = {
   'gocardless-init': {
     onSuccess: () => void;
   };
+  'simplefin-init': {
+    onSuccess: () => void;
+  };
+
   'gocardless-external-msg': {
     onMoveExternal: (arg: {
       institutionId: string;
diff --git a/packages/loot-core/src/server/accounts/link.ts b/packages/loot-core/src/server/accounts/link.ts
index 52e8d071b9084b098637ef8a26e268743b5a3737..ff2618af87d94171bc403a29d805f745bd9a985b 100644
--- a/packages/loot-core/src/server/accounts/link.ts
+++ b/packages/loot-core/src/server/accounts/link.ts
@@ -144,7 +144,7 @@ export async function addGoCardlessAccounts(
       });
 
       // Do an initial sync
-      await bankSync.syncGoCardlessAccount(
+      await bankSync.syncExternalAccount(
         userId,
         userKey,
         id,
diff --git a/packages/loot-core/src/server/accounts/sync.ts b/packages/loot-core/src/server/accounts/sync.ts
index fa6d3134057bb8516ec30fce3ddc3dba1bb17dd3..efd4b9cf391dd78ddd85fed1cb3484560a930143 100644
--- a/packages/loot-core/src/server/accounts/sync.ts
+++ b/packages/loot-core/src/server/accounts/sync.ts
@@ -216,6 +216,38 @@ async function downloadGoCardlessTransactions(
   };
 }
 
+async function downloadSimpleFinTransactions(acctId, since) {
+  const userToken = await asyncStorage.getItem('user-token');
+  if (!userToken) return;
+
+  const res = await post(
+    getServer().SIMPLEFIN_SERVER + '/transactions',
+    {
+      accountId: acctId,
+      startDate: since,
+    },
+    {
+      'X-ACTUAL-TOKEN': userToken,
+    },
+  );
+
+  if (res.error_code) {
+    throw BankSyncError(res.error_type, res.error_code);
+  }
+
+  const {
+    transactions: { all },
+    balances,
+    startingBalance,
+  } = res;
+
+  return {
+    transactions: all,
+    accountBalance: balances,
+    startingBalance,
+  };
+}
+
 async function resolvePayee(trans, payeeName, payeesToCreate) {
   if (trans.payee == null && payeeName) {
     // First check our registry of new payees (to avoid a db access)
@@ -778,13 +810,7 @@ export async function addTransactions(
   return newTransactions;
 }
 
-export async function syncGoCardlessAccount(
-  userId,
-  userKey,
-  id,
-  acctId,
-  bankId,
-) {
+export async function syncExternalAccount(userId, userKey, id, acctId, bankId) {
   // TODO: Handle the case where transactions exist in the future
   // (that will make start date after end date)
   const latestTransaction = await db.first(
@@ -814,14 +840,21 @@ export async function syncGoCardlessAccount(
       ]),
     );
 
-    const { transactions: originalTransactions, accountBalance } =
-      await downloadGoCardlessTransactions(
+    let download;
+
+    if (acctRow.account_sync_source === 'simpleFin') {
+      download = await downloadSimpleFinTransactions(acctId, startDate);
+    } else if (acctRow.account_sync_source === 'goCardless') {
+      download = await downloadGoCardlessTransactions(
         userId,
         userKey,
         acctId,
         bankId,
         startDate,
       );
+    }
+
+    const { transactions: originalTransactions, accountBalance } = download;
 
     if (originalTransactions.length === 0) {
       return { added: [], updated: [] };
@@ -841,20 +874,33 @@ export async function syncGoCardlessAccount(
     // Otherwise, download transaction for the past 90 days
     const startingDay = monthUtils.subDays(monthUtils.currentDay(), 90);
 
-    const { transactions, startingBalance } =
-      await downloadGoCardlessTransactions(
+    let download;
+
+    if (acctRow.account_sync_source === 'simpleFin') {
+      download = await downloadSimpleFinTransactions(acctId, startingDay);
+    } else if (acctRow.account_sync_source === 'goCardless') {
+      download = await downloadGoCardlessTransactions(
         userId,
         userKey,
         acctId,
         bankId,
         startingDay,
       );
+    }
 
-    // We need to add a transaction that represents the starting
-    // balance for everything to balance out. In order to get balance
-    // before the first imported transaction, we need to get the
-    // current balance from the accounts table and subtract all the
-    // imported transactions.
+    const { transactions, startingBalance } = download;
+
+    let balanceToUse = startingBalance;
+
+    if (acctRow.account_sync_source === 'simpleFin') {
+      const currentBalance = startingBalance;
+      const previousBalance = transactions.reduce((total, trans) => {
+        return (
+          total - parseInt(trans.transactionAmount.amount.replace('.', ''))
+        );
+      }, currentBalance);
+      balanceToUse = previousBalance;
+    }
 
     const oldestTransaction = transactions[transactions.length - 1];
 
@@ -868,7 +914,7 @@ export async function syncGoCardlessAccount(
     return runMutator(async () => {
       const initialId = await db.insertTransaction({
         account: id,
-        amount: startingBalance,
+        amount: balanceToUse,
         category: acctRow.offbudget === 0 ? payee.category : null,
         payee: payee.id,
         date: oldestDate,
diff --git a/packages/loot-core/src/server/aql/schema/index.ts b/packages/loot-core/src/server/aql/schema/index.ts
index 9d893e6b3dac122576a60c0742df72eaad455974..c129710188dc4f22ff9f5aa7b2c8b959cc01bc78 100644
--- a/packages/loot-core/src/server/aql/schema/index.ts
+++ b/packages/loot-core/src/server/aql/schema/index.ts
@@ -69,6 +69,7 @@ export const schema = {
     closed: f('boolean'),
     sort_order: f('float'),
     tombstone: f('boolean'),
+    account_sync_source: f('string'),
   },
   categories: {
     id: f('id'),
diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts
index 0354083439514d5e405c33c4f3f1617983b26d0e..d0ecf74a040bb1ae331958b7b61588cd77ce82dc 100644
--- a/packages/loot-core/src/server/main.ts
+++ b/packages/loot-core/src/server/main.ts
@@ -671,6 +671,7 @@ handlers['gocardless-accounts-link'] = async function ({
       id,
       account_id: account.account_id,
       bank: bank.id,
+      account_sync_source: 'goCardless',
     });
   } else {
     id = uuidv4();
@@ -681,6 +682,7 @@ handlers['gocardless-accounts-link'] = async function ({
       name: account.name,
       official_name: account.official_name,
       bank: bank.id,
+      account_sync_source: 'goCardless',
     });
     await db.insertPayee({
       name: '',
@@ -688,7 +690,7 @@ handlers['gocardless-accounts-link'] = async function ({
     });
   }
 
-  await bankSync.syncGoCardlessAccount(
+  await bankSync.syncExternalAccount(
     undefined,
     undefined,
     id,
@@ -704,6 +706,64 @@ handlers['gocardless-accounts-link'] = async function ({
   return 'ok';
 };
 
+handlers['simplefin-accounts-link'] = async function ({
+  externalAccount,
+  upgradingId,
+}) {
+  let id;
+
+  const institution = {
+    name: externalAccount.institution ?? 'Unknown',
+  };
+
+  const bank = await link.findOrCreateBank(
+    institution,
+    externalAccount.orgDomain,
+  );
+
+  if (upgradingId) {
+    const accRow = await db.first('SELECT * FROM accounts WHERE id = ?', [
+      upgradingId,
+    ]);
+    id = accRow.id;
+    await db.update('accounts', {
+      id,
+      account_id: externalAccount.account_id,
+      bank: bank.id,
+      account_sync_source: 'simpleFin',
+    });
+  } else {
+    id = uuidv4();
+    await db.insertWithUUID('accounts', {
+      id,
+      account_id: externalAccount.account_id,
+      name: externalAccount.name,
+      official_name: externalAccount.name,
+      bank: bank.id,
+      account_sync_source: 'simpleFin',
+    });
+    await db.insertPayee({
+      name: '',
+      transfer_acct: id,
+    });
+  }
+
+  await bankSync.syncExternalAccount(
+    undefined,
+    undefined,
+    id,
+    externalAccount.account_id,
+    bank.bank_id,
+  );
+
+  await connection.send('sync-event', {
+    type: 'success',
+    tables: ['transactions'],
+  });
+
+  return 'ok';
+};
+
 handlers['accounts-connect'] = async function ({
   institution,
   publicToken,
@@ -1119,6 +1179,38 @@ handlers['gocardless-status'] = async function () {
   );
 };
 
+handlers['simplefin-status'] = async function () {
+  const userToken = await asyncStorage.getItem('user-token');
+
+  if (!userToken) {
+    return { error: 'unauthorized' };
+  }
+
+  return post(
+    getServer().SIMPLEFIN_SERVER + '/status',
+    {},
+    {
+      'X-ACTUAL-TOKEN': userToken,
+    },
+  );
+};
+
+handlers['simplefin-accounts'] = async function () {
+  const userToken = await asyncStorage.getItem('user-token');
+
+  if (!userToken) {
+    return { error: 'unauthorized' };
+  }
+
+  return post(
+    getServer().SIMPLEFIN_SERVER + '/accounts',
+    {},
+    {
+      'X-ACTUAL-TOKEN': userToken,
+    },
+  );
+};
+
 handlers['gocardless-get-banks'] = async function (country) {
   const userToken = await asyncStorage.getItem('user-token');
 
@@ -1195,7 +1287,7 @@ handlers['gocardless-accounts-sync'] = async function ({ id }) {
     const acct = accounts[i];
     if (acct.bankId) {
       try {
-        const res = await bankSync.syncGoCardlessAccount(
+        const res = await bankSync.syncExternalAccount(
           userId,
           userKey,
           acct.id,
@@ -1281,6 +1373,10 @@ handlers['account-unlink'] = mutator(async function ({ id }) {
     return 'ok';
   }
 
+  const accRow = await db.first('SELECT * FROM accounts WHERE id = ?', [id]);
+
+  const isGoCardless = accRow.account_sync_source === 'goCardless';
+
   await db.updateAccount({
     id,
     account_id: null,
@@ -1288,8 +1384,13 @@ handlers['account-unlink'] = mutator(async function ({ id }) {
     balance_current: null,
     balance_available: null,
     balance_limit: null,
+    account_sync_source: null,
   });
 
+  if (isGoCardless === false) {
+    return;
+  }
+
   const { count } = await db.first(
     'SELECT COUNT(*) as count FROM accounts WHERE bank = ?',
     [bankId],
diff --git a/packages/loot-core/src/server/server-config.ts b/packages/loot-core/src/server/server-config.ts
index cde33082465ab7d16b27bcc8cc29c06d0c1f72c6..446d88eb03555e7f0eff5c7cd059e520cc8da556 100644
--- a/packages/loot-core/src/server/server-config.ts
+++ b/packages/loot-core/src/server/server-config.ts
@@ -6,6 +6,7 @@ type ServerConfig = {
   SIGNUP_SERVER: string;
   PLAID_SERVER: string;
   GOCARDLESS_SERVER: string;
+  SIMPLEFIN_SERVER: string;
 };
 
 let config: ServerConfig | null = null;
@@ -33,6 +34,7 @@ export function getServer(url?: string): ServerConfig | null {
       SIGNUP_SERVER: joinURL(url, '/account'),
       PLAID_SERVER: joinURL(url, '/plaid'),
       GOCARDLESS_SERVER: joinURL(url, '/gocardless'),
+      SIMPLEFIN_SERVER: joinURL(url, '/simplefin'),
     };
   }
   return config;
diff --git a/packages/loot-core/src/shared/transactions.test.ts b/packages/loot-core/src/shared/transactions.test.ts
index 6d701ca0e2f822b8ddb62f87f2a4cf7460cedcb4..1f81ecadd4711c8fa7935691b1cf4d3f312e61bf 100644
--- a/packages/loot-core/src/shared/transactions.test.ts
+++ b/packages/loot-core/src/shared/transactions.test.ts
@@ -30,6 +30,7 @@ function makeTransaction(data: Partial<TransactionEntity>): TransactionEntity {
       balance_current: null,
       balance_available: null,
       balance_limit: null,
+      account_sync_source: null,
     },
     ...data,
   };
diff --git a/packages/loot-core/src/types/models/account.d.ts b/packages/loot-core/src/types/models/account.d.ts
index 6bc9ce4d460cdedea92d74c29af30b48ff7ed1bc..cb668f6622f9bd7f486997b4002c705fecb34cfb 100644
--- a/packages/loot-core/src/types/models/account.d.ts
+++ b/packages/loot-core/src/types/models/account.d.ts
@@ -15,4 +15,7 @@ type _SyncFields<T> = {
   balance_current: T extends true ? number : null;
   balance_available: T extends true ? number : null;
   balance_limit: T extends true ? number : null;
+  account_sync_source: T extends true ? AccountSyncSource : null;
 };
+
+export type AccountSyncSource = 'simpleFin' | 'goCardless';
diff --git a/packages/loot-core/src/types/models/simplefin.d.ts b/packages/loot-core/src/types/models/simplefin.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5eddc6680ab7ab8963099761e0f0c975de32acd8
--- /dev/null
+++ b/packages/loot-core/src/types/models/simplefin.d.ts
@@ -0,0 +1,10 @@
+export type SimpleFinOrganization = {
+  name: string;
+  domain: string;
+};
+
+export type SimpleFinAccount = {
+  id: string;
+  name: string;
+  org: SimpleFinOrganization;
+};
diff --git a/packages/loot-core/src/types/prefs.d.ts b/packages/loot-core/src/types/prefs.d.ts
index 31a25962c49751ad636398a630af7f761a50930e..f483d672ef906475a96f17d7b221bb443154c281 100644
--- a/packages/loot-core/src/types/prefs.d.ts
+++ b/packages/loot-core/src/types/prefs.d.ts
@@ -5,7 +5,8 @@ export type FeatureFlag =
   | 'sankeyReport'
   | 'reportBudget'
   | 'goalTemplatesEnabled'
-  | 'customReports';
+  | 'customReports'
+  | 'simpleFinSync';
 
 export type LocalPrefs = Partial<
   {
diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts
index f5022b44440a5811600fc314ece078733a0fa649..1883a36ccac11d502f297d4a129cc3c2d6afda57 100644
--- a/packages/loot-core/src/types/server-handlers.d.ts
+++ b/packages/loot-core/src/types/server-handlers.d.ts
@@ -14,6 +14,7 @@ import {
   CategoryGroupEntity,
   GoCardlessToken,
   GoCardlessInstitution,
+  SimpleFinAccount,
   PayeeEntity,
 } from './models';
 import { EmptyObject } from './util';
@@ -161,6 +162,11 @@ export interface ServerHandlers {
     upgradingId;
   }) => Promise<'ok'>;
 
+  'simplefin-accounts-link': (arg: {
+    externalAccount;
+    upgradingId;
+  }) => Promise<'ok'>;
+
   'accounts-connect': (arg: {
     institution;
     publicToken;
@@ -216,6 +222,10 @@ export interface ServerHandlers {
 
   'gocardless-status': () => Promise<{ configured: boolean }>;
 
+  'simplefin-status': () => Promise<{ configured: boolean }>;
+
+  'simplefin-accounts': () => Promise<{ accounts: SimpleFinAccount[] }>;
+
   'gocardless-get-banks': (country: string) => Promise<{
     data: GoCardlessInstitution[];
     error?: { reason: string };
diff --git a/upcoming-release-notes/2188.md b/upcoming-release-notes/2188.md
new file mode 100644
index 0000000000000000000000000000000000000000..8029cdb4e41c464744f6bc813166730bbd67dbf7
--- /dev/null
+++ b/upcoming-release-notes/2188.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [zachwhelchel,duplaja,lancepick,latetedemelon]
+---
+
+Add option to link an account with SimpleFIN for syncing transactions.