Merge Gate API
Machine-readable PR review for agents — queue reviews over REST or MCP, receive structured verdicts and findings via webhooks or poll.
The Merge Gate API is Critique’s Platform API v1 surface for orchestrators and coding-agent stacks. It answers one question programmatically: should this pull request merge?
Critique is not a coding agent host in this story — your writer agent (Cursor, Devin, Codex, internal bot) opens the PR; Critique judges it and returns structured evidence agents can act on without scraping GitHub comments.
Marketing overview, cookbooks, and signed-in key generation: /merge-gate-api.
Long-form essay — why, benefits, and where Critique sits in the agent wave: The Merge Gate API: Why Agent Teams Need a Judge That Is Not the Writer.
Need a sandbox that writes code? See the sibling Coding Agent API. Need raw tokens only? See Inference API.
What this API is (and is not)
| Merge Gate API | Coding Agent API | |
|---|---|---|
| Job | Inspect a PR; return PASS / WARN / FAIL + findings | Run OpenCode in E2B; return patch / draft PR |
| Primary caller | CI orchestrator, agent supervisor, merge bot | Automation that needs a sandboxed coder |
| Typical loop | Agent opens PR → Critique gate → fix agent reads findings | Prompt → sandbox → optional draft PR |
| Scopes | read:reviews, write:reviews, manage:webhooks | read:builder, write:builder |
Critique’s product principle: the agent that wrote the code should not be the only thing that approves it. The merge gate is a separate role with separate success criteria.
Architecture: agent merge loop
sequenceDiagram
participant Writer as Writer agent
participant GH as GitHub
participant Orch as Your orchestrator
participant CG as Critique merge gate
participant Fix as Fix agent
Writer->>GH: Open / update PR
Orch->>CG: POST /api/v1/reviews
CG->>GH: Check run + comments (parallel)
CG-->>Orch: Webhook review.completed + findings[]
alt verdict FAIL or WARN
Orch->>Fix: findings[] as blockers
Fix->>GH: Push fix commit
Orch->>CG: POST /api/v1/reviews (new headSha)
else verdict PASS
Orch->>GH: Approve / merge (your policy)
endHost-agnostic: today Critique reads PRs from GitHub. The REST contract (repositoryFullName, pullRequestNumber, headSha) stays stable if you add other hosts later — orchestrators should not depend on HTML check pages.
Authentication
| Item | Value |
|---|---|
| Auth | Authorization: Bearer crt_… |
| Create keys | Settings → Connections → Critique API keys |
| OpenAPI | GET /api/v1/openapi (YAML) |
Keys authenticate as your Critique user. The repository must already be on a GitHub App installation you control. Browser session cookies also work on v1 routes when signed in.
Scopes you need
| Scope | Merge gate use |
|---|---|
write:reviews | POST /api/v1/reviews, MCP queue_review, MCP queue_remedy |
read:reviews | GET /api/v1/review-runs/:id, MCP get_review_run, MCP list_review_runs |
read:passports | GET /api/v1/passports, MCP list_passports / get_passport |
manage:webhooks | Installation lifecycle subscriptions (POST /api/v1/webhook-endpoints) |
New keys include these scopes by default. Rotate older keys if automation returns 403.
REST endpoints
| Method | Path | Scope | Purpose |
|---|---|---|---|
POST | /api/v1/reviews | write:reviews | Queue a PR review |
GET | /api/v1/review-runs/{id} | read:reviews | Poll status, verdict, findings |
GET | /api/v1/passports | read:passports | Change passport queue |
GET | /api/v1/passports/{id}/export | read:passports | Compliance bundle JSON |
GET/POST/DELETE | /api/v1/webhook-endpoints | manage:webhooks | Installation lifecycle subscriptions |
POST | /api/mcp | varies | MCP JSON-RPC for IDE agents |
Queue a review
Request body
| Field | Required | Notes |
|---|---|---|
repositoryFullName | Yes | owner/repo |
pullRequestNumber | Yes | Integer ≥ 1 |
headSha | Yes | Commit SHA on the PR head (min 7 chars) |
pullRequestTitle | No | Stored on the review run for dashboards |
webhook | No | Per-run callback — see Per-run webhooks |
Returns 202 with:
{
"review": {
"reviewRunId": "clx…",
"status": "QUEUED",
"repository": "acme/web",
"pullRequestNumber": 42,
"headSha": "abc123…"
}
}Critique deduplicates by (repository, pullRequestNumber, headSha). Re-queuing the same SHA with forceRerun semantics (API always forces rerun on terminal runs) updates the run and enqueues pipeline work.
Example — queue with per-run webhook
curl https://critique.sh/api/v1/reviews \
-H "Authorization: Bearer crt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"repositoryFullName": "acme/web",
"pullRequestNumber": 42,
"headSha": "abc123def4567890abcdef1234567890abcdef12",
"webhook": {
"url": "https://example.com/hooks/critique-review",
"secret": "replace-with-at-least-32-random-characters",
"events": ["review.completed", "review.failed"]
}
}'Example — poll until terminal
RUN_ID="clx…"
until true; do
BODY=$(curl -s "https://critique.sh/api/v1/review-runs/$RUN_ID?findings=all" \
-H "Authorization: Bearer crt_YOUR_KEY")
STATUS=$(echo "$BODY" | jq -r '.reviewRun.status')
echo "status=$STATUS"
case "$STATUS" in
COMPLETED|FAILED)
echo "$BODY" | jq '{verdict: .reviewRun.verdict, findingsTotal: .reviewRun.findingsTotal, findings: .reviewRun.findings}'
break
;;
esac
sleep 20
doneReview run snapshot
GET /api/v1/review-runs/{id} returns { "reviewRun": { … } }.
Query parameters
| Param | Effect |
|---|---|
| (default) | Up to 5 findings (chat-sized payload) |
?findings=all | All findings, sorted by severity then confidence |
When truncated, findingsTruncated: true and findingsTotal report the full count.
Response fields (automation-relevant)
| Field | Type | Description |
|---|---|---|
reviewRunId | string | Stable id for polls and webhooks |
status | string | QUEUED, IN_PROGRESS, COMPLETED, FAILED, CANCELLED |
verdict | string | null | PASS, WARN, FAIL when completed |
summary | string | null | Human-readable lead summary |
riskBand | string | null | Risk tier from change-control scoring |
riskScore | number | null | Numeric risk score when computed |
headSha | string | SHA this run reviewed |
changePassportId | string | null | Linked Change Passport |
findings | array | Structured findings (see below) |
findingsTotal | number | Count in database |
findingsTruncated | boolean | True when default cap applied |
urls.reviewRunWithFindings | string | Path with ?findings=all |
latestRemedyRun | object | null | Remedy status if a fix was queued |
Finding object
Each finding is machine-readable — designed for fix agents, not dashboard prose:
| Field | Description |
|---|---|
id | Finding id (when persisted) |
specialist | SECURITY, TESTS, ARCHITECTURE, PERFORMANCE, CODE_QUALITY, … |
severity | INFO, WARNING, FAIL |
confidence | 0–1 model/heuristic confidence |
title | Short label |
summary | Evidence-oriented explanation |
filePath | File path or null |
startLine, endLine | Line range or null |
fingerprint | Stable id for dedupe / memory |
Example:
{
"id": "clf…",
"specialist": "SECURITY",
"severity": "FAIL",
"confidence": 0.91,
"title": "Missing authorization on admin route",
"summary": "POST /api/admin/users accepts requests without session check.",
"filePath": "src/app/api/admin/users/route.ts",
"startLine": 14,
"endLine": 28,
"fingerprint": "sec:admin-route-no-auth"
}Webhooks
Critique supports two webhook channels for reviews. They can fire in parallel for the same run.
Comparison
| Installation lifecycle | Per-run (on POST /reviews) | |
|---|---|---|
| Register | POST /api/v1/webhook-endpoints | webhook field on queue request |
| User-Agent | Critique-Lifecycle-Webhook/1.0 | Critique-Review-Webhook/1.0 |
| Events | review.run.completed, review.run.failed, … | review.completed, review.failed, review.queued |
| Scope | All reviews on installation | Single queued run |
| Retries | V1: single attempt, 8s timeout | Same |
Shared headers on every delivery:
| Header | Value |
|---|---|
X-Critique-Webhook-ID | Unique delivery id |
X-Critique-Webhook-Event | Event name |
X-Critique-Webhook-Signature | sha256=<hex HMAC> over raw JSON body |
Verify before trusting:
import crypto from 'node:crypto'
export function verifyCritiqueWebhook(secret: string, rawBody: string, signature: string) {
const expected = `sha256=${crypto.createHmac('sha256', secret).update(rawBody).digest('hex')}`
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
}Lifecycle: review.run.completed payload
{
"event": "review.run.completed",
"timestamp": "2026-06-17T12:00:00.000Z",
"id": "clx…",
"object": "review.run",
"status": "completed",
"installationId": "inst_…",
"repository": "acme/web",
"pullRequest": { "number": 42, "headSha": "abc123…" },
"changePassportId": "clp…",
"verdict": "FAIL",
"riskBand": "high",
"riskScore": 78,
"summary": "One blocking security finding.",
"findingsCount": 3,
"findings": [
{
"specialist": "SECURITY",
"severity": "FAIL",
"confidence": 0.91,
"title": "Missing authorization on admin route",
"summary": "…",
"filePath": "src/app/api/admin/users/route.ts",
"startLine": 14,
"endLine": 28,
"fingerprint": "sec:admin-route-no-auth"
}
],
"urls": {
"reviewRun": "/api/v1/review-runs/clx…",
"reviewRunWithFindings": "/api/v1/review-runs/clx…?findings=all",
"dashboard": "/dashboard/review-runs/clx…"
}
}On failure, review.run.failed includes failureReason and summary (no findings).
Per-run webhooks
| Event | When |
|---|---|
review.queued | Run accepted from API |
review.completed | Pipeline finished — verdict + findings[] |
review.failed | Pipeline error |
Default events when omitted: review.completed, review.failed.
Other lifecycle events (installation)
Subscribe explicitly when building agent supervisors:
| Event | Agent use |
|---|---|
checkpoint.gate.evaluated | Cheap pre-filter before full review — see Checkpoint |
merge_policy.evaluated | Programmatic merge-policy outcome after review |
passport.snapshot.created | Audit / compliance hooks |
remedy.run.completed | Fix agent finished — new SHA may need re-review |
Full catalog: Connections & Platform API — lifecycle webhooks.
MCP for IDE agents
External clients (Cursor, Claude Desktop, custom LangGraph apps) call Critique over MCP:
| Item | Value |
|---|---|
| URL | https://critique.sh/api/mcp |
| Auth | Authorization: Bearer crt_… |
| Methods | initialize, tools/list, tools/call, ping |
Merge-gate tools
| Tool | Scope | Notes |
|---|---|---|
queue_review | write:reviews | Same body as POST /api/v1/reviews (including optional webhook in schema where supported) |
get_review_run | read:reviews | Returns all findings (not capped at 5) |
list_review_runs | read:reviews | Recent runs for your installations |
queue_remedy | write:reviews | Queue bounded fix run for a review |
get_remedy_status | read:reviews | Poll Remedy progress |
list_passports / get_passport | read:passports | Change-control context |
Example tools/call:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "queue_review",
"arguments": {
"repositoryFullName": "acme/web",
"pullRequestNumber": 42,
"headSha": "abc123def4567890abcdef1234567890abcdef12"
}
}
}Configure MCP in Cursor with the Critique URL and a crt_ key. MCP does not replace GitHub App install.
See Connections & Platform API — MCP for the full ten-tool list and scope enforcement.
Checkpoint first (for agent PR floods)
When coding agents open many PRs per day, run Checkpoint before paying for full multi-agent review:
- Subscribe to
checkpoint.gate.evaluatedon your installation webhook. - On
outcome: block— stop the merge loop; do not queue review (or only queue after human override). - On
outcome: warn— queuePOST /api/v1/reviewsfor full findings. - On
outcome: pass— queue review only if your policy requires Critique on every agent PR.
Checkpoint is deterministic and fast; the merge gate is evidence-heavy. Together they mirror “lint then test” for agent-generated code.
After FAIL: fix paths
| Path | When to use |
|---|---|
| Your fix agent | Feed findings[] to Cursor / Codex / Devin with file paths and summaries |
MCP queue_remedy | Critique-managed Remedy sandbox (Remedy blueprint) |
| BYOA handoff | GET /api/review-runs/:id/byoa/cursor (session) — requires keys in Settings → Agents |
Re-queue review after the head SHA changes:
curl https://critique.sh/api/v1/reviews \
-H "Authorization: Bearer crt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"repositoryFullName": "acme/web",
"pullRequestNumber": 42,
"headSha": "NEW_HEAD_SHA_AFTER_FIX"
}'HTTP errors
| Status | Meaning |
|---|---|
| 401 | Missing or invalid crt_ key |
| 403 | Valid key but missing scope (e.g. write:reviews) |
| 404 | Repository not on your installation, or review run not visible |
| 422 | Validation error (bad SHA, missing fields) |
| 500 | Queue or pipeline failure — check failureReason on run or webhook |
Responses use { "error": "…" } and Cache-Control: no-store.
Credits
Each full review run consumes credits like an automated GitHub-triggered review. Checkpoint-only paths are cheaper. See Billing & credits.
Security
- Treat
crt_keys like passwords — revoke in Connections if leaked. - Use separate keys per orchestrator environment.
- Webhook secrets must be ≥ 32 characters; stored encrypted at rest (
CRITIQUE_SECRETS_ENCRYPTION_KEY). - Critique never exposes full secrets after save.
Related
- Merge Gate product page — cookbooks, FAQ-style examples, key gate
- PR review — pipeline, verdicts, GitHub output
- Connections & Platform API — Linear, Slack, full scope table
- Change control — passports, merge policy
- Checkpoint — pre-review trust gates
- Coding Agent API — when you need a writer sandbox, not a judge
- BYOA — external fix agents from review runs
- Event-driven pipeline — async processing overview