diff --git a/packages/api/methods.js b/packages/api/methods.js
index ccad98af6c265f501de597ca2b8ed94b557c8ed9..90cda1ae2666b746e2b7e72a7d119e5840f96df0 100644
--- a/packages/api/methods.js
+++ b/packages/api/methods.js
@@ -149,19 +149,3 @@ export function updatePayee(id, fields) {
 export function deletePayee(id) {
   return send('api/payee-delete', { id });
 }
-
-export function getPayeeRules(payeeId) {
-  return send('api/payee-rules-get', { payeeId });
-}
-
-export function createPayeeRule(payeeId, rule) {
-  return send('api/payee-rule-create', { payee_id: payeeId, rule });
-}
-
-export function updatePayeeRule(id, fields) {
-  return send('api/payee-rule-update', { id, fields });
-}
-
-export function deletePayeeRule(id) {
-  return send('api/payee-rule-delete', { id });
-}
diff --git a/packages/desktop-client/src/components/payees/ManagePayeesWithData.js b/packages/desktop-client/src/components/payees/ManagePayeesWithData.js
index d02ffd3594e75ebc004139817d5fe5dc9003c4a1..6c86e2cc3d4d51740d4c30d506d0967166a8b7dd 100644
--- a/packages/desktop-client/src/components/payees/ManagePayeesWithData.js
+++ b/packages/desktop-client/src/components/payees/ManagePayeesWithData.js
@@ -58,11 +58,7 @@ function ManagePayeesWithData({
   }, []);
 
   async function onUndo({ tables, messages, meta, url }, scroll = false) {
-    if (
-      !tables.includes('payees') &&
-      !tables.includes('payee_mapping') &&
-      !tables.includes('payee_rules')
-    ) {
+    if (!tables.includes('payees') && !tables.includes('payee_mapping')) {
       return;
     }
 
diff --git a/packages/loot-core/migrations/1682974838138_remove_payee_rules.sql b/packages/loot-core/migrations/1682974838138_remove_payee_rules.sql
new file mode 100644
index 0000000000000000000000000000000000000000..070aecd534dbd4a2d62d5f789cec82027ab7dae0
--- /dev/null
+++ b/packages/loot-core/migrations/1682974838138_remove_payee_rules.sql
@@ -0,0 +1,5 @@
+BEGIN TRANSACTION;
+
+DROP TABLE payee_rules;
+
+COMMIT;
diff --git a/packages/loot-core/src/server/accounts/transaction-rules.test.ts b/packages/loot-core/src/server/accounts/transaction-rules.test.ts
index c3fc3a40cd708774a9c66b6372298c0ef47cf617..50211264f6e965e8fe54dbd4328634b14e285c45 100644
--- a/packages/loot-core/src/server/accounts/transaction-rules.test.ts
+++ b/packages/loot-core/src/server/accounts/transaction-rules.test.ts
@@ -15,7 +15,6 @@ import {
   resetState,
   getProbableCategory,
   updateCategoryRules,
-  migrateOldRules,
 } from './transaction-rules';
 
 // TODO: write tests to make sure payee renaming is "pre" and category
@@ -368,64 +367,6 @@ describe('Transaction rules', () => {
     });
   });
 
-  test('migrating from the old payee rules works', async () => {
-    await loadRules();
-    let categoryGroupId = await db.insertCategoryGroup({ name: 'general' });
-    let categoryId = await db.insertCategory({
-      name: 'food',
-      cat_group: categoryGroupId,
-    });
-    let krogerId = await db.insertPayee({ name: 'kroger' });
-    let lowesId = await db.insertPayee({ name: 'lowes', category: categoryId });
-
-    await db.insertPayeeRule({
-      payee_id: krogerId,
-      type: 'contains',
-      value: 'kroger',
-    });
-    await db.insertPayeeRule({
-      payee_id: lowesId,
-      type: 'equals',
-      value: '123 lowes',
-    });
-    await db.insertPayeeRule({
-      payee_id: lowesId,
-      type: 'equals',
-      value: 'lowes 456',
-    });
-
-    // Migrate!
-    await migrateOldRules();
-
-    expect(getRules().length).toBe(3);
-
-    expect(runRules({ payee: null, imported_payee: '123 lowes' })).toEqual({
-      category: categoryId,
-      imported_payee: '123 lowes',
-      payee: lowesId,
-    });
-    expect(runRules({ payee: null, imported_payee: 'lowes 456' })).toEqual({
-      category: categoryId,
-      imported_payee: 'lowes 456',
-      payee: lowesId,
-    });
-    expect(runRules({ payee: null, imported_payee: '1 lowes 2' })).toEqual({
-      imported_payee: '1 lowes 2',
-      payee: null,
-    });
-    expect(
-      runRules({
-        payee: null,
-        imported_payee: 'blah blah kroger bla',
-        category: null,
-      }),
-    ).toEqual({
-      imported_payee: 'blah blah kroger bla',
-      payee: krogerId,
-      category: null,
-    });
-  });
-
   test('transactions can be queried by rule', async () => {
     await loadRules();
     let account = await db.insertAccount({ name: 'bank' });
diff --git a/packages/loot-core/src/server/accounts/transaction-rules.ts b/packages/loot-core/src/server/accounts/transaction-rules.ts
index d1f3602cc416fc8609b6ac2f8395bc8087895fc9..46c7c5593640f8e5a33ac8c59e71fa90e4ebe2e0 100644
--- a/packages/loot-core/src/server/accounts/transaction-rules.ts
+++ b/packages/loot-core/src/server/accounts/transaction-rules.ts
@@ -16,7 +16,7 @@ import * as db from '../db';
 import { getMappings } from '../db/mappings';
 import { RuleError } from '../errors';
 import { requiredFields, toDateRepr } from '../models';
-import { setSyncingMode, batchMessages } from '../sync';
+import { batchMessages } from '../sync';
 import { addSyncListener } from '../sync/index';
 
 import {
@@ -712,129 +712,3 @@ export async function updateCategoryRules(transactions) {
     }
   });
 }
-
-// This can be removed in the future
-export async function migrateOldRules() {
-  let allPayees = await db.all(
-    `SELECT p.*, c.id as category FROM payees p
-    LEFT JOIN category_mapping cm ON cm.id = p.category
-    LEFT JOIN categories c ON (c.id = cm.transferId AND c.tombstone = 0)
-    WHERE p.tombstone = 0 AND transfer_acct IS NULL`,
-  );
-  let allRules = await db.all(
-    `SELECT pr.*, pm.targetId as payee_id FROM payee_rules pr
-      LEFT JOIN payee_mapping pm ON pm.id = pr.payee_id
-      WHERE pr.tombstone = 0`,
-  );
-
-  let payeesById = new Map();
-  for (let i = 0; i < allPayees.length; i++) {
-    payeesById.set(allPayees[i].id, allPayees[i]);
-  }
-
-  let rulesByPayeeId = new Map();
-  for (let i = 0; i < allRules.length; i++) {
-    let item = allRules[i];
-    let rules = rulesByPayeeId.get(item.payee_id) || [];
-    rules.push(item);
-    rulesByPayeeId.set(item.payee_id, rules);
-  }
-
-  let rules = [];
-
-  // Convert payee name rules
-  for (let [payeeId, payeeRules] of rulesByPayeeId.entries()) {
-    let equals = payeeRules.filter(r => {
-      let payee = payeesById.get(r.payee_id);
-
-      return (
-        (r.type === 'equals' || r.type == null) &&
-        (!payee || r.value.toLowerCase() !== payee.name.toLowerCase())
-      );
-    });
-    let contains = payeeRules.filter(r => r.type === 'contains');
-    let actions = [{ op: 'set', field: 'payee', value: payeeId }];
-
-    if (equals.length > 0) {
-      rules.push({
-        stage: null,
-        conditionsOp: 'and',
-        conditions: [
-          {
-            op: 'oneOf',
-            field: 'imported_payee',
-            value: equals.map(payeeRule => payeeRule.value),
-          },
-        ],
-        actions,
-      });
-    }
-
-    if (contains.length > 0) {
-      rules = rules.concat(
-        contains.map(payeeRule => ({
-          stage: null,
-          conditionsOp: 'and',
-          conditions: [
-            {
-              op: 'contains',
-              field: 'imported_payee',
-              value: payeeRule.value,
-            },
-          ],
-          actions,
-        })),
-      );
-    }
-  }
-
-  // Convert category rules
-  let catRules = allPayees
-    .filter(p => p.category)
-    .reduce((map, payee) => {
-      let ids = map.get(payee.category) || new Set();
-      ids.add(payee.id);
-      map.set(payee.category, ids);
-      return map;
-    }, new Map());
-
-  for (let [catId, payeeIds] of catRules) {
-    rules.push({
-      stage: null,
-      conditionsOp: 'and',
-      conditions: [
-        {
-          op: 'oneOf',
-          field: 'payee',
-          value: [...payeeIds],
-        },
-      ],
-      actions: [
-        {
-          op: 'set',
-          field: 'category',
-          value: catId,
-        },
-      ],
-    });
-  }
-
-  // Very important: we never want to sync migration changes, but it
-  // still has to run through the syncing layer to make sure
-  // projections are correct. This is only OK because we require a
-  // sync reset after this.
-  let prevMode = setSyncingMode('disabled');
-  await batchMessages(async () => {
-    for (let rule of rules) {
-      await insertRule({
-        stage: rule.stage,
-        conditionsOp: rule.conditionsOp,
-        conditions: rule.conditions,
-        actions: rule.actions,
-      });
-    }
-
-    await db.runQuery('DELETE FROM payee_rules', []);
-  });
-  setSyncingMode(prevMode);
-}
diff --git a/packages/loot-core/src/server/api-models.ts b/packages/loot-core/src/server/api-models.ts
index ea1da3d13b3335239552ae40b4f29ab308d2b889..482a527a750424e358591a2e975c7772ba3dadb4 100644
--- a/packages/loot-core/src/server/api-models.ts
+++ b/packages/loot-core/src/server/api-models.ts
@@ -167,16 +167,3 @@ export const payeeModel = {
     return payee;
   },
 };
-
-export const payeeRuleModel = {
-  ...models.payeeRuleModel,
-
-  toExternal(rule) {
-    let { tombstone, ...result } = rule;
-    return result;
-  },
-
-  fromExternal(rule) {
-    return rule;
-  },
-};
diff --git a/packages/loot-core/src/server/api.ts b/packages/loot-core/src/server/api.ts
index 4c73f5419d6d0f33f0eb531b775f4eed8824514b..fc2c3c4abed3606f21f6b096e5d8bb42523434b8 100644
--- a/packages/loot-core/src/server/api.ts
+++ b/packages/loot-core/src/server/api.ts
@@ -19,7 +19,6 @@ import {
   categoryModel,
   categoryGroupModel,
   payeeModel,
-  payeeRuleModel,
 } from './api-models';
 import { runQuery as aqlQuery } from './aql';
 import * as cloudStorage from './cloud-storage';
@@ -545,36 +544,6 @@ handlers['api/payee-delete'] = withMutation(async function ({ id }) {
   return handlers['payees-batch-change']({ deleted: [{ id }] });
 });
 
-handlers['api/payee-rules-get'] = async function ({ payeeId }) {
-  let rules = await handlers['payees-get-rules']({ id: payeeId });
-  return rules.map(payeeRuleModel.toExternal);
-};
-
-handlers['api/payee-rule-create'] = withMutation(async function ({
-  payee_id,
-  rule,
-}) {
-  return handlers['payees-add-rule']({
-    payee_id,
-    type: rule.type,
-    value: rule.value || null,
-  });
-});
-
-handlers['api/payee-rule-update'] = withMutation(async function ({
-  id,
-  fields,
-}) {
-  return handlers['payees-update-rule']({
-    id,
-    ...payeeRuleModel.fromExternal(fields),
-  });
-});
-
-handlers['api/payee-rule-delete'] = withMutation(async function ({ id }) {
-  return handlers['payees-delete-rule']({ id });
-});
-
 export default function installAPI(serverHandlers) {
   handlers = Object.assign({}, serverHandlers, handlers);
   return handlers;
diff --git a/packages/loot-core/src/server/db/index.ts b/packages/loot-core/src/server/db/index.ts
index 1d08c3d16e927bcbf9530ff818f702d47e0d3908..a68a64e21601931291ec06624b85ef7a1bed0347 100644
--- a/packages/loot-core/src/server/db/index.ts
+++ b/packages/loot-core/src/server/db/index.ts
@@ -24,7 +24,6 @@ import {
   categoryModel,
   categoryGroupModel,
   payeeModel,
-  payeeRuleModel,
 } from '../models';
 import { sendMessages, batchMessages } from '../sync';
 
@@ -454,10 +453,6 @@ export async function deletePayee(payee) {
   //   mappings.map(m => update('payee_mapping', { id: m.id, targetId: null }))
   // );
 
-  let rules = await all('SELECT * FROM payee_rules WHERE payee_id = ?', [
-    payee.id,
-  ]);
-  await Promise.all(rules.map(rule => deletePayeeRule({ id: rule.id })));
   return delete_('payees', payee.id);
 }
 
@@ -533,29 +528,6 @@ export async function getPayeeByName(name) {
   );
 }
 
