Inspect

Review providers

Show reviews from your own automated code reviewer in Inspect.

Alpha. Review providers are available by request only. Behavior, fields, and limits can change without notice. Ask your Inspect contact to turn them on for your organization.

Overview

A review provider is an automated code reviewer that is not built into Inspect. It is a GitHub App that posts inline pull request review comments and check runs.

Inspect already shows any GitHub App's comments and check runs. When an organization admin registers the app as a review provider, and the app uses the format on this page, Inspect also shows:

  • A row for the provider in the Reviews menu of a pull request, with its status and its finding counts by severity.
  • The provider's summary and its open findings, grouped by severity, when you select that row.
  • Auto-address by severity: Inspect can fix the provider's critical and warning findings and skip its nits.
  • If the provider uses the status API: a status, a message, and a summary that come from the provider directly, also when the review fails.
  • If the provider has a webhook: Run review, Re-run review, and Retry review in the provider row, and a reason when people resolve a finding. Inspect sends these to the provider as signed webhooks.

Merge gating stays in GitHub. Inspect does not decide whether a review blocks a merge. To block merges, the provider posts its own check run, and the organization marks it as required in GitHub branch protection. Inspect shows that check like any other check.

Without registration

If your GitHub App posts inline review comments and check runs, Inspect already does this with no setup:

  • Your inline comments appear in Inspect's diff views and in the Comments list, grouped by file. The changed-files list shows how many comments each file has.
  • People can reply to your comments, resolve them, or send them to Inspect's coding agent from inside Inspect. Replies and resolutions go to GitHub.
  • Your check runs appear in the Checks list with their GitHub status.
  • A pull request author can turn on auto-address for comments from all robot reviewers or from your bot. Without registration, this applies to all of your comments, because Inspect cannot tell a critical finding from a nit.

Register a provider

An admin of the Inspect organization registers the provider in Settings → Review agent, in Review providers. The admin enters:

FieldNotes
Display nameShown in the Reviews menu.
SlugLowercase letters, digits, and hyphens, 3 to 40 characters, for example acme-review. It cannot change later.
GitHub App IDThe numeric ID of the app that posts the check runs and comments.
GitHub App slugThe app's URL name, as in github.com/apps/acme-review. Inspect identifies the app's comments by its bot user, acme-review[bot].
Logo URLOptional. An https:// image URL.
RepositoriesThe owner/name repositories that the provider reviews. Leave it empty for all repositories.
Webhook URLOptional. The HTTPS URL that receives webhooks from Inspect.

The GitHub App must be installed on the repositories. It needs checks: write to create check runs and pull_requests: write to post review comments.

Trust rules

  • Inspect uses only check runs that the registered GitHub App created.
  • Inspect reads a finding marker only from a comment that the app's bot user wrote.
  • If a person edits a finding comment, Inspect ignores its marker until the app edits the comment again.

Markers are visible to anyone who can read the pull request. Do not put secrets or private data in them.

Report review status with a check run

Create a check run from your GitHub App for each commit that you review.

Check run fieldValue
nameYour reviewer's name, for example Acme Review. Keep it stable, because branch protection matches checks by name.
head_shaThe commit that you reviewed.
statusqueued, in_progress, or completed.
conclusionYour choice. The Checks list shows it as is.
details_urlA link to the review in your product. The provider row links to it.
output.summaryYour summary in Markdown. Inspect shows the first 16 KB.

The provider row shows the newest check run from your app on the pull request:

Check runReviews menu
status: queuedQueued
status: in_progressReviewing
status: completed, conclusion: cancelled or timed_outDid not finish
status: completed, any other conclusionYour finding counts, or No findings
On a commit that is not the pull request headOutdated review

If your review stops because of an error, complete the check run with conclusion: cancelled or timed_out, so that Inspect does not show it as a finished review. If you create more than one check run for a commit, for example when you review again, Inspect uses the newest one.

The row appears after Inspect receives a check run from your app for the pull request, or when the pull request has an open finding from your app. If your webhook is on (see Level 3), the row always appears, as Not reviewed yet until you report a status, so people can start a review.

Report review status with the API

As an alternative to a check run, the provider can send its review status to Inspect directly. Use the API when you want to show a failure reason, or when your review is not a check run. Findings still come from your inline comments.

Get an API key

The organization admin selects Create API key for the provider in Review providers and gives the key (rip_…) to the provider. Inspect shows it only once. The key works only for this provider.

To change the key, the admin selects Rotate API key. The old key stops working at once, so put the new key in place soon after the rotation.

Send the key in each request:

Authorization: Bearer rip_...

Send a status

Send the full current state of your review of one commit. Send it again each time the state changes.

