Entity Health
Entity Health shows how the agents, models, knowledge bases, MCP servers and tools of a project are doing. Each kind of entity has a scoreboard with a status per entity, each entity has a detail page with its history, and a daily or weekly digest can be sent to the channels you choose. Every figure is set against the previous period and, where there is enough history, against the entity's own normal.
Enterprise module
Entity Health is an enterprise module (health). The Health page exists in the community build and shows an upgrade prompt, and /api/entity-health/* answers HTTP 402 for a tenant without an active enterprise licence. Two related parts stay in the community edition: the Agents list and the agent Health panel (see Agent Tracing), and alert rules, incidents and notification channels (see Monitoring). Baseline and SLO burn alert rules, which also feed this page, are enterprise as well. See Licensing.
What it covers
| Entity | Traffic counted | Read permission needed |
|---|---|---|
| Agents | Sessions | tracing |
| Models | Requests | models |
| Knowledge bases | Queries | rag |
| MCP servers | Requests (tool calls) | mcp |
| Tools | Calls | tools |
Everything is computed from the 5-minute health rollups that also drive alert rules, so the scoreboard, the charts and the rules read the same numbers. The rollups are kept for ENTITY_METRICS_RETENTION_DAYS (45 days by default), which is also how far back any health view can look. A session counts when it reaches a final status, so a running session shows up when it ends. See Session status and the metric catalog.
The scoreboard
Open Operate → Agent Observability and choose Health in the sidebar. The page has two tabs: Scoreboard and Reports (the digest).
The scoreboard shows one kind of entity at a time:
- Type selector. Agents, Models, Knowledge bases, MCP and Tools. Only the kinds you may read are listed. A badge on a kind counts the entities that need attention (critical, warning or degraded). The badge of a kind you are not looking at is an estimate and carries a
~. - Period. The date filter at the top right: last 24 hours (the default), last 7 days, last 30 days, Total or a custom range. Health data is kept for a limited time, so Total shows the last 30 days at most, and a notice says so.
- Status tiles. Five tiles count the entities per status. Click one to show only that status, click it again to clear the filter.
- Search and status filter. Search matches the entity's name and its key.
| Column | Shows |
|---|---|
| Status | The status of the entity. The default order is most severe first |
| Name | The entity's name in the console, with its key underneath when they differ. Click to open its detail page |
| Sessions, Requests, Queries or Calls | Traffic in the period, with the change against the period before (a signed percentage, shown from 5 %, or "new" for an entity that had no traffic before) |
| Error rate | Failed share of the traffic, with the previous value and, when it is clearly above normal, a N× normal chip |
| p95 latency | 95th percentile latency (session duration for agents), with the previous value and the same chip |
| Cost | Model cost attributed to the entity. Empty when there is none |
| Trend | Two sparklines over the period: traffic and error rate |
| Service level (SLO) | Attainment and remaining error budget, for entities covered by an SLO rule |
| Alert | Firing or Pending when an alert rule is in that state for the entity. It links to the incident |
Columns can be sorted, and rows without a value for the sorted column come last. The row menu offers View health, Open agent / model / knowledge base / MCP server / tool (the entity's own page) and Create alert, which opens the new-rule form with the entity as the target and its error-rate metric selected. Create alert is hidden while the alert state cannot be read.
The table lists the entities that had traffic in the period or in the period before it, and every entity that has an alert pending or firing, even when it went quiet. At most 500 entities are listed, the busiest first and never dropping one that has an active alert. A notice shows when entities were left out. The footer shows how many of the listed entities match the filters.
The page keeps its state in the address, so a view can be shared: /dashboard/tracing/health?tab=reports, ?type=model, ?status=critical and ?period=last_7_days. Defaults (scoreboard, agents, all statuses) are left out.
Statuses
| Status | Meaning |
|---|---|
| Critical | An alert of critical severity is firing for the entity |
| Warning | An alert of any other severity is firing for the entity |
| Degraded | An alert is pending, or the error rate or p95 latency is clearly above the entity's own normal |
| Healthy | The entity had traffic, no alert is firing or pending and nothing is above normal |
| Idle | No traffic in the period and no alert pending or firing |
An alert beats everything else: an entity with a firing alert is critical or warning even when it has no traffic. The severity is the one of the incident the alert opened (an auto rule derives it when it fires), or the rule's own severity.
How normal is judged. The comparison does not use the sensitivity presets of your alert rules. It has one fixed, balanced setting, and it only looks for values above normal. An entity is above normal for a metric when all of these hold:
| Condition | Error rate | p95 latency |
|---|---|---|
| At least this many times normal | 1.5 | 1.5 |
| At least this many standard deviations above normal | 3 | 3 |
| At least this much above normal | 2 percentage points | 100 ms |
"Normal" is taken from the same time of day on each of the previous 14 days, one window per day. For p95 latency it is the median of those windows, and the standard deviation is their robust spread. For the error rate it is the pooled rate over those windows (all failures divided by all requests); with at least 5 windows, the failures of each are capped at the 90th-percentile window rate (never below one failure), so a past incident does not raise normal. Its standard deviation is the largest of the statistical noise expected at the current window's volume (the binomial standard error), the spread of the windows' rates and 0.5 percentage points, so one error in a quiet window does not count as above normal. The comparison is made as of the end of the period, over the largest alert window (5 minutes up to 24 hours) that fits the period. For a period longer than that window, the value compared with normal is the one of the last window, not of the whole period, so the chip can show on an error rate that looks unremarkable over 30 days.
Only the 50 busiest entities that have at least 20 requests in the period are compared (the 25 busiest per kind for the overview card), and only when at least 5 of the previous 14 days give a comparable value. Other entities are not judged against normal, so only their alerts decide their status. When the comparison itself fails for some entities, a notice says they may look healthier than they are.
The N× normal chip. It shows the ratio to normal for the error rate and for p95 latency. It appears from 1.25× and turns red from 1.5×.
Service level. The SLO column is filled for entities that an enabled SLO burn rule covers. Only the 50 busiest entities of a board are evaluated and up to 5 rules per kind are read. When several rules cover an entity, the one with the least budget left is shown. Attainment is cut, never rounded up, so 99.96 % is not shown as 100 %. An exhausted budget is shown in red.
Entity detail
Click an entity, or choose View health in its row, to open /dashboard/tracing/health/<type>/<key> (type is agent, model, kb, mcp or tool). The period you came from is kept.
- Header. The status badge, how many alerts are firing (it links to the incident) or pending, and Error rate N× normal and p95 latency N× normal badges when they apply.
- Actions. Create alert and Open incidents (filtered to this entity), Failing sessions for agents (the session list filtered to the agent and to errors), and Open agent / model / ... for the entity's own page. Create alert and Open incidents are hidden while the alert state cannot be read.
- Service level. A tile with the attainment and the budget left, when an SLO rule covers the entity.
- Charts. Tiles with the key figures, each with its change against the previous period (signed and with an arrow for the direction, for example
+12%or−3.4 pp: rates change in percentage points, other figures in percent), and charts of traffic with error rate, latency (p50 and p95) and, for agents, tool errors with abandoned runs. The error-rate and p95 charts show a shaded band: the normal range of the same bucket on the previous 14 days, drawn where at least 3 of those days have data. For p95 it is the median plus or minus 3 robust standard deviations. For the error rate it is the pooled rate plus or minus 3 times the standard deviation described under Statuses, worked out with that bucket's own traffic, so a quiet bucket gets a wider band than a busy one. Markers show when incidents opened in the period. Each is tagged "Incident" on the chart and shows its full text when you hover it, and the Markers list under the charts names each one with its time and links it to its incident.
An address that is not one of the five kinds, or an empty key, shows an "Unknown entity" page with a way back. An entity without traffic in the period shows a "No traffic" note and no charts.
Health on the overview
The Enterprise Overview (/dashboard/overview) has a Health card next to the active incidents. It counts the entities in each status across the kinds you may read and lists the 3 that need attention most (critical first, then warning, then degraded), each with its error rate and p95 and a link to its detail page. Open health goes to the scoreboard. Counts cover at most the 200 busiest entities of each kind and never drop one with an active alert, and a notice names the kinds that were cut or could not be read. The card follows the date filter of the overview and is left out when you have no read access to any kind.
Health dashboards
Five dashboards in Analytics (/dashboard/analytics) chart the same rollups: Agent Health, Model Health, Knowledge Base Health, MCP Server Health and Tool Health. They belong to the enterprise analytics module (reports), and each is listed only for people who may read the service that owns its data.
All five have the same layout: a selector for one entity (all entities by default), four tiles (traffic, error rate, p95 latency and cost, each against the previous period), charts of traffic with errors, error rate, and latency p50 and p95 over time, and a scoreboard of the busiest entities. A CSV export of the dashboard is available from its header.
Health digests
A digest is a summary of a project's health that is sent on a schedule. Open the Reports tab of the Health page to set it up. There is one digest per project, and it stays off until you configure it.
| Setting | Notes |
|---|---|
| Enabled | Switch off to pause the schedule and keep the settings. Send now still works |
| Frequency | Daily or Weekly |
| Hour (UTC) | 0 to 23, 7 by default. The digest goes out within a few minutes after that hour |
| Day of the week | For weekly digests, Sunday to Saturday (UTC), Monday by default |
| Channels | Up to 10: email, webhook, Slack or Teams. An enabled digest needs at least one. PagerDuty cannot carry a digest and is not offered. Credentials are stored sealed and never shown again, and the destination rules of alert channels apply |
| Content | The sections to include, at least one. All are on by default |
| Written summary | Optional. A chat model of the project, which you choose, writes a short summary on top of the figures |
The digest covers the period since the previous one: the last 24 hours for a daily digest and the last 7 days for a weekly one, compared with the period before it.
| Section | Content |
|---|---|
| Overview | Entities, traffic, error rate, p95 and cost for each kind, against the previous period |
| Regressions | Up to 10 entities that clearly got worse, with at least 20 requests in both periods (20 successful ones for a p95). An error rate counts when it is at least 1.5× the previous one and at least 1 point higher, with at least 3 errors (an entity with no errors before must reach 2 %). A p95 counts when it is at least 1.5× and at least 100 ms higher. A sustained shift over the last 21 days (the recent level at least twice the earlier one) is listed too |
| Top errors | The most frequent error messages of up to 5 agents that fail most, read from up to 200 failed sessions each |
| Incidents | Incidents opened and resolved in the period, how many are still open, the mean time to resolve, and up to 10 incidents, the most severe first |
| Cost | Spend per kind and the 5 entities whose cost moved most, for moves of at least $0.01 |
| SLO | Attainment and budget left for your SLO burn rules: up to 10 rules, 20 entities per rule and 10 rows |
Delivery. Email sends every section you chose to each recipient you list, or to the workspace owners and admins when the list is empty (disabled accounts are skipped). Slack, Teams and webhook channels receive a compact message: a title (<project>: daily health digest), a one-line headline, the period in UTC, a line per kind and the first rows of each section, and a link to the Health page. The message is marked critical when a critical incident is open, and warning when other incidents are open or regressions were found. A webhook receives it as a signed notice, with the same headers and signature as alert webhooks:
{
"type": "notice",
"id": "alr_9d1e4b7a2c3f58061a2b3c4d",
"occurredAt": "2026-10-16T07:00:03.512Z",
"data": {
"title": "Production: daily health digest",
"summary": "2 regressions, 3 incidents opened, 1 still open, 412 agent requests",
"severity": "warning",
"fields": [
{ "label": "Period (UTC)", "value": "2026-10-15 07:00 UTC to 2026-10-16 07:00 UTC" },
{ "label": "Agents", "value": "412 requests, 2.18% errors (was 1.4%), p95 6,400ms" },
{ "label": "Incidents", "value": "3 opened, 2 resolved, 1 still open, mean time to resolve 42 min" }
],
"url": "https://console.example.com/dashboard/tracing/health",
"urlLabel": "Open the health dashboard",
"footer": "Cognipeer Console, Acme"
}
}Written summary. The model is given the finished digest as JSON, which includes entity names, rule names and the error messages it lists, and writes a short briefing in English. It may only restate figures that are in the digest. If the model call fails, the digest is still sent without the summary and the run is marked partial.
Permissions. A digest is sent as written, so saving or sending one needs read access to everything it covers. Top errors alone needs tracing. Every other section needs the service of every kind (tracing, models, rag, mcp and tools), and Incidents and the written summary also need alerts. Without it the request is refused with 403 and code: "digest_scope_forbidden", and the message names what is missing: turn the sections off, or ask an administrator. When someone opens a past run, rows of kinds they cannot read are left out, incidents need alerts, and the written summary is shown only to people who can read everything.
Send now builds the digest and sends it to every channel immediately. A request within 30 seconds of the project's last digest answers 429 (too_soon), and one while a digest is being sent answers 409 (run_in_progress).
Recent digests lists the last 20 runs of the project (100 are kept): when it was sent, the period, whether it was scheduled or manual, the status, and how many deliveries succeeded (one per channel, and one per email recipient). Open a run to see exactly what it said and the result of each delivery.
| Run status | Meaning |
|---|---|
Sent (success) | Everything was built and delivered |
Partly sent (partial) | A delivery failed, a section or a kind could not be read, or the written summary failed |
Failed (failed) | The digest could not be built, or no delivery succeeded |
The scheduler checks every 5 minutes and handles at most 20 projects per check, so on a deployment with many configured projects a digest can go out later than its hour.
Scheduled prescription reports
Prescriptions can generate reports for your busiest agents on a schedule. Open Cost & Optimization → Prescriptions, choose Settings, and use Scheduled reports. It is off by default because every run uses a model.
| Setting | Range | Default |
|---|---|---|
| Generate reports on schedule | on or off | off |
| Run every (days) | 1 to 30 | 7 |
| Agents per run | 1 to 10 | 3 |
On each run the busiest agents of the project come first, and only agents with at least PRESCRIPTIONS_MIN_SESSIONS sessions (50 by default) in the last 14 days are analysed. A report that is already being generated is not started twice. The reports appear in the Prescriptions list like any other. This part belongs to the Cost module (cost) and is checked by the same 5-minute scheduler as the digests.
Permissions
| What | Needs |
|---|---|
| See a scoreboard, a detail page or a dashboard | Read access to the service that owns the kind (see What it covers). Without it the kind is missing from the selector and the page shows a no-access message |
| See alert rule names, incident links and incident markers | Read access to alerts. Without it the status and severity still show |
| Read a digest or its runs | The tracing service at read level. What the runs show depends on what you can read, see above |
| Change a digest or send it | The tracing service at write level, plus read access to everything the sections cover |
| Read or change the prescription schedule | The cost service at read or write level |
API
These are dashboard APIs: they authenticate with a browser session and read the active project. They answer 402 without the enterprise licence and 403 without the permission above.
| Endpoint | Description |
|---|---|
GET /api/entity-health/summary | The scoreboard of one kind: entities with totals, previous period, sparkline, status, deviation, SLO and alert |
GET /api/entity-health/series | One entity: buckets, totals, previous period, normal band, incident markers, status and alert counts |
GET /api/entity-health/overview | Status counts per kind and the entities that need attention most |
GET /api/entity-health/digests | The digest settings and the last 20 runs |
PUT /api/entity-health/digests/config | Create or replace the digest settings |
POST /api/entity-health/digests/run | Send the digest now |
GET /api/entity-health/digests/runs/:runId | One run with its content |
GET /api/prescriptions/schedule | The scheduled prescription settings |
PUT /api/prescriptions/schedule | Opt in or out, the interval and the number of agents |
| Parameter | Applies to | Description |
|---|---|---|
entityType | summary, series | Required: agent, model, kb, mcp or tool |
entityKey | series | Required, at most 200 characters |
period | summary, series, overview | last_day (default), last_7_days, last_30_days or total (the last 30 days) |
from, to | summary, series, overview | ISO dates. When given they win over period; to is never later than now and from defaults to 24 hours before to |
mode | overview | counts for the status counts only (an estimate, with no comparison, SLO or incident reads), or full (default) |
Invalid dates, a from that is not earlier than to, and a range outside the retained data answer 400, as on the agent health API. An unknown entityType or mode answers 400 as well.
Every health response says what could not be read, so a client can warn instead of showing an outage as healthy:
| Field | Meaning |
|---|---|
alertsAvailable | false when the alert rules or their states could not be read. Statuses then ignore alerts |
baselineFailed | The comparison with normal failed for some entities |
baselineAvailable | false when no comparison is available, so only alerts decide the status (summary) |
sloFailed | The service levels could not be read for some entities (summary and series) |
truncated | More entities exist than are listed (summary and overview) |
truncatedTypes, unavailableTypes | Overview: the kinds whose counts cover only the busiest entities, and the kinds that could not be read and are left out |
approximate | Overview with mode=counts: true, because that mode skips the comparison with normal |
Built boards are cached for about a minute per project and set of permissions, so a change can take that long to show.
Digest settings. PUT /api/entity-health/digests/config takes the whole configuration:
{
"enabled": true,
"frequency": "weekly",
"hourUtc": 7,
"weekday": 1,
"channels": [
{ "type": "email", "recipients": ["ops@example.com"] },
{ "type": "slack", "url": "https://hooks.slack.com/services/T000/B000/XXXX" }
],
"sections": { "summary": true, "regressions": true, "errors": true, "incidents": true, "cost": false, "slo": true },
"includeNarrative": true,
"narratorModelKey": "my-chat-model"
}| Field | Notes |
|---|---|
enabled | Boolean, false when omitted. true needs at least one channel |
frequency | Required: daily or weekly |
hourUtc | Required, a whole number from 0 to 23 |
weekday | 0 (Sunday) to 6, default 1. Used by weekly digests |
channels | Up to 10. email takes recipients (up to 50 addresses), webhook takes url and an optional secret, slack and teams take url. Each url is at most 2,048 characters and a secret at most 512 |
sections | Booleans for summary, regressions, errors, incidents, cost and slo. Omitted ones are on; at least one must be on |
includeNarrative | Boolean, false when omitted. true needs narratorModelKey |
narratorModelKey | The key of a chat model of the project |
Channels come back masked, as on alert rules: id, type and display hints, never a URL, secret or key. To keep a stored URL or secret when you save again, send the channel's id and leave the field out. A malformed body answers 400 with code invalid_body or invalid_channel.
The digest endpoints answer { "config": ... } or { "run": ... }, and a run carries id, trigger (schedule or manual), status, periodFromMs, periodToMs, createdAt, error, deliveries (one { type, target, success, error? } per channel or email recipient) and, on the detail read and on Send now, content.
PUT /api/prescriptions/schedule takes enabled (boolean), intervalDays (1 to 30) and maxAgents (1 to 10), all required. Both GET and PUT answer { "schedule": ... } with those fields and configured, minSessions, windowDays, lastRunAt and updatedAt.
Troubleshooting
| Symptom | Cause |
|---|---|
| Everything is Idle | No traffic was counted in the period. Pick a longer period. Sessions count when they reach a final status |
| An entity looks healthy but is slower than usual | It has fewer than 20 requests in the period, or not enough history yet. Only its alerts judge it |
| A notice says alert state could not be read | Statuses ignore alerts and alert links are hidden until the next successful read |
| A kind is missing from the selector | You have no read access to its service |
The digest cannot be saved (403) | A section covers data you cannot read. Turn it off or ask an administrator |
Send now answers 429 | A digest was sent less than 30 seconds ago |
| The Health page asks for an upgrade | The tenant has no active enterprise licence |
See also
- Agent Tracing: session status, the Agents list and the agent health API
- Monitoring: alert rules, baseline and SLO burn, incidents, channels and Prometheus metrics
- Alerts API and Incidents API
- Configuration: the rollup and alerting settings

