# Events API

Clicks on and views of an answer's hits and banners, as a storefront reports them for Insights.

Base URL in the compose stack: `http://127.0.0.1:7800`. It is served by the Query API and, like it, takes no key.

| Endpoint | What it does |
| --- | --- |
| [`POST /v1/events`](#events) | Report clicks on and views of an answer's hits and banners |

## Report clicks on and views of an answer's hits and banners

`POST /v1/events`

Takes a batch of clicks and views from one page load, each naming the query ID of the answer it belongs to, and hands it to the search log without waiting for it, as a search is: Insights joins each to its search at the next roll-up. A browser sends it with `navigator.sendBeacon`, which posts it as `text/plain`, so no preflight precedes it; it carries no cookie and the answer sets none. An event whose answer is older than [`event_hours`](/docs/reference/limits#event_hours) is refused by its query ID's time alone and counted as late; the others are accepted. Events marked as test or bench traffic, or sent by a browser the bot list names, are left out of the reports as their searches are.

### Body

The events of one page load, as a storefront sends them: a click at once, views together when the page is hidden.

Sent as `application/json` or `text/plain`.

- `page_view` (string): The page the events happened on, as its searches named it.

- `traffic` (string): Whose events these are, as the searches said. Default: `shopper`.
  
  - `shopper`: Everyone a storefront serves that none of the below is.
  
  - `test`: End-to-end tests, which mark their requests so.
  
  - `bench`: A latency benchmark, which marks its requests so.
  
  - `bot`: A crawler or another bot, as uap-core's maintained user agent rules name it; the Query API decides this from the `User-Agent` header of a request that says it is a shopper's.

- `events` (array of objects · 1 to 100 items · required): One or more, up to the limit [`events_per_call`](/docs/reference/limits#events_per_call).
  
  A click on or a view of one hit or banner of an answer. It names the hit by its entity or the banner by its rule, never both.
  
  - `type` (string · required): - `click`: The shopper opened the hit or followed the banner.
    
    - `view`: Half of the hit or banner was visible for a second. A storefront reports one per query ID and hit or banner.
  
  - `query_id` (string · required): The query ID of the answer whose first page the shopper saw: a hit that a later page brought names the search it continues. The Query API takes it for [`event_hours`](/docs/reference/limits#event_hours) after the answer; the id says when that was.
  
  - `entity` (string): The hit, such as `product:SOFA-LUND-3`. Its type names the section it stood in.
  
  - `placement` (string): The banner, by the rule that placed it.
  
  - `position` (integer · 1 to 1000): Where the hit stood, counted from 1: in the listed section across the pages, as a grid banner's position counts, and in a section beside it among the hits it shows, up to the limit [`reachable_hits`](/docs/reference/limits#reachable_hits). Left out for a banner.
  
  - `campaign` (string): The campaign of a sponsored hit, as the answer names it.
  
  - `surface` (string): Who acted. Default: `storefront`.
    
    - `storefront`: The shopper, on the storefront's page.
    
    - `agent`: An agent acting for the shopper through a tool the storefront offers, such as a WebMCP tool call.

### Answers

- `202` (object): What it took; the events show after the next roll-up.
  
  - `accepted` (integer · required)
  
  - `late` (integer · required): Events whose answer is older than [`event_hours`](/docs/reference/limits#event_hours), refused by their query ID's time alone; the report counts them as late.

- `400` ([problem](/docs/reference/problems#400)): The body is not a batch of events: no events or more than [`events_per_call`](/docs/reference/limits#events_per_call), an event naming both or neither of an entity and a placement, a position no page reaches, or a query ID that is not a UUIDv7 or names a time still to come. Nothing of it is taken.

- `413` ([problem](/docs/reference/problems#413)): The body is larger than the 64 kB a beacon may carry.

```sh title="Terminal"
query_id=$(curl -s 'http://127.0.0.1:7800/v1/search?query=sofa&traffic=test' | jq -r .query_id)
curl -s http://127.0.0.1:7800/v1/events -H 'Content-Type: application/json' \
  -d '{ "traffic": "test", "events": [{ "type": "click", "query_id": "'"$query_id"'", "entity": "product:SOFA-ASKA-2", "position": 1 }] }'
```

```json title="Answer: 202 · 1.6 ms"
{"accepted":1,"late":0}
```

**Guide:** [Clicks and views](/docs/clicks-and-views)
