Full reference for the on-demand review endpoint. Send a PR, get a verdict on the pull request. Everything you need to call POST /api/reviews from CI or anywhere you can fire a request.
Single endpoint, two modes:
POST https://<host>/api/reviews # async (default): ack fast, review runs in background
POST https://<host>/api/reviews?wait=true # sync: blocks until the review finishes
POST https://<host>/api/reviews?sync=1 # alias for ?wait=true
<host> is hound-ai-review.polsia.app in production and localhost:3000 in dev.
All requests must include the server-side API token as a Bearer header:
Authorization: Bearer <HOUND_API_TOKEN>
The expected token is whatever you set as the HOUND_API_TOKEN environment variable on the Hound deployment. The endpoint compares the presented header to process.env.HOUND_API_TOKEN using a constant-time comparison — keep the token out of source control and rotate it like any other shared secret.
Dev escape hatch. When NODE_ENV !== 'production' and HOUND_API_TOKEN is not set, the auth check is skipped. Don't rely on this in any deployed environment.
Required: the GitHub App must already be installed on the target repository (the endpoint uses your instance's GitHub App installation token server-side to fetch the PR and post comments — you don't pass a GitHub token in this request).
A JSON object with two required fields:
{
"repo": "owner/name",
"pr_number": 42
}
repo — required string. Must match ^[\w.-]+/[\w.-]+$ (typical "owner/name" GitHub repo path). The receiver validates this format and rejects anything that doesn't match.
pr_number — required integer ≥ 1. The pull request number on the target repo.
Source. The trigger source is set internally to "api" by the endpoint — clients do not pass it. The webhook pipeline uses "webhook"; this endpoint always records "api".
Async (default — fire-and-forget):
{
"status": "queued",
"repo": "owner/name",
"pr_number": 42
}
The endpoint returns this as soon as the review is accepted. The actual review and the GitHub comment happen asynchronously — failures during the async run are logged server-side and do not surface here.
Sync (?wait=true or ?sync=1) — blocks until the review finishes:
{
"status": "ok",
"repo": "owner/name",
"pr_number": 42,
"review_id": 17,
"comment_posted": true
}
review_id is the row id from the reviews table — use it to join to the full review if you want to inspect the stored verdict or findings later.
The human-readable verdict (summary + severity-tagged findings) is posted as a comment on the PR and persisted in the reviews table:
review_text (TEXT) — the prose summary and findings as written to the PR comment.findings (JSONB) — the parsed severity-tagged findings list.Neither response includes a latency field — the endpoint does not measure call duration. If you need timing, wrap the request yourself.
All errors come back as JSON with an error field:
// 401 — missing or invalid Bearer
{ "error": "Missing or malformed Authorization header" }
{ "error": "Invalid API token" }
// 400 — body failed validation
{ "error": "Invalid request body", "message": "`pr_number` must be an integer >= 1" }
// 500 — review pipeline failed while running (sync mode only)
{ "error": "Review failed", "message": "Failed to fetch PR metadata: 404 — ..." }
In async mode, a 200 acknowledges queuing even if the run later fails server-side — failure logs end up in the server console.
The endpoint itself does not enforce a per-token quota — there's no rate-limit middleware in front of POST /api/reviews. The binding constraints live downstream:
GET /repos/{owner}/{repo}/pulls/{n} and (when posting) one POST /repos/{owner}/{repo}/issues/{n}/comments.If you're going to fire this from CI on every commit, batch your triggers or rely on the GitHub App's automatic webhook pipeline instead.
Async (default):
curl -X POST https://<host>/api/reviews \
-H "Authorization: Bearer $HOUND_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"repo":"owner/name","pr_number":42}'
Sync (?wait=true):
curl -X POST "https://<host>/api/reviews?wait=true" \
-H "Authorization: Bearer $HOUND_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"repo":"owner/name","pr_number":42}'
JavaScript (Node / browser fetch):
const res = await fetch('https://<host>/api/reviews?wait=true', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.HOUND_API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
repo: 'owner/name',
pr_number: 42
})
});
const body = await res.json();
// body.status === 'ok' -> verdict is on the PR (body.comment_posted)
// body.status === 'queued' -> poll the PR for the comment when it lands
← Back to the quickstart · Verdict usage guide · Setup guide · Troubleshooting