Documentation
Coming from another tool
Reading the reports
Count what matters
Machine access
Every report, over HTTPS.
The same numbers the panel shows, from the same functions, behind one bearer key. Reports, raw export, and an OpenAPI schema that cannot drift.
Keys and scopes
Create a key on the panel's API keys screen. It is shown once and stored only as a hash. A lost key is replaced, never recovered. Each key belongs to one site and carries one scope:
| Scope | Can |
|---|---|
| read | Read reports, stream exports, answer MCP. Cannot write |
| commerce | Send orders to the commerce endpoint. Cannot read anything |
| full | Both |
The key is the whole address: it names the site and the site's time zone, and there is no site parameter to pass, which means there is also none to get wrong. Give an assistant or a contractor a read key and they can see numbers and touch nothing.
Reports
Every report the panel shows is a GET, answered by the same functions the screens call, so the API and the panel cannot disagree. Headline figures for a range:
curl -H "Authorization: Bearer $KEHAI_KEY" \
"https://app.kehai.io/api/v1/stats/summary?from=2026-08-01&to=2026-08-31"A supported dimension as a breakdown, exported as CSV with format=csv for a spreadsheet:
curl -H "Authorization: Bearer $KEHAI_KEY" \
"https://app.kehai.io/api/v1/stats/breakdown?by=country&format=csv"Dates are plain days, read in the site's own time zone. A report belonging to a module you switched off answers 404 rather than an empty result, because for that site it does not exist. A filter that cannot be answered is refused rather than ignored, because a report that silently drops a filter is worse than an error.
Report segments use country, channel, device, path, the five utm_* fields, referrer, browser, os, city, hour, product, sku, and coupon. Each report accepts the dimensions its rows can answer. The hour is an integer from 0 through 23 in the site's timezone, applied to event or transaction timestamps. Product and SKU select whole order baskets; Channels and Campaigns can show their money without inventing a traffic audience. See segment scope and the generated API schema for each report's exact parameters.
The views your team saved in the panel are readable too: GET /api/v1/views lists them with their screen and their state, in the same from, to, and filter parameters the reports take, so a script or an assistant can ask for "Poland, mobile" by name. Keys read views and never write them.
Raw export
Everything leaves. The export streams raw rows as newline-delimited JSON, selecting every column, so nothing a report does not surface is trapped in the product:
curl -H "Authorization: Bearer $KEHAI_KEY" \
"https://app.kehai.io/api/v1/export?dataset=events&from=2026-01-01&to=2026-08-31" \
-o events.ndjsonLimits
- Sixty reads a minute per key. A listing costs the same as a call.
- At most 366 days in one report call, and 3,660 in one export, which is above the longest retention a site can keep.
- Reports support format=json. Supported flat results also accept format=csv. Forecast, Realtime, Pacing, comparisons, series=daily, and Vitals totals require JSON. Raw export is always newline-delimited JSON.
The schema that cannot drift
The whole contract, every report, every parameter, and what each refusal means, is one address, and no key is needed to read it:
app.kehai.io/api/v1/openapi.json
It is generated from the same registry the panel, the REST routes, and MCP all run through. Point a client generator, a Postman import, or an assistant at it and they get the truth of the deployed version, not of a hand-maintained document.