Analytics and saved views
Comparison reports, the vocabulary a filter is written against, saved views, and which columns a list screen shows.
Reports answers a fixed question per route. These answer whatever question a filter set describes: a date range against a comparison period, narrowed by filters and cut by a dimension, with every metric carrying its own definition. See Reports and the rollup.
Compare a period against another
/api/v1/kahunacart/reports/comparison
Every published metric for a date range, compared with the period before it or with the same period a year earlier, and the change in basis points (250 means +2.5%). Each metric comes with the sentence that defines it, so you can tell exactly what it counts. source tells you whether the answer came from the daily rollup or from reading the orders table. A filtered report always reads the orders table. Money is in minor units, all in one currency (the base currency unless you pass currency), with orders in other currencies counted in other_currencies. Cost of goods and margin are the base currency's, so they come back null in any other currency. Comparison periods use calendar dates in the store's own timezone, so a leap February compares against a February.
Parameters
| Name | Type | Description |
|---|---|---|
| currency_picker optional | string |
Ask for report_currency_available when the store has several currencies or historical foreign orders; no extra field on a single-currency store
One of:
1 |
| currency optional | string | A known currency code for native totals, or all for exact totals in base currency using each order's frozen fx_to_base. Unrated orders and refunds are excluded and counted separately. Left out, the base currency. A single-currency store stays unchanged; an unknown code is a 422. |
| from optional | string | YYYY-MM-DD; defaults to 30 days before `to` |
| to optional | string | YYYY-MM-DD; defaults to today |
| comparison optional | string |
One of:
none, previous_period, previous_yearDefault:
previous_period |
| segment optional | string | A segmentable field code from get_report_vocabulary — cuts every metric by it |
| filters optional | string | JSON: {"match":"all|any","filters":[{"field":…,"operator":…,"value":…}]}. An unknown field is a 422 naming it. |
What a report can be filtered and cut by
/api/v1/kahunacart/reports/vocabulary
Every field a report can be filtered or broken down by, with its type, the operators it accepts, and whether a breakdown on it can be read from the rollup. Also every metric, with its unit and the sentence that defines it. Build filters and breakdowns from this list rather than guessing. A field that is not on the list gets a 422 that names it, and is never quietly ignored. A field marked available: false is one this store cannot answer yet, and it explains why.
List saved report views
/api/v1/kahunacart/report-views
The views the calling user has saved for one report, with their filters, columns, range and comparison mode, and which one is their default. Views belong to the person who saved them. This never returns anyone else's.
Parameters
| Name | Type | Description |
|---|---|---|
| report_code required | string | Which report or list screen the views belong to: sales, products, customers, taxes, refunds, orders |
Save a report view
/api/v1/kahunacart/report-views
Save a named set of filters, columns, range and comparison mode for a report. Saving with a name that already exists replaces that view instead of adding a second one. is_default makes it the view this user sees when they open the report. Only one view per report can be the default.
Parameters
| Name | Type | Description |
|---|---|---|
| report_code required | string | |
| name required | string | What the merchant calls it |
| filters optional | object | {"match":"all|any","filters":[…]} |
| columns optional | object | Column code to on/off |
| range optional | object | {"preset":…} or {"from":…,"to":…}, plus {"comparison":…,"currency":"USD|EUR|all"} |
| is_default optional | boolean |
Default:
false |
Update a saved report view
/api/v1/kahunacart/report-views/{id}
Change a saved view's filters, columns, range or default flag. You can only update your own views.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Saved view id |
| report_code optional | string | |
| name optional | string | |
| filters optional | object | |
| columns optional | object | |
| range optional | object | Dates, comparison and currency selection; currency is a known code or all |
| is_default optional | boolean |
Delete a saved report view
/api/v1/kahunacart/report-views/{id}
Delete one of your own saved views. Someone else's view returns a 404.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Saved view id |
Which columns a list screen shows
/api/v1/kahunacart/view-configs/{entity}
Every column an admin list screen offers, with whether the calling user sees it, and the store's own names for those fields. custom tells you whether this is the user's saved choice or the shipped defaults. Columns added in a later release show up here for everyone, including users who saved a choice before those columns existed.
Parameters
| Name | Type | Description |
|---|---|---|
| entity required | string |
One of:
orders, products, customers |
Choose which columns a list screen shows
/api/v1/kahunacart/view-configs/{entity}
Save the calling user's column choice for a list screen. Send columns as a map of column code to on or off, or as a list meaning "show exactly these". Columns the screen cannot do without stay on whatever you send. Leave columns out entirely to go back to the shipped defaults.
Parameters
| Name | Type | Description |
|---|---|---|
| entity required | string |
One of:
orders, products, customers |
| columns optional | object | Column code to on/off. Omit to restore the defaults. |
Rename fields across the admin
/api/v1/kahunacart/property-labels
Replace the store's own names for fields. Call an order's number "Order Number" once and it is renamed on the list, the detail screen, the column picker and the export. The map is keyed by language code, and an empty key means every language, so renaming keeps translations working. This replaces the whole map, so anything you leave out is removed. Only fields the store declares can be renamed, and anything else gets a 422 that names it.
Parameters
| Name | Type | Description |
|---|---|---|
| labels optional | object | Language code (or "" for all) to a map of field code to label |
Every figure comes back three ways: its value for the range, the same figure for the comparison period, and the change between them. Money is minor units and a percent metric is basis points, where 250 is 2.5%. Null is not zero, so average order value over a range with no orders is null rather than a figure nobody measured. Refunds are never netted off revenue: a month with 10,000 of sales and 400 of refunds reports both numbers, because the second one is the one worth watching. cogs, margin and margin_rate need a costed catalogue, and a catalogue that is only partly costed reports a margin that is too high.
comparison is previous_period, previous_year or none, and reports.default_comparison picks which one a report opens on. A comparison is calendar arithmetic done in reports.timezone, not in UTC and not in the browser's zone, so a daylight-saving week really is an hour shorter than the week it is compared against and 29 February compares against 28 February the year before.
A filter set is a match mode of all or any and a list of field, operator and value clauses. GET /kahunacart/reports/vocabulary publishes every field with its operators, its grain and whether it can be broken down by, every metric with the sentence that defines it, and the two match modes, so a client draws the filter row without hardcoding any of it. A clause naming a field that is not on that list is refused with a 422 naming the field rather than quietly answering a different question. product and category are line-grain: filtering by one restricts the report to orders containing a matching line, and the money is still the whole order's.
Every payload says whether it was read from the nightly rollup or scanned from orders, per breakdown as well as overall, and never falls back without saying so. A filtered report always scans, because the rollup holds day rows with nothing on them to filter against.
Saved views are per user and a store keeps at most 50 per report. Somebody else's view answers 404 rather than 403, because whose it is is not information you are owed. A view stores the same filters, breakdown and range the Analytics URL carries, so a saved view and a copied link are the same state under two names.
view-configs/{entity} covers orders, products and customers. The choice is stored as a diff of the columns you turned off rather than as an allowlist, so a column added by a later release appears for everybody, and a PUT with no columns puts the code defaults back. Each screen has one column that cannot be turned off, because a row you cannot identify is not a row. PUT /kahunacart/property-labels renames a field everywhere the admin names it, stored per language and replaced whole, with a blank value meaning a removal rather than a rename to nothing. A key nobody declared is refused by name. Everything in this group needs kahunacart.reports except property labels, which need kahunacart.settings.