-export function insertPayeeRule(rule) {
-  rule = payeeRuleModel.validate(rule);
-  return insertWithUUID('payee_rules', rule);
-}
-
-export function deletePayeeRule(rule) {
-  return delete_('payee_rules', rule.id);
-}
-
-export function updatePayeeRule(rule) {
-  rule = payeeModel.validate(rule, { update: true });
-  return update('payee_rules', rule);
-}
-
-export function getPayeeRules(id) {
-  return all(
-    `SELECT pr.* FROM payee_rules pr
-     LEFT JOIN payee_mapping pm ON pm.id = pr.payee_id
-     WHERE pm.targetId = ? AND pr.tombstone = 0`,
-    [id],
-  );
-}
-
 export function getAccounts() {
   return all(
     `SELECT a.*, b.name as bankName, b.id as bankId FROM accounts a
diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts
index a07047f9e7317e86ed9c2c9d92f7dfdd527c803b..ac508c21828c57311b4ba769d7aa7b5609069cd9 100644
--- a/packages/loot-core/src/server/main.ts
+++ b/packages/loot-core/src/server/main.ts
@@ -505,34 +505,6 @@ handlers['payees-get-rules'] = async function ({ id }) {
   return rules.getRulesForPayee(id).map(rule => rule.serialize());
 };
 
-handlers['payees-delete-rule'] = mutator(async function ({ id, payee_id }) {
-  return withUndo(
-    async () => {
-      return await db.deletePayeeRule({ id });
-    },
-    { payeeId: payee_id },
-  );
-});
-
-handlers['payees-update-rule'] = mutator(async function (rule) {
-  return withUndo(
-    async () => {
-      return await db.updatePayeeRule(rule);
-    },
-    { payeeId: rule.payee_id },
-  );
-});
-
-handlers['payees-add-rule'] = mutator(async function (rule) {
-  return withUndo(
-    async () => {
-      let id = await db.insertPayeeRule(rule);
-      return { ...rule, id };
-    },
-    { payeeId: rule.payee_id },
-  );
-});
-
 function validateRule(rule) {
   // Returns an array of errors, the array is the same link as the
   // passed-in `array`, or null if there are no errors
@@ -655,10 +627,6 @@ handlers['rules-run'] = async function ({ transaction }) {
   return rules.runRules(transaction);
 };
 
-handlers['rules-migrate'] = async function () {
-  await rules.migrateOldRules();
-};
-
 handlers['make-filters-from-conditions'] = async function ({ conditions }) {
   return rules.conditionsToAQL(conditions);
 };
diff --git a/packages/loot-core/src/server/models.ts b/packages/loot-core/src/server/models.ts
index 86bcd3ed589c19172c1021af56e8f0b664fa3980..40cb1002a4cda48b69a4412fd2ba3d5f750348af 100644
--- a/packages/loot-core/src/server/models.ts
+++ b/packages/loot-core/src/server/models.ts
@@ -102,24 +102,6 @@ export const payeeModel = {
   },
 };
 
-export const payeeRuleModel = {
-  validateType(rule) {
-    const { type } = rule;
-    if (type !== 'equals' && type !== 'contains') {
-      throw new Error('Invalid rule type: ' + type);
-    }
-  },
-
-  validate(rule, { update }: { update?: boolean } = {}) {
-    if (!update || 'type' in rule) {
-      payeeRuleModel.validateType(rule);
-    }
-
-    requiredFields('payee_rules', rule, ['payee_id', 'type'], update);
-    return rule;
-  },
-};
-
 export const transactionModel = {
   validate(trans, { update }: { update?: boolean } = {}) {
     requiredFields('transaction', trans, ['date', 'acct'], update);
diff --git a/packages/loot-core/src/server/sync/sync.property.test.ts b/packages/loot-core/src/server/sync/sync.property.test.ts
index 0b636cbde83a74c478b1ef9764e08aac82a6475d..4242e3c6122e39711fc703cc8dd2e7c59caba539 100644
--- a/packages/loot-core/src/server/sync/sync.property.test.ts
+++ b/packages/loot-core/src/server/sync/sync.property.test.ts
@@ -84,12 +84,6 @@ let schema = {
     category: 'text',
     tombstone: 'integer',
   },
-  payee_rules: {
-    payee_id: 'text',
-    type: 'text',
-    value: 'text',
-    tombstone: 'integer',
-  },
   payee_mapping: { targetId: 'text' },
 };
 
diff --git a/packages/loot-core/src/types/api.handlers.d.ts b/packages/loot-core/src/types/api.handlers.d.ts
index 962f081d71300ef3f0d0eebc34c5eae4d400f01f..30e7b5b66bca95b839b20042e5ad7e4b37f05a85 100644
--- a/packages/loot-core/src/types/api.handlers.d.ts
+++ b/packages/loot-core/src/types/api.handlers.d.ts
@@ -112,12 +112,4 @@ export interface ApiHandlers {
   'api/payee-update': (arg: { id; fields }) => Promise<unknown>;
 
   'api/payee-delete': (arg: { id }) => Promise<unknown>;
-
-  'api/payee-rules-get': (arg: { payeeId }) => Promise<unknown>;
-
-  'api/payee-rule-create': (arg: { payee_id; rule }) => Promise<unknown>;
-
-  'api/payee-rule-update': (arg: { id; fields }) => Promise<unknown>;
-
-  'api/payee-rule-delete': (arg: { id }) => Promise<unknown>;
 }
diff --git a/packages/loot-core/src/types/main.handlers.d.ts b/packages/loot-core/src/types/main.handlers.d.ts
index 49d3c00053ec221c70f03030650541a35c033ea4..90cb6cc8b81ab28c99ee10927746ea5ef75f8558 100644
--- a/packages/loot-core/src/types/main.handlers.d.ts
+++ b/packages/loot-core/src/types/main.handlers.d.ts
@@ -81,12 +81,6 @@ export interface MainHandlers {
 
   'payees-get-rules': (arg: { id }) => Promise<unknown>;
 
-  'payees-delete-rule': (arg: { id; payee_id }) => Promise<unknown>;
-
-  'payees-update-rule': (rule) => Promise<unknown>;
-
-  'payees-add-rule': (rule) => Promise<unknown>;
-
   'rule-validate': (rule) => Promise<{ error: unknown }>;
 
   'rule-add': (rule) => Promise<{ error: unknown } | { id: string }>;
@@ -107,8 +101,6 @@ export interface MainHandlers {
 
   'rules-run': (arg: { transaction }) => Promise<unknown>;
 
-  'rules-migrate': () => Promise<unknown>;
-
   'make-filters-from-conditions': (arg: { conditions }) => Promise<unknown>;
 
   getCell: (arg: { sheetName; name }) => Promise<unknown>;
diff --git a/upcoming-release-notes/985.md b/upcoming-release-notes/985.md
new file mode 100644
index 0000000000000000000000000000000000000000..890b46234dddf245c7725bee0340ce3921165732
--- /dev/null
+++ b/upcoming-release-notes/985.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [j-f1]
+---
+
+Remove unused payee rules feature