stable

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

ParameterDefaultWhat it does
formatjsondigest returns the text format on this page. json returns the full JSON summary.
waitfalsetrue 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.
timeout120Seconds 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_fastfalseWith 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.

  • DIFF lists changed screenshots that nobody has decided yet.
  • NEW lists 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>
ColumnContent
handleThe run’s short handle.
nameThe snapshot name.
viewportWidth and height, such as 1280x720.
browser2cx (Chromium), ff (Firefox) or wk (WebKit).
ratio%Share of pixels that changed, such as 0.42%. Diffs only.
severitytrivial, minor, moderate or major. Diffs only.
regionsWhere the change is. Diffs only.

regions takes one of three forms:

  • <n>rgn <x>,<y> <w>x<h>: the changes are clustered in n regions. 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 GROUP line 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) or color(high), instead of regions.

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.

VerdictExit codeWhenWhat to do
PASS0Every screenshot is unchanged or approved, and nothing is rejected.Continue.
REVIEW1At 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_NEW2New screenshots are waiting for a decision, and there are no diffs.Approve them to set their baselines.
FAIL3A 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.
WAIT0The build is still running: screenshots are still being uploaded or compared. Only returned without wait=true.Poll again. This is not a pass.
UNCHANGED0The 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: h7k2 in another build is a different screenshot. Always send the build ID with it.
  • Handles use digits and lowercase letters, without i, l, o and u, so they can’t be misread.
  • Wherever a handle is accepted, the full run ID is accepted too.