Agent digest format
The digest is a compact, line-oriented text summary of a build. It’s written for AI coding agents: it names the verdict on the second line, gives every screenshot that needs a decision a short handle, and leaves out everything that doesn’t.
You get it from the build summary endpoint with format=digest:
curl --fail --silent --show-error \
--header "Authorization: Bearer $PIXMOAT_TOKEN" \
"https://app.pixmoat.com/api/builds/$PIXMOAT_BUILD_ID/agent-summary?format=digest&wait=true"
A project access token or a personal access token works (see Access tokens). The response is text/plain. The check_build tool of the MCP server and pixmoat check return the same text (see the MCP tools reference and the CLI reference).
If target baselines changed after capture, a previously passing verdict becomes FAIL. The digest includes a note to update the branch and capture again. Its digest and ETag change even if the review decisions did not change. This checks visual baselines, not the captured code or target commit. See When the target baselines change.
Query parameters
| Parameter | Default | What it does |
|---|---|---|
format | json | digest returns the text format on this page. json returns the full JSON summary. |
wait | false | true holds the request while the build is still running, until its screenshots are uploaded and compared. It doesn’t wait for anyone to review them. |
timeout | 120 | Seconds to hold a wait=true request, up to 300. When the time runs out, the response is HTTP 408. |
since | — | The digest= value from your previous response. If nothing has changed, the response is the one-line unchanged form. |
min_mismatch | — | JSON only: leaves out diffs whose mismatch ratio is below this fraction. The digest always lists every pending diff. |
fail_fast | false | With wait=true, returns as soon as one diff of severity minor or higher is found, without waiting for the rest. The verdict line then ends with PARTIAL and is never PASS. |
Examples
A build with pending changes, where pixmoat grouped diffs that share a cause:
pixmoat 7f3a2b1c proj=my-app branch=feat/checkout commit=a1b2c3d
VERDICT REVIEW 3 diff · 1 new · 44 unchanged · 1 flaky · 0 removed digest=9c1fa3b2
web https://app.pixmoat.com/p/my-app/b/7f3a2b1c
DIFF 2 groups, 3 screenshots
GROUP g1 shift dy=+8 2 screenshots "Content shifted down 8px below y≈120 — 2 screenshots"
h7k2 checkout/cart-desktop 1280x720 cx 0.42% moderate shift(dy=+8,high)
h7k3 checkout/cart-mobile 390x844 cx 0.39% moderate shift(dy=+8,high) [console: 1 new error]
g2 p4tt legal/footer-desktop 1280x720 cx 1.80% major content_added(high)
! rejected 2026-07-22 by ana@acme.com: "footer spacing regressed, revert"
NEW
n3p4 checkout/coupon-badge 1280x720 cx
FLAKY (skipped — not your regression)
f2q9 home/hero-desktop 1280x720 cx quarantined
NEXT inspect: pixmoat view --build <build> h7k2 | approve: pixmoat approve --build <build> --run h7k2
NOTE each run needs its own approve command (4 actionable runs)
The same kind of build without change groups lists each diff on its own line:
DIFF
h7k2 checkout/cart-desktop 1280x720 cx 0.42% moderate 1rgn 120,340 220x48
h9m1 header/nav-desktop 1280x720 cx 0.02% trivial scattered 7rgn
A clean build is three lines:
pixmoat 7f3a2b1c proj=my-app branch=main commit=a1b2c3d
VERDICT PASS 0 diff · 0 new · 24 unchanged · 0 flaky · 0 removed digest=abc12345
web https://app.pixmoat.com/p/my-app/b/7f3a2b1c
Grammar
The line order, the section names and the column order are stable, so a script or an agent can parse them. Columns are separated by two spaces; a snapshot name can contain single spaces.
Header line
pixmoat <build_short_id> proj=<project-slug> branch=<branch> commit=<sha7>
build_short_id is the first 8 characters of the build ID. sha7 is the first 7 characters of the commit SHA.
Verdict line
VERDICT <verdict> <n> diff · <n> new · <n> unchanged · <n> flaky · <n> removed digest=<hex8>
verdict is one of the values in Verdicts. digest is an 8-character hash of the build’s state; pass it back as since to poll cheaply. A fail_fast response that returned early appends PARTIAL <compared>/<total>.
Web line
web <url>
The build’s review page. The line is left out when no web address is available for the build.
Sections
DIFF, NEW and FLAKY follow, in that order. Each one starts after a blank line. A section with nothing in it is left out, and so are unchanged, removed and already decided screenshots.
DIFFlists changed screenshots that nobody has decided yet.NEWlists screenshots without a baseline that nobody has decided yet.FLAKY (skipped — not your regression)lists screenshots pixmoat has marked flaky or quarantined. They need no action.
Run line
In NEW, and in a DIFF section without change groups, each screenshot is one line:
<handle> <name> <viewport> <browser2> <ratio%> <severity> <regions>
| Column | Content |
|---|---|
handle | The run’s short handle. |
name | The snapshot name. |
viewport | Width and height, such as 1280x720. |
browser2 | cx (Chromium), ff (Firefox) or wk (WebKit). |
ratio% | Share of pixels that changed, such as 0.42%. Diffs only. |
severity | trivial, minor, moderate or major. Diffs only. |
regions | Where the change is. Diffs only. |
regions takes one of three forms:
<n>rgn <x>,<y> <w>x<h>: the changes are clustered innregions. The first two are listed with their position and size.scattered <n>rgn: small changes spread over the page.full-page: most of the page changed.
A NEW line stops after browser2.
Grouped diff section
When pixmoat finds diffs that share a cause, such as a header that grew and pushed everything below it down, the DIFF section is organised by change group:
DIFF <n> groups, <n> screenshots
GROUP <group-id> <nature> <params> <n> screenshots "<label>"
<handle> <name> <viewport> <browser2> <ratio%> <severity> <nature>(<params>,<confidence>)
<group-id> <handle> <name> <viewport> <browser2> <ratio%> <severity> <nature>(<params>,<confidence>)
- A group of several screenshots has a
GROUPline with the kind of change, its parameters (dx=,dy=for a shift,<from>→<to>for a colour change), the screenshot count and a one-line label. Its screenshots follow, indented four spaces. - A group of one screenshot is a single line, indented two spaces and prefixed with its group ID. Inspect it on its own: one screenshot is no evidence of a shared cause.
- In this section the last column is the change’s kind and confidence, such as
shift(dy=+8,high)orcolor(high), instead ofregions.
Approving a group approves each of its screenshots under the same rules as approving them one by one.
Console marker
When a screenshot has new console findings, the run line ends with a marker that counts the page’s console errors and failed requests:
[console: 2 new errors, 1 failed request]
When there are no errors or failed requests, the marker counts warnings instead, for example [console: 1 warning].
Rejection line
! rejected <yyyy-mm-dd> by <email>: "<comment>"
It follows a run line when a person rejected this snapshot before. Line breaks in the comment are replaced with spaces. Read the comment before deciding: it says what was wrong last time.
Next and note lines
NEXT inspect: pixmoat view --build <build> <handle> | approve: pixmoat approve --build <build> --run <handle>
NOTE each run needs its own approve command (<n> actionable runs)
NEXT appears when at least one screenshot in DIFF or NEW needs a decision, and names the first one. NOTE follows when more than one does, because the approve command takes a single handle.
Unchanged response
When the request passes since and the build hasn’t changed since that digest, the whole response is one line:
pixmoat 7f3a2b1c UNCHANGED digest=9c1fa3b2
It means “nothing new since your last poll”, not “the build passed”. Keep the previous verdict. The response also carries an ETag header with the same hash; a request that sends it back in If-None-Match gets HTTP 304 when nothing has changed.
Verdicts
The verdict is the one word to act on. pixmoat check turns it into an exit code; this table matches the CLI exit codes.
| Verdict | Exit code | When | What to do |
|---|---|---|---|
PASS | 0 | Every screenshot is unchanged or approved, and nothing is rejected. | Continue. |
REVIEW | 1 | At least one diff is waiting for a decision. New screenshots may be waiting too. | Inspect the diffs. Fix the code, or approve the changes you intended. |
REVIEW_NEW | 2 | New screenshots are waiting for a decision, and there are no diffs. | Approve them to set their baselines. |
FAIL | 3 | A screenshot was rejected or the build failed. pixmoat check also exits 3 when processing doesn’t finish in time. | Read the rejection comment and fix what it describes. |
WAIT | 0 | The build is still running: screenshots are still being uploaded or compared. Only returned without wait=true. | Poll again. This is not a pass. |
UNCHANGED | 0 | The unchanged response: nothing changed since the digest you sent. | Keep the previous verdict and poll again. This is not a pass. |
A verdict describes the screenshots’ decisions, not the review. It can be PASS while the review is still open, for example when the approval policy needs a person’s approval before the review can finish.
Handles
A handle is a short code, such as h7k2, that identifies one screenshot in one build.
- It’s usually 4 characters. When two screenshots in a build would get the same code, one gets 5 or 6 characters instead.
- It’s unique within the build, but it isn’t stable across builds: the same screenshot gets a different handle in the next build.
- It belongs to its build:
h7k2in another build is a different screenshot. Always send the build ID with it. - Handles use digits and lowercase letters, without
i,l,oandu, so they can’t be misread. - Wherever a handle is accepted, the full run ID is accepted too.