Skip to content
Documentation

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:

ScopeCan
readRead reports, stream exports, answer MCP. Cannot write
commerceSend orders to the commerce endpoint. Cannot read anything
fullBoth

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:

the summary
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:

a breakdown, as CSV
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:

raw rows, streamed
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.ndjson

Limits

  • 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.