diff --git a/Documentation/overview.rst b/Documentation/overview.rst
index 55c07dffd20ff97c4682bf4783d8d951072b37bd..ae7506a7f37e5d5f23461b0129f3f1983f75c596 100644
--- a/Documentation/overview.rst
+++ b/Documentation/overview.rst
@@ -14,31 +14,34 @@ Epicardium
 Epicardium is based on `FreeRTOS <https://www.freertos.org/>`_.  There are a
 number of tasks that will have been keeping card10 running.  These are:
 
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| Name              | ID Global                     | Priority | Description                               |
-+===================+===============================+==========+===========================================+
-| `vPmicTask`_      | ``pmic_task_id`` (static)     | +4       | Power Management (and Reset Button)       |
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| `vLifecycleTask`_ | ``lifecycle_task`` (static)   | +3       | Control of the payload running on core 1. |
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| `vBleTask`_       | ``ble_task_id`` (static)      | +3       | Bluetooth Low Energy Stack                |
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| `vSerialTask`_    | ``serial_task_id``            | +3       | Serial Output via UART/CDC-ACM/BLE        |
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| `vApiDispatcher`_ | ``dispatcher_task_id``        | +2       | Epicardium API dispatcher                 |
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| `vLedTask`_       | -/-                           | +1       | LED Animations                            |
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| `vMAX30001Task`_  | ``max30001_task_id`` (static) | +1       | `MAX30001`_ ECG driver                    |
-+-------------------+-------------------------------+----------+-------------------------------------------+
-| `vBhi160Task`_    | ``bhi160_task_id`` (static)   | +1       | `BHI160`_ sensor fusion driver            |
-+-------------------+-------------------------------+----------+-------------------------------------------+
++--------------------+-------------------------------+----------+-------------------------------------------+
+| Name               | ID Global                     | Priority | Description                               |
++====================+===============================+==========+===========================================+
+| `vPmicTask`_       | ``pmic_task_id`` (static)     | +4       | Power Management (and Reset Button)       |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vLifecycleTask`_  | ``lifecycle_task`` (static)   | +3       | Control of the payload running on core 1. |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vBleTask`_        | ``ble_task_id`` (static)      | +3       | Bluetooth Low Energy Stack                |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vSerialTask`_     | ``serial_task_id``            | +3       | Serial Output via UART/CDC-ACM/BLE        |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vApiDispatcher`_  | ``dispatcher_task_id``        | +2       | Epicardium API dispatcher                 |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vInterruptsTask`_ | ``interrupts_task`` (static)  | +2       | Interrupt dispatcher worker               |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vLedTask`_        | -/-                           | +1       | LED Animations                            |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vMAX30001Task`_   | ``max30001_task_id`` (static) | +1       | `MAX30001`_ ECG driver                    |
++--------------------+-------------------------------+----------+-------------------------------------------+
+| `vBhi160Task`_     | ``bhi160_task_id`` (static)   | +1       | `BHI160`_ sensor fusion driver            |
++--------------------+-------------------------------+----------+-------------------------------------------+
 
 .. _vPmicTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/pmic.c#L281
 .. _vLifecycleTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/lifecycle.c#L361
 .. _vBleTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/ble/ble.c#L237
 .. _vSerialTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/serial.c#L289
 .. _vApiDispatcher: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/dispatcher.c#L25
+.. _vInterruptsTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/interrupts.c#L119
 .. _vLedTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/personal_state.c#L58
 .. _vMAX30001Task: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/max30001.c#L378
 .. _vBhi160Task: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/bhi.c#L419
diff --git a/epicardium/api/control.c b/epicardium/api/control.c
index 99c99953344d6b2700e5c657cc4c6ade1fbe9b4e..deff433010bf426c1c8a59bcab5450d7787fe55a 100644
--- a/epicardium/api/control.c
+++ b/epicardium/api/control.c
@@ -1,6 +1,5 @@
 #include "epicardium.h"
 #include "api/dispatcher.h"
-#include "api/interrupt-sender.h"
 #include "modules/log.h"
 
 #include "card10.h"
@@ -10,6 +9,7 @@
 #include "tmr.h"
 
 static void __core1_init(void);
+extern void interrupt_trigger_sync(api_int_id_t id);
 
 struct core1_info {
 	/* Location of core1's interrupt vector table */
@@ -206,8 +206,13 @@ void core1_boot(void)
 
 void core1_trigger_reset(void)
 {
-	/* Signal core 1 that we intend to load a new payload. */
-	api_interrupt_trigger(EPIC_INT_RESET);
+	/*
+	 * Signal core 1 that we intend to load a new payload.
+	 *
+	 * This needs to be synchroneous because otherwise we will deadlock
+	 * (Lifecycle task busy-spins and interrupt can never get dispatched).
+	 */
+	interrupt_trigger_sync(EPIC_INT_RESET);
 }
 
 void core1_wait_ready(void)
diff --git a/epicardium/api/interrupt-sender.c b/epicardium/api/interrupt-sender.c
index 5117bea201e2e866e606e42cb8639ed48c162513..fad7ed674ef85f7e0553abbfc86ed1cca6af37d5 100644
--- a/epicardium/api/interrupt-sender.c
+++ b/epicardium/api/interrupt-sender.c
@@ -1,53 +1,22 @@
 #include "api/interrupt-sender.h"
 #include "api/common.h"
 #include "tmr_utils.h"
-
-static bool int_enabled[EPIC_INT_NUM];
-
-int api_interrupt_trigger(api_int_id_t id)
-{
-	if (id >= EPIC_INT_NUM) {
-		return -EINVAL;
-	}
-
-	if (int_enabled[id]) {
-		while (API_CALL_MEM->int_id != (api_int_id_t)(-1))
-			;
-
-		API_CALL_MEM->int_id = id;
-		TMR_TO_Start(MXC_TMR5, 1, 0);
-	}
-	return 0;
-}
+#include <assert.h>
 
 void api_interrupt_init(void)
 {
 	API_CALL_MEM->int_id = (-1);
-
-	for (int i = 0; i < EPIC_INT_NUM; i++) {
-		int_enabled[i] = false;
-	}
-
-	/* Reset interrupt is always enabled */
-	int_enabled[EPIC_INT_RESET] = true;
 }
 
-int epic_interrupt_enable(api_int_id_t int_id)
+bool api_interrupt_is_ready(void)
 {
-	if (int_id >= EPIC_INT_NUM) {
-		return -EINVAL;
-	}
-
-	int_enabled[int_id] = true;
-	return 0;
+	return API_CALL_MEM->int_id == (api_int_id_t)(-1);
 }
 
-int epic_interrupt_disable(api_int_id_t int_id)
+void api_interrupt_trigger(api_int_id_t id)
 {
-	if (int_id >= EPIC_INT_NUM || int_id == EPIC_INT_RESET) {
-		return -EINVAL;
-	}
+	assert(API_CALL_MEM->int_id == (api_int_id_t)(-1));
 
-	int_enabled[int_id] = false;
-	return 0;
+	API_CALL_MEM->int_id = id;
+	TMR_TO_Start(MXC_TMR5, 1, 0);
 }
diff --git a/epicardium/api/interrupt-sender.h b/epicardium/api/interrupt-sender.h
index 419993c72388d15a234d4887a4a71d0a00ccd6d7..d4924c0995b2ed46474f64155db67db9415e7843 100644
--- a/epicardium/api/interrupt-sender.h
+++ b/epicardium/api/interrupt-sender.h
@@ -2,4 +2,5 @@
 #include "api/common.h"
 
 void api_interrupt_init(void);
-int api_interrupt_trigger(api_int_id_t id);
+bool api_interrupt_is_ready(void);
+void api_interrupt_trigger(api_int_id_t id);
diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h
index f617054319da5ecc573a3bd29dff011832ef2eaa..8814692a62a06edce3b9db0cf58c6090ba78c86a 100644
--- a/epicardium/epicardium.h
+++ b/epicardium/epicardium.h
@@ -158,6 +158,10 @@ typedef uint32_t api_int_id_t;
  * (masked/unmasked) using :c:func:`epic_interrupt_enable` and
  * :c:func:`epic_interrupt_disable`.
  *
+ * These interrupts work similar to hardware interrupts:  You will only get a
+ * single interrupt, even if multiple events occured since the ISR last ran
+ * (*this behavior is new since version 1.16*).
+ *
  * .. warning::
  *
  *    Never attempt to call the API from inside an ISR.  This might trigger an
diff --git a/epicardium/main.c b/epicardium/main.c
index c5e2768c37177f97f6a2388bdfdd8b71f10cd591..89054e9e199f9ade4236e7e270a0068ddec67ce9 100644
--- a/epicardium/main.c
+++ b/epicardium/main.c
@@ -133,6 +133,16 @@ int main(void)
 		    &dispatcher_task_id) != pdPASS) {
 		panic("Failed to create %s task!", "API Dispatcher");
 	}
+	/* Interrupts */
+	if (xTaskCreate(
+		    vInterruptsTask,
+		    (const char *)"Interrupt Dispatcher",
+		    configMINIMAL_STACK_SIZE,
+		    NULL,
+		    tskIDLE_PRIORITY + 2,
+		    NULL) != pdPASS) {
+		panic("Failed to create %s task!", "Interrupt Dispatcher");
+	}
 
 	/* BLE */
 	if (ble_shall_start()) {
diff --git a/epicardium/modules/bhi.c b/epicardium/modules/bhi.c
index 5a5528f5f2ce1c5dde700866847240eb6c69bc5a..baa2d16220be0754bcbb321ef04fa852a43d7a85 100644
--- a/epicardium/modules/bhi.c
+++ b/epicardium/modules/bhi.c
@@ -11,7 +11,6 @@
 #include "task.h"
 #include "queue.h"
 
-#include "api/interrupt-sender.h"
 #include "epicardium.h"
 #include "modules/log.h"
 #include "modules/modules.h"
@@ -320,7 +319,7 @@ bhi160_handle_packet(bhy_data_type_t data_type, bhy_data_generic_t *sensor_data)
 		}
 
 		if (wakeup) {
-			api_interrupt_trigger(epic_int);
+			interrupt_trigger(epic_int);
 		}
 		break;
 	default:
diff --git a/epicardium/modules/hardware.c b/epicardium/modules/hardware.c
index 358154334f1b7f3a2d508e7348e70999aa67257c..6d118fe73ef59007c4e3cb915afdba004e678d56 100644
--- a/epicardium/modules/hardware.c
+++ b/epicardium/modules/hardware.c
@@ -1,7 +1,6 @@
 #include "epicardium.h"
 
 #include "api/dispatcher.h"
-#include "api/interrupt-sender.h"
 #include "usb/epc_usb.h"
 #include "modules/filesystem.h"
 #include "modules/log.h"
@@ -171,7 +170,7 @@ int hardware_early_init(void)
 	/*
 	 * API Dispatcher & API Interrupts
 	 */
-	api_interrupt_init();
+	interrupt_init();
 	api_dispatcher_init();
 
 	/*
@@ -237,7 +236,7 @@ int hardware_reset(void)
 	/*
 	 * API Dispatcher & API Interrupts
 	 */
-	api_interrupt_init();
+	interrupt_init();
 	api_dispatcher_init();
 
 	/*
diff --git a/epicardium/modules/interrupts.c b/epicardium/modules/interrupts.c
new file mode 100644
index 0000000000000000000000000000000000000000..0ead54fc58b1d6341071cd377a8ab239bbbff8e7
--- /dev/null
+++ b/epicardium/modules/interrupts.c
@@ -0,0 +1,156 @@
+#include "modules/mutex.h"
+#include "modules/log.h"
+#include "epicardium.h"
+#include "api/interrupt-sender.h"
+#include <assert.h>
+
+struct interrupt_priv {
+	/* Whether this interrupt can be triggered */
+	bool int_enabled[EPIC_INT_NUM];
+	/* Whether this interrupt is waiting to be delivered */
+	bool int_pending[EPIC_INT_NUM];
+	/* Whether any interrupts are currently waiting to be triggered */
+	bool has_pending;
+};
+
+static struct interrupt_priv interrupt_data;
+static struct mutex interrupt_mutex;
+static TaskHandle_t interrupts_task;
+
+void interrupt_trigger(api_int_id_t id)
+{
+	assert(id < EPIC_INT_NUM);
+
+	mutex_lock(&interrupt_mutex);
+
+	if (interrupt_data.int_enabled[id]) {
+		interrupt_data.int_pending[id] = true;
+		interrupt_data.has_pending     = true;
+		mutex_unlock(&interrupt_mutex);
+		xTaskNotifyGive(interrupts_task);
+	} else {
+		mutex_unlock(&interrupt_mutex);
+	}
+}
+
+void interrupt_trigger_sync(api_int_id_t id)
+{
+	assert(id < EPIC_INT_NUM);
+
+	mutex_lock(&interrupt_mutex);
+	if (!interrupt_data.int_enabled[id])
+		goto out;
+
+	while (!api_interrupt_is_ready())
+		;
+
+	api_interrupt_trigger(id);
+out:
+	mutex_unlock(&interrupt_mutex);
+}
+
+/*
+ * This function solely exists because of that one use of interrupts that breaks
+ * the rules:  The RTC ALARM interrupt is triggered from a hardware ISR where
+ * interrupt_trigger_sync() won't work because it needs to lock a mutex.
+ *
+ * DO NOT USE THIS FUNCTION IN ANY NEW CODE.
+ */
+void __attribute__((deprecated)) interrupt_trigger_unsafe(api_int_id_t id)
+{
+	assert(id < EPIC_INT_NUM);
+
+	if (!interrupt_data.int_enabled[id])
+		return;
+
+	while (!api_interrupt_is_ready())
+		;
+
+	api_interrupt_trigger(id);
+}
+
+static void interrupt_set_enabled(api_int_id_t id, bool enabled)
+{
+	assert(id < EPIC_INT_NUM);
+
+	mutex_lock(&interrupt_mutex);
+	interrupt_data.int_enabled[id] = enabled;
+	mutex_unlock(&interrupt_mutex);
+}
+
+void interrupt_init(void)
+{
+	if (interrupt_mutex.name == NULL)
+		mutex_create(&interrupt_mutex);
+
+	api_interrupt_init();
+
+	/* Reset all irqs to disabled */
+	for (size_t i = 0; i < EPIC_INT_NUM; i++) {
+		interrupt_set_enabled(i, false);
+	}
+
+	/* Reset interrupt is always enabled */
+	interrupt_set_enabled(EPIC_INT_RESET, true);
+}
+
+/* Epic-calls {{{ */
+int epic_interrupt_enable(api_int_id_t int_id)
+{
+	if (int_id >= EPIC_INT_NUM) {
+		return -EINVAL;
+	}
+
+	interrupt_set_enabled(int_id, true);
+	return 0;
+}
+
+int epic_interrupt_disable(api_int_id_t int_id)
+{
+	if (int_id >= EPIC_INT_NUM || int_id == EPIC_INT_RESET) {
+		return -EINVAL;
+	}
+
+	interrupt_set_enabled(int_id, false);
+	return 0;
+}
+/* }}} */
+
+void vInterruptsTask(void *pvParameters)
+{
+	interrupts_task = xTaskGetCurrentTaskHandle();
+
+	while (true) {
+		mutex_lock(&interrupt_mutex);
+
+		if (!interrupt_data.has_pending) {
+			/* Wait for a wakeup event from interrupt_trigger() */
+			mutex_unlock(&interrupt_mutex);
+			ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+			mutex_lock(&interrupt_mutex);
+		}
+
+		while (!api_interrupt_is_ready()) {
+			mutex_unlock(&interrupt_mutex);
+			vTaskDelay(pdMS_TO_TICKS(5));
+			mutex_lock(&interrupt_mutex);
+		}
+
+		api_int_id_t current_irq = EPIC_INT_NUM;
+		for (size_t i = 0; i < EPIC_INT_NUM; i++) {
+			if (interrupt_data.int_pending[i]) {
+				current_irq                   = i;
+				interrupt_data.int_pending[i] = false;
+				break;
+			}
+		}
+
+		if (current_irq == EPIC_INT_NUM) {
+			interrupt_data.has_pending = false;
+		} else if (interrupt_data.int_enabled[current_irq]) {
+			api_interrupt_trigger(current_irq);
+		}
+
+		mutex_unlock(&interrupt_mutex);
+	}
+}
diff --git a/epicardium/modules/lifecycle.c b/epicardium/modules/lifecycle.c
index 2e72880fb892fc38db427c36494b4044d40d1401..76c5648cc20120eca6de28fad2ed4329404943da 100644
--- a/epicardium/modules/lifecycle.c
+++ b/epicardium/modules/lifecycle.c
@@ -4,7 +4,6 @@
 #include "modules/config.h"
 #include "modules/mutex.h"
 #include "api/dispatcher.h"
-#include "api/interrupt-sender.h"
 #include "l0der/l0der.h"
 
 #include "card10.h"
@@ -355,9 +354,17 @@ void vLifecycleTask(void *pvParameters)
 
 	mutex_unlock(&core1_mutex);
 
-	/* If `main.py` exists, start it.  Otherwise, start `menu.py`. */
-	if (epic_exec("main.py") < 0) {
-		return_to_menu();
+	/*
+	 * If `main.py` exists, start it.  Otherwise, start `menu.py`.
+	 *
+	 * We are not using epic_exec() & return_to_menu() here because those
+	 * trigger a reset which is undesirable during startup.
+	 */
+	mutex_lock(&core1_mutex);
+	int ret = load_sync("main.py", false);
+	mutex_unlock(&core1_mutex);
+	if (ret < 0) {
+		load_menu(false);
 	}
 
 	hardware_init();
diff --git a/epicardium/modules/max30001.c b/epicardium/modules/max30001.c
index 5c2ff10f8965e7a9e28c30ab51bbc00fd0af0c45..39379703ec292458a404a63346d4bf6258b04d84 100644
--- a/epicardium/modules/max30001.c
+++ b/epicardium/modules/max30001.c
@@ -11,7 +11,6 @@
 #include "task.h"
 #include "queue.h"
 
-#include "api/interrupt-sender.h"
 #include "epicardium.h"
 #include "modules/log.h"
 #include "modules/modules.h"
@@ -141,7 +140,7 @@ static void max30001_handle_samples(int16_t *sensor_data, int16_t n)
 			LOG_WARN("max30001", "queue full");
 		}
 	}
-	api_interrupt_trigger(EPIC_INT_MAX30001_ECG);
+	interrupt_trigger(EPIC_INT_MAX30001_ECG);
 }
 
 /***** Functions *****/
diff --git a/epicardium/modules/max86150.c b/epicardium/modules/max86150.c
index 428d0caf22f8f78511e1e7a0792602af092605e1..03527052c5b372d4a9ca30ae64d767d5ce51a79c 100644
--- a/epicardium/modules/max86150.c
+++ b/epicardium/modules/max86150.c
@@ -13,7 +13,6 @@
 #include "task.h"
 #include "queue.h"
 
-#include "api/interrupt-sender.h"
 #include "modules/modules.h"
 
 static const gpio_cfg_t max86150_interrupt_pin = {
@@ -140,7 +139,10 @@ static int max86150_handle_sample(struct max86150_sensor_data *data)
 		LOG_WARN("max86150", "queue full");
 		return -EIO;
 	}
-	return api_interrupt_trigger(EPIC_INT_MAX86150);
+
+	interrupt_trigger(EPIC_INT_MAX86150);
+
+	return 0;
 }
 
 static int max86150_fetch_fifo(void)
diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build
index 474b32930f9b4970fdde4cf65c6891f5eead8a1f..548d8563ea1e7ac2943843931e1733af5fc07dd2 100644
--- a/epicardium/modules/meson.build
+++ b/epicardium/modules/meson.build
@@ -9,6 +9,7 @@ module_sources = files(
   'gpio.c',
   'hardware.c',
   'hw-lock.c',
+  'interrupts.c',
   'leds.c',
   'lifecycle.c',
   'light_sensor.c',
diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h
index 67557ad4f226aa6ad9a116aa0b940b09889ed2a2..c26a1ce6cf7a2480eb982d8b80ab1215d4c06278 100644
--- a/epicardium/modules/modules.h
+++ b/epicardium/modules/modules.h
@@ -4,6 +4,7 @@
 #include "FreeRTOS.h"
 #include "gpio.h"
 #include "modules/mutex.h"
+#include "epicardium.h"
 
 #include <stdint.h>
 #include <stdbool.h>
@@ -27,6 +28,15 @@ int hardware_reset(void);
 void vLifecycleTask(void *pvParameters);
 void return_to_menu(void);
 
+/* ---------- Interrupts --------------------------------------------------- */
+void interrupt_init(void);
+void interrupt_trigger(api_int_id_t id);
+void interrupt_trigger_sync(api_int_id_t id);
+void interrupt_trigger_unsafe(api_int_id_t id) __attribute__((deprecated(
+	"interrupt_trigger_unsafe() is racy and only exists for legacy code."
+)));
+void vInterruptsTask(void *pvParameters);
+
 /* ---------- Serial ------------------------------------------------------- */
 #define SERIAL_READ_BUFFER_SIZE 128
 #define SERIAL_WRITE_STREAM_BUFFER_SIZE 512
diff --git a/epicardium/modules/rtc.c b/epicardium/modules/rtc.c
index b563462dbc242194e7f931721d82fcd21d0b344e..7d75fd169ca88aebb80e3d4def52b586b2684581 100644
--- a/epicardium/modules/rtc.c
+++ b/epicardium/modules/rtc.c
@@ -1,6 +1,6 @@
 #include "epicardium.h"
 #include "modules/log.h"
-#include "api/interrupt-sender.h"
+#include "modules/modules.h"
 
 #include "FreeRTOS.h"
 #include "task.h"
@@ -84,19 +84,23 @@ void epic_rtc_set_milliseconds(uint64_t milliseconds)
 	monotonic_offset += diff;
 }
 
+/* We need to use interrupt_trigger_unsafe() here */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 void RTC_IRQHandler(void)
 {
 	int flags = RTC_GetFlags();
 
 	if (flags & MXC_F_RTC_CTRL_ALDF) {
 		RTC_ClearFlags(MXC_F_RTC_CTRL_ALDF);
-		api_interrupt_trigger(EPIC_INT_RTC_ALARM);
+		interrupt_trigger_unsafe(EPIC_INT_RTC_ALARM);
 	} else {
 		LOG_WARN("rtc", "Unknown IRQ caught!");
 		/* Disable IRQ so it does not retrigger */
 		NVIC_DisableIRQ(RTC_IRQn);
 	}
 }
+#pragma GCC diagnostic pop
 
 int epic_rtc_schedule_alarm(uint32_t timestamp)
 {
@@ -107,7 +111,7 @@ int epic_rtc_schedule_alarm(uint32_t timestamp)
 	 * immediately.
 	 */
 	if (epic_rtc_get_seconds() >= timestamp) {
-		api_interrupt_trigger(EPIC_INT_RTC_ALARM);
+		interrupt_trigger(EPIC_INT_RTC_ALARM);
 		return 0;
 	}
 
diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c
index 25dd91846c9ea60a2aa26faa67cfce357ada7e9c..e80e30f75f2b718028238dd5270a0d4690b846c0 100644
--- a/epicardium/modules/serial.c
+++ b/epicardium/modules/serial.c
@@ -1,5 +1,4 @@
 #include "epicardium.h"
-#include "api/interrupt-sender.h"
 #include "modules/log.h"
 #include "modules/modules.h"
 
@@ -280,7 +279,7 @@ void serial_enqueue_char(char chr)
 {
 	if (chr == 0x3) {
 		/* Control-C */
-		api_interrupt_trigger(EPIC_INT_CTRL_C);
+		interrupt_trigger(EPIC_INT_CTRL_C);
 	}
 
 	if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) {
@@ -288,7 +287,7 @@ void serial_enqueue_char(char chr)
 		vTaskDelay(portTICK_PERIOD_MS * 50);
 	}
 
-	api_interrupt_trigger(EPIC_INT_UART_RX);
+	interrupt_trigger(EPIC_INT_UART_RX);
 }
 
 void vSerialTask(void *pvParameters)