PUT /api/public/v1/review-providers/{provider}/repos/{owner}/{repo}/pulls/{number}/reviews/{sha}
Content-Type: application/json

{
  "status": "completed",
  "updated_at": "2026-10-22T14:03:11Z",
  "message": "3 findings",
  "summary_markdown": "Found one retry loop with no upper bound.",
  "details_url": "https://acme.example/reviews/7781"
}

{provider} is your slug, and {sha} is the full 40-character commit SHA that you reviewed. Use the same host as the Inspect web app.

FieldRequiredNotes
statusYesqueued, running, completed, failed, or canceled.
updated_atYesWhen you produced this state, in RFC 3339 with at most millisecond precision. Inspect does not apply an update older than the one it has.
messageNoOne line, at most 200 characters, shown in the provider row.
summary_markdownNoYour summary in Markdown, at most 16 KB.
details_urlNoAn https:// link to the review in your product. The provider row links to it.
error_messageNoAt most 2,000 characters. Shown in place of message when status is failed.

The response is {"data": {"applied": true, "review": {...}}}. If you send an updated_at older than the one Inspect has for the commit, Inspect keeps the newer state and returns {"data": {"applied": false, "reason": "stale_update"}}. Do not retry these. An update with the same updated_at applies again, so you can safely retry a request.

To read your state back, send GET to the same URL, or to .../pulls/{number}/reviews for your 50 newest reviews of the pull request. Each review has outdated: true when its commit is no longer the pull request head.

How Inspect shows it

The provider row uses your API status for the pull request head. If you also post a check run on the same head, the API status takes its place. An open pull request page shows a new status as soon as Inspect stores it.

API statusReviews menu
queuedQueued
runningReviewing
completedYour finding counts, or No findings
failed or canceledDid not finish, with your error_message or message, and Retry review if you have a webhook
On a commit that is not the pull request headOutdated review

The API status does not appear in the Checks list and does not block merges. To gate merges, also post a check run.

Errors

Errors return {"message": "...", "code": "..."}.

StatuscodeMeaning
400invalid_payloadThe body does not match the schema. issues lists the fields.
400invalid_sha{sha} is not a full 40-character lowercase commit SHA.
400invalid_updated_atupdated_at is not an RFC 3339 date-time with at most millisecond precision.
401invalid_keyThe key is missing, wrong, revoked, or for another provider.
403repository_not_allowedThe repository is not in the provider's Repositories.
404repository_not_registeredInspect does not know the repository.
404pull_request_not_foundInspect does not know the pull request.
404review_not_foundGET only: you have not sent a status for this commit.
404not_foundReview providers are not on for the organization.

Retry 5xx responses with the same body.

Report findings with inline comments

Post each finding as an inline pull request review comment. Put a finding marker at the end of the comment body. GitHub supplies the file, line, and commit.

The retry loop has no upper bound. A poisoned message blocks the queue.

<!-- inspect-review:v1 {"severity":"warning","auto_address":true} -->
FieldRequiredNotes
severityYescritical, warning, suggestion, or informational.
auto_addressNofalse stops Inspect from fixing the finding automatically. The default is true for critical and warning, and false for the others.
categoryNoFree text, at most 64 characters, for example security.

The Reviews menu counts your open findings on the pull request, from any commit. The comment is the finding, so:

  • To update a finding, such as its text or severity, edit the comment.
  • If the issue is still there on a new commit, keep the existing comment. Do not post a second comment for the same issue, because Inspect shows both.
  • When the issue is fixed or no longer applies, resolve the thread or delete the comment. Inspect then stops counting it.

People can also reply to and resolve your finding comments. Inspect counts a resolved thread as addressed.

Marker rules

  • Use exactly one marker in a comment. Inspect ignores markers inside fenced code blocks.
  • The JSON must be one object of at most 4 KB.
  • The JSON must not contain --. If a string needs two hyphens together, escape one as -.
  • Inspect ignores unknown fields. New optional fields can appear in v1.
  • A breaking change will use a new marker name, such as inspect-review:v2.
  • An invalid marker never breaks the pull request. Inspect shows the comment as an ordinary bot comment.

Auto-address

A pull request author turns on auto-address in their developer settings, for robot reviewers or for your bot as a specific reviewer. For a registered provider's marked findings, Inspect then:

  • Addresses critical and warning findings.
  • Skips suggestion and informational findings, unless the marker sets "auto_address": true.
  • Never addresses a finding whose marker sets "auto_address": false.

A rule that ignores your bot still applies. Comments without a marker follow the setting for all robot reviewers.

Receive webhooks from Inspect

