CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/API & SDK
API & SDK/2026-04-17Advanced

Building a GitHub PR Review Bot with Claude API — From Webhook Signature Verification to Structured Reviews with Tool Use

Build a production PR review bot with Claude API and GitHub Webhooks. Enforce structured scoring, security scanning, and suggestions via Tool Use — with signature verification, debouncing, rate limits, and cost tracking drawn from real-world operation.

Claude API115GitHub5PR ReviewTool Use8Webhook3TypeScript24Automation38Code Review2

Premium Article

Once your team grows past five engineers, code review consistency becomes a real problem. Some reviewers catch every instance of a particular bug pattern; others let the same thing slide. Review quality drops when people are tired. And more fundamentally, nobody has agreed on what "good" actually looks like.

Claude API can help. But asking it to "review this code" in the simplest possible way won't give you something you can rely on — the output varies too much. What makes this work at production scale is two things: using Tool Use to enforce structured output, and building a clean pipeline from GitHub Webhook all the way to posted review comments.

This article builds a bot that automatically posts review comments when a PR is opened or updated. We'll go from architecture design to deployment, explaining the why behind each implementation choice along the way.

System Architecture Overview

The flow is straightforward:

GitHub PR opened/updated
    ↓
Webhook event (POST /webhook)
    ↓
Express server (signature verification)
    ↓
Fetch PR diff via GitHub API
    ↓
Claude API (Tool Use) generates structured review
    ↓
Post review comment via GitHub API

The tech stack:

  • Runtime: Node.js 22 + TypeScript
  • Web framework: Express.js 4.x
  • Claude API: @anthropic-ai/sdk v1.x
  • GitHub API: @octokit/rest v21
  • Deployment: Railway / Render (always-on server required)

Why You Need an Always-On Server

GitHub Webhooks deliver POST requests in real time. While Cloudflare Workers or Vercel Serverless Functions technically work, architectures with cold starts are risky here. GitHub sets a 10-second timeout on webhook delivery, and a cold start that delays your response can trigger a retry loop — meaning the same PR gets reviewed twice or more.

Railway starts at around $5/month for an always-on server. That's more than sufficient for most teams.

Start by installing dependencies:

npm init -y
npm install express @anthropic-ai/sdk @octokit/rest
npm install -D typescript @types/express @types/node ts-node

Step 1: The Webhook Server

Receiving webhooks is where most implementations first go wrong: signature verification. GitHub attaches an X-Hub-Signature-256 header to every webhook request. Without verifying it, anyone can send forged requests to your endpoint.

// src/webhook.ts
import express from 'express';
import crypto from 'crypto';
import { handlePullRequestEvent } from './handlers/pullRequest';
 
const app = express();
 
// ⚠️ Critical: raw body required — do NOT use express.json()
app.use(
  express.raw({ type: 'application/json' })
);
 
function verifyWebhookSignature(
  rawBody: Buffer,
  signature: string,
  secret: string
): boolean {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(rawBody);
  const expectedSignature = `sha256=${hmac.digest('hex')}`;
 
  // Use timingSafeEqual to prevent timing attacks
  try {
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expectedSignature)
    );
  } catch {
    // timingSafeEqual requires equal-length buffers
    return false;
  }
}
 
app.post('/webhook', async (req, res) => {
  const signature = req.headers['x-hub-signature-256'] as string;
  const eventType = req.headers['x-github-event'] as string;
 
  if (!signature) {
    return res.status(401).json({ error: 'Missing signature' });
  }
 
  const isValid = verifyWebhookSignature(
    req.body as Buffer,
    signature,
    process.env.GITHUB_WEBHOOK_SECRET!
  );
 
  if (!isValid) {
    console.error('Invalid webhook signature — possible spoofed request');
    return res.status(401).json({ error: 'Invalid signature' });
  }
 
  // Respond immediately — GitHub's timeout is 10 seconds
  res.status(202).json({ accepted: true });
 
  const payload = JSON.parse((req.body as Buffer).toString());
 
  if (
    eventType === 'pull_request' &&
    ['opened', 'synchronize'].includes(payload.action)
  ) {
    // Process asynchronously after responding
    handlePullRequestEvent(payload).catch(err => {
      console.error('[PR handler] Unhandled error:', err);
    });
  }
});
 
app.get('/health', (_req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
 
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Webhook server listening on port ${PORT}`);
});

Two things worth explaining here.

Why express.raw() instead of express.json(): The HMAC signature is computed against the raw bytes of the request body. Once Express parses the JSON, you've lost the original bytes. Even re-serializing with JSON.stringify() won't work reliably — property ordering and whitespace can differ. This is the single most common source of 401 errors in webhook integrations.

Why respond before processing: GitHub's webhook timeout is 10 seconds. Claude API calls can take several seconds. If you wait for the review to complete before responding, you'll hit the timeout, GitHub will retry, and the same PR gets reviewed multiple times. Send 202 immediately, then process asynchronously.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Prevent GitHub's 10s timeout and retry loops with express.raw() signature verification and an immediate 202 response
Force structured overall_score / security_issues through Tool Use (tool_choice: any) with exponential backoff for production stability
Operational metrics: a 30→90s debounce cut duplicate reviews ~70%; output tokens run 600-900 (clean) vs 2,500-3,500 (critical found)
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API & SDK2026-05-06
Building an Autonomous Research Agent with Claude API: Web Search, Summarization, and Knowledge Management
A complete guide to designing and implementing an autonomous research agent using Claude API and web search tools. Covers budget control, quality assurance, and knowledge base storage for production use.
API & SDK2026-07-12
When You Give an API Key an Expiration Date, Expiry Becomes a Plan Instead of an Accident
The Console now lets you set expiration dates on API keys. Here is how to fold planned expiry into unattended operations — with overlapping dual keys and a local expiry ledger — so your nightly jobs never go dark.
API & SDK2026-07-09
When the RAG Started Being Confidently Wrong — Field Notes on Measuring Retrieval Misses With Groundedness
In a Claude API RAG, the answers stay fluent while the facts drift. Often the cause is a silent recall decay on the retrieval side, missing the document that holds the answer. Field notes on measuring groundedness and retrieval hit rate and walking the system back, with working code and real numbers.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →