Claude API Code Review Automation Workflow
What a Claude API code review workflow actually does
A Claude API code review automation workflow is a pipeline that triggers on a pull request, sends the diff (and relevant file context) to Claude, and posts the model's findings back as comments or a summary check — without a human reviewer having to do that first pass manually. It doesn't replace human review; it catches the obvious stuff (missing error handling, unclear naming, unhandled edge cases, security smells, inconsistent style) before a human spends time on it.
The practical question most teams are actually trying to answer is: how do you wire this up reliably in CI, keep prompts consistent across repos, and avoid drowning PRs in noisy AI comments? That's what this guide covers — the architecture, the prompt structure, and the integration code, using a standard Messages API call that works whether you're calling Claude directly or through a gateway like SubToAPI.
Architecture: where the workflow fits
A code review automation workflow has four moving parts:
- Trigger — a CI job (GitHub Actions, GitLab CI) fires on
pull_requestevents. - Diff extraction — pull the changed files and the unified diff via the Git provider's API.
- Model call — send the diff plus context to Claude with a structured review prompt.
- Output delivery — post the response as a PR comment, inline annotations, or a check run.
Keeping these steps decoupled matters. If the model call fails or times out, you still want the CI job to fail gracefully instead of blocking the merge silently. If you're running this across many repos and want a single dashboard for usage, cost, and team seats instead of managing raw provider keys per repo, that's exactly the kind of operational overhead SubToAPI is built to remove — you get an application key (sub_live_...) per project, usage metadata per call, and a shared billing view across the team.
Step 1: extract the diff in CI
In a GitHub Actions job, you already have the PR diff available through the event payload or the GitHub API:
name: ai-code-review
on: pull_request
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get diff
run: git diff origin/${{ github.base_ref }}...HEAD > diff.txt
- name: Run Claude review
env:
SUBTOAPI_KEY: ${{ secrets.SUBTOAPI_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node review.js
Truncate the diff before sending it — large diffs blow past context windows and produce vague, low-signal reviews. A reasonable cap is a few hundred changed lines per call; split larger PRs by file or directory and run multiple calls.
Step 2: design the review prompt
The quality of this workflow lives almost entirely in the prompt. Vague prompts ("review this code") produce vague output. Be explicit about what you want flagged and in what format:
const systemPrompt = `You are a senior code reviewer. Review the diff for:
- bugs and unhandled edge cases
- security issues (injection, secrets, unsafe deserialization)
- missing error handling
- inconsistent naming or dead code
Ignore formatting and style unless it harms readability.
Respond as a JSON array of objects: { "file": "", "line": number, "comment": "" }.
If there are no issues, return an empty array.`;
Forcing structured JSON output is what makes delivery (step 4) reliable — you can parse it and map comments to exact lines instead of posting one long unstructured block.
Step 3: call the Messages API
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-opus-4-1",
max_tokens: 2000,
system: systemPrompt,
messages: [
{ role: "user", content: `Diff:\n\n${diffText}` },
],
}),
});
const data = await res.json();
const review = JSON.parse(data.content[0].text);
This is a standard Messages call — see /docs/messages for the full request/response shape, and /docs/quickstart if this is your first integration. No streaming needed here since you're waiting for a complete JSON response before posting comments; streaming matters more for interactive review UIs, not CI jobs.
Step 4: post results back to the PR
Map the structured output to inline review comments using the Git provider's API:
for (const item of review) {
await octokit.pulls.createReviewComment({
owner, repo, pull_number: prNumber,
body: item.comment,
commit_id: headSha,
path: item.file,
line: item.line,
});
}
If you want the model to fetch additional context on its own — like pulling the full contents of a file it only sees a diff for — this is a good use case for tool use rather than stuffing every file into the prompt upfront. Define a get_file_contents tool and let Claude call it when a diff hunk isn't self-explanatory. Details on wiring that up are in /docs/tools.
Keeping it maintainable
A few things matter once this workflow is running on real repos, not just a demo PR:
- Rate-limit noise. Cap comments per PR (e.g., top 5 by severity) so reviewers don't tune out the bot.
- Skip trivial diffs. Don't call the model on lockfile-only or generated-file changes.
- Track cost per repo. Each Messages call returns usage metadata (input/output tokens) — log it so you can see which repos or PR sizes are driving spend. SubToAPI surfaces this per key in the dashboard automatically, which is useful once you have more than one repo running the workflow.
- Version your prompt. Treat the system prompt like code — commit it, review changes to it, and roll back if a prompt update makes reviews worse.
- Fail open, not closed. If the API call errors or times out, the CI job should log it and let the PR proceed rather than blocking merges on an infrastructure hiccup.
If you're scaling this across a team with multiple repos and want everyone using the same key management and billing rather than individual provider accounts, check /pricing for the Team and Scale plans with per-seat API keys.
questions
Does an AI code review workflow replace human reviewers? No. It's a first pass that catches obvious issues — missing error handling, security smells, inconsistent naming — so human reviewers can focus on architecture and intent instead of line-by-line nitpicking.
How do I avoid noisy or low-value comments on every PR? Use a structured output format (JSON with file/line/comment), cap the number of comments posted per PR, and skip calls entirely on diffs that are generated files, lockfiles, or pure formatting changes.
What model settings work best for code review? Use a lower max_tokens cap tuned to expected output size, a focused system prompt listing exactly what to flag, and skip streaming — CI jobs need the complete structured response before posting comments, not a token stream.