CLClaude Lessons Try the free lessons

Claude Lessons / Guides

Claude Code Cross-Session Messaging: Let Your Sessions Talk to Each Other

If you run more than one Claude Code session, you have been the message bus. One session renames a column; you alt-tab to the other terminal and re-explain it. One session finishes a migration; you keep checking whether it's done. Cross-session messaging removes you from that loop: Claude in one session can hand a finding, a status or a decision to Claude in another, on this machine or on a different one. There is nothing to install and no flag to set — if your version is recent enough, it is already on. This guide covers how a message actually travels, the inbound controls that decide whether it gets delivered at all, the limits that bite in practice, the socket underneath it that hooks can post into, and the decision most people get wrong: whether they wanted messaging or an agent team.

Illustration of named, labelled work sessions passing notes between each other

What cross-session messaging is — and is not

Cross-session messaging lets Claude in one of your sessions deliver a piece of text to Claude in another. That is the whole primitive, and the word text is doing a lot of work. A message is never the sender's conversation history, never its files, never its context window. It is one Claude writing a note to another Claude.

That boundary explains most of the feature's behaviour. If you want to move a whole conversation — the reasoning, the file reads, the accumulated context — you don't message it, you resume the session instead. Messaging is for the sideways handoff: the thing one session knows that another needs right now, mid-task.

The four cases it was built for:

Claude can decide to send a message on its own when it sees the need. It is not purely something you trigger — which is exactly why the inbound controls further down exist.

The 60-second version

Open two sessions. In the first, give it a name so the second can address it:

claude --name api-worker

In the second session, confirm it can see the first:

/list-agents

The first line of the output, when present, is this session's own name — the one your other sessions use to reach it. The rows below are what Claude can reach. Now just say what the other session should know. You are prompting Claude, not writing the message:

Let @api-worker know the schema migration finished

Claude writes the message itself and sends it with SendMessage. In the receiving terminal a dim preview line appears in the conversation, something like › Message from @api-worker: Schema migration finished (ctrl+o to expand). That's it.

You never call the tools yourself. ListAgents discovers the targets and SendMessage delivers. Both are Claude's tools, not commands you type. The only things you type are /list-agents (to see for yourself), /rename or --name (to label a session), and your prompt.

Do you have it? Version and provider requirements

There is no "enable messaging" setting. A session either meets the requirements or it doesn't, and when it does, messaging is on.

Capability Minimum Claude Code version
Messaging at all — macOS, Linux, Linux inside WSL 2 v2.1.224
Messaging at all — native Windows v2.1.234
Starting a conversation with a session on another machine v2.1.225
@-mentioning a session in your prompt v2.1.232
The /config row for inbound control v2.1.232
Asking a session for a notice when it next goes idle v2.1.236 (in both sessions)
Same-machine messaging on non-Anthropic providers, or with feature-flag fetching off v2.1.248
Seeing undelivered messages under Waiting to be delivered in the transcript v2.1.309

Providers. Same-machine messaging works on every provider, including Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform and Microsoft Foundry — and in sessions running with feature-flag fetching off — from v2.1.248 onward. That is because same-machine delivery never touches Anthropic servers; it is a local socket. Reaching beyond this machine is the part that needs Remote Control and a claude.ai sign-in, so it is not available with an API key or on those providers.

How to check a session in one step. Type /list-agents (also available as /peers). If the command isn't recognized, that session doesn't have the feature — start with claude --version. If it works but a message you expected never arrived, messaging is fine and something narrower applies: a deny rule, the receiving session's inbound controls, or a target that isn't reachable. A session with messaging also shows a Peer address row in /status.

Messaging vs. teams vs. subagents vs. resume vs. channels

This is the decision that matters, and it is the one third-party coverage skips. Claude Code now has five ways to run work in parallel — subagents, agent view, agent teams, dynamic workflows and projects — plus three supporting tools that aren't ways to run agents at all: worktrees, cross-session messaging, and the /batch skill. Messaging sits in that second group. It is the wire, not the org chart.

You want to… Use Why not messaging
Pass a finding between sessions you started and steer Cross-session messaging This is the case it exists for
Have Claude split a project, assign pieces and supervise workers Agent teams Messaging has no lead, no shared task list and no structured protocol — you hold the plan
Keep a noisy side task out of your main context window Subagents A subagent reports back to its own parent conversation; no peer wiring needed
Move a whole conversation and its context somewhere Resume the session A message carries text only — never history, files or context
Dispatch several independent tasks and check status at a glance Agent view (claude agents) Messaging is complementary here, not a substitute — background sessions can be messaged too
Push external events (CI results, alerts, chat) into a running session Channels Messaging only carries text from your other Claude sessions, not from outside systems
Stop two parallel sessions from clobbering each other's files Worktrees Messaging coordinates information, not file access