A provider with a webhook gets requests from people in Inspect:

  • review.requested when someone selects Run review, Re-run review, or Retry review in the provider row of the Reviews menu.
  • review.finding_feedback when someone resolves one of the provider's finding comments with a reason.

Set up the webhook

  1. The admin enters the Webhook URL for the provider. It must be HTTPS on a public host name.
  2. The admin selects Create secret for the provider and gives the secret (whsec_…) to the provider. Inspect shows it only once.
  3. To change the secret, the admin selects Rotate secret. For 24 hours after a rotation, Inspect signs each webhook with both the old and the new secret, so the provider can change its secret without missed deliveries.

The provider row shows Run review only when the webhook has a URL and a secret and is on.

Delivery

Inspect sends a POST request with a JSON body and these Standard Webhooks headers:

HeaderValue
webhook-idThe delivery ID. It is the same on each retry of the same delivery. Use it to drop duplicates.
webhook-timestampUnix seconds when Inspect signed the request.
webhook-signaturev1,<base64 signature>. During a secret rotation, it has two signatures separated by a space.

Each body has type, schema_version (now 1), delivery_id (the same as webhook-id), occurred_at, and organization.id.

Verify the signature

The signed content is {webhook-id}.{webhook-timestamp}.{raw body}. The HMAC-SHA256 key is the base64-decoded part of the secret after whsec_. Use the raw request body, not JSON that you serialized again. Reject timestamps older than 5 minutes.


def verify(secret: str, headers: dict, body: bytes) -> None:
    msg_id = headers["webhook-id"]
    ts = headers["webhook-timestamp"]
    if abs(time.time() - int(ts)) > 300:
        raise ValueError("stale timestamp")
    key = base64.b64decode(secret.removeprefix("whsec_"))
    signed = f"{msg_id}.{ts}.".encode() + body
    expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
    for candidate in headers["webhook-signature"].split():
        version, _, sig = candidate.partition(",")
        if version == "v1" and hmac.compare_digest(sig, expected):
            return
    raise ValueError("bad signature")

review.requested

{
  "type": "review.requested",
  "schema_version": 1,
  "delivery_id": "dlv_V1StGXR8Z5jdHi6BmyT",
  "occurred_at": "2026-10-22T14:03:05.000Z",
  "organization": { "id": "acme" },
  "trigger": "inspect_ui",
  "actor": { "github_login": "jdoe" },
  "repository": { "github_id": 5521, "full_name": "acme/api" },
  "pull_request": { "number": 812, "base_ref": "main", "draft": false },
  "sha": "9c1e5d0b7a2f4e1c8d3b6a5f0e9d8c7b6a5f4e3d"
}

sha is the pull request's head commit when the person made the request. Review that commit and report it with a check run. Inspect sends this event only for requests from the Inspect UI (trigger is inspect_ui). Two requests for the same pull request and sha ask for the same review, so you can ignore the second one while the first one runs.

review.finding_feedback

{
  "type": "review.finding_feedback",
  "schema_version": 1,
  "delivery_id": "dlv_8c1rWq0Yb4KpN2sTz7a",
  "occurred_at": "2026-10-22T14:09:41.000Z",
  "organization": { "id": "acme" },
  "github_comment_id": 1987654321,
  "comment_url": "https://github.com/acme/api/pull/812#discussion_r1987654321",
  "reason": "incorrect",
  "note": null,
  "actor": { "github_login": "jdoe" },
  "repository": { "full_name": "acme/api" },
  "pull_request": { "number": 812 }
}

Inspect sends this event after it resolves the thread on GitHub. It sends it only for a comment that the provider's bot wrote with a valid finding marker.

reasonShown in Inspect as
addressedFixed it
incorrectIncorrect
not_relevantNot relevant
noiseToo minor
wont_fixWon't fix
staleOutdated

Responses and retries

Your responseInspect
2xxRecords the delivery as done.
408, 429, 5xx, no answer in 10 seconds, or a connection errorTries again with exponential backoff, for up to 24 hours.
410Stops and turns off the webhook. An admin turns it on again when they save the provider.
Any other status, including a redirectStops. Inspect does not follow redirects.

Delivery is at least once and can be out of order. When Inspect stops a delivery, the admin sees the HTTP status for the provider in Review providers.

Launch checklist

  • The organization admin has your GitHub App ID and slug, and the app has checks: write and pull_requests: write.
  • You post one check run for each reviewed commit, and you complete it with cancelled or timed_out when the review fails. Or you send each status change to the status API with a new updated_at.
  • Each finding comment ends with a marker, is edited in place on later commits, and has its thread resolved when it is fixed.
  • If organizations want a merge gate, your check run has a stable name that they can require in branch protection.
  • If you have a webhook: you check the signature on the raw body, and you drop duplicates by webhook-id.