---
title: Agent intent
description: "What agents asked your website: search queries, zero-result demand, AI referrers. Captured at emission, PII-screened, and rendered in Console answers and the weekly digest."
canonical_url: https://rebilder.com/docs/intent
last_updated: "2026-10-07"
---

# [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 answers and the weekly digest.

- **Updated:** 2026-10-07
- **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 answers 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. |
| `capability_version` | Version of the explicit capability vocabulary: `1` for the fields below. Older emitters omit these fields. |
| `requested_capability` | One supported capability identifier, such as `pricing.read`, `quote.request` or `booking.create`. This describes the request; it does not authorize an action. |
| `missing_capability` | The requested capability when the adapter explicitly reports it unavailable. A page miss or an absent field alone does not prove a capability is unavailable. |
| `action_status` | For action capabilities: `requested`, `offered`, `succeeded`, `failed` or `unsupported`, as explicitly reported by the adapter. A succeeded action does not establish a paid order or revenue. |

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. The capability channel adds `x-rebilder-intent-capability`, `x-rebilder-intent-missing-capability` and `x-rebilder-intent-action-status`. These carry only supported identifiers and statuses, never private terms, prices or credentials; the gateway strips them with the other intent headers.

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

## Where it renders

- [Console answers](/console/answers): 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.