Three rules of thumb that resolve most real cases:

Worth knowing: messaging and worktrees compose beautifully. Give each session its own worktree so they can't collide on files, then let them message each other so they don't duplicate work. That pairing is the practical answer to "how do I run three sessions on one repo without chaos", and it is strictly better than an agent team for that specific shape, because agent teams do not isolate teammates in worktrees — in a team you have to partition files by hand.

Sending: four ways to aim a message

You describe the outcome; Claude writes the text and picks the tool calls. There are four levels of precision available to you.

1. Describe the target by what it's doing

Claude discovers sessions with ListAgents, so a plain description is usually enough:

Ask the session running in my other terminal whether the migration finished

Note that this is a prompt you type, not the message Claude sends. Claude composes the actual wording, so leaving the content to Claude is fine and often better:

Explain what we just did to the session working on the payments API

2. Name the target with an @-mention

Type @ followed by the first letters of the session's name and pick it from the typeahead — the same gesture as @-mentioning a subagent. Claude Code inserts the mention, such as @api-worker, and tells Claude which session it names, so Claude can send without listing your sessions first. Requires v2.1.232 or later.

Two cases need more than the first few letters:

You can type a mention without the picker too. If more than one live session answers to that name, Claude asks you which one you mean before sending.

3. Give sessions names worth mentioning

A session answers to the name you set with /rename or the --name flag. If you set nothing, Claude Code names the session itself — which works, but makes your prompts vaguer than they need to be. Naming sessions after the work (api-worker, migration, frontend) is the single highest-leverage habit for this feature.

When several live sessions share a name, or Claude Code couldn't check everywhere your sessions run, Claude adds a short identifier to each row of its listing and addresses the message with that identifier instead of the bare name. Same-named sessions in different directories are also distinguishable in /list-agents, which shows each local session's working directory.

4. Read the listing yourself with /list-agents

You don't need to run anything before asking Claude to send — but when something isn't working, /list-agents is where you look. It groups what this session can reach:

Remote Control withholds details. While this session is connected to Remote Control, /list-agents leaves out each local session's working directory, leaves out any session name it can't attribute to a person (that row reads (unnamed session)), and leaves out the first line with this session's own name unless you typed that name at this terminal with --name or /rename. The output ends with a note saying details were withheld. None of this changes what Claude itself sees when it looks for a target — it is about what is shown to whoever is reading the screen.

What arrives, and what the receiving session does with it

Timing. The receiving Claude reads the message between tool calls during an active turn, so a running tool is never interrupted. If the receiving session is idle, Claude Code starts a new turn with the message.

Appearance. Claude Code shows the message as a dim one-line preview in the conversation, and that preview line stays there afterward. To see the full text: press Ctrl+O for the transcript viewer, click a truncated preview line in fullscreen rendering, or run the session with --verbose, which shows the full text instead of a preview. The preview shortens only what you see — Claude always reads the whole message.

A real example, as the full text reads when you expand it:

Schema migration finished
The new column is tenant_id, and rebasing on main is safe now.

Mentions are not attachments. A message arrives as plain text. If it mentions a file or an MCP resource with @, Claude sees the mention as written and Claude Code attaches nothing — whether the message starts a new turn or lands mid-turn. Claude can still open a mentioned path with its own tools on the receiving machine, subject to that session's permissions.

The four things an incoming message cannot do

This is the part to understand before you let sessions talk, and it is reassuring. Claude Code tells the receiving Claude that the message came from another session, not from you, and then limits it:

Permission boundaries stay per-session in the other direction too: Claude is instructed never to ask another session for an action that was denied or blocked in its own session, or that its own permission settings would block, and to route that work back to you instead. If you are writing permission policy for a team, that property is the one to quote — messaging is not a privilege-escalation path between sessions.

One cost note: once delivered, a message counts toward usage like a prompt you typed. Chatty sessions are not free.

The inbound controls that decide delivery

Every arriving message is checked by the receiving session, and the check ends in one of three outcomes:

You pick the policy with the crossSessionInbound setting:

Value Behaviour
accept Claude Code delivers each message to Claude
hold Claude Code shows a notice for each message and doesn't deliver it. If an accept later applies, held messages are released
refuse Claude Code drops each message without delivering it

Beyond editing a settings file you can select the value in the /config row Messages from your other sessions, which writes it to your user settings. That row needs v2.1.232 or later and doesn't appear while managed settings or the --settings flag set the key, since a user-settings value wouldn't apply then. The /config crossSessionInbound=value shorthand is rejected for this key — use the row or the file.

Precedence. Claude Code reads managed settings first, then the --settings flag, then user settings, and applies the first value found. refuse is stricter than hold, which is stricter than accept. When none of those trusted sources sets a value, a project or local hold or refuse still applies — a checked-in project file can tighten the policy but not loosen it.

A typo fails closed, loudly. Set crossSessionInbound to something unrecognized — the classic being "reject" instead of "refuse" — and Claude Code warns, ignores the value, and holds inbound messages for your approval while it is present, even when a higher-precedence source sets a real value. In managed settings an unrecognized value is treated as refuse outright. Before v2.1.248 the bad value was ignored silently, so if you are on an older build, check the spelling by hand.

The default, when no value applies

This is the cleverest and least-documented part of the feature. With no crossSessionInbound set, Claude Code decides per message from the two sessions' permission modes. It sorts every session into two classes: those that bypass permission prompts, and everything else. Plan mode counts as bypassing in interactive terminal sessions where bypass permissions are available; auto, acceptEdits and dontAsk all count as prompting.

Receiving session Sending session Outcome
Prompts for permissions Prompts for permissions Delivered
Prompts for permissions Bypasses prompts Held for your approval
Bypasses prompts Prompts for permissions Held for your approval
Bypasses prompts Bypasses prompts Delivered

Read that as a rule: a message crossing the bypass boundary in either direction gets held. A session that skips permission checks shouldn't be able to inject instructions into a careful one, and a careful session's text shouldn't silently steer one that will act on anything.

When the default holds a message in an interactive terminal session, an approval dialog opens there showing the sender and a preview. Approve delivers that one message; Deny, or dismissing the dialog, drops it. Leave it unanswered past the dialogExpiry deadline — default 5m, settable to 60s, 10m or never — and Claude Code closes it and drops the message. For a background session with no terminal attached, the dialog stays open past the deadline; after you attach, a full deadline period of silence closes it.

If the session's permission-mode class changes while messages are held, Claude Code re-applies the inbound rules, delivers whatever they now accept, and shows a notice. Sessions in the VS Code extension or the Desktop app can't show the dialog at all, so a held message there waits out the same deadline.

Headless sessions. A claude -p session binds an inbox socket like an interactive one, so a long-running worker can receive messages and appears in the listing — but it can't show the approval dialog. A default-held message there is kept for the same dialogExpiry deadline: if a mode or settings change allows it in time, it's delivered; past the deadline it's dropped and reported as expired to a sender Claude Code can reach. Set dialogExpiry to "never" to keep default-held messages until the session ends. To let a -p worker take messages unattended, start it with crossSessionInbound set to accept in its --settings value — an accept in your user settings works too, but applies to every session you run. A message held by an explicit hold never expires; it is delivered whenever an accept later applies.

Asking a session to tell you when it goes idle

The most immediately useful thing here is not messaging at all — it's the notice. Claude can ask one of your sessions on this machine to send back one notice when that session next goes idle or exits. Idle means it finished a turn with nothing queued. Requires v2.1.236 or later in both sessions.

You ask for it in plain language:

Tell me when the migration session finishes what it's working on

Claude subscribes using the SendMessage tool's notify_when_idle input — either attached to a message it was sending anyway, or on its own. On its own, Claude Code subscribes without starting a turn or spending tokens in the watched session, and sends the notice immediately if that session is already idle. Attached to a message, the message is delivered first and the notice follows later.

The watched session shows a line saying another process asked to be told when it is next idle. The asking session shows the notice as a line naming the watched session, and that line can include the time the turn finished plus a one-line status from it. If the asking session is idle when the notice arrives, Claude Code starts a new turn with it.

Four limits to know:

Crossing machines and the cloud

Where the target runs changes how the message travels — and whether it passes through Anthropic servers at all. This table is the one to screenshot if you are answering a security question about this feature.

Where the other session runs How the message travels
On this machine Over a per-session Unix socket on macOS and Linux, or a per-session named pipe on native Windows — never through Anthropic servers
On another of your machines Through Anthropic servers, arriving over that machine's Remote Control connection
In the cloud Through Anthropic servers, straight to the cloud session

Starting a conversation with a session on another of your machines requires v2.1.225 or later and a target that actually appears in the listing. A row can carry a condition that changes what happens:

Replies don't always work. A cloud session, including one on a self-hosted runner, can't use SendMessage to answer a message from another machine even when the message carries a reply address — the message still arrives, but you read the response in that session's transcript at claude.ai/code. Similarly, if the sending session isn't connected to Remote Control when Claude sends beyond this machine, the message goes through without a reply address, and the receiving Claude can't answer it.

Container and WSL boundaries are hard walls. A session inside a container and a session on the host can't reach each other. Two sessions inside the same container can, including on a self-hosted runner. A session inside WSL 2 and a native Windows session on the same computer can't reach each other either. These are the most common "why can't it see my other session" causes after the version check.

Gating cross-machine sends. Set isolatePeerMachines to true to require your explicit approval before any SendMessage reaches a session beyond this machine. The prompt appears even in bypassPermissions mode, which skips ordinary permission prompts — one of very few things that does. A true from any settings scope applies, so a checked-in project file can turn the requirement on but not off, and same-machine messages never prompt.

{
  "isolatePeerMachines": true
}

One more listing quirk worth knowing if a session seems missing: Claude Code reads the cloud and other-machine session lists newest first and stops after a bounded number of pages, so a session that fell past them can't be messaged by name.

The limits that bite in practice

These are properties of the channel itself and apply wherever messaging runs. Three of them produce errors you will eventually read.

Limit What happens
Plain text only Structured agent team protocol messages stay inside a team; across sessions, text is all there is
Message size ≈ 1,048,576 characters serialized Refused at the sender, naming both sizes. Nothing reaches the receiving session. Resending the same text fails identically
Rapid bursts to one session Once a burst reaches what that session's inbox accepts, further sends are refused at the sender. The refusal tells Claude to batch the rest into one message or wait
Held messages: 100 Past 100, Claude Code drops the oldest
Accepted queue: 50 At most 50 accepted messages queue for Claude to read
Loop protection The receiving session rate-limits repeated messages per sender and drops identical repeats arriving within a short window, so a two-session message loop stops on its own

The errors, verbatim-ish, so you recognize them:

The practical habit: if you catch yourself prompting a session to relay a large file's contents, stop. Have it write a file and send the path. The receiving Claude can read it with its own tools — subject to its own permissions — and you stay far away from the size cap.

The inbox socket: hooks, scripts and sandboxes

Read this section for three reasons: a session you expected isn't in the agent list, you want a script or hook to post into a session, or a sandboxed command can't reach the socket.

Claude Code binds an inbox socket for each session with messaging enabled — a Unix domain socket on macOS and Linux including WSL 2, a named pipe on native Windows. Two places show you the path:

Who can write to it. On macOS and Linux the socket is restricted to your operating-system user. On native Windows every connection must first authenticate with a key only your OS user can read. Either way, another user's sessions on a shared machine can't deliver to it. On macOS and Linux, Claude Code also refuses to create the socket in a directory it can't accept — one another user owns, for instance — and uses a private per-user directory, /tmp/cc-socks-<uid>, instead. When no directory is acceptable the session runs without an inbox: you get a notice, /status shows unavailable and the reason in Peer address, and the --debug log records the full refusal.

Posting into your own session. Alongside the path, Claude Code exports a per-session token as CLAUDE_CODE_MESSAGING_TOKEN. A script posting to its own session's socket sends {"type":"auth","token":"<token>"} as the first line of the connection. The line is optional on macOS and Linux including WSL 2 — connections are accepted with or without it — and required on native Windows, where Claude Code closes any connection whose first line isn't a valid auth line and delivers nothing from it.

Open the connection only when your text is ready. Claude Code closes a connection that hasn't sent a complete line within 30 seconds. So capture a slow command's output first, then open the connection and send it. Piping a long-running command straight into the socket is the mistake here.

Own-child messages. Socket arrivals go through the same inbound controls as any peer message, with one exception: when no crossSessionInbound value applies, Claude Code delivers a message it can verify came from the session's own child processes — a hook or Bash command posting back to its own session. How it verifies varies by platform:

The practical upshot: always send the auth line if your hook or script needs reliable delivery. It costs one line and removes the platform variance entirely.

Sandboxed sessions. Whether a Bash command can reach the socket from inside the sandbox is controlled by the sandbox's Unix-socket settings, sandbox.network.allowAllUnixSockets and sandbox.network.allowUnixSockets. If a hook that posts to the socket works outside the sandbox and silently does nothing inside it, that is the setting to look at.

Bare mode binds nothing. A session started in bare mode doesn't bind the socket, so it can't receive messages and doesn't appear in the agent list. Ordinary claude -p sessions do bind one.

Turning it off, for a session or an organization

Receiving and sending are separate controls, so turn off whichever direction you need — or both.

For an organization-wide policy, administrators combine both in managed settings:

{
  "permissions": {
    "deny": ["SendMessage", "ListAgents"]
  },
  "crossSessionInbound": "refuse"
}

Two consequences that surprise people:

Common mistakes

Practice the multi-session habits, not just the flags

Claude Lessons has 38 free interactive lessons — naming and resuming sessions, running parallel work in worktrees, delegating to subagents, choosing a permission mode — practiced hands-on in a simulated Claude Code workspace with instant feedback.

Practice hands-on in the free interactive lessons

FAQ

How do I send a message from one Claude Code session to another?

You don't send it yourself — you ask Claude to. Tell the session what the other session needs to know, for example "let the session working on the payments API know the schema migration finished". Claude finds the target with the ListAgents tool and delivers with SendMessage. You never call either tool directly. To name the target explicitly, type @ followed by the first letters of the session's name and pick it from the typeahead, which requires Claude Code v2.1.232 or later.

Do I need to enable cross-session messaging in Claude Code?

No. There is no setting to switch on. Messaging is on in any session that meets the version requirement: Claude Code v2.1.224 or later on macOS, Linux and Linux inside WSL 2, or v2.1.234 or later on native Windows. To check a session, run /list-agents. If the command isn't recognized, that session doesn't have the feature and the first thing to check is claude --version.

Can another session approve a permission prompt or change my settings?

No. Claude Code tells the receiving Claude that the message came from another session rather than from you, so it never counts as your consent and can't answer a pending permission prompt. The receiving Claude is also instructed never to change permission settings, CLAUDE.md or other configuration because another session asked. A slash command inside the message text, such as /compact, arrives as plain text and is never executed. If acting on the message needs a permission the receiving session doesn't have, you get the same prompt you'd get for any other work.

Can Claude Code sessions message each other across machines?

Yes, with a condition. Same-machine messages travel over a per-session socket and never touch Anthropic servers. To reach a session on another of your machines or in the cloud, both this session and the target must be connected to Remote Control, which needs a claude.ai sign-in as the session's active authentication. Starting a conversation with a session on another machine requires Claude Code v2.1.225 or later. Claude can't find those sessions with an API key, or on Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform or Microsoft Foundry.

Why doesn't one of my sessions show up in /list-agents?

A session appears in the listing only when it binds an inbox socket. The usual causes are a session started in bare mode, which never binds one, a version below the requirement, or a directory Claude Code refused to create the socket in — check the Peer address row in that session's /status, which reads unavailable with a reason. Containers also matter: a session inside a container and a session on the host can't reach each other, and neither can a session inside WSL 2 and a native Windows session on the same computer. Cloud sessions and sessions on other machines appear only while this session is connected to Remote Control.

How do I stop my Claude Code sessions from messaging each other?

Receiving and sending are separate controls. To stop a session receiving, set crossSessionInbound to "refuse" and Claude Code drops inbound peer messages without delivering them. To stop a session sending or listing, add permission deny rules naming SendMessage and ListAgents, both written as the bare tool name with no specifier. Note that denying SendMessage also removes messaging to subagents and agent-team teammates, because the same tool serves all three.

Can a script or a hook post a message into a Claude Code session?

Yes. Claude Code exports each session's inbox address as CLAUDE_CODE_MESSAGING_SOCKET and a per-session token as CLAUDE_CODE_MESSAGING_TOKEN to hooks and Bash commands, and /status shows the address in its Peer address row prefixed with uds:. A script posting to its own session's socket can authenticate by sending {"type":"auth","token":"<token>"} as the first line of the connection — optional on macOS and Linux, required on native Windows. Open the connection only when the text is ready, because Claude Code closes a connection that hasn't sent a complete line within 30 seconds.

Cross-session messaging or agent teams — which should I use?

Use messaging when you are driving the sessions. You start each one, you decide what it works on, and Claude passes findings sideways when one session learns something another needs. Use an agent team when you want Claude to split a project up, assign the pieces and supervise the workers, with a shared task list and structured protocol messages. The practical difference is who holds the plan: with messaging you do, with a team the lead does. Messaging is also the only one of the two that reaches a session on another machine or in the cloud.

Running more than one session at a time?

Messaging is the wire between sessions. The lessons cover the rest of the multi-session toolkit — isolation, delegation, and keeping context under control — as hands-on practice rather than reference reading.

Start the free lessons