diff --git a/packages/loot-core/src/mocks/budget.ts b/packages/loot-core/src/mocks/budget.ts
index e4d9506030f97ddc350be0fb8b8a5c2dbc3cd965..4aa912cfe511a403d0f1fe4466a285e4f93de4ab 100644
--- a/packages/loot-core/src/mocks/budget.ts
+++ b/packages/loot-core/src/mocks/budget.ts
@@ -9,6 +9,7 @@ import * as sheet from '../server/sheet';
 import { batchMessages, setSyncingMode } from '../server/sync';
 import * as monthUtils from '../shared/months';
 import { q } from '../shared/query';
+import type { Handlers } from '../types/handlers';
 import type {
   CategoryGroupEntity,
   PayeeEntity,
@@ -566,7 +567,7 @@ async function createBudget(accounts, payees, groups) {
   await sheet.waitOnSpreadsheet();
 }
 
-export async function createTestBudget(handlers) {
+export async function createTestBudget(handlers: Handlers) {
   setSyncingMode('import');
 
   await db.execQuery('PRAGMA journal_mode = OFF');
@@ -577,15 +578,15 @@ export async function createTestBudget(handlers) {
   await db.runQuery('DELETE FROM categories;');
   await db.runQuery('DELETE FROM category_groups');
 
-  const accounts: { name: string; offBudget?: 1; id?: string }[] = [
+  const accounts: { name: string; offBudget?: boolean; id?: string }[] = [
     { name: 'Bank of America' },
     { name: 'Ally Savings' },
     { name: 'Capital One Checking' },
     { name: 'HSBC' },
-    { name: 'Vanguard 401k', offBudget: 1 },
-    { name: 'Mortgage', offBudget: 1 },
-    { name: 'House Asset', offBudget: 1 },
-    { name: 'Roth IRA', offBudget: 1 },
+    { name: 'Vanguard 401k', offBudget: true },
+    { name: 'Mortgage', offBudget: true },
+    { name: 'House Asset', offBudget: true },
+    { name: 'Roth IRA', offBudget: true },
   ];
   await runMutator(() =>
     batchMessages(async () => {
@@ -657,7 +658,7 @@ export async function createTestBudget(handlers) {
     for (const group of categoryGroups) {
       group.id = await handlers['category-group-create']({
         name: group.name,
-        isIncome: group.is_income ? 1 : 0,
+        isIncome: group.is_income,
       });
 
       for (const category of group.categories) {
diff --git a/packages/loot-core/src/platform/server/connection/index.web.ts b/packages/loot-core/src/platform/server/connection/index.web.ts
index 8b8bcd5b4884d01604ac303ae48b6a56feabe510..c1e3af987fd8530c153cf0287d39d95c003472ee 100644
--- a/packages/loot-core/src/platform/server/connection/index.web.ts
+++ b/packages/loot-core/src/platform/server/connection/index.web.ts
@@ -47,14 +47,10 @@ export const init: T.Init = function (serverChn, handlers) {
       if (handlers[name]) {
         runHandler(handlers[name], args, { undoTag, name }).then(
           result => {
-            if (catchErrors) {
-              result = { data: result, error: null };
-            }
-
             serverChannel.postMessage({
               type: 'reply',
               id,
-              result,
+              result: catchErrors ? { data: result, error: null } : result,
               mutated: isMutating(handlers[name]),
               undoTag,
             });
diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts
index 8d8da4116ecfb0e4aa1ca6d3f11bae9c4f31a2a9..417e417260a34536523bab65529fd404c299069b 100644
--- a/packages/loot-core/src/server/main.ts
+++ b/packages/loot-core/src/server/main.ts
@@ -1673,14 +1673,11 @@ handlers['set-server-url'] = async function ({ url, validate = true }) {
 
     if (validate) {
       // Validate the server is running
-      const { error } = await runHandler(
-        handlers['subscribe-needs-bootstrap'],
-        {
-          url,
-        },
-      );
-      if (error) {
-        return { error };
+      const result = await runHandler(handlers['subscribe-needs-bootstrap'], {
+        url,
+      });
+      if ('error' in result) {
+        return { error: result.error };
       }
     }
   }
@@ -2296,7 +2293,10 @@ export async function init(config) {
 export const lib = {
   getDataDir: fs.getDataDir,
   sendMessage: (msg, args) => connection.send(msg, args),
-  send: async (name, args) => {
+  send: async <K extends keyof Handlers, T extends Handlers[K]>(
+    name: K,
+    args?: Parameters<T>[0],
+  ): Promise<Awaited<ReturnType<T>>> => {
     const res = await runHandler(app.handlers[name], args);
     return res;
   },
diff --git a/packages/loot-core/src/server/mutators.ts b/packages/loot-core/src/server/mutators.ts
index 3988e5c58f813f806ea8f2bf7f874693f09bfef6..d7b60ea1e1cbc3ebde594df504c2861d8e81a15a 100644
--- a/packages/loot-core/src/server/mutators.ts
+++ b/packages/loot-core/src/server/mutators.ts
@@ -1,6 +1,6 @@
 import { captureException, captureBreadcrumb } from '../platform/exceptions';
 import { sequential } from '../shared/async';
-import { type HandlerFunctions } from '../types/handlers';
+import { type HandlerFunctions, type Handlers } from '../types/handlers';
 
 const runningMethods = new Set();
 
@@ -37,11 +37,11 @@ function wait(time) {
   return new Promise(resolve => setTimeout(resolve, time));
 }
 
-export async function runHandler(
-  handler,
-  args?,
+export async function runHandler<T extends Handlers[keyof Handlers]>(
+  handler: T,
+  args?: Parameters<T>[0],
   { undoTag, name }: { undoTag?; name? } = {},
-) {
+): Promise<ReturnType<T>> {
   // For debug reasons, track the latest handlers that have been
   // called
   _latestHandlerNames.push(name);
diff --git a/packages/loot-core/src/server/schedules/types/handlers.ts b/packages/loot-core/src/server/schedules/types/handlers.ts
index c15fb1479e887473bc7cac5b7e4adb463ac2cf1c..293fcc24007296e5fb714723fab35a5f15b2d24b 100644
--- a/packages/loot-core/src/server/schedules/types/handlers.ts
+++ b/packages/loot-core/src/server/schedules/types/handlers.ts
@@ -5,7 +5,7 @@ export interface SchedulesHandlers {
     schedule: {
       id?: string;
       name?: string;
-      post_transaction?: boolean;
+      posts_transaction?: boolean;
     };
     conditions: unknown[];
   }) => Promise<string>;
diff --git a/packages/loot-core/src/types/api-handlers.d.ts b/packages/loot-core/src/types/api-handlers.d.ts
index 71bc15306665e3b3579144325ec54458a5f9b976..c4e8c2b11b84df750d7e853b2f1fddfbb27838f3 100644
--- a/packages/loot-core/src/types/api-handlers.d.ts
+++ b/packages/loot-core/src/types/api-handlers.d.ts
@@ -59,7 +59,12 @@ export interface ApiHandlers {
     transactions;
   }) => Promise<unknown>;
 
-  'api/transactions-add': (arg: { accountId; transactions }) => Promise<'ok'>;
+  'api/transactions-add': (arg: {
+    accountId;
+    transactions;
+    runTransfers?: boolean;
+    learnCategories?: boolean;
+  }) => Promise<'ok'>;
 
   'api/transactions-get': (arg: {
     accountId;
@@ -74,9 +79,11 @@ export interface ApiHandlers {
 
   'api/transaction-delete': (arg: { id }) => Promise<unknown>;
 
-  'api/accounts-get': () => Promise<unknown>;
+  'api/sync': () => Promise<unknown>;
+
+  'api/accounts-get': () => Promise<AccountEntity[]>;
 
-  'api/account-create': (arg: { account; initialBalance }) => Promise<unknown>;
+  'api/account-create': (arg: { account; initialBalance? }) => Promise<string>;
 
   'api/account-update': (arg: { id; fields }) => Promise<unknown>;
 
@@ -90,9 +97,11 @@ export interface ApiHandlers {
 
   'api/account-delete': (arg: { id }) => Promise<unknown>;
 
-  'api/categories-get': (arg: { grouped }) => Promise<unknown>;
+  'api/categories-get': (arg: {
+    grouped;
+  }) => Promise<Array<CategoryGroupEntity> | Array<CategoryEntity>>;
 
-  'api/category-group-create': (arg: { group }) => Promise<unknown>;
+  'api/category-group-create': (arg: { group }) => Promise<string>;
 
   'api/category-group-update': (arg: { id; fields }) => Promise<unknown>;
 
@@ -101,15 +110,15 @@ export interface ApiHandlers {
     transferCategoryId;
   }) => Promise<unknown>;
 
-  'api/category-create': (arg: { category }) => Promise<unknown>;
+  'api/category-create': (arg: { category }) => Promise<string>;
 
   'api/category-update': (arg: { id; fields }) => Promise<unknown>;
 
   'api/category-delete': (arg: { id; transferCategoryId }) => Promise<unknown>;
 
-  'api/payees-get': () => Promise<unknown>;
+  'api/payees-get': () => Promise<PayeeEntity[]>;
 
-  'api/payee-create': (arg: { payee }) => Promise<unknown>;
+  'api/payee-create': (arg: { payee }) => Promise<string>;
 
   'api/payee-update': (arg: { id; fields }) => Promise<unknown>;
 
diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts
index 5a6487b21abe3e69213cecf067c9209605960306..f5022b44440a5811600fc314ece078733a0fa649 100644
--- a/packages/loot-core/src/types/server-handlers.d.ts
+++ b/packages/loot-core/src/types/server-handlers.d.ts
@@ -78,20 +78,20 @@ export interface ServerHandlers {
   'category-create': (arg: {
     name;
     groupId;
-    isIncome;
-    hidden: boolean;
-  }) => Promise<unknown>;
+    isIncome?;
+    hidden?: boolean;
+  }) => Promise<string>;
 
   'category-update': (category) => Promise<unknown>;
 
   'category-move': (arg: { id; groupId; targetId }) => Promise<unknown>;
 
-  'category-delete': (arg: { id; transferId }) => Promise<{ error?: string }>;
+  'category-delete': (arg: { id; transferId? }) => Promise<{ error?: string }>;
 
   'category-group-create': (arg: {
     name;
     isIncome?: boolean;
-  }) => Promise<unknown>;
+  }) => Promise<string>;
 
   'category-group-update': (group) => Promise<unknown>;
 
@@ -101,7 +101,7 @@ export interface ServerHandlers {
 
   'must-category-transfer': (arg: { id }) => Promise<unknown>;
 
-  'payee-create': (arg: { name }) => Promise<unknown>;
+  'payee-create': (arg: { name }) => Promise<string>;
 
   'payees-get': () => Promise<PayeeEntity[]>;
 
@@ -165,7 +165,7 @@ export interface ServerHandlers {
     institution;
     publicToken;
     accountIds;
-    offbudgetIds;
+    offbudgetIds?;
   }) => Promise<unknown>;
 
   'gocardless-accounts-connect': (arg: {
@@ -177,7 +177,7 @@ export interface ServerHandlers {
 
   'account-create': (arg: {
     name: string;
-    balance: number;
+    balance?: number;
     offBudget?: boolean;
     closed?: 0 | 1;
   }) => Promise<string>;
@@ -197,7 +197,7 @@ export interface ServerHandlers {
 
   'poll-web-token-stop': () => Promise<'ok'>;
 
-  'accounts-sync': (arg: { id }) => Promise<{
+  'accounts-sync': (arg: { id? }) => Promise<{
     errors: unknown;
     newTransactions: unknown;
     matchedTransactions: unknown;
diff --git a/upcoming-release-notes/2136.md b/upcoming-release-notes/2136.md
new file mode 100644
index 0000000000000000000000000000000000000000..90a5e66c3b61f790a4a9bc2ac12e4ea8fd667d88
--- /dev/null
+++ b/upcoming-release-notes/2136.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [twk3]
+---
+
+TypeScript: Add proper types to runHandler