Skip to content

Reports & Analytics

Breeze provides a full reporting and analytics subsystem for generating on-demand or scheduled fleet reports and querying real-time analytics data. Reports are scoped to organizations (the business reports can also run partner-wide) and enforce multi-tenant access at every layer – organization-scoped users see only their own data, partner-scoped users can access reports for any organization they manage, and system-scoped users have unrestricted access.

The system is split into two complementary route groups:

  • /reports – Saved report definitions, ad-hoc generation, run history, and raw data endpoints for device inventory, software, alerts, compliance, and performance metrics.
  • /analytics – Time-series queries, custom dashboards with widgets, capacity planning, SLA tracking, executive summaries, and OS distribution breakdowns.

Every report has a type that determines which data is queried and how the output is structured.

Type Description Key columns / metrics
device_inventory Full hardware and software asset listing per device hostname, OS, agent version, CPU model, RAM, disk, serial number
software_inventory All installed software across targeted devices software name, version, publisher, install date, device hostname
alert_summary Alert history with severity breakdown title, severity, status, triggered/acknowledged/resolved timestamps, rule name
compliance Device health and compliance posture hostname, OS, status, last seen, compliance flag, identified issues
performance Aggregated CPU, RAM, and disk metrics per device avg/max CPU %, avg/max RAM %, avg/max disk %
executive_summary High-level fleet overview with device counts, alert stats, OS distribution, and site breakdown (see Executive Summary Report) online/offline totals, health %, critical/high alert counts, resolution rate
security_compliance_posture Per-control security and compliance evidence for one organization, built for cyber-insurance applications and vendor security questionnaires EDR/AV coverage, disk encryption %, firewall %, patch currency %, password policy, local-admin exposure, MFA/PAM posture, backup, DNS filtering, optional CIS pass-rate (see Security & Compliance Posture Report)
identity_access_review A month of Microsoft 365 sign-in and identity activity: identity inventory, conditional-access policy posture, dormant accounts, admin sign-ins, MFA registration status, and which remote-access client (if any) was running on each device interactive sign-in counts, conditional-access coverage, dormant-account list, admin sign-in detail, MFA registration rate
vulnerability_management Findings-level vulnerability detail: open vulnerabilities, accepted/mitigated exceptions, remediation ranking, and vulnerabilities that are actively exploited in the wild or have a high exploit probability open/accepted/mitigated counts, remediation ranking, known-exploited and high-probability findings
endpoint_management_review Device enrollment and compliance inventory pulled from Microsoft Intune, plus license-seat usage and a compliance-trend chart enrollment/compliance counts, license seat usage, daily compliance trend
threat_detection_review A record of threat detections for the period, with resolution status and the vendor’s recommendation text detection count, resolution status, recommendation text
ticket_sla_attainment Response and resolution SLA attainment for a period, recalculated from each ticket’s own timestamps rather than read off the stored breach flags, grouped by organization, priority, technician or category (see Business Reports) response/resolution attainment %, missed targets, tickets with no SLA target
technician_time_billability Logged time against an assumed weekly capacity, the billable / included / non-billable split, and how much billable time has been approved and billed — billable value reported per currency (see Business Reports) utilization %, billable %, billing conversion %, logged/billable/non-billable minutes, billable value per currency
ar_aging Unpaid invoice balances bucketed current / 1–30 / 31–60 / 61–90 / 90+ days past due, one set of totals per currency (see Business Reports) outstanding balance per bucket, open invoice count, no-due-date balance, other open balance
ai_usage_by_client AI requests, tokens, Breeze cost and the amount billable to each client for a period, split into billed, unbilled and unpriced (see AI usage chargeback) requests, tokens, Breeze cost, billable amount, billed and unbilled amount, unpriced usage

When creating a saved report, you provide a config object that controls date ranges, filters, column selection, and sorting.

The config.dateRange object supports preset shortcuts or custom boundaries:

Preset Meaning
last_7_days Data from the past 7 days
last_30_days Data from the past 30 days
last_90_days Data from the past 90 days
custom Use the explicit start and end ISO date strings

The config.filters object narrows the data:

