> For an index of all Botscent documentation, see https://botscent.nibnalin.me/llms.txt.

# Let a verified agent through

Skip a challenge, a sign-in wall or a rate limit for an agent whose signature verified.

Let an agent with a verified signature past a challenge, a sign-in wall or a rate limit. Agents that prove who runs them get through, and every other visitor meets the same checks as before.

## Before you start

* Install the server half of Botscent. See [Next.js](/docs/nextjs) or [Express](/docs/express).
* Find which agents sign their requests. [Agents](/docs/agents) lists them. In this release, ChatGPT, Grok Bot, Manus and Cloudflare Browser Run sign.

## 1. Choose what a verified agent can skip

A verified signature proves that the agent operator's infrastructure signed the request. It does not prove who the person is, or which model runs the session.

So let a verified agent past checks that protect your site. Examples are a challenge, a rate limit, and a sign-in wall in front of pages that hold no person's data. Keep the checks that protect a person's account.

Each signature has a time window, from 5 s before its `created` time to its `expires` time. Someone who captures a signed request can replay the signature to the same host within that window. See [The trust model](/docs/trust-model).

## 2. Check the signature on the server

In `proxy.ts`, call [`isVerified`](/docs/server-api#isverified) on the request's own verdict before the challenge:

```ts title="proxy.ts"
import { NextResponse, type NextRequest } from 'next/server'
import { withBotscent } from 'botscent/next'
import { inspect, isVerified } from 'botscent/server'

async function challenge(request: NextRequest) {
  if (!request.nextUrl.pathname.startsWith('/search')) return NextResponse.next()
  if (request.cookies.has('challenge-passed')) return NextResponse.next()
  if (isVerified(await inspect(request))) return NextResponse.next()
  return NextResponse.redirect(new URL('/challenge', request.url))
}

export const proxy = withBotscent(challenge)
```

A verified agent now reaches `/search` without the challenge. Other visitors without the cookie go to `/challenge`.

Without a name, `isVerified` is true for a Web Bot Auth signature that verified against a key in this release. It is also true for the platform's verified-bot field, such as Cloudflare's. That field says that some agent was verified, not which agent.

> **Warning:** Do not let an agent through on a reason string, a bare `agent_name` or a page report. Anyone can send the headers that produce a name, and scripts on the page can forge a report. Use `isVerified` on the request's own verdict.

## 3. Let one agent through

Pass the agent name to `isVerified`. With a name, `isVerified` is true only when a verified signature names that agent. The platform's verified-bot field never passes a check with a name.

In Express, the request's own verdict is `req.botscent`. The Express adapter does not add `botscent` to Express's `Request` type, so this code reads it with a type assertion:

```ts title="server.ts"
import express, { type NextFunction, type Request, type Response } from 'express'
import { botscent } from 'botscent/express'
import { isVerified, type Verdict } from 'botscent/server'

const app = express()
app.use(botscent())

// 60 requests a minute for each address. A verified ChatGPT has no limit.
const hits = new Map<string, number>()
setInterval(() => hits.clear(), 60_000)

function rateLimit(req: Request, res: Response, next: NextFunction) {
  const verdict = (req as Request & { botscent?: Verdict }).botscent
  if (isVerified(verdict, 'chatgpt')) return next()
  const key = req.ip ?? ''
  const count = (hits.get(key) ?? 0) + 1
  hits.set(key, count)
  if (count > 60) return res.status(429).send('Too many requests')
  next()
}

app.use(rateLimit)
app.get('/search', (req, res) => {
  res.json({ results: [] })
})

app.listen(3000)
```

ChatGPT's verified requests now skip the limit. Every other request counts against it.

A hosted agent can sign its page loads and nothing else. Then its other requests carry no signature, and they count against the limit.

## 4. Keep the package current

Each release bundles the signers' keys, and the server half fetches no key. A key that a signer adds after the release gives `signer.web-bot-auth.declared` until you upgrade. A key that a signer removes, because it rotated or leaked, stays verified until you upgrade.

Upgrade to the latest release, and keep the exact version pinned:

```sh title="Terminal"
npm install --save-exact botscent@latest
```

The release notes give the date of the key fetch, and the output-change report of the release. See [Versions and stability](/docs/versions).

## Result

Agents with a verified signature skip the challenge, and a verified ChatGPT skips the rate limit. Every other visitor meets the same checks as before. To see why a signature did or did not verify, start the server with `BOTSCENT_DEBUG=1`.

## Next steps

* [The trust model](/docs/trust-model): what a verified signature proves, and which verdicts are fit for access.
* [Agents](/docs/agents): which agents sign their requests, and the signer host of each.
* [Server API (TypeScript)](/docs/server-api#isverified): both forms of `isVerified`.
