DocsDeploy checkAll pages

Deploy check

The question right after shipping is “did that break anything?” Mark a deploy, and PageHum compares the hours after it with the same hours on the week before: client errors, failing requests, broken links, rage clicks, the funnel, and speed. It says what got worse, where, and by how much, with a prompt to fix it.

Plans

FreeStarterProUltra
Windows judged after each deploy—First 3 hoursFirst hour, 3 hours, and dayFirst hour, 3 hours, and day

Each deploy is checked from when it’s marked; nothing extra runs on your site.

Marking a deploy

A deploy marker is the line on the board’s charts for a deploy. Any of these starts its check:

  • The site’s deploy hook, from Changes on the board: one URL to call from Vercel, Netlify, GitHub, or any CI, no key needed. It answers with the marker’s id.
  • The API: POST /sites/{key}/markers with a write key. Put the commit in the text, like a1b2c3d New checkout, and the prompt starts from it.
  • Changes on the board, with the kind set to Deploy.

A deploy marked more than a day after it happened isn’t checked: the hours after it are already behind other changes.

How it's judged

WindowIsJudged
First hourThe hour the deploy landed in (if in its first 10 minutes), else the next5 minutes after it ends
First 3 hoursThat hour and the next two5 minutes after they end
First day24 hours from there5 minutes after they end

Each window is compared with the same hours on each of the 7 days before. PageHum keeps each hour’s counts for 8 days for this; until a site has 3 days of them, it compares with the 7 whole days before instead.

ReadingWorse whenNeeds
Client errors per pageviewUp at least 1 point and by half100 pageviews
Failing requests per pageviewUp at least half a point and by half100 pageviews
Broken link hits per pageviewUp at least half a point and by half100 pageviews
Rage clicks per pageviewUp at least half a point and by half100 pageviews
Visits reaching the goalDown at least 5 points50 visits
LCP, p75Up at least 500 ms, and past 2.5 s30 readings
INP, p75Up at least 100 ms, and past 200 ms30 readings
TTFB, p75Up at least 300 ms, and past 800 ms30 readings
  • Worse: the change is also past three times how much that reading usually moves from day to day, and for a rate, how much it moves by chance in a window that size. A site whose errors swing a lot needs a bigger jump.
  • Watch: past twice that, and half the size. A new error seen 10 times or more also makes a fine deploy one to watch.
  • Looks fine: everything within its usual range.
  • Too quiet to tell: no reading had enough traffic in the window, and at least 3 of the 7 days before with half that. A quiet site never gets a false Worse.

A checked page or flow that started failing after the deploy makes it worse whatever the counts say. Errors and failing endpoints that none of the 7 days before had are listed by page, at 3 or more.

Where it shows

  • Since the last deploy, near the top of the board for a day after each deploy: each window, what moved with its numbers before and after, and Copy prompt to fix it when it’s worse or one to watch.
  • A lamp on the deploy’s marker in every chart: green, amber, or red. Hover the marker for the verdict.
  • When it’s worse, once per deploy: a notification, browser push, and your Slack, Discord, Teams, Telegram, or webhook channels (the webhook event is deploy.worse; see payloads).

In CI

GET /sites/{key}/deploys/{id} answers for a deploy marker’s id, or latest, with a read key. A pipeline can wait for the window it cares about and fail on regressed. Or ask your assistant: the MCP tool is get_deploy_check.

The answer

{
  "ok": true,
  "data": {
    "marker": { "id": "m_8f2k1c", "at": 1791986700000, "text": "a1b2c3d New checkout", "source": "api" },
    "verdict": "worse",
    "regressed": true,
    "settled": false,
    "nextAt": 1791994500000,
    "windows": [
      {
        "key": "1h", "label": "First hour", "ready": true, "verdict": "worse", "pageviews": 412, "baseline": "hours",
        "findings": [
          { "metric": "errors", "label": "Client errors per pageview", "kind": "rate", "before": 0.011, "after": 0.136, "verdict": "worse" }
        ]
      },
      { "key": "3h", "label": "First 3 hours", "ready": false, "verdict": null, "pageviews": 780, "findings": [] },
      { "key": "24h", "label": "First day", "ready": false, "verdict": null, "pageviews": 780, "findings": [] }
    ],
    "newErrors": [{ "path": "/cart", "message": "TypeError: cart.items is undefined", "count": 57 }],
    "newRequests": [],
    "failing": [],
    "prompt": "My website \"Lumen\" got worse after a deploy…"
  }
}

This GitHub Actions workflow marks each deploy, then waits for the first hour’s verdict and fails the run if it’s worse, with the prompt in the run’s summary. Add PAGEHUM_KEY (a key with write access, from your account page) to the repository’s secrets.

.github/workflows/deploy.yml

name: Deploy
on:
  push:
    branches: [main]
env:
  PAGEHUM_SITE: s_your_site_key
jobs:
  deploy:
    runs-on: ubuntu-latest
    outputs:
      marker: ${{ steps.mark.outputs.id }}
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh # your deploy
      - id: mark
        name: Mark the deploy on PageHum
        run: |
          id=$(curl -fsS -X POST "https://api.pagehum.com/v1/sites/$PAGEHUM_SITE/markers" \
            -H "Authorization: Bearer ${{ secrets.PAGEHUM_KEY }}" -H "Content-Type: application/json" \
            -d "$(jq -n --arg text "$(git log -1 --pretty='%h %s')" '{text: $text}')" | jq -r .data.id)
          echo "id=$id" >> "$GITHUB_OUTPUT"

  deploy-check:
    needs: deploy
    runs-on: ubuntu-latest
    timeout-minutes: 150
    steps:
      - name: Fail if the first hour after the deploy got worse
        run: |
          url="https://api.pagehum.com/v1/sites/$PAGEHUM_SITE/deploys/${{ needs.deploy.outputs.marker }}"
          while :; do
            check=$(curl -fsS "$url" -H "Authorization: Bearer ${{ secrets.PAGEHUM_KEY }}")
            [ "$(jq '.data.windows[0].ready' <<< "$check")" = "true" ] && break
            sleep 300
          done
          echo "Deploy check: $(jq -r '.data.verdict' <<< "$check")"
          if [ "$(jq '.data.regressed' <<< "$check")" = "true" ]; then
            jq -r '.data.prompt' <<< "$check" >> "$GITHUB_STEP_SUMMARY"
            exit 1
          fi