Pass a webhook_url to create_review, and ReviseMy POSTs to it when the person decides. A CI job can open a review, stop, and pick up again when the answer comes in.
Setting one
Add webhook_url when you create the review, over MCP or REST. It has to be https. Later passes made with parent_id inherit it, so set it once.
What arrives
POST /hooks/revisemy HTTP/1.1
Content-Type: application/json
X-ReviseMy-Event: review.decided
X-ReviseMy-Review: 01J9Z…
X-ReviseMy-Signature: sha256=5d41402abc4b2a76b9719d911017c592…
{
"event": "review.decided",
"decided_at": "2026-10-08T14:02:11+00:00",
"review": {
"id": "01J9Z…",
"status": "changes_requested",
"next_action": { "action": "apply_pins_then_next_pass", "summary": "…" },
"work_packets": { "pins": [], "must_fix": [] }
}
}
review is the whole review, the same object get_review returns. Branch on review.status (approved or changes_requested), and read review.next_action for what comes next.
Checking the signature
X-ReviseMy-Signature is sha256= followed by an HMAC-SHA256 of the raw request body. The key is the review's secret token: the last part of its review_url, after /r/. Your code already has it from when it created the review.
Always compute it over the raw bytes, before any JSON parsing, and compare in constant time.
Node
import crypto from 'node:crypto';
function verify(rawBody, header, reviewToken) {
const expected = 'sha256=' + crypto.createHmac('sha256', reviewToken).update(rawBody).digest('hex');
return header.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
PHP
$expected = 'sha256='.hash_hmac('sha256', $request->getContent(), $reviewToken);
if (! hash_equals($expected, (string) $request->header('X-ReviseMy-Signature'))) {
abort(401);
}
Python
import hashlib, hmac
def verify(raw_body: bytes, header: str, review_token: str) -> bool:
expected = "sha256=" + hmac.new(review_token.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
Delivery
- Answer with any
2xx. Anything else is a failure. - A failure is retried up to three times, after 10 seconds, a minute and five minutes.
- Redirects aren't followed. A
3xxcounts as a failure. - Addresses inside private networks are refused, and that's checked again before every send.
- Five failures in a row pause the webhook.
get_reviewshowswebhook.paused, the failure count and the last error, never the URL itself.
Deliveries run on the queue. If you're running your own copy, start a worker (php artisan queue:work) or nothing is sent.
Gating CI on a review
A typical job:
- Build a preview and capture it.
POST /api/reviewswith the shots, awebhook_urlpointing at your CI's webhook trigger, and aparent_idif this is a later pass. Post thereview_urlto the pull request.- Stop the job.
- When the webhook arrives, check the signature. On
approved, mark the check as passed. Onchanges_requested, handreview.work_packetsto your agent and let it open the next pass.