diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx
index 6898967617e708fb1f0e43564cccf2f72d36ecca..38095bb40c5564bf197b0f361aa0f48caa53154d 100644
--- a/packages/desktop-client/src/components/Modals.tsx
+++ b/packages/desktop-client/src/components/Modals.tsx
@@ -38,7 +38,6 @@ import { ManageRulesModal } from './modals/ManageRulesModal';
 import { MergeUnusedPayees } from './modals/MergeUnusedPayees';
 import { Notes } from './modals/Notes';
 import { PayeeAutocompleteModal } from './modals/PayeeAutocompleteModal';
-import { PlaidExternalMsg } from './modals/PlaidExternalMsg';
 import { ReportBalanceMenuModal } from './modals/ReportBalanceMenuModal';
 import { ReportBudgetMenuModal } from './modals/ReportBudgetMenuModal';
 import { ReportBudgetSummaryModal } from './modals/ReportBudgetSummaryModal';
@@ -216,20 +215,6 @@ export function Modals() {
             />
           );
 
-        case 'plaid-external-msg':
-          return (
-            <PlaidExternalMsg
-              key={name}
-              modalProps={modalProps}
-              onMoveExternal={options.onMoveExternal}
-              onClose={() => {
-                options.onClose?.();
-                send('poll-web-token-stop');
-              }}
-              onSuccess={options.onSuccess}
-            />
-          );
-
         case 'gocardless-init':
           return (
             <GoCardlessInitialise
diff --git a/packages/desktop-client/src/components/modals/PlaidExternalMsg.tsx b/packages/desktop-client/src/components/modals/PlaidExternalMsg.tsx
deleted file mode 100644
index bab96140e80374152956f07c7ec816d459945017..0000000000000000000000000000000000000000
--- a/packages/desktop-client/src/components/modals/PlaidExternalMsg.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-// @ts-strict-ignore
-import React, { useState, useRef } from 'react';
-
-import { AnimatedLoading } from '../../icons/AnimatedLoading';
-import { theme } from '../../style';
-import { Error } from '../alerts';
-import { Button } from '../common/Button';
-import { Modal, ModalButtons } from '../common/Modal';
-import { Paragraph } from '../common/Paragraph';
-import { Text } from '../common/Text';
-import { View } from '../common/View';
-import { type CommonModalProps } from '../Modals';
-
-function renderError(error) {
-  return (
-    <Error style={{ alignSelf: 'center' }}>
-      {error === 'timeout'
-        ? 'Timed out. Please try again.'
-        : 'An error occurred while linking your account, sorry!'}
-    </Error>
-  );
-}
-
-type PlainExternalMsgProps = {
-  modalProps: CommonModalProps;
-  onMoveExternal: () => Promise<{ error; data }>;
-  onSuccess: (data: unknown) => Promise<void>;
-  onClose?: () => void;
-};
-
-export function PlaidExternalMsg({
-  modalProps,
-  onMoveExternal,
-  onSuccess,
-  onClose: originalOnClose,
-}: PlainExternalMsgProps) {
-  const [waiting, setWaiting] = useState(null);
-  const [success, setSuccess] = useState(false);
-  const [error, setError] = useState(null);
-  const data = useRef(null);
-
-  async function onJump() {
-    setError(null);
-    setWaiting('browser');
-
-    const res = await onMoveExternal();
-    if (res.error) {
-      setError(res.error);
-      setWaiting(null);
-      return;
-    }
-
-    data.current = res.data;
-    setWaiting(null);
-    setSuccess(true);
-  }
-
-  function onClose() {
-    originalOnClose?.();
-    modalProps.onClose();
-  }
-
-  async function onContinue() {
-    setWaiting('accounts');
-    await onSuccess(data.current);
-    setWaiting(null);
-  }
-
-  return (
-    <Modal
-      title="Link Your Bank"
-      {...modalProps}
-      onClose={onClose}
-      style={{ flex: 0 }}
-    >
-      {() => (
-        <View>
-          <Paragraph style={{ fontSize: 15 }}>
-            To link your bank account, you will be moved to your browser for
-            enhanced security. Click below and Actual will automatically resume
-            when you have given your bank’s credentials.
-          </Paragraph>
-          {error && renderError(error)}
-
-          {waiting ? (
-            <View style={{ alignItems: 'center', marginTop: 15 }}>
-              <AnimatedLoading
-                color={theme.pageTextDark}
-                style={{ width: 20, height: 20 }}
-              />
-              <View style={{ marginTop: 10, color: theme.pageText }}>
-                {waiting === 'browser'
-                  ? 'Waiting on browser...'
-                  : waiting === 'accounts'
-                    ? 'Loading accounts...'
-                    : null}
-              </View>
-            </View>
-          ) : success ? (
-            <Button
-              type="primary"
-              style={{
-                padding: '10px 0',
-                fontSize: 15,
-                fontWeight: 600,
-                marginTop: 10,
-              }}
-              onClick={onContinue}
-            >
-              Success! Click to continue &rarr;
-            </Button>
-          ) : (
-            <Button
-              type="primary"
-              style={{
-                padding: '10px 0',
-                fontSize: 15,
-                fontWeight: 600,
-                marginTop: 10,
-              }}
-              onClick={onJump}
-            >
-              Link bank in browser &rarr;
-            </Button>
-          )}
-          <div style={{ marginTop: waiting ? 30 : 35 }}>
-            <Text style={{ color: theme.pageText, fontWeight: 600 }}>
-              Why not link it in the app?
-            </Text>
-          </div>
-          <Text
-            style={{
-              marginTop: 10,
-              color: theme.pageText,
-              fontSize: 13,
-              '& a, & a:visited': {
-                color: theme.pageText,
-              },
-            }}
-          >
-            Typing your bank’s username and password is one of the most
-            security-sensitive things you can do, and the browser is the most
-            secure app in the world. Why not use it to make sure your
-            information is safe? [TODO: Link to docs article]
-          </Text>
-
-          <ModalButtons style={{ marginTop: 10 }}>
-            <Button onClick={() => modalProps.onBack()}>Back</Button>
-          </ModalButtons>
-        </View>
-      )}
-    </Modal>
-  );
-}
diff --git a/packages/desktop-client/src/plaid.js b/packages/desktop-client/src/plaid.js
deleted file mode 100644
index 7f54ca3528005eed14dae2ed1db342fcb37fbbb0..0000000000000000000000000000000000000000
--- a/packages/desktop-client/src/plaid.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* eslint-disable import/no-unused-modules */
-import { send } from 'loot-core/src/platform/client/fetch';
-
-function _authorize(pushModal, plaidToken, { onSuccess, onClose }) {
-  pushModal('plaid-external-msg', {
-    onMoveExternal: async () => {
-      const token = await send('create-web-token');
-      let url = 'http://link.actualbudget.com/?token=' + token;
-      // let url = 'http://localhost:8080/?token=' + token;
-      if (plaidToken) {
-        url = url + '&plaidToken=' + plaidToken;
-      }
-      window.Actual?.openURLInBrowser(url);
-
-      const { error, data } = await send('poll-web-token', { token });
-
-      return { error, data };
-    },
-
-    onClose,
-    onSuccess,
-  });
-}
-
-export async function authorizeBank(pushModal, { upgradingId } = {}) {
-  _authorize(pushModal, null, {
-    onSuccess: async data => {
-      pushModal('select-linked-accounts', {
-        institution: data.metadata.institution,
-        publicToken: data.publicToken,
-        accounts: data.metadata.accounts,
-        upgradingId,
-      });
-    },
-  });
-}
-
-export async function reauthorizeBank(pushModal, bankId, onSuccess) {
-  const { linkToken } = await send('make-plaid-public-token', {
-    bankId,
-  });
-
-  // We don't do anything with the error right now
-  if (!linkToken) {
-    return false;
-  }
-
-  // When the modal is closed here, always try to re-sync the account
-  // by calling `onSuccess`
-  _authorize(pushModal, linkToken, { onSuccess, onClose: onSuccess });
-  return true;
-}
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 88583c8fc55fa2abda1a4f18843163e1e958e31b..83646290c1d6560974e698a7eee443286baed893 100644
--- a/packages/loot-core/src/client/state-types/modals.d.ts
+++ b/packages/loot-core/src/client/state-types/modals.d.ts
@@ -59,12 +59,6 @@ type FinanceModals = {
     targetPayeeId: string;
   };
 
-  'plaid-external-msg': {
-    onMoveExternal: () => Promise<void>;
-    onClose?: () => void;
-    onSuccess: (data: unknown) => Promise<void>;
-  };
-
   'gocardless-init': {
     onSuccess: () => void;
   };
diff --git a/packages/loot-core/src/mocks/plaid.ts b/packages/loot-core/src/mocks/plaid.ts
deleted file mode 100644
index 74a37651833196148d4227012e0e31f1c13fff0d..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/mocks/plaid.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-// @ts-strict-ignore
-import { v4 as uuidv4 } from 'uuid';
-
-export function generateAccount(balance) {
-  return {
-    account_id: uuidv4(),
-    balances: {
-      available: balance,
-      current: balance,
-      limit: null,
-    },
-    mask: '0000',
-    name: 'Plaid Checking',
-    official_name: 'Plaid Interest Checking',
-    subtype: 'checking',
-    type: 'depository',
-  };
-}
-
-export function generateTransaction(
-  acctId,
-  amount,
-  name,
-  date,
-  pending = false,
-) {
-  return {
-    account_id: acctId,
-    account_owner: null,
-    amount,
-    category: [],
-    category_id: '',
-    date,
-    location: {
-      address: null,
-      city: null,
-      lat: null,
-      lon: null,
-      state: null,
-      store_number: null,
-      zip: null,
-    },
-    name,
-    payment_meta: {
-      by_order_of: null,
-      payee: null,
-      payer: null,
-      payment_method: null,
-      payment_processor: null,
-      ppd_id: null,
-      reason: null,
-      reference_number: null,
-    },
-    pending,
-    pending_transaction_id: null,
-    transaction_id: uuidv4(),
-    transaction_type: 'special',
-  };
-}
diff --git a/packages/loot-core/src/server/accounts/link.ts b/packages/loot-core/src/server/accounts/link.ts
index 2f7282c5c7a8521803f002f7eeba7d86c6e2d6ef..db48e8c25304efbb91d6cb961e85beaebdd0245d 100644
--- a/packages/loot-core/src/server/accounts/link.ts
+++ b/packages/loot-core/src/server/accounts/link.ts
@@ -1,46 +1,7 @@
 // @ts-strict-ignore
 import { v4 as uuidv4 } from 'uuid';
 
-import * as asyncStorage from '../../platform/server/asyncStorage';
-import { amountToInteger } from '../../shared/util';
 import * as db from '../db';
-import { runMutator } from '../mutators';
-import { post } from '../post';
-import { getServer } from '../server-config';
-
-import * as bankSync from './sync';
-
-export async function handoffPublicToken(institution, publicToken) {
-  const [[, userId], [, key]] = await asyncStorage.multiGet([
-    'user-id',
-    'user-key',
-  ]);
-
-  if (institution == null || !institution.institution_id || !institution.name) {
-    throw new Error('Invalid institution object');
-  }
-
-  const id = uuidv4();
-
-  // Make sure to generate an access token first before inserting it
-  // into our local database in case it fails
-  await post(getServer().PLAID_SERVER + '/handoff_public_token', {
-    userId,
-    key,
-    item_id: id,
-    public_token: publicToken,
-  });
-
-  await runMutator(() =>
-    db.insertWithUUID('banks', {
-      id,
-      bank_id: institution.institution_id,
-      name: institution.name,
-    }),
-  );
-
-  return id;
-}
 
 export async function findOrCreateBank(institution, requisitionId) {
   const bank = await db.first(
@@ -62,49 +23,3 @@ export async function findOrCreateBank(institution, requisitionId) {
 
   return bankData;
 }
-
-export async function addGoCardlessAccounts(
-  bankId,
-  accountIds,
-  offbudgetIds = [],
-) {
-  const [[, userId], [, userKey]] = await asyncStorage.multiGet([
-    'user-id',
-    'user-key',
-  ]);
-
-  // Get all the available accounts
-  let accounts = await bankSync.getGoCardlessAccounts(userId, userKey, bankId);
-
-  // Only add the selected accounts
-  accounts = accounts.filter(acct => accountIds.includes(acct.account_id));
-
-  return Promise.all(
-    accounts.map(async acct => {
-      const id = await runMutator(async () => {
-        const id = await db.insertAccount({
-          account_id: acct.account_id,
-          name: acct.name,
-          official_name: acct.official_name,
-          balance_current: amountToInteger(acct.balances.current),
-          mask: acct.mask,
-          bank: bankId,
-          offbudget: offbudgetIds.includes(acct.account_id) ? 1 : 0,
-        });
-
-        // Create a transfer payee
-        await db.insertPayee({
-          name: '',
-          transfer_acct: id,
-        });
-
-        return id;
-      });
-
-      // Do an initial sync
-      await bankSync.syncAccount(userId, userKey, id, acct.account_id, bankId);
-
-      return id;
-    }),
-  );
-}
diff --git a/packages/loot-core/src/server/accounts/sync.test.ts b/packages/loot-core/src/server/accounts/sync.test.ts
index 0c8c9e8e1196a57c123dc4be0f07206a92a04209..1ea00a4d0a59605f4a08428a8e43e7c1abb8f487 100644
--- a/packages/loot-core/src/server/accounts/sync.test.ts
+++ b/packages/loot-core/src/server/accounts/sync.test.ts
@@ -41,7 +41,7 @@ async function prepareDatabase() {
     is_income: 1,
   });
 
-  const { accounts } = await post(getServer().PLAID_SERVER + '/accounts', {
+  const { accounts } = await post(getServer().GOCARDLESS_SERVER + '/accounts', {
     client_id: '',
     group_id: '',
     item_id: '1',
diff --git a/packages/loot-core/src/server/accounts/sync.ts b/packages/loot-core/src/server/accounts/sync.ts
index 03bd3fb9daeadbb4a96d5910b12ffca5e901f4ec..d8f919524f0d5df02f4576a9a76f23e7bc0076e6 100644
--- a/packages/loot-core/src/server/accounts/sync.ts
+++ b/packages/loot-core/src/server/accounts/sync.ts
@@ -20,9 +20,6 @@ import { title } from './title';
 import { runRules } from './transaction-rules';
 import { batchUpdateTransactions } from './transactions';
 
-// Plaid article about API options:
-// https://support.plaid.com/customer/en/portal/articles/2612155-transactions-returned-per-request
-
 function BankSyncError(type: string, code: string) {
   return { type: 'BankSyncError', category: type, code };
 }
@@ -60,22 +57,6 @@ async function updateAccountBalance(id, balance) {
   ]);
 }
 
-export async function getAccounts(userId, userKey, id) {
-  const res = await post(getServer().PLAID_SERVER + '/accounts', {
-    userId,
-    key: userKey,
-    item_id: id,
-  });
-
-  const { accounts } = res;
-
-  accounts.forEach(acct => {
-    acct.balances.current = getAccountBalance(acct);
-  });
-
-  return accounts;
-}
-
 export async function getGoCardlessAccounts(userId, userKey, id) {
   const userToken = await asyncStorage.getItem('user-token');
   if (!userToken) return;
@@ -101,80 +82,6 @@ export async function getGoCardlessAccounts(userId, userKey, id) {
   return accounts;
 }
 
-export function fromPlaid(trans) {
-  return {
-    imported_id: trans.transaction_id,
-    payee_name: trans.name,
-    imported_payee: trans.name,
-    amount: -amountToInteger(trans.amount),
-    date: trans.date,
-  };
-}
-
-async function downloadTransactions(
-  userId,
-  userKey,
-  acctId,
-  bankId,
-  since,
-  count?: number,
-) {
-  let allTransactions = [];
-  let accountBalance = null;
-  const pageSize = 100;
-  let offset = 0;
-  let numDownloaded = 0;
-
-  while (1) {
-    const endDate = monthUtils.currentDay();
-
-    const res = await post(getServer().PLAID_SERVER + '/transactions', {
-      userId,
-      key: userKey,
-      item_id: '' + bankId,
-      account_id: acctId,
-      start_date: since,
-      end_date: endDate,
-      count: pageSize,
-      offset,
-    });
-
-    if (res.error_code) {
-      throw BankSyncError(res.error_type, res.error_code);
-    }
-
-    if (res.transactions.length === 0) {
-      break;
-    }
-
-    numDownloaded += res.transactions.length;
-
-    // Remove pending transactions for now - we will handle them in
-    // the future.
-    allTransactions = allTransactions.concat(
-      res.transactions.filter(t => !t.pending),
-    );
-    accountBalance = getAccountBalance(res.accounts[0]);
-
-    if (
-      numDownloaded === res.total_transactions ||
-      (count != null && allTransactions.length >= count)
-    ) {
-      break;
-    }
-
-    offset += pageSize;
-  }
-
-  allTransactions =
-    count != null ? allTransactions.slice(0, count) : allTransactions;
-
-  return {
-    transactions: allTransactions.map(fromPlaid),
-    accountBalance,
-  };
-}
-
 async function downloadGoCardlessTransactions(
   userId,
   userKey,
@@ -745,29 +652,8 @@ export async function syncAccount(
         startDate,
       );
     } else {
-      // Get all transactions since the latest transaction, plus any 5
-      // days before the latest transaction. This gives us a chance to
-      // resolve any transactions that were entered manually.
-      //
-      // TODO: What this really should do is query the last imported_id
-      // and since then
-      let date = monthUtils.subDays(
-        db.fromDateRepr(latestTransaction.date),
-        31,
-      );
-
-      // Never download transactions before the starting date. This was
-      // when the account was added to the system.
-      if (date < startingDate) {
-        date = startingDate;
-      }
-
-      download = await downloadTransactions(
-        userId,
-        userKey,
-        acctId,
-        bankId,
-        date,
+      throw new Error(
+        `Unrecognized bank-sync provider: ${acctRow.account_sync_source}`,
       );
     }
 
diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts
index 7fd73231948cc36dcc4adc170527c615ec86a990..aa13465ac81cc9ad8457e88ca64f3768084ac9d5 100644
--- a/packages/loot-core/src/server/main.ts
+++ b/packages/loot-core/src/server/main.ts
@@ -567,25 +567,6 @@ handlers['query'] = async function (query) {
   return aqlQuery(query);
 };
 
-handlers['bank-delete'] = async function ({ id }) {
-  const accts = await db.runQuery(
-    'SELECT * FROM accounts WHERE bank = ?',
-    [id],
-    true,
-  );
-
-  await db.delete_('banks', id);
-  await Promise.all(
-    accts.map(async acct => {
-      // TODO: This will not sync across devices because we are bypassing
-      // the "recorded" functions
-      await db.runQuery('DELETE FROM transactions WHERE acct = ?', [acct.id]);
-      await db.delete_('accounts', acct.id);
-    }),
-  );
-  return 'ok';
-};
-
 handlers['account-update'] = mutator(async function ({ id, name }) {
   return withUndo(async () => {
     await db.update('accounts', { id, name });
@@ -720,21 +701,6 @@ handlers['simplefin-accounts-link'] = async function ({
   return 'ok';
 };
 
-handlers['gocardless-accounts-connect'] = async function ({
-  institution,
-  publicToken,
-  accountIds,
-  offbudgetIds,
-}) {
-  const bankId = await link.handoffPublicToken(institution, publicToken);
-  const ids = await link.addGoCardlessAccounts(
-    bankId,
-    accountIds,
-    offbudgetIds,
-  );
-  return ids;
-};
-
 handlers['account-create'] = mutator(async function ({
   name,
   balance,
@@ -777,8 +743,8 @@ handlers['account-close'] = mutator(async function ({
   categoryId,
   forced,
 }) {
-  // Unlink the account if it's linked. This makes sure to remove it
-  // from Plaid. (This should not be undo-able, as it mutates the
+  // Unlink the account if it's linked. This makes sure to remove it from
+  // bank-sync providers. (This should not be undo-able, as it mutates the
   // remote server and the user will have to link the account again)
   await handlers['account-unlink']({ id });
 
@@ -878,142 +844,6 @@ handlers['account-move'] = mutator(async function ({ id, targetId }) {
 
 let stopPolling = false;
 
-handlers['poll-web-token'] = async function ({ token }) {
-  const [[, userId], [, key]] = await asyncStorage.multiGet([
-    'user-id',
-    'user-key',
-  ]);
-
-  const startTime = Date.now();
-  stopPolling = false;
-
-  async function getData(cb) {
-    if (stopPolling) {
-      return;
-    }
-
-    if (Date.now() - startTime >= 1000 * 60 * 10) {
-      cb('timeout');
-      return;
-    }
-
-    const data = await post(
-      getServer().PLAID_SERVER + '/get-web-token-contents',
-      {
-        userId,
-        key,
-        token,
-      },
-    );
-
-    if (data) {
-      if (data.error) {
-        cb('unknown');
-      } else {
-        cb(null, data);
-      }
-    } else {
-      setTimeout(() => getData(cb), 3000);
-    }
-  }
-
-  return new Promise(resolve => {
-    getData((error, data) => {
-      if (error) {
-        resolve({ error });
-      } else {
-        resolve({ data });
-      }
-    });
-  });
-};
-
-handlers['poll-web-token-stop'] = async function () {
-  stopPolling = true;
-  return 'ok';
-};
-
-handlers['accounts-sync'] = async function ({ id }) {
-  const [[, userId], [, userKey]] = await asyncStorage.multiGet([
-    'user-id',
-    'user-key',
-  ]);
-  let accounts = await db.runQuery(
-    `SELECT a.*, b.id as bankId FROM accounts a
-         LEFT JOIN banks b ON a.bank = b.id
-         WHERE a.tombstone = 0 AND a.closed = 0`,
-    [],
-    true,
-  );
-
-  if (id) {
-    accounts = accounts.filter(acct => acct.id === id);
-  }
-
-  const errors = [];
-  let newTransactions = [];
-  let matchedTransactions = [];
-  let updatedAccounts = [];
-
-  for (let i = 0; i < accounts.length; i++) {
-    const acct = accounts[i];
-    if (acct.bankId) {
-      try {
-        const res = await bankSync.syncAccount(
-          userId,
-          userKey,
-          acct.id,
-          acct.account_id,
-          acct.bankId,
-        );
-        const { added, updated } = res;
-
-        newTransactions = newTransactions.concat(added);
-        matchedTransactions = matchedTransactions.concat(updated);
-
-        if (added.length > 0 || updated.length > 0) {
-          updatedAccounts = updatedAccounts.concat(acct.id);
-        }
-      } catch (err) {
-        if (err.type === 'BankSyncError') {
-          errors.push({
-            type: 'SyncError',
-            accountId: acct.id,
-            message: 'Failed syncing account “' + acct.name + '.”',
-            category: err.category,
-            code: err.code,
-          });
-        } else if (err instanceof PostError && err.reason !== 'internal') {
-          errors.push({
-            accountId: acct.id,
-            message: `Account “${acct.name}” is not linked properly. Please link it again`,
-          });
-        } else {
-          errors.push({
-            accountId: acct.id,
-            message:
-              'There was an internal error. Please get in touch https://actualbudget.org/contact for support.',
-            internal: err.stack,
-          });
-
-          err.message = 'Failed syncing account: ' + err.message;
-
-          captureException(err);
-        }
-      }
-    }
-  }
-
-  if (updatedAccounts.length > 0) {
-    connection.send('sync-event', {
-      type: 'success',
-      tables: ['transactions'],
-    });
-  }
-
-  return { errors, newTransactions, matchedTransactions, updatedAccounts };
-};
-
 handlers['secret-set'] = async function ({ name, value }) {
   const userToken = await asyncStorage.getItem('user-token');
 
@@ -1370,25 +1200,6 @@ handlers['account-unlink'] = mutator(async function ({ id }) {
   return 'ok';
 });
 
-handlers['make-plaid-public-token'] = async function ({ bankId }) {
-  const [[, userId], [, userKey]] = await asyncStorage.multiGet([
-    'user-id',
-    'user-key',
-  ]);
-
-  const data = await post(getServer().PLAID_SERVER + '/make-public-token', {
-    userId,
-    key: userKey,
-    item_id: '' + bankId,
-  });
-
-  if (data.error_code) {
-    return { error: '', code: data.error_code, type: data.error_type };
-  }
-
-  return { linkToken: data.link_token };
-};
-
 handlers['save-global-prefs'] = async function (prefs) {
   if ('maxMonths' in prefs) {
     await asyncStorage.setItem('max-months', '' + prefs.maxMonths);
diff --git a/packages/loot-core/src/server/server-config.ts b/packages/loot-core/src/server/server-config.ts
index 446d88eb03555e7f0eff5c7cd059e520cc8da556..79f4933254e07ff76142875b4d71aedd79d0e1df 100644
--- a/packages/loot-core/src/server/server-config.ts
+++ b/packages/loot-core/src/server/server-config.ts
@@ -4,7 +4,6 @@ type ServerConfig = {
   BASE_SERVER: string;
   SYNC_SERVER: string;
   SIGNUP_SERVER: string;
-  PLAID_SERVER: string;
   GOCARDLESS_SERVER: string;
   SIMPLEFIN_SERVER: string;
 };
@@ -32,7 +31,6 @@ export function getServer(url?: string): ServerConfig | null {
       BASE_SERVER: url,
       SYNC_SERVER: joinURL(url, '/sync'),
       SIGNUP_SERVER: joinURL(url, '/account'),
-      PLAID_SERVER: joinURL(url, '/plaid'),
       GOCARDLESS_SERVER: joinURL(url, '/gocardless'),
       SIMPLEFIN_SERVER: joinURL(url, '/simplefin'),
     };
diff --git a/packages/loot-core/src/server/tests/mockSyncServer.ts b/packages/loot-core/src/server/tests/mockSyncServer.ts
index 4852c0955078d9fe2d8115d82b3d83d440a9f61b..08d3eb4018102c25769127acdedc6b2191ed13db 100644
--- a/packages/loot-core/src/server/tests/mockSyncServer.ts
+++ b/packages/loot-core/src/server/tests/mockSyncServer.ts
@@ -78,34 +78,11 @@ handlers['/sync/sync'] = async (data: Uint8Array): Promise<Uint8Array> => {
   return responsePb.serializeBinary();
 };
 
-handlers['/plaid/handoff_public_token'] = () => {
-  // Do nothing
-};
-
-handlers['/plaid/accounts'] = () => {
+handlers['/gocardless/accounts'] = () => {
   // Ignore the parameters and just return the accounts.
   return { accounts: currentMockData.accounts };
 };
 
-handlers['/plaid/transactions'] = ({
-  account_id,
-  start_date,
-  end_date,
-  count,
-  offset,
-}) => {
-  const accounts = currentMockData.accounts;
-  const transactions = currentMockData.transactions[account_id].filter(
-    t => t.date >= start_date && t.date <= end_date,
-  );
-
-  return {
-    accounts: accounts.filter(acct => acct.account_id === account_id),
-    transactions: transactions.slice(offset, offset + count),
-    total_transactions: transactions.length,
-  };
-};
-
 export const filterMockData = func => {
   const copied = JSON.parse(JSON.stringify(defaultMockData));
   currentMockData = func(copied);
diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts
index 476bbeaa97b33016e641e993e3603ede5a0280ee..9610727fb9dd667788ddf0e63ee6b2f7539db69b 100644
--- a/packages/loot-core/src/types/server-handlers.d.ts
+++ b/packages/loot-core/src/types/server-handlers.d.ts
@@ -139,8 +139,6 @@ export interface ServerHandlers {
 
   query: (query) => Promise<{ data; dependencies }>;
 
-  'bank-delete': (arg: { id }) => Promise<unknown>;
-
   'account-update': (arg: { id; name }) => Promise<unknown>;
 
   'accounts-get': () => Promise<AccountEntity[]>;
@@ -160,13 +158,6 @@ export interface ServerHandlers {
     upgradingId;
   }) => Promise<'ok'>;
 
-  'gocardless-accounts-connect': (arg: {
-    institution;
-    publicToken;
-    accountIds;
-    offbudgetIds;
-  }) => Promise<unknown>;
-
   'account-create': (arg: {
     name: string;
     balance?: number;
@@ -185,17 +176,6 @@ export interface ServerHandlers {
 
   'account-move': (arg: { id; targetId }) => Promise<unknown>;
 
-  'poll-web-token': (arg: { token }) => Promise<unknown>;
-
-  'poll-web-token-stop': () => Promise<'ok'>;
-
-  'accounts-sync': (arg: { id? }) => Promise<{
-    errors: unknown;
-    newTransactions: unknown;
-    matchedTransactions: unknown;
-    updatedAccounts: unknown;
-  }>;
-
   'secret-set': (arg: { name: string; value: string }) => Promise<null>;
   'secret-check': (arg: string) => Promise<string | { error?: string }>;
 
@@ -247,10 +227,6 @@ export interface ServerHandlers {
 
   'account-unlink': (arg: { id }) => Promise<'ok'>;
 
-  'make-plaid-public-token': (arg: {
-    bankId;
-  }) => Promise<{ error: ''; code; type } | { linkToken }>;
-
   'save-global-prefs': (prefs) => Promise<'ok'>;
 
   'load-global-prefs': () => Promise<GlobalPrefs>;
diff --git a/upcoming-release-notes/2616.md b/upcoming-release-notes/2616.md
new file mode 100644
index 0000000000000000000000000000000000000000..dfc9abeed0b18105d85f959f6cc20061fe2aeedc
--- /dev/null
+++ b/upcoming-release-notes/2616.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+Delete old Plaid integration that is no longer working.