Filter Applies to Accepts
siteIds device_inventory, compliance Array of site UUIDs
deviceIds software_inventory Array of device UUIDs
osTypes device_inventory windows, macos, linux
status alert_summary Array of status strings
severity alert_summary Array of severity strings
Format Description
csv Comma-separated values (default)
pdf Formatted PDF document
excel Excel spreadsheet (.xlsx)

For recurring reports, the Schedule and delivery panel in the report builder (also available as config.schedule and config.emailRecipients via the API) controls exactly when the report runs and who gets emailed the result:

Field Builder UI label Applies to Format
schedule.time Run time All recurring cadences 24-hour HH:MM, e.g. 09:00
schedule.day Day of week weekly Weekday name, e.g. monday
schedule.date Day of month monthly Day of month, 1-31
emailRecipients Email distribution list Any recurring cadence Array of up to 50 email addresses

See Scheduling below for how these are evaluated and what recipients receive.


Breeze supports two generation flows: ad-hoc (stateless) and saved (tracked with run history).

Use POST /reports/generate to produce report data immediately without creating a saved report definition. The response contains the generated data inline.

Terminal window
curl -X POST /api/v1/reports/generate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "device_inventory",
"format": "csv",
"orgId": "ORG_UUID",
"config": {
"dateRange": { "preset": "last_30_days" },
"filters": {
"osTypes": ["windows", "linux"]
}
}
}'

The response includes:

{
"type": "device_inventory",
"format": "csv",
"generatedAt": "2026-02-18T12:00:00.000Z",
"data": {
"rows": [...],
"rowCount": 42
}
}

For saved reports, trigger generation with POST /reports/:id/generate. This creates a report run, computes the data, and stores the result as a point-in-time snapshot – all in the same request, so the response comes back already completed (or failed). The snapshot is what you download later, so the file always reflects the data as of when the run executed rather than re-querying live.

  1. Create a saved report definition with POST /reports (see the API reference below).
  2. Trigger generation with POST /reports/:id/generate. The response returns the new runId with status: "completed" once the snapshot is stored.
  3. Download the result with GET /reports/runs/:runId/download?format=csv (or pdf / excel).
Terminal window
# Trigger generation for a saved report
curl -X POST /api/v1/reports/REPORT_UUID/generate \
-H "Authorization: Bearer $TOKEN"

Response:

{
"message": "Report generation started",
"runId": "a1b2c3d4-...",
"status": "pending"
}

Saved reports support recurring schedules. When you set the schedule field, the report will be generated automatically on the configured cadence.

Schedule Behavior
one_time No recurring generation (default). Must be triggered manually.
daily Generated once per day
weekly Generated once per week
monthly Generated once per month

Set the schedule when creating or updating a report:

Terminal window
curl -X POST /api/v1/reports \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Weekly Compliance Report",
"type": "compliance",
"orgId": "ORG_UUID",
"schedule": "weekly",
"format": "pdf",
"config": {
"filters": { "siteIds": ["SITE_UUID"] }
}
}'

Scheduled runs fire in the organization’s local time zone (falling back to the partner’s time zone, then UTC if neither is configured), pinned to the schedule.time / schedule.day / schedule.date values from config.schedule – see Schedule Details and Email Recipients above.

Add addresses to the Email distribution list (or config.emailRecipients) to have each scheduled run emailed automatically, in addition to appearing in the reports list:

  • PDF-format reports attach the same branded PDF you’d download from the run. If it’s larger than 5 MB or fails to render, the email falls back to a link into Breeze instead of an attachment.
  • CSV and Excel-format reports attach the data as a CSV file.
  • The email leads with a one-line trend summary when a prior run exists to compare against, for example “Posture score 79 – up from 74 last run.”

The reports list also shows the next scheduled run time (e.g. “Next: Mon, Jul 6, 9:00 AM”) and a recipient count badge on recurring reports.

You can also attach one of the report types above to a service deliverable as its evidence source. Set this up once, partner-wide, on a deliverable template item, and every organization the template applies to gets a period-correct evidence artifact generated automatically – no technician has to remember to run and attach a report each period. Each generated artifact is reviewed before it’s shown to the customer, and only becomes visible in the portal once its deliverable occurrence is marked Delivered.


Each time a saved report is generated, a report run record tracks the process through a status lifecycle.

Status Meaning
pending The run has been created and is queued for processing
running The report is actively being generated
completed Generation succeeded; outputUrl contains the download path
failed Generation failed; errorMessage contains the reason

The run record stores:

Field Description
id Unique run identifier (UUID)
reportId The parent saved report definition
status Current lifecycle status
startedAt Timestamp when processing began
completedAt Timestamp when processing finished (success or failure)
outputUrl Download path for the generated file (set on completed)
errorMessage Error description (set on failed)
rowCount Number of data rows in the output

The Security & Compliance Posture report (security_compliance_posture) packages your security evidence for one organization into a single document – purpose-built for filling out cyber-insurance applications and answering vendor security questionnaires. It joins the security data Breeze already collects into per-control answers with percent-implemented rollups: EDR/MDR and native antivirus coverage, the list of unprotected devices, disk-encryption and host-firewall coverage, patch currency, antivirus-definition freshness, password-complexity posture, local-admin exposure, MFA and privileged-access posture, backup coverage, DNS filtering, identity-provider connection, and an optional CIS hardening pass-rate.

It is scoped to a single organization (with an optional site filter) and is honest about gaps: a control that hasn’t been assessed is reported as N/A, never as a misleading 0% or a falsely favorable score. The same principle applies to stale readings: a device whose firewall/encryption status hasn’t been reported within the freshness window (default 30 days, configurable 1–365 via the report’s security-status age setting) is counted as unknown for those controls — its cells show “no data” instead of a months-old pass/fail being scored as current. The summary reports separate firewall-unknown and encryption-unknown device counts, mirroring how unmeasured patch data is handled. The PDF renders as a branded scorecard (overall score out of 100 with a STRONG / GOOD / FAIR / AT RISK band and a per-control grid) and carries point-in-time, not-a-certification disclaimers.

The Security products in use inventory follows the same principle. A product is listed with the number of devices it is installed on, and – where Breeze has per-device antivirus evidence – how many of those devices actually have real-time protection switched on. A product installed on 200 devices with real-time protection active on only one reads as 200 devices, 1 with real-time protection on, so the inventory can no longer imply full coverage that the report’s own metric grid contradicts. The extra count is shown only when the protected subset is smaller than the install count; products whose evidence comes from an integration rather than per-device status (DNS filtering, backup, Microsoft 365) show the install count alone.

The PDF also includes a Recommended actions section generated from whichever controls are currently failing, and a plain-language glossary so a non-technical reader – an insurance underwriter, a customer’s compliance contact – can follow it without help. When a prior run exists to compare against, the scorecard shows how the score has moved since then (for example “+5 since Jun 1”). The cover carries your logo and name if partner branding is configured; otherwise it uses the standard Breeze mark.


The Executive Summary report (executive_summary) is built for handing to a customer at a quarterly business review. Its PDF leads with a designed cover: a fleet-health scorecard, device and alert metrics, OS and site composition, and a Recommended actions section – the same trend-aware, branded cover treatment as the Security & Compliance Posture report, including the partner logo when branding is configured and a score-change chip when a prior run exists to compare against.


Three report types cover the PSA side of the business — service performance, technician time, and money owed. Unlike the report types above, they are internal: they are for MSP staff only (organization-scope users, and API or MCP keys, never see them, even when they hold the underlying permission), they never appear in a customer’s portal, and generating one requires the type’s own permission on top of reports:read/reports:write – tickets:read for ticket SLA attainment, time_entries:read and tickets:read for technician time & billability, and invoices:read for AR aging.

Ownership: one organization, or all of them

Section titled “Ownership: one organization, or all of them”

A business report can be owned by a single organization or by the partner. A partner-owned report covers every organization the MSP manages, resolved live at each run against the partner’s active and trial organizations – suspended, archived and deleted organizations (and the internal quick-support organization) are excluded, and are listed in the report’s own notes when they are. Ownership is chosen when the report is created and cannot be changed afterwards; create a second report if you need both views. In the reports list, a partner-owned report carries a Partner-wide badge and appears both in the All organizations view and when an organization is selected (for users with access to all organizations).

Creating or running a partner-owned report requires a partner-scope login with access to all organizations. A user restricted to selected organizations is refused: aggregating customers they cannot open individually would be an escalation, not a convenience. Business reports – org-owned or partner-owned – deliver to email addresses entered on the report itself; they cannot be delivered to a customer contact.

Ticket SLA attainment (ticket_sla_attainment)

Section titled “Ticket SLA attainment (ticket_sla_attainment)”

Measures the share of support tickets created in the period that met their response and resolution targets. Attainment is recalculated from each ticket’s own timestamps and recorded paused minutes against its own targets, rather than read off the stored breach flags. Only support work is measured – planned work is excluded. Tickets with no target set are never counted as met or missed; they are reported as their own figure.

Grouping is by organization, priority, technician or category; left unset, it defaults to organization for a partner-owned report and priority for an org-owned one. Two limits are disclosed on the report itself:

  • Paused time is a lifetime total – a ticket paused after its first reply has that pause counted against the response clock too, which slightly flatters response attainment.
  • The technician axis is the ticket’s current assignee – reassignment history isn’t tracked.

Technician time & billability (technician_time_billability)

Section titled “Technician time & billability (technician_time_billability)”

Measures logged time against capacity, and how much of it converts to revenue.

  • Utilization is logged minutes ÷ capacity minutes. Capacity is a report setting – an assumed weekly-hours figure (1–80, default 40) – prorated over the working days in the period. This assumes uniform capacity: PTO and part-time schedules aren’t modeled, so an individual technician’s figure can mislead even when the team figure is sound. At organization scope the denominator is still each technician’s whole capacity, so the figure is a share of their capacity spent on that organization, not their overall utilization.
  • Billable share is billable minutes ÷ logged minutes, split three ways – billable, included in plan, and non-billable.
  • Billing conversion is approved-and-billed minutes ÷ billable minutes. It is deliberately not called “realization” – there is no fee-schedule baseline to compute financial realization against.

Technicians with no time in the period are listed at 0% rather than omitted – dropping them would inflate the team average. Billable value is reported once per currency, never summed across currencies; billable minutes with no hourly rate or currency are called out and excluded from that total. An organization-scoped report covers ticket-linked time only – time logged with no organization has no organization to be scoped to.

Buckets unpaid invoice balances by how far past due they are: current, 1–30, 31–60, 61–90 and 90+ days, plus two reconciliation buckets:

  • No due date – an open balance on an invoice with no due date is aged into its own bucket, never silently into “current”.
  • Other open balance – an invoice carrying a balance in an unexpected status appears here rather than vanishing, so buckets plus this figure always reconcile to total AR.

Aging is measured as of the moment the report runs, in the report owner’s resolved timezone, unless an explicit as-of date is set. The as-of date moves only the aging reference date – it does not reconstruct historical balances or undo payments recorded after it. An optional note can also list invoices that were fully paid month-to-date as of that date; it’s informational and never moves a bucket. Totals are reported once per currency, with no conversion – there is deliberately no combined figure, because an FX-converted headline would be wrong the day after it was produced.

Every total is computed in SQL over all matching rows. The stored detail rows – the per-ticket, per-technician or per-invoice lines behind the totals – are capped at 5,000 per run; a truncated detail list never implies a truncated total. A downloaded PDF shows up to 500 detail rows; CSV and Excel downloads of a stored run carry the full (capped) set as raw numeric values – balances and rates as plain decimals, durations as minutes, percentages as fractions – each money row tagged with a currency-code column, so a spreadsheet can total them directly. A ratio with nothing to measure (for example, no tickets with a response target) prints N/A, never 0%. A user whose access is limited to specific sites gets an explanatory note instead of figures, because tickets, time entries, and invoices are not tied to sites.

The web options forms set the same keys. Business reports select their data by period and owner (one organization, or partner-wide) – never by the date range, site, device, or organization filters the other report types use. Sending any of those filters returns 400.

Option Applies to Values Default
period.kind SLA attainment, time & billability last_full_month, last_30_days, last_quarter, custom (with start and end as YYYY-MM-DD; end is inclusive) last_full_month
groupBy SLA attainment organization, priority, technician, category organization partner-wide, priority for one organization
includeNoSla SLA attainment Include tickets with no SLA target in the detail rows true
groupBy Time & billability technician, organization, work_type technician
weeklyCapacityHours Time & billability 1-80 40
asOf AR aging YYYY-MM-DD Today
groupBy AR aging organization, currency organization
includePaidInPeriod AR aging Add an informational note on invoices fully paid month-to-date (never moves a bucket) false

Periods and “today” are resolved in the report owner’s time zone – the organization’s, then the partner’s, then UTC. If the configured time zone isn’t recognized, the report runs in UTC and says so.

Creating a partner-wide report through the API

Section titled “Creating a partner-wide report through the API”

Pass "ownerScope": "partner" and omit orgId. The report belongs to your partner, not to any one organization; ownerScope is set at creation and can’t be changed later.

Terminal window
curl -X POST /api/v1/reports \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Monthly SLA attainment (all customers)",
"type": "ticket_sla_attainment",
"ownerScope": "partner",
"schedule": "monthly",
"format": "pdf",
"config": {
"period": { "kind": "last_full_month" },
"groupBy": "organization",
"schedule": { "time": "08:00", "date": 1 },
"emailRecipients": ["ops@your-msp.example.com"]
}
}'

For a one-off, POST /reports/generate accepts the same ownerScope, type, and config and returns the data inline. To run a report for a single customer instead, leave ownerScope out and pass orgId as you would for any other report type.

Scheduled business reports are delivered only to the email distribution list (config.emailRecipients); they can’t be sent to organization contacts.


A multi-org report is one definition that produces a separate copy for every organization it covers. Each copy is sent to that organization’s own contacts. It is not a combined report: combined, cross-organization reports are the partner-owned reports described under Business Reports.

Only partner users with access to every organization can create or change one.

On New Report (or a Hardware Lifecycle / Security & Compliance Posture template), set Covers to One report per organization, then choose which organizations it covers:

  • All organizations (default): every current and future organization is included automatically. Untick any organization to exclude it. A new organization starts receiving the report within about five minutes of being created.
  • Chosen organizations: exactly the organizations you tick.

Only active and trial organizations receive a copy. A suspended or offboarding organization’s copy is archived, and it comes back automatically if the organization is reactivated.

Site, device and group filters are unavailable here, because each organization’s copy covers the whole organization.

A multi-org report always runs on a schedule (daily, weekly or monthly); it can’t be one-time. When you start from a one-time template, choose how often it is sent before creating it.

  • The organization’s primary contact and/or contacts with these roles. This rule is worked out separately in every organization.
  • Internal copies: fixed addresses on your team, sent every organization’s copy.
  • The form previews the result, for example “Resolves to 23 contacts across 17 organizations · 1 organization has no customer recipient: Acme Dental”. An organization with no customer recipient is always flagged, never silently skipped.

To change who receives one organization’s copy, open that copy (Edit recipients). For each contact, choose Follow rule, Always send or Never send.

On the All organizations view, each multi-org report is one row. Its last-run column reads, for example, Oct 1 · 17/18 delivered · 1 no recipient. Expand the row to see one line per organization, with these actions:

  • Run now generates that organization’s copy immediately.
  • Edit recipients opens that organization’s copy.
  • Exclude / Include stops or restarts that organization’s copy. An excluded copy is archived with its run history, and including it again restores the same copy.
  • Detach turns that organization’s copy into a standalone report you can edit freely. It keeps its run history and stops following the multi-org report. While that standalone copy exists, the organization stays out of the multi-org report, even if you include it again, so it never receives two copies.

Pause (on the row, or at the top of the expanded list) stops every organization’s copy from running until you Resume it. A paused report is marked Paused in the list.

The All · Multi-org · Single-org · Combined chips filter the list. With one organization selected in the switcher, its copy appears as an ordinary row with a Multi-org badge that links to the multi-org report.

An organization’s copy is locked: only its recipients can be changed there. Organization users see it marked Managed by your MSP.

Scheduled copies run with the owner’s access (the creator, by default). If the owner loses access to an organization, that organization’s copy shows Blocked: owner has no access and does not run. It is never run with anyone else’s access. Use Transfer owner on the row to hand the report to another partner user with access to every organization.

When a report run reaches the completed status, download its stored snapshot from GET /reports/runs/:id/download. Pass the desired format as a query parameter – ?format=csv, ?format=pdf, or ?format=excel. Because the run captured the data at generation time, the download is stable: re-downloading the same run always yields the same data. Downloading a run that hasn’t completed returns 409 Conflict.

Terminal window
# Download a completed run as CSV
curl "/api/v1/reports/runs/RUN_UUID/download?format=csv" \
-H "Authorization: Bearer $TOKEN" -OJ

CSV and Excel files are produced on the server; PDF downloads are rendered in the browser from the same snapshot (scheduled-email PDF attachments are rendered server-side instead, using the same branded layout, so the two always match). When retrieving a single report via GET /reports/:id, the response includes the five most recent runs, making it easy to find the latest completed download.


When a customer organization is suspended, churned, offboarding or archived, its report history stays readable to the MSP: a partner user can still open the organization’s saved report definitions and its run history (status, dates, row counts, errors) – on the organization’s record page under Report history, or through the API with an explicit organization:

  • GET /reports?orgId=ORG_UUID and GET /reports/templates?orgId=ORG_UUID
  • GET /reports/:id
  • GET /reports/runs?orgId=ORG_UUID and GET /reports/runs/:id

It is read-only. New runs, schedules, edits, recipient lists, downloads (/reports/runs/:id/download) and the raw data endpoints (/reports/data/*) stay refused for an inactive organization, and its devices, scripts and other records stay unavailable. The unfiltered lists (GET /reports, GET /reports/runs without orgId) keep showing active and trial organizations only.

Who can read it: an active user of an active partner whose role grants reports:read. A user limited to selected organizations sees only the inactive organizations on their list; a user with no organization access sees none. A deleted organization’s history is never shown.

The /analytics route group provides real-time operational intelligence beyond static reports.

POST /analytics/query executes a flexible time-series query across device metrics.

Terminal window
curl -X POST /api/v1/analytics/query \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"deviceIds": ["DEVICE_UUID_1", "DEVICE_UUID_2"],
"metricTypes": ["cpu", "ram"],
"startTime": "2026-02-01T00:00:00Z",
"endTime": "2026-02-18T00:00:00Z",
"aggregation": "avg",
"interval": "hour"
}'

Supported aggregations: avg, min, max, sum, count, p95, p99.

Supported intervals: minute, hour, day, week, month.

An optional groupBy array lets you split the series by additional dimensions.

Dashboards are per-organization containers for widgets. Each widget has a type and a config object that determines what data it renders.

Terminal window
curl -X POST /api/v1/analytics/dashboards \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orgId": "ORG_UUID",
"name": "NOC Overview",
"description": "Primary network operations dashboard",
"layout": {}
}'

GET /analytics/executive-summary returns a high-level organizational overview including device totals (online/offline), a 12-week enrollment trend, and configurable period types.

Query parameter Values Default
periodType daily, weekly, monthly monthly
range Freeform string –
startDate ISO date string –
endDate ISO date string –

GET /analytics/os-distribution returns the count of active devices grouped by osType and osVersion, excluding decommissioned devices.

GET /analytics/capacity returns capacity predictions based on current metric trends. Accepts optional deviceId and metricType query parameters to narrow scope.

SLA definitions set uptime or performance targets that are evaluated over a window.

Field Description
name Human-readable SLA name
targetPercentage Target compliance percentage (0–100)
evaluationWindow daily, weekly, or monthly
scope device, site, or organization
filters Additional scoping filters (JSON)

Compliance history for an SLA is retrieved via GET /analytics/sla/:id/compliance, which returns period-by-period entries with a status of met, breached, or warning.


The /reports/data/* endpoints provide raw, paginated data used to power report views and dashboards. These are read-only query endpoints that accept common filters.

Endpoint Returns
GET /reports/data/device-inventory Device list with hardware details (CPU, RAM, disk, serial number, manufacturer, model)
GET /reports/data/software-inventory Installed software list with per-title device counts in a summary array
GET /reports/data/alerts-summary Alert statistics: counts by severity, by status, daily trend (last 30 days), and top 10 alerting rules
GET /reports/data/compliance Compliance overview with device status breakdown, OS distribution, agent version spread, and identified issues (stale devices, outdated agents)
GET /reports/data/metrics Performance metrics: fleet-wide averages for CPU/RAM/disk plus the top 10 consumers for each metric

All data endpoints accept these common query parameters:

Parameter Description
orgId Filter to a specific organization (required for partner scope)
siteId Filter to a specific site
startDate Start of date range (ISO string)
endDate End of date range (ISO string)
limit Page size (default 100, max 1000)
offset Pagination offset

Method Endpoint Description
GET /reports List saved reports. Filter by type, schedule, orgId, and ownerScope (partner = only reports owned partner-wide across all organizations, organization = only single-organization reports). ownerScope only narrows the list: a caller who cannot manage partner-wide reports (organization logins, partner users with selected-organization access) gets an empty list for ownerScope=partner. Paginated.
POST /reports Create a saved report definition with name, type, config, schedule, and format. Pass ownerScope: "partner" (and no orgId) for a partner-wide business report.
GET /reports/:id Get a single report with its 5 most recent runs.
PUT /reports/:id Update report name, config, schedule, or format.
DELETE /reports/:id Delete a report and all associated runs.
Method Endpoint Description
POST /reports/generate Ad-hoc generation. Returns data inline. Accepts ownerScope: "partner" for business report types.
POST /reports/:id/generate Trigger async generation for a saved report. Returns a runId.
Method Endpoint Description
GET /reports/runs List runs. Filter by orgId (one organization), reportId, status. Paginated.
GET /reports/runs/:id Get a single run with parent report metadata and download URL.
GET /reports/runs/:id/download Download the run’s stored snapshot. Requires ?format=csv|pdf|excel. Returns 409 if the run is not completed.
Method Endpoint Description
GET /reports/data/device-inventory Raw device inventory with hardware join.
GET /reports/data/software-inventory Raw software inventory with per-title summary.
GET /reports/data/alerts-summary Alert breakdown by severity, status, daily trend, top rules.
GET /reports/data/compliance Compliance overview, stale device detection, agent version audit.
GET /reports/data/metrics Fleet performance averages and top-10 resource consumers.
Method Endpoint Description
POST /analytics/query Execute a time-series metrics query with aggregation and interval.
GET /analytics/dashboards List dashboards. Filter by orgId. Paginated.
POST /analytics/dashboards Create a dashboard.
GET /analytics/dashboards/:id Get dashboard with all widgets.
PATCH /analytics/dashboards/:id Update dashboard name, description, or layout.
DELETE /analytics/dashboards/:id Delete dashboard and all its widgets.
POST /analytics/dashboards/:id/widgets Add a widget to a dashboard.
PATCH /analytics/widgets/:id Update widget name, type, config, or layout.
DELETE /analytics/widgets/:id Remove a widget from its dashboard.
GET /analytics/capacity Capacity planning predictions.
GET /analytics/sla List SLA definitions. Filter by orgId. Paginated.
POST /analytics/sla Create an SLA definition.
GET /analytics/sla/:id/compliance Get compliance history for an SLA.
GET /analytics/executive-summary Executive fleet summary with enrollment trends.
GET /analytics/os-distribution OS type/version distribution for active devices.
GET /analytics/anomalies/evaluation Anomaly detection quality counts and rates for a range of 7d, 30d (default), or 90d. Filter by orgId or deviceId. See AI Insights.

POST /reports/:id/generate computes the report and stores its snapshot in the same request, so a run normally comes back completed immediately. If it comes back failed, inspect the run’s errorMessage field (via GET /reports/runs/:id) for the cause – most often a configuration problem such as an invalid date range or a scope the caller can’t access.

“Organization context required” (403) on report endpoints

Section titled ““Organization context required” (403) on report endpoints”

This error occurs when an organization-scoped token is used but the token lacks an orgId claim. Verify that the JWT includes the orgId field. Partner-scoped users must pass orgId explicitly as a query parameter or in the request body.

Data endpoint returns empty results despite existing devices

Section titled “Data endpoint returns empty results despite existing devices”

The /reports/data/* endpoints join against related tables (deviceHardware, deviceSoftware, deviceMetrics). If devices exist but related data has not been collected yet (e.g., hardware inventory has not been reported by the agent), joins may exclude those devices or return null fields. Check that agents are running and have submitted at least one heartbeat with hardware and software data.

The compliance report flags a device as non-compliant if its status is decommissioned or if it has not been seen in the last 7 days. Devices that are powered off or in maintenance for extended periods will lower the compliance score. The compliance data endpoint also identifies stale_devices (not seen in 7+ days) and outdated_agents (running a version other than the most common version) as separate issues. Review the issues array in the response to understand which factors are contributing to the score.