power user

Remote Triggers for Claude Code: Webhooks, Scheduled Runs, and API Calls

Quick Answer

Trigger Claude Code remotely by running 'claude --headless --print "task"' from any shell — in GitHub Actions, a cron job, a webhook handler, or an API route. Combine with git worktrees for isolated execution and push results back via git or a webhook.

Once you think of Claude Code as a headless CLI tool rather than an interactive assistant, a whole category of automation opens up. Any system that can run a shell command can trigger a Claude Code task: GitHub Actions, AWS Lambda functions via a shell layer, cron jobs, Slack slash commands forwarded to a server, or a simple webhook endpoint.

The most common remote trigger pattern is GitHub Actions. On pull request events, a workflow runs 'claude --headless --print "Review this PR for security issues"' and posts the result as a PR comment. On push to main, it runs 'claude --headless --print "Write a changelog entry for these commits"' and commits the result.

For Slack-triggered tasks, a simple Express endpoint receives the webhook, spawns a Claude headless process with the task from the Slack message, and replies to the thread with the result. The whole server is under 50 lines of Node.js.

When building remote triggers, always set execution limits: '--max-turns 20' to cap cost, a timeout (timeout 300 claude ...) to prevent runaway processes, and a dedicated git branch or worktree so remote tasks never modify your main branch directly.

Examples

GitHub Actions PR review triggeryaml
name: Claude PR Review
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  claude-review:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install Claude Code
        run: npm install -g @anthropic-ai/claude-code

      - name: Run Claude review
        id: review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          DIFF=$(git diff origin/${{ github.base_ref }}...HEAD -- '*.ts' '*.tsx' | head -c 8000)
          REVIEW=$(echo "Review this diff for bugs, security issues, and missing error handling. Be concise.\n\n$DIFF" | \
            claude --headless --print --max-turns 5)
          echo "review<<EOF" >> $GITHUB_OUTPUT
          echo "$REVIEW" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Post review comment
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Claude Code Review\n\n${{ steps.review.outputs.review }}`
            })
Webhook server for Slack-triggered taskstypescript
import express from "express";
import { execFile } from "child_process";
import { promisify } from "util";

const execFileAsync = promisify(execFile);
const app = express();
app.use(express.json());

app.post("/claude-task", async (req, res) => {
  const { task, channel_id, response_url } = req.body;
  res.json({ text: "Running Claude task..." });

  try {
    const { stdout } = await execFileAsync("claude", [
      "--headless", "--print", "--max-turns", "20",
      task
    ], {
      env: { ...process.env, ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
      timeout: 120_000,
      cwd: "/path/to/your/repo"
    });

    await fetch(response_url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text: stdout.slice(0, 3000) })
    });
  } catch (err) {
    console.error(err);
  }
});

app.listen(3001);

Tips

  • Always set a timeout on remote Claude invocations — 'timeout 180 claude ...' prevents runaway costs from stuck tasks.
  • Use '--max-turns 10' for simple review tasks, '--max-turns 30' for tasks that may require multiple tool calls.
  • Store ANTHROPIC_API_KEY in your CI secrets, not in the workflow YAML — treat it like a database password.
  • For webhook endpoints that trigger Claude, add a secret token check to prevent unauthorized task execution.
  • Post results back via the GitHub API or Slack webhook rather than trying to stream them — headless mode is fire-and-forget.

FAQ

How much does a typical remote Claude trigger task cost?+

A code review task with a few thousand tokens of diff costs roughly $0.01-0.05 with Claude Sonnet. A more complex multi-step task with 20 turns might cost $0.10-0.50. Set billing alerts in your Anthropic console when running automated triggers.

Can I run remote triggers on a schedule without GitHub Actions?+

Yes. A simple cron entry works: '0 9 * * 1-5 ANTHROPIC_API_KEY=xxx claude --headless --print "Generate daily standup report" >> /var/log/claude-standup.log'. Or use the Claude Code scheduled triggers feature for managed scheduling.

What happens if the remote trigger task fails halfway through?+

Claude Code exits with a non-zero code. In GitHub Actions, the step fails and the workflow aborts. In cron jobs, the exit code is logged. Add error handling in your trigger script to notify you (Slack message, email) on failure.

Related Guides