> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brussle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Summarize every tenant

> Publish a banded document per tenant namespace into your template's namespace, then judge, query and subscribe to your tenants like any documents.

A platform asks one question no single tenant can answer: across all my tenants, which are turning? Each tenant is a namespace Brussle keeps apart on purpose, so nothing reads across them. A **tenant summary** is the one way up: a [template](/guides/templates) publishes a **tenant document** per namespace under it, into the template's own namespace, built from that tenant's [groups](/guides/groups) and banded. Your platform then defines ordinary judgments, queries and subscriptions over the tenant documents.

Only bands cross the boundary. A tenant document holds band labels, never an aggregate's value, and never a key value you did not list: key values come from your tenants' attributes. A judgment over tenant documents reads no tenant's text, only the labels each tenant published. It is the one [relation](/concepts/relations) that crosses namespaces, and only upward.

## Set one up

Say every tenant under `acme/prod/*` has a template group `abuse_by_day`, keyed by a day bucket you write and keeping the last 7 days, and `by_plan`, keyed by plan. Choose the groups each tenant publishes, a band for each aggregate you want to judge by, and the key values you want published one by one:

```json POST /v1/namespaces/acme%2Fprod%2F*/tenant_summary theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "groups": ["by_plan", "abuse_by_day"],
  "bands": {
    "abuse_by_day.count": {"bands": [10, 100], "labels": ["quiet", "some", "busy"]},
    "by_plan.avg(state.mrr)": {"bands": [50, 500], "labels": ["small", "mid", "large"]}
  },
  "keys": {"by_plan": ["free", "pro", "team"]}
}
```

Every group must exist on the template, and every key of `bands` names one of their aggregates as `<group>.<aggregate>`, as a group's rows key them. A value takes the label of how many cut points are at or below it, so each band has one more label than cut points. `keys` is optional: each group it names must be one of `groups`, with the key values you want broken out. Templates need the Team plan or above.

## The tenant document

Once an hour a run reads each tenant's group rows and writes its document into the template's namespace, here `acme/prod`:

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "id": "acme/prod/tenant_123",
  "attributes": {"kind": "tenant"},
  "state": {
    "groups": {
      "abuse_by_day": {"all": {"count": "busy"}},
      "by_plan": {
        "all": {"avg(state.mrr)": "mid"},
        "keys": {"pro": {"avg(state.mrr)": "mid"}, "team": {"avg(state.mrr)": "large"}}
      }
    },
    "source": {"as_of": 20488},
    "published_at": "2026-09-29T14:00:03.120Z"
  }
}
```

* `id` is the tenant's namespace name, and `attributes.kind` is `tenant`.
* `state.groups` holds labels only. Each group's `all` bands its aggregates over all its key values together: here, the abuse count over the 7 days `abuse_by_day` keeps. A group you listed in `keys` also has `keys`, the labels of each listed key value the tenant has; this tenant has no `free` row, so `free` is absent. An aggregate without a band does not appear, and neither does a key value you did not list.
* `state.source` says where the labels came from, without any of the tenant's data: the position in the tenant's history they are exact at (`as_of`).

A run writes a tenant's document only when its labels moved, so a quiet tenant costs nothing to keep current. A tenant none of whose groups has rows yet has no document.

## Judge your tenants

A tenant document is an ordinary document, so a judgment reads it like any other:

```json POST /v1/namespaces/acme%2Fprod/judgments theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "name": "tenant_health",
  "type": "bool",
  "applies_to": {"attributes.kind": "tenant"},
  "question": "Is this community's abuse rising?",
  "context": {"fields": ["state.groups"]},
  "engine": {"name": "jev", "version": "current"},
  "freshness": {"policy": "on_change"}
}
```

It is judged when a tenant's labels move, queried like any judgment, and a [subscription](/concepts/subscriptions) on it tells you when a tenant turns. Its audit stops at the tenant document: the labels it read, and `state.source`, the position in the tenant's history they are exact at.

## How current it is

`GET` lists each tenant by name with its status:

```json GET /v1/namespaces/acme%2Fprod%2F*/tenant_summary theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "template": "acme/prod/*",
  "groups": ["by_plan", "abuse_by_day"],
  "bands": {
    "abuse_by_day.count": {"bands": [10, 100], "labels": ["quiet", "some", "busy"]},
    "by_plan.avg(state.mrr)": {"bands": [50, 500], "labels": ["small", "mid", "large"]}
  },
  "keys": {"by_plan": ["free", "pro", "team"]},
  "created_at": "2026-09-20T08:30:00Z",
  "warnings": [],
  "last_run_at": "2026-09-29T14:02:11.004Z",
  "tenants": [
    {"namespace": "acme/prod/tenant_123", "status": "published", "published_at": "2026-09-29T14:00:03.120Z", "source_as_of": 20488},
    {"namespace": "acme/prod/tenant_124", "status": "unchanged", "published_at": "2026-09-28T09:00:01.842Z", "source_as_of": 18230},
    {"namespace": "acme/prod/tenant_125", "status": "skipped", "reason": "no_groups", "published_at": null, "source_as_of": null},
    {"namespace": "acme/prod/tenant_126", "status": "pending", "published_at": null, "source_as_of": null}
  ],
  "next_cursor": null
}
```

| Status | Meaning |
| - | - |
| `published` | the last run wrote its document |
| `unchanged` | its labels had not moved, so nothing was written |
| `skipped` | with `reason`: `deleting` (its namespace is being deleted), `new_life` (its namespace was re-created while the run read it), or `no_groups` (none of the groups has rows there yet) |
| `pending` | no run has reached it yet |

A group's rows are at most about an hour behind its tenant's writes, and the run reads it once an hour and pages through your tenants. So a tenant document lags its tenant by up to a run plus the paging, not one hour: read `published_at` and `source_as_of` rather than assuming.

## Changing it

* **Groups, bands and keys** change with `PATCH`, each replacing the setting whole. The next run rewrites every tenant document whose labels move, which re-judges each of them, so a `PATCH` without `"confirm": true` changes nothing and returns what that costs: one line per judgment on the template's namespace that reads tenant documents. Send it again with `"confirm": true` and it applies from the next run, unattended.
* **Delete** with `DELETE`. Runs stop; the tenant documents stay, as ordinary documents.

## Tenants that come and go

* A tenant created under the template gets its document at the first run after its groups first have rows.
* A tenant deleted and created again under the same name has its document deleted and written anew, so the history of anything judging it splits at the new incarnation, as it does for the tenant itself.
* A tenant document is deleted only once a strong read says the tenant's namespace is deleted, never because a listing missed it.

## Billing and failures

Tenant documents are written like any write to the template's namespace: they bill your organization as writes and stored bytes, and judgments over them bill as any judgment does. An idle tenant writes nothing.

If the template's namespace refuses the writes, for example because it is over its budget with `on_exceeded: reject`, the tenant summary shows the warning `tenant_summary_failed` and the [events feed](/guides/events-feed) carries one `namespace.tenant_summary_failed` for the run, with the reason. Nothing is lost: the next run that can write catches every tenant up and clears the warning.


## Related topics

- [Relations](/concepts/relations.md)
- [Multi-tenant platforms](/guides/multi-tenant-platforms.md)
- [Templates](/guides/templates.md)
- [Get the tenant summary and each tenant's status](/api-reference/groups/get-the-tenant-summary-and-each-tenants-status.md)
- [Publish a tenant document per namespace under a template](/api-reference/groups/publish-a-tenant-document-per-namespace-under-a-template.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.