Log verdicts for a week
Log the verdict of each page load on your server for a week, and change nothing for visitors.
Log the verdict of each page load for a week before you act on it. After the week, you know which agents visit, how often, and which pages they read.
Visitors see no change. The log uses both halves, because each half sees different agents. The server half sees agents that sign or declare their requests, such as ChatGPT and Devin. The page half sees agents that operate a person's own browser, such as Claude for Chrome.
Before you start
- Install both halves of Botscent. See Next.js, or pick your framework in the Quickstart.
- Find where your server's logs go. Each line that this guide adds is one JSON object, written with
console.log.
1. Log the request's own verdict
In proxy.ts, wrap a logging function with withBotscent. The function logs one line for each GET request that asks for HTML, that is for each page load.
import { NextResponse, type NextRequest } from 'next/server'
import { withBotscent } from 'botscent/next'
import { inspect } from 'botscent/server'
async function logPageLoad(request: NextRequest) {
const accept = request.headers.get('accept') ?? ''
if (request.method === 'GET' && accept.includes('text/html')) {
const verdict = await inspect(request)
console.log(JSON.stringify({ time: new Date().toISOString(), kind: 'request', path: request.nextUrl.pathname, ...verdict }))
}
return NextResponse.next()
}
export const proxy = withBotscent(logPageLoad)Every page load now writes the request's own verdict to your server's log.
If proxy.ts already wraps a function, add the logging lines to that function. Scripts, images and API calls do not ask for HTML, so they write no line. A click on a Next.js Link loads no new document, so it writes no line either.
2. Send the page verdict to your server
In a client component, send one request to your server when the page verdict first becomes agent. reportHeaders adds the page report to the request.
'use client'
import { diagnostics, reportHeaders, subscribe, verdict, type Verdict } from 'botscent'
import { useEffect } from 'react'
export function VisitLog() {
useEffect(() => {
let sent = false
const send = (current: Verdict) => {
if (current.type !== 'agent' || sent) return
sent = true
void fetch('/api/visit', {
method: 'POST',
headers: { 'content-type': 'application/json', ...reportHeaders('/api/visit') },
body: JSON.stringify({ path: location.pathname, transport: diagnostics().transport }),
})
}
send(verdict())
return subscribe(send)
}, [])
return null
}Render <VisitLog /> once in app/layout.tsx. A person's page sends no request, because its page verdict stays human.
The page verdict can become agent minutes after load. An extension agent, such as Claude for Chrome, shows its evidence only when it acts. subscribe calls send at that moment.
3. Log the page report
In a route handler, read the page report with readReport. Join the report to the request's own verdict with combine.
import { combine, inspect, readReport } from 'botscent/server'
export async function POST(request: Request) {
const own = await inspect(request)
const report = readReport(request.headers.get('botscent-report'))
const { path, transport } = await request.json()
console.log(JSON.stringify({ time: new Date().toISOString(), kind: 'page', path, ...combine(own, report), request: own.type, transport }))
return new Response(null, { status: 204 })
}Each page where the page half sees an agent now writes a page line.
The request field holds the type of the request's own verdict. The transport field says whether the page got the server's verdict in a Server-Timing entry. See From the server to the page. Measure agent traffic uses both fields to count each visit once.
4. Read the log after a week
After a week, read the log for 3 things:
- Which agents visit: each
agent_name, and the agent lines that have no name. - How many page loads come from agents, as a share of all page loads.
- Which pages each agent reads.
Each count of agents is a floor, not an exact count. human means that Botscent saw no agent evidence. So an agent that shows no evidence counts as a person.
Keep the reasons of each line. They show which evidence found each agent.
Result
Your server's log now has one request line for each page load. It also has one page line for each page where the page half saw an agent:
{"time":"2026-10-03T09:12:44.120Z","kind":"request","path":"/pricing","type":"agent","agent_name":"chatgpt","reasons":["signer.web-bot-auth.verified"]}
{"time":"2026-10-03T09:13:02.871Z","kind":"request","path":"/docs","type":"human","reasons":[]}
{"time":"2026-10-03T09:13:31.406Z","kind":"page","path":"/docs","type":"agent","agent_name":"claude-chrome","reasons":["page.claude.marker.active"],"request":"human","transport":"absent"}Next steps
- Measure agent traffic: turn the log into counts per agent and per page.
- Adapt the page for agents: change what agents see, after you know who they are.
- The verdict: what
type,agent_nameandreasonsmean. - Privacy: what your site collects when a page report leaves the page.