Choose an event source

  • Webhooks from GitHub, GitLab, Linear, or your own services.
  • Message queues such as Cloudflare Queues, AWS SQS, or RabbitMQ.
  • File system watchers for local automation.
  • Scheduled triggers for recurring checks.

Route events to Autohand

Use a small worker to validate the event, extract identifiers, and call the Autohand SDK or CLI. Keep the router stateless and fast. Hand all reasoning to the agent.

# Minimal router using the Autohand CLI
autohand -p "Review pull request $PR_NUMBER in repo $REPO" --restricted --json local > result.json
# result.json holds one object: {"type":"result","content":"..."}

Pass context in the prompt

Include all relevant context in the prompt so the agent does not need to search. Pass identifiers, URLs, and prior state. Keep the prompt focused on the task.

Handle results and failures

  • Write agent output to durable storage or a queue.
  • Post results back to the source system, such as a PR comment or ticket note.
  • Retry transient failures with exponential backoff.
  • Alert operators when the agent cannot complete a task.

Example: auto-review pull requests

A GitHub webhook calls a Cloudflare Worker on every opened pull request. The worker runs an Autohand agent with the PR context and posts the review as a comment.

JavaScript

export default {
  async fetch(request, env) {
    const payload = await request.json();
    if (payload.action !== 'opened') return new Response('Ignored');

    const pr = payload.pull_request;
    const prompt = `Review pull request #${pr.number}: ${pr.title}`;
    const result = await env.AUTOHAND.run({ cwd: '/tmp/repo', prompt });

    await postComment(pr.comments_url, result.text, env.GITHUB_TOKEN);
    return new Response('Review posted');
  }
};

TypeScript

import { AutohandSDK } from '@autohandai/agent-sdk';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const payload = await request.json() as GitHubPullRequestEvent;
    if (payload.action !== 'opened') return new Response('Ignored');

    const sdk = new AutohandSDK({ cwd: '/tmp/repo', apiKey: env.AUTOHAND_KEY });
    const result = await sdk.run(`Review pull request #${payload.pull_request.number}`);
    await postComment(payload.pull_request.comments_url, result.text, env.GITHUB_TOKEN);

    return new Response('Review posted');
  }
};

Python

from fastapi import FastAPI, Request
from autohand_sdk import AutohandSDK

app = FastAPI()

@app.post("/github")
async def github_webhook(request: Request):
    payload = await request.json()
    if payload.get("action") != "opened":
        return {"status": "ignored"}

    pr = payload["pull_request"]
    async with AutohandSDK(cwd="/tmp/repo") as sdk:
        result = await sdk.run(f"Review pull request #{pr['number']}: {pr['title']}")
    await post_comment(pr["comments_url"], result.text)
    return {"status": "review_posted"}

Go

func githubWebhook(w http.ResponseWriter, r *http.Request) {
  var payload PullRequestEvent
  json.NewDecoder(r.Body).Decode(&payload)
  if payload.Action != "opened" {
    w.WriteHeader(http.StatusOK)
    return
  }

  sdk := autohand.NewClient(autohand.Config{Cwd: "/tmp/repo"})
  result, _ := sdk.Run(r.Context(), fmt.Sprintf("Review pull request #%d", payload.PullRequest.Number))
  postComment(payload.PullRequest.CommentsURL, result.Text)
}

Java

@PostMapping("/github")
ResponseEntity<String> githubWebhook(@RequestBody PullRequestEvent payload) {
  if (!payload.action().equals("opened")) {
    return ResponseEntity.ok("ignored");
  }

  var sdk = AutohandClient.builder().cwd("/tmp/repo").build();
  var result = sdk.run("Review pull request #" + payload.pullRequest().number());
  github.postComment(payload.pullRequest().commentsUrl(), result.text());
  return ResponseEntity.ok("review posted");
}

Swift

app.post("github") { req async throws -> String in
    let payload = try req.content.decode(PullRequestEvent.self)
    guard payload.action == "opened" else { return "ignored" }

    let sdk = AutohandClient(cwd: "/tmp/repo")
    let result = try await sdk.run("Review pull request #\(payload.pullRequest.number)")
    try await github.postComment(payload.pullRequest.commentsURL, body: result.text)
    return "review posted"
}

curl

curl -X POST "$WEBHOOK_URL/github" \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: pull_request" \
  -d '{"action":"opened","pull_request":{"number":42,"title":"Add auth","comments_url":"https://api.github.com/repos/acme/app/issues/42/comments"}}'

Best practices

  • Validate webhook signatures before running the agent.
  • Run agents in isolated environments for untrusted input.
  • Limit the agent's permissions in CI and production.
  • Log session IDs so you can trace every event end to end.