diff --git a/src/components/dashboard/inbox.tsx b/src/components/dashboard/inbox.tsx
index ff2c0c7..c79ac1c 100644
--- a/src/components/dashboard/inbox.tsx
+++ b/src/components/dashboard/inbox.tsx
@@ -135,8 +135,10 @@ function Detail({ it, onError, onDone, onSnooze, onArchive }: {
{it.threadId ? (
// A message/mail item → open the conversation to read + reply.
+ // key by threadId: the SDK's useQuery only refetches when the ACTION changes, not the
+ // variables — so switching items must remount MailReader to load the new thread's history.
-
+
) : (
// A non-threaded item (e.g. a system alert) → show its detail.
diff --git a/src/lib/inbox-api.ts b/src/lib/inbox-api.ts
index 6f3d2e6..6ce40fd 100644
--- a/src/lib/inbox-api.ts
+++ b/src/lib/inbox-api.ts
@@ -8,7 +8,7 @@
// query crm.inbox.list { state? } -> InboxItem[]
// cmd crm.inbox.transition { id, state, reason? } -> InboxItem
-import { useCallback, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
import { useAppShell, useQuery } from "@abe-kap/appshell-sdk/react";
import { isShellConfigured } from "./appshell";
import type { MailThread } from "./mail-api";
@@ -40,6 +40,11 @@ function useLiveInbox(state?: InboxState): InboxData {
const showMail = !state || state === "OPEN";
const mq = useQuery("crm.mail.list", {});
+ // The SDK's useQuery only refetches when the ACTION changes, not the variables — so a filter
+ // change (same action, new { state }) wouldn't reload. Force a refetch when the filter changes.
+ const refetchInbox = q.refetch;
+ useEffect(() => { refetchInbox(); }, [state, refetchInbox]);
+
const items = useMemo(() => {
const inboxItems = q.data ?? [];
const mailItems: UiInboxItem[] = showMail