Remote Control & Headless Automation
13 min read
What You Will Learn
Claude Code doesn’t have to live inside your terminal window. This chapter shows you how to access your local session from a phone or browser via remote control, run a persistent server that handles multiple concurrent sessions, interact with Claude Code through messaging platforms like Telegram and Discord, run Claude Code programmatically with the -p flag for scripts and CI/CD pipelines, and set up recurring tasks with cron expressions so work happens even when you’re not watching.
Remote Control
Remote control lets you access your local Claude Code session from any device, whether a phone, tablet, or another computer, using just a web browser. Your machine connects outbound to Anthropic’s relay service over HTTPS, so there’s nothing to configure on the network side. No inbound ports get opened, no firewall rules need changing, and no VPN is required.
To get started, fire up a remote control session from your terminal.
claude remote-controlClaude Code displays a QR code and a URL. Scan the QR code from your phone or open the URL in any browser, and you’re connected to the same session running on your machine.
Already have an active Claude Code session? You can start remote control from within it using the slash command.
/remote-controlGenerates a QR code and URL without starting a new session.
Once you’re connected, everything you type in the remote browser gets sent to your local Claude Code instance and responses appear in real time. Claude Code still runs on your machine, reads your local files, and executes commands in your local environment. The remote connection is really just a display and input relay.
Server Mode
For scenarios where you need a persistent remote control service that handles multiple users or automated connections, server mode turns remote control into a long-running server.
claude remote-control --spawn worktree --capacity 3Accepts up to 3 concurrent sessions, each in its own git worktree.
The --spawn flag activates server mode and accepts one of three isolation modes that control how sessions share your working directory:
same-dir— All sessions share the same working directory. Simple but sessions can step on each other’s changes. Best for sequential use or read-heavy workflows.worktree— Each session gets its own git worktree, providing full isolation between concurrent sessions. Changes in one session don’t affect another. Best for parallel development work.session— Each session gets a separate directory. Similar isolation to worktree mode but without the git integration.
The --capacity flag limits how many concurrent sessions the server accepts. The default is 1. Set it higher when you expect multiple users connecting at the same time.
For automated environments where you need predictable session names, the --remote-control-session-name-prefix flag adds a prefix to auto-generated session names.
claude remote-control --spawn worktree --capacity 5 --remote-control-session-name-prefix "ci-review"Sessions will be named ci-review-1, ci-review-2, etc.
Server mode is designed for persistent services: a shared development server that team members connect to from their browsers, a CI/CD pipeline that spawns review sessions on demand, or a kiosk setup where multiple users take turns.
Remote vs Web Sessions
These two might sound similar, but they’re fundamentally different.
Remote control means Claude runs locally on your machine. It has full access to your files, terminal, Git history, and entire development environment. The phone or browser is just a window into that local session. When Claude Code edits a file through remote control, the change hits your disk immediately.
Web sessions at console.anthropic.com run on Anthropic’s cloud infrastructure. Claude operates in a sandboxed cloud environment with no access to your local filesystem. They’re useful for quick questions, exploring ideas, or working when you’re away from your development machine.
Reach for remote control when you need Claude Code to work with your local project: reviewing code from your phone during a commute, pairing with a colleague by sharing the URL, or picking up a task from a different room. Go with web sessions when you don’t need local file access.
If you started something in a web session and want to continue locally, the --teleport flag picks up a web session in your local terminal.
claude --teleportOpens a session selector to pick up where you left off on the web.
Channels
Research Preview: Channels are experimental and disabled by default. The setup flow, supported platforms, and security model may change. This section documents the feature as of April 2026.
Channels let you push messages from external messaging platforms into a running Claude Code session. Instead of opening a browser or terminal, you send a Telegram message, a Discord message, or an iMessage, and it arrives in your Claude Code session as an event. Claude reads the message, processes it, and replies back through the same channel.
This turns Claude Code into something you can interact with from anywhere your phone has signal. Ask Claude to check on a deployment from Telegram while you’re out, get a code review summary in Discord, or send a quick task from iMessage. The session keeps running on your machine with full access to your local files and tools.
Setting Up Channels
Channels are installed as plugins from the official marketplace. Each messaging platform has its own plugin.
# Install a channel pluginclaude plugin install telegram@claude-plugins-official
# Start a session with channels enabledclaude --channelsThe --channels flag activates installed channel plugins for the session.
You can also specify which channel plugin to activate directly.
claude --channels plugin:telegram@claude-plugins-officialFor installation details and marketplace browsing, see Plugins.
Supported Platforms
Three messaging platforms are currently supported:
- Telegram — Uses a BotFather bot. Pair with a code sent to the bot.
- Discord — Uses a Discord bot. Pair with a code sent in a DM.
- iMessage — Reads the Messages database and replies via AppleScript. macOS only. Pair by per-handle access.
Each platform has its own setup flow for creating the bot or granting access, but the pattern is the same: install the plugin, configure credentials, and pair the sender.
Security Model
Channels use a sender allowlist to control who can push messages into your session. When you set up a channel, you go through a pairing process that generates a pairing code. Only senders you’ve explicitly paired can send messages that reach your Claude Code session.
This means a random Telegram user can’t send commands to your session just because they find your bot. The pairing code handshake establishes trust before any messages flow.
For remote tool approval, channels support a permission relay capability. If Claude needs approval for an action while you’re away from your terminal, the approval request can be forwarded through the channel so you can approve or deny from your phone.
Enterprise Controls
Organizations can control channel availability through managed settings. The channelsEnabled setting acts as a master switch, and allowedChannelPlugins restricts which specific channel plugins can be used.
{ "channelsEnabled": true, "allowedChannelPlugins": ["telegram@claude-plugins-official"]}Restricts channels to Telegram only across the organization.
For the full set of enterprise managed settings, see Security.
Programmatic Usage with -p
The -p flag runs Claude Code with a single prompt and exits when it’s done. This is your entry point for integrating Claude Code into scripts, CI/CD pipelines, and automated workflows.
claude -p "Explain the main function in src/index.ts"Claude Code processes the prompt, writes output to stdout, and exits.
Here’s headless mode in action, running prompts, getting JSON output, and piping results to other tools:
View as text (accessible transcript)
$ claude -p 'List all exported functions in this project'
Functions: isValidEmail, authMiddleware, default router (auth routes)
Interfaces: CreateUserInput, LoginInput, UserPayload, AuthenticatedRequest
$ claude -p 'How many TypeScript files are in src/?' --output-format json | jq .
{ "result": "There are 6 TypeScript files...", "cost": 0.003, ... }
$ claude -p 'Generate a one-line summary of this project' | cat -n
1 A Node.js REST API with JWT authentication built using Express and TypeScript.Output comes back as plain text by default. You can change that with --output-format.
# Plain text (default)claude -p "List all exported functions in src/utils.ts" --output-format text
# Structured JSON with metadataclaude -p "List all exported functions in src/utils.ts" --output-format json
# Streaming JSON (one event per line)claude -p "List all exported functions in src/utils.ts" --output-format stream-jsonThe JSON format gives you a structured response with the result text, token usage, cost, and session metadata, making it easy to parse Claude Code output in scripts.
{ "result": "The file exports three functions: parseConfig, validateInput, formatOutput...", "cost": 0.003, "duration_ms": 2400, "session_id": "abc123", "num_turns": 1}Exact fields may vary. Use this format for programmatic consumption.
With stream-json, you get one JSON object per line as Claude Code processes the prompt. Each line represents an event, such as an assistant message, a tool call, or a tool result. It’s great for real-time monitoring in pipelines where you’d rather see progress as it happens instead of waiting for the final result.
The —bare Flag
When running Claude Code in scripts or CI where you don’t need project-specific configuration, the --bare flag skips auto-discovery of hooks, LSP servers, plugins, MCP servers, auto-memory, and CLAUDE.md files. This gives you a clean, fast invocation without any project-specific configuration loading.
claude -p "Summarize this diff" --bare < changes.diffSkips all auto-discovery for faster startup in scripted contexts.
Reach for --bare when you want Claude Code to behave as a pure language model interface without any of the project-aware features. It’s particularly useful in CI pipelines where the overhead of discovering project configuration adds latency and the configuration isn’t relevant to the task.
For building production automation beyond simple -p invocations, the Agent SDK provides a full programmatic interface with streaming, hooks as callbacks, and session management.
Deep Links
External tools can launch Claude Code with pre-filled prompts using the claude-cli:// protocol. Multi-line prompts use %0A encoding for newlines. This is useful for editor integrations and custom tooling that need to hand off tasks to Claude Code.
Structured Output with —json-schema
When you need Claude Code to return data in a specific shape, pass a JSON schema with the --json-schema flag. Claude Code will structure its response to match.
claude -p "Analyze src/auth.ts and extract all function signatures" --output-format json --json-schema '{ "type": "object", "properties": { "functions": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "params": { "type": "array", "items": { "type": "string" } }, "returnType": { "type": "string" } }, "required": ["name", "params", "returnType"] } } }, "required": ["functions"]}'The response conforms to whatever schema you provide.
{ "functions": [ { "name": "validateToken", "params": ["token: string", "options?: AuthOptions"], "returnType": "Promise<TokenPayload>" }, { "name": "refreshSession", "params": ["sessionId: string"], "returnType": "Promise<Session>" } ]}This opens up a lot of possibilities for building tooling on top of Claude Code. You can extract code metrics, generate documentation, audit dependencies, or build custom analysis pipelines, all with predictable output shapes that your downstream tools can rely on.
System Prompt Customization
Two flags let you control the system prompt Claude Code uses when running with -p.
--system-prompt replaces the default system prompt entirely. Reach for this when you want Claude Code to behave in a completely different way from its defaults.
claude -p "Review this PR" --system-prompt "You are a strict code reviewer. Focus only on security issues and performance problems. Ignore style."--append-system-prompt adds to the default system prompt instead of replacing it. This is what you’ll want when Claude Code should keep its default capabilities but also follow additional project-specific rules.
claude -p "Add a new API endpoint for user preferences" --append-system-prompt "Always use the repository's existing error handling patterns. Never add new dependencies without justification."In most cases, --append-system-prompt is the safer choice. The default system prompt contains important instructions about tool usage, safety, and output formatting. Replacing it entirely with --system-prompt strips all of that out, which can lead to unexpected behavior.
Auto-Approving Tools in Scripts
When you run Claude Code with the -p flag, every tool invocation that would normally need your approval blocks the process. In a script or CI/CD pipeline, there’s nobody around to click “approve.” You need to explicitly declare which tools Claude Code can use without asking.
The --allowedTools flag handles this. It uses the same permission rule syntax described in Models, Cost Economics & Permissions.
claude -p "Fix the failing tests" --allowedTools "Bash(npm test)" "Bash(npm run lint)" "Edit" "Write"Without --allowedTools, Claude Code will fail on any action requiring permission.
You can be as specific or as broad as the task calls for.
# Allow all bash commands (broad)claude -p "Set up the project" --allowedTools "Bash(*)"
# Allow only specific operations (narrow)claude -p "Run the test suite and fix failures" --allowedTools "Bash(npm test)" "Bash(npx vitest run)" "Edit"
# Allow file operations only (no command execution)claude -p "Refactor the utils module" --allowedTools "Edit" "Write"If you run Claude Code with -p and don’t provide --allowedTools, any action that requires approval causes the process to exit with an error. Always specify the tools your script needs.
Session Management
By default, each claude -p invocation starts a fresh session with no prior context. For multi-step workflows where you want to build on what happened before, use session continuation flags.
--continue picks up from the most recent session.
# Step 1: Analyze the codebaseclaude -p "Analyze the authentication module and identify security issues" --output-format json > analysis.json
# Step 2: Continue the same session to fix the issues foundclaude -p "Fix the security issues you identified" --continue --allowedTools "Edit" "Bash(npm test)"--resume resumes a specific session by ID. This comes in handy when you’re running multiple pipelines in parallel and need to continue a particular session.
# Get the session ID from JSON outputSESSION_ID=$(cat analysis.json | jq -r '.session_id')
# Resume that specific sessionclaude -p "Now write tests for the fixes" --resume "$SESSION_ID" --allowedTools "Write" "Bash(npm test)"Here’s --continue in action. The second prompt picks up context from the first without rereading the codebase:
View as text (accessible transcript)
$ claude -p 'What authentication method does this project use?'
This project uses JWT (JSON Web Token) authentication...
$ claude --continue -p 'What are the security risks with that approach?'
Claude remembers the previous conversation about JWT auth
and discusses risks without needing to re-read the codebase:
- Token theft if not stored securely
- No built-in revocation mechanism
- Secret key management concernsSession continuation preserves the full conversation history, including which files Claude Code read, what changes it made, and any context it accumulated. This keeps multi-step workflows coherent without you having to repeat instructions.
Web-Based Workflows
Claude Code’s web sessions at console.anthropic.com support automated workflows that run entirely in the cloud. PR Auto-Fix watches your CI pipeline and automatically fixes failures, handles reviewer nits, and pushes commits until the build is green. Toggle it on from the CI panel in a web session when you want Claude to keep a pull request moving forward without manual intervention.
Scheduled Tasks
Claude Code can run tasks on a recurring schedule. The /loop skill lets you create scheduled tasks conversationally. Just describe what you want to happen and when, and Claude Code sets up the automation for you.
Every morning at 9 AM, check for outdated npm dependenciesand create a summary report in reports/deps-check.mdClaude Code parses the schedule and creates the cron job for you.
Behind the scenes, three tools power scheduled task management.
CronCreate registers a new recurring task with a cron expression, a prompt to execute, and optional tool permissions.
CronList shows all your registered scheduled tasks along with their schedules, last run time, and next scheduled run.
CronDelete removes a scheduled task by its ID.
You can interact with these tools directly, but the /loop skill is more natural. Just describe your automation need in plain language and Claude Code translates it into the right cron configuration.
Cron Expressions
Scheduled tasks use standard 5 field cron expressions: minute, hour, day of month, month, and day of week.
# ┌─── minute (0-59)# │ ┌─── hour (0-23)# │ │ ┌─── day of month (1-31)# │ │ │ ┌─── month (1-12)# │ │ │ │ ┌─── day of week (0-6, Sunday=0)# │ │ │ │ │# * * * * *
# Every weekday at 9 AM0 9 * * 1-5
# Every 30 minutes*/30 * * * *
# Every Sunday at midnight0 0 * * 0
# First day of every month at 8 AM0 8 1 * *Scheduled tasks run in your local timezone, not UTC. This is an important detail when you’re scheduling tasks around your work hours.
Critical limitation: tasks expire after 7 days. If Claude Code Desktop isn’t running for 7 days, scheduled tasks created through the CLI expire and won’t resume automatically. This is by design; CLI created cron tasks are meant for short-lived automation, not permanent infrastructure. For something more durable, see the alternatives below.
Durable Scheduling Alternatives
When you need scheduled tasks that survive restarts, machine reboots, or extended offline periods, reach for one of these alternatives instead of CLI cron tasks.
Claude Code Desktop scheduled tasks are persistent. Tasks created through the Desktop app survive restarts and don’t have the 7 day expiry limitation. If your automation needs to keep running reliably for weeks or months, create the schedule through the Desktop app.
GitHub Actions with schedule trigger moves the automation entirely to CI. The task runs on GitHub’s infrastructure regardless of whether your local machine is on.
name: Weekly Dependency Review
on:schedule: - cron: '0 9 * * 1' # Every Monday at 9 AM UTC
jobs:review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Claude Code run: curl -fsSL https://claude.ai/install.sh | bash - name: Run dependency review run: | claude -p "Review package.json for outdated or vulnerable dependencies. Create a report." --output-format text --allowedTools "Bash(npm audit)" "Bash(npm outdated)" "Read" > dependency-report.md env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}Runs in CI with no local machine dependency. Note the cron uses UTC.
So which do you pick? CLI cron works well for quick, temporary automation (checking something for the next few days). Desktop scheduled tasks are your go to for persistent local automation. And GitHub Actions is the right call for durable, team visible automation that runs regardless of any individual machine’s state.
Best Practices
-
Use
-pfor CI/CD integration. It’s the entry point for every automated Claude Code workflow. Pair it with--output-format jsonfor machine readable output and--allowedToolsfor unattended execution. -
Use
--barein CI pipelines. Skip project configuration loading when it’s not relevant to the task. Faster startup, cleaner execution, no side effects from project-specific hooks or plugins. -
Prefer
--append-system-promptover--system-prompt. The default system prompt has safety and formatting instructions you almost always want to keep. Append project-specific rules rather than replacing everything. -
Always specify
--allowedToolsin scripts. Without explicit tool permissions, Claude Code exits with an error on any action that requires approval. Specify the minimum set of tools your script actually needs. -
Use JSON output for programmatic consumption. The structured JSON response includes token usage, cost, and session ID alongside the result, making it straightforward to build dashboards, cost tracking, and audit logs.
-
Test cron schedules with short intervals first. Before setting up a daily cron, try
*/5 * * * *(every 5 minutes) to verify the task works as expected, then switch to the production schedule. -
Prefer Desktop scheduled tasks or GitHub Actions over CLI cron for anything durable. CLI cron tasks expire after 7 days. If the automation matters, use a scheduling mechanism that persists.
-
Use
--continuefor multi-step pipelines. Break complex automated workflows into sequential steps that build on each other’s context, rather than cramming everything into one massive prompt. -
Keep Channels for trusted environments. The sender allowlist and pairing codes provide security, but treat channel access like terminal access. Only pair devices and accounts you trust completely.
Further Reading
- Remote Control: official documentation on remote access setup and security
- Running Claude Code Programmatically: the
-pflag, output formats, and system prompt customization - Scheduled Tasks: cron expressions, task management, and limitations
- Channels: Telegram, Discord, and iMessage integration
Up next, learn how to extend Claude Code with external tools and data sources in Model Context Protocol.