diff --git a/epicardium/api/caller.c b/epicardium/api/caller.c
index 8e61b674b4179832223f1c85adafca137c2ea113..54d9c4aea555950816bf7ab3f52898c6ca73572c 100644
--- a/epicardium/api/caller.c
+++ b/epicardium/api/caller.c
@@ -5,8 +5,17 @@
 #define MXC_ASSERT_ENABLE
 #include "mxc_assert.h"
 
+static uint32_t irq_save = 0;
+
 void *_api_call_start(api_id_t id, uintptr_t size)
 {
+	/*
+	 * Disable all maskable interrupts here, to be turned on again at the
+	 * end of _api_call_transact().
+	 */
+	irq_save = __get_PRIMASK();
+	__set_PRIMASK(1);
+
 	while (SEMA_GetSema(_API_SEMAPHORE) == E_BUSY) {
 	}
 
@@ -51,6 +60,12 @@ void *_api_call_transact(void *buffer)
 	API_CALL_MEM->call_flag = _API_FLAG_IDLE;
 	SEMA_FreeSema(_API_SEMAPHORE);
 
+	/*
+	 * Re-enable interrupts (if previously enabled) after completing the API
+	 * call.
+	 */
+	__set_PRIMASK(irq_save);
+
 	return API_CALL_MEM->buffer;
 }
 
diff --git a/epicardium/api/control.c b/epicardium/api/control.c
index 102c07e27d325004e4ae584c7cd327ecb67503ce..27b88c2c9a18dd358632f39f504f1f4aa0efa0ad 100644
--- a/epicardium/api/control.c
+++ b/epicardium/api/control.c
@@ -214,24 +214,32 @@ void core1_trigger_reset(void)
 	interrupt_trigger_sync(EPIC_INT_RESET);
 }
 
+bool core1_is_ready(void)
+{
+	bool ready;
+
+	while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
+	}
+
+	/*
+	 * core 1 will set the ready flag once it is spinning in the
+	 * above loop, waiting for a new IVT.
+	 */
+	ready = core1_info.ready;
+
+	SEMA_FreeSema(_CONTROL_SEMAPHORE);
+
+	return ready;
+}
+
 void core1_wait_ready(void)
 {
 	/* Wait for the core to accept */
 	while (1) {
-		while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
-		}
-
-		/*
-		 * core 1 will set the ready flag once it is spinning in the
-		 * above loop, waiting for a new IVT.
-		 */
-		if (core1_info.ready) {
-			SEMA_FreeSema(_CONTROL_SEMAPHORE);
+		if (core1_is_ready()) {
 			break;
 		}
 
-		SEMA_FreeSema(_CONTROL_SEMAPHORE);
-
 		for (int i = 0; i < 10000; i++) {
 		}
 	}
diff --git a/epicardium/api/dispatcher.h b/epicardium/api/dispatcher.h
index 4b79095ea530aa8b06cd1bf71d5d5b91972eb049..727fe5acd339e1ce2e882b6bf4a73b433daf09c3 100644
--- a/epicardium/api/dispatcher.h
+++ b/epicardium/api/dispatcher.h
@@ -39,6 +39,9 @@ void core1_boot(void);
 /* Reset core 1 into a state where it can accept a new payload */
 void core1_trigger_reset(void);
 
+/* Check if core 1 is ready for a new payload */
+bool core1_is_ready(void);
+
 /* Wait for core 1 to respond that it is ready for a new payload */
 void core1_wait_ready(void);
 
diff --git a/epicardium/user_core/lifecycle.c b/epicardium/user_core/lifecycle.c
index efe1f532a3eb36d2fcd626aaa978634b221d14b2..89d4dce4973ed9df3a11a0ba89dcad7d9878dcb8 100644
--- a/epicardium/user_core/lifecycle.c
+++ b/epicardium/user_core/lifecycle.c
@@ -119,8 +119,18 @@ static int do_load(struct load_info *info)
 
 	/*
 	 * Wait for the core to become ready to accept a new payload.
+	 *
+	 * If it is not yet ready, hand back control of the API mutex to the
+	 * dispatcher so it can finish dispatching a current API call.  This is
+	 * necessary for payloads which have interrupts disabled during an API
+	 * call.
 	 */
-	core1_wait_ready();
+	while (!core1_is_ready()) {
+		mutex_unlock(&api_mutex);
+		/* Sleep so the dispatcher task can take the lock. */
+		vTaskDelay(8);
+		mutex_lock(&api_mutex);
+	}
 
 	/*
 	 * Reinitialize Hardware & Drivers