---
title: "Query, search and analytics"
url: /model/analytics
---

# Query, search and analytics

> One query grammar on every list, search by words, meaning and facets, metrics as a layer, scheduled reports, and sinks that stream a product's data out.

Query, search and analytics read the record at volume: one query grammar on every list, search indexes, metrics, reports, and sinks that stream it out.

**From the record to every reader.** The record and its Events.

1. Projections: rebuilt. Or behind: says how far.
2. Query (any list): where and group.
3. Search (an index): words, meaning.
4. Metrics (the layer): one definition. Or report: on a schedule.
5. Sink (streamed out): warehouse, SIEM.

Every read is masked by the caller's Role before it answers.

## Reading the record at volume
Every read below answers on every [Interface](/every-product.md) and shows only what the caller's Role may see. An empty or partial answer says why.

| Family | What it does |
| --- | --- |
| One query grammar | Every list takes `where` (equality, ranges, sets, text, across [Relationships](/patterns/headless-saas.md)), `sort`, `group`, `count`, `sum`, `avg`, `min`, `max`, `fields`, `limit` and `cursor`; the same words over the API, MCP, the SDK, the CLI and a view |
| Search indexes | `search` per Noun: words, typo-tolerant and per language; meaning, kept in the Customer's own [Namespace](/reference/platform.md#namespace); and facets with their counts. One search across Nouns ranks both |
| Views | A saved filter and sort as data, served over every Interface with its count; grouping and totals are the query grammar's or a metric's |
| Metrics | One layer of measures (`sum`, `count`, `avg`, a `ratio` of two), dimensions (`group`), time grains (`by`) and filters, defined once in the file and answered the same everywhere |
| Comparisons | Any metric for two periods, or against a target the Customer sets, with the change between them |
| Reports | Saved metrics sent on a schedule to members by email or to a webhook, and any view or metric as a file on demand |
| Sinks | Continuous export of traces, logs, Events, the sales record, the audit log and a Namespace's change data, to OpenTelemetry, a SIEM, object storage or a warehouse |
| Two owners of sinks | You stream your product's own data on the [Operator plane](/trust/planes.md); each Customer streams its own Namespace's records and audit log, on its own Interfaces |

A view, a metric and the [UI contracts](/interfaces/ui-contracts.md) read this one grammar. A chart a UI draws and a number an agent asks for agree.

### Example
Deals searched by title, meaning and stage; won value and win rate by month; and a weekly report to every member.

```ts title="definition.ts"
import { define } from 'apis.dev'

/** Deals for each Customer, kicked off when won. */
export default define({
  name: 'pipeline', domain: 'pipeline.apis.dev',
  Company: { name: 'string', domain: 'url!#', deals: '<-Deal.company' },
  Deal: {
    title: 'string', amount: 'decimal(12,2)', company: '->Company.deals', notes: 'markdown?',
    stage: { open: [], qualify: 'opened', win: 'qualified', lose: ['opened', 'qualified'] },
  },
  search: { Deal: { text: ['title'], meaning: ['notes'], facets: ['stage'] } },
  metrics: {
    won:     { sum: 'Deal.amount', where: { stage: 'won' }, by: 'month', group: ['company'] },
    wins:    { count: 'Deal', where: { stage: 'won' }, by: 'month' },
    closed:  { count: 'Deal', where: { stage: ['won', 'lost'] }, by: 'month' },
    winRate: { ratio: ['wins', 'closed'], by: 'month' },
  },
  reports: { weekly: { metrics: ['won', 'winRate'], every: 'every Mon 08:00', to: 'email' } },
  offer: { Team: { month: 49, seats: 5, seat: 9 }, 'Deal.open': 0.001 },
})
```

A member's agent asks for the win rate by month; the answer says how current it is.

```http title="Response"
HTTP/1.1 200 OK
Content-Type: application/json
X-Cost: price=0, currency="USD"
X-Request-Id: req_0b9d4f27e61a8c53d2e8

{
  "metric": "winRate",
  "by": "month",
  "items": [
    { "month": "2026-08", "value": 0.29 },
    { "month": "2026-09", "value": 0.34 },
    { "month": "2026-10", "value": 0.31 }
  ],
  "total": 3,
  "asOf": "2026-10-27T09:00:00Z"
}
```

That is `GET /metrics/winRate?by=month`, `getMetric` over MCP and `pipeline metrics get winRate --by month` in the CLI.

## Metrics, reports and what a product tells you
A metric is defined once, in the file, and every reader gets the same number.

[What your product tells you](/lifecycle.md#learn) is a set of these: callers, the funnel, revenue, work and runs, each rebuilt from the record.

| Reader | What it gets |
| --- | --- |
| A Customer's member | Metrics and views over its own org's records, masked by its Role, in the portal, any UI and the API |
| A Customer's admin | The same across child orgs its org graph rolls up, and a sink of its own Namespace to its own warehouse |
| You | Your product's signals across every Customer, without any Customer's private fields, and sinks of your own data |
| An agent | Any metric or query by Verb, with `asOf` on each answer so it knows how fresh the number is |
| A tool outside the product | Sinks and the change feed: it reads the product through them and acts through its Verbs |

## Indexes, reports and sinks, state by state
Each is a Thing with Verbs on the product's API, MCP server and CLI, and each move writes its Event.

| Thing | States | What moves it |
| --- | --- | --- |
| A search index | `building`, `current`, `behind`, `rebuilding` | A deploy that adds or changes it; the Events it reads; a reply names how far behind it is |
| A metric answer | `current` or `behind`, with `asOf` | The Events it reads; a period that has not closed is marked open |
| A report | `scheduled`, `sending`, then `sent` or `failed` | Its schedule, in each Customer's timezone; a failed send retries, then falls back to another channel |
| A sink | `configured`, `backfilling`, `flowing`, then `lagging`, `failed` or `paused` | `Sink.create`, then `Sink.backfill` from a point in time; a target that refuses holds the data and retries |
| A sink's schema | Follows the [Version](/reference/platform.md#version) it ships in | A deploy; a breaking change opens a new schema version beside the old one |

Every sink keeps what it could not deliver for seven days, and `Sink.backfill` sends any range again.

## What members, admins and agents meet
- **A member.** Filter, group and total any list, search by words or meaning, and never see a record their Role hides.
- **An admin.** One report each week, and the org's records and audit log flowing to its own warehouse and SIEM.
- **You.** Traces and logs in your own observability tools, and the sales record in your own warehouse.
- **An agent.** The same query words on every tool, metrics by name, and `asOf` on every number.

---

Every list, search, metric and sink reads one record through one grammar, so a chart, a report and an agent's answer always agree.