# [Agent intent](https://rebilder.com/docs/intent)

> What agents asked your website: search queries, zero-result demand, AI referrers. Captured at emission, PII-screened, and rendered in Console insights and the weekly digest.

- **Updated:** 2026-08-15
- **Author:** Rebilder
- **Section:** Concepts
- **Description:** What agents asked your website: search queries, zero-result demand, AI referrers. Captured at emission, PII-screened, and rendered in Console insights and the weekly digest.
- **Publisher:** Rebilder

## What gets captured

Since events v0.4, `request.intent_signals` is populated at emission instead of being an empty object. The shape stays open (`Record<string, unknown>`), and these are the well-known keys every emitter uses:

| Key | Meaning |
| --- | --- |
| `query` | Free-text search sent to this site: a `?q=`-style param or a protocol search tool argument. Recorded only after the screen below. |
| `query_param` | Where the query came from: `q`, `query`, `search`, `s` (WordPress), `keyword`, `term`, or a protocol tool name like `mcp.search_catalog`. |
| `referrer_platform` | AI platform classified from the Referer header (`chatgpt`, `perplexity`, `claude`, `gemini`, `copilot`, …). On a `human` event this marks a visitor an AI conversation sent. |
| `utm_source` / `utm_medium` | Sanitized campaign params. `utm_source=chatgpt.com` is a live convention on AI click-throughs. |
| `tool` | Protocol tool or operation that ran: `mcp.search_catalog`, `mcp.get_product`, `mcp.get_policies`, `ucp.discovery`, `ucp.catalog`, `ucp.checkout`, `acp.feed`. |
| `result_count` | Result count for a search-shaped request. `0` is the zero-result-demand signal: an agent asked for something the catalog could not answer. |

The shared implementation lives in `@rebilder/events` (`scrubQueryText`, `classifyReferrerPlatform`, `extractUrlIntentSignals`, `buildProtocolIntentSignals`), so the gateway, the protocol adapters, and platform apps emit identical shapes.

## Screening query text

> **Query screening before recording** Free-text queries pass `scrubQueryText`, which rejects patterns such as email addresses, long digit sequences, token-like strings and pasted URLs. Keep personal information out of search terms and event fields; automated screening does not identify every form of personal data.

Recorded URLs are unchanged by this feature: `request.url` keeps its emission-time allowlist (`page`, `variant`, `sku`, `lang`, `locale`, `currency`), so search text never rides the URL and Miss-Report grouping stays clean. Queries live only in `intent_signals.query`.

## The protocol channel

On the protocol path, the adapter is the only code that knows which tool ran and how many results came back, while the event belongs to whichever emitter mounted it. Adapters therefore stamp `x-rebilder-intent-tool`, `x-rebilder-intent-query` (URI-encoded, post-screen) and `x-rebilder-intent-results` on their responses. Mounted through `@rebilder/gateway`, those headers are folded into the gateway’s event and **stripped before the response leaves**. On a standalone protocol mount they stay on the wire. They only ever echo what the client sent plus a count visible in the body.

> The reader re-screens the query after decoding, the emitter never trusts that the writer was our package.

## Where it renders

- [Console insights](/console/insights): the answer-rate headline (answered = served on the markdown or protocol path), the query table with zero-result counts, AI-referred visitors by platform with the orders that followed (labeled inference, same 7-day product-URL evidence standard as funnel attribution), and the per-platform breakdown.
- The **unanswered searches** finding: zero-result searches surface as an attention finding with the query list and a concrete fix.
- The **weekly digest** (opt-in, [Console settings](/console/settings)): the same numbers, mailed. Setting the address turns it on; clearing it turns it off immediately; unsubscribes are honoured permanently.

> **Older traffic has no intent signals** Capture happens at emission, so events recorded before your gateway picked up events v0.4 have an empty `intent_signals`. The Console says “nothing captured yet” rather than showing zeros it cannot back.

The full feature story is at [Agent intent](/agent-intent); the event schema itself is on the [Events](/docs/events) page.