> ## Documentation index
> Fetch the complete documentation index at: https://developer.rhombus.com/llms.txt
> Use it to discover all available pages before exploring further.

# Reports & Analytics

> Pull count and time-series analytics from the Rhombus API — people and vehicle counts, occupancy trends, line-crossing (threshold) counts, and CSV export.

## Overview

The Rhombus Report Webservice turns detections from your cameras into aggregated, time-bucketed analytics. Instead of iterating over individual events, you request a count report for a time range and interval, and Rhombus returns one data point per bucket with a per-type count.

Through the API, you can:

- **Build count time-series** for people, vehicles, motion, faces, and other detection types across an org, location, or single device
- **Track people counts and occupancy** with the latest readings and bucketed occupancy trends
- **Measure line-crossing (threshold) counts** for humans or vehicles that cross a configured line
- **Export report data as CSV** for spreadsheets, BI tools, and compliance reporting

A few conventions apply across this API:

- **Timestamps are epoch milliseconds** on the V2, occupancy, and threshold endpoints (`startTimeMs` / `endTimeMs`). The legacy v1 `getCountReport` uses `"YYYY-MM-DD"` **date strings** instead.
- **Reports are described by three enums** — a report type (what to count), an interval (bucket size), and a scope (org, location, or device). See the [reference tables](https://developer.rhombus.com/implementations/reports-analytics#enum-reference) below.
- **There is no "list report types" endpoint.** The available types are fixed and documented in the [`ReportType` table](https://developer.rhombus.com/implementations/reports-analytics#reporttype).

## Prerequisites

> **Note**: Before you begin, make sure you have:
>
> - A **Rhombus API key** with report permissions (generated in the [Rhombus Console](https://console.rhombussystems.com/settings/integrations/api-tokens/) under **Settings → Integrations & Developer Resources → API Tokens**)
> - At least one **camera** deployed and reporting the analytics you want to query (for example, a camera running people counting or LPR)
> - The **UUID** of the org, location, or device you want to scope the report to
> - For **CSV export** and audit/diagnostic feeds: the **`REPORT_ADMINISTRATION`** permission on the API key

All requests are `POST`, send a JSON body, and use the base URL `https://api2.rhombussystems.com` with these headers:

```
x-auth-scheme: api-token
x-auth-apikey: YOUR_API_KEY
Content-Type: application/json
```

## Build a count time-series

`getCountReportV2` is the primary reporting endpoint. Supply a time range in epoch milliseconds, an interval, a scope, and one or more report types. Rhombus returns one data point per interval bucket, each carrying an `eventCountMap` keyed by report type.

This example pulls hourly **people** and **vehicle** counts for a single location over the last 24 hours.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
one_day_ago_ms = now_ms - (24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/report/getCountReportV2",
    headers=headers,
    json={
        "scope": "LOCATION",
        "uuid": "YOUR_LOCATION_UUID",
        "types": ["PEOPLE", "VEHICLES"],
        "interval": "HOURLY",
        "startTimeMs": one_day_ago_ms,
        "endTimeMs": now_ms,
        "timeZone": "America/Los_Angeles"
    }
)

data = response.json()
for point in data.get("timeSeriesDataPoints", []):
    counts = point.get("eventCountMap", {})
    print(f"{point.get('dateLocal')}: "
          f"people={counts.get('PEOPLE', 0)}, "
          f"vehicles={counts.get('VEHICLES', 0)}")
```

```javascript JavaScript
const now = Date.now();
const oneDayAgo = now - (24 * 60 * 60 * 1000);

const response = await fetch('https://api2.rhombussystems.com/api/report/getCountReportV2', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    scope: 'LOCATION',
    uuid: 'YOUR_LOCATION_UUID',
    types: ['PEOPLE', 'VEHICLES'],
    interval: 'HOURLY',
    startTimeMs: oneDayAgo,
    endTimeMs: now,
    timeZone: 'America/Los_Angeles'
  })
});

const data = await response.json();
for (const point of data.timeSeriesDataPoints || []) {
  const counts = point.eventCountMap || {};
  console.log(`${point.dateLocal}: people=${counts.PEOPLE || 0}, vehicles=${counts.VEHICLES || 0}`);
}
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/report/getCountReportV2 \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "scope": "LOCATION",
    "uuid": "YOUR_LOCATION_UUID",
    "types": ["PEOPLE", "VEHICLES"],
    "interval": "HOURLY",
    "startTimeMs": 1712620800000,
    "endTimeMs": 1712707200000,
    "timeZone": "America/Los_Angeles"
  }'
```

### Request fields

- **`scope` (body, string, required)**: Aggregation level. One of `ORG`, `LOCATION`, `DEVICE`, or `REGION`. See the [`ReportScope` table](https://developer.rhombus.com/implementations/reports-analytics#reportscope).
- **`uuid` (body, string)**: The location or device UUID that matches your `scope`. Omit for `scope: "ORG"` to report across the whole organization.
- **`types` (body, string\[], required)**: One or more report types to count. See the [`ReportType` table](https://developer.rhombus.com/implementations/reports-analytics#reporttype).
- **`interval` (body, string, required)**: Bucket size for each data point. One of `MINUTELY`, `QUARTERHOURLY`, `HOURLY`, `DAILY`, `WEEKLY`, `MONTHLY`. See the [`ReportInterval` table](https://developer.rhombus.com/implementations/reports-analytics#reportinterval).
- **`startTimeMs` (body, integer, required)**: Start of the range, in epoch milliseconds.
- **`endTimeMs` (body, integer, required)**: End of the range, in epoch milliseconds.
- **`timeZone` (body, string)**: Optional IANA time zone (for example, `America/New_York`). Controls where interval bucket boundaries fall and populates `dateLocal`. Defaults to UTC when omitted.

> **Warning**: The V2 endpoint accepts legacy `startDate` / `endDate` string fields, but these are deprecated. Use the epoch-millisecond `startTimeMs` / `endTimeMs` fields instead.

### Response fields

- **`timeSeriesDataPoints` (object\[])**: One entry per interval bucket.

  **TimeSeriesDataPointV2Type**

  - **`dateUtc` (string)**: Bucket start expressed in UTC.
  - **`dateLocal` (string)**: Bucket start expressed in the requested `timeZone`.
  - **`eventCountMap` (object)**: Map of report type to count for this bucket, for example `{"PEOPLE": 42, "VEHICLES": 7}`.
  - **`reportingDevicesMap` (object)**: Map of report type to the list of device UUIDs that contributed counts in this bucket.

> **Tip**: Need a single total instead of a bucketed series? Call [`/api/report/getSummaryCountReport`](https://developer.rhombus.com/api-reference/report-webservice/get-summary-count-report) with the same `startTimeMs`, `endTimeMs`, `interval`, `scope`, and a single `type`.

### Legacy date-string reports

The v1 [`/api/report/getCountReport`](https://developer.rhombus.com/api-reference/report-webservice/get-count-report) endpoint predates epoch-millisecond timestamps. It takes `startDate` and `endDate` as `"YYYY-MM-DD"` strings and a single `type` rather than a `types` array. Prefer `getCountReportV2` for new integrations; use v1 only if you already depend on its date-string behavior.

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/report/getCountReport \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "scope": "LOCATION",
    "uuid": "YOUR_LOCATION_UUID",
    "type": "PEOPLE",
    "interval": "DAILY",
    "startDate": "2024-01-01",
    "endDate": "2024-01-31"
  }'
```

## Build a people-count and occupancy dashboard

For an occupancy dashboard, use `getOccupancyCountsV2` for a bucketed occupancy trend over time, and chart `getCountReportV2` with `types: ["PEOPLE"]` alongside it for foot traffic (see [Build a count time-series](https://developer.rhombus.com/implementations/reports-analytics#build-a-count-time-series)).

### Occupancy trend over time

`getOccupancyCountsV2` returns bucketed occupancy for a device across a time range, using the same interval model as count reports.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
one_day_ago_ms = now_ms - (24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/report/getOccupancyCountsV2",
    headers=headers,
    json={
        "deviceUuid": "YOUR_CAMERA_UUID",
        "startTimeMs": one_day_ago_ms,
        "endTimeMs": now_ms,
        "interval": "HOURLY"
    }
)

data = response.json()
for point in data.get("timeSeriesDataPoints", []):
    print(f"{point.get('dateLocal')}: {point.get('eventCountMap')}")
```

```javascript JavaScript
const now = Date.now();
const oneDayAgo = now - (24 * 60 * 60 * 1000);

const response = await fetch('https://api2.rhombussystems.com/api/report/getOccupancyCountsV2', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    deviceUuid: 'YOUR_CAMERA_UUID',
    startTimeMs: oneDayAgo,
    endTimeMs: now,
    interval: 'HOURLY'
  })
});

const data = await response.json();
for (const point of data.timeSeriesDataPoints || []) {
  console.log(`${point.dateLocal}: ${JSON.stringify(point.eventCountMap)}`);
}
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/report/getOccupancyCountsV2 \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "deviceUuid": "YOUR_CAMERA_UUID",
    "startTimeMs": 1712620800000,
    "endTimeMs": 1712707200000,
    "interval": "HOURLY"
  }'
```

The response `timeSeriesDataPoints` carry the same `dateUtc`, `dateLocal`, `eventCountMap`, and `reportingDevicesMap` fields as count reports, plus a `timestampMs` (bucket start in epoch milliseconds) and an `approximateTimestampMsMap`.

> **Note**: This occupancy report is derived from camera analytics via the Report Webservice. It is distinct from the Occupancy Webservice (`/api/occupancy/*`), which reads physical BLE and motion sensors — a separate product.

## Count line crossings (threshold counts)

When a camera has a crossing line configured, Rhombus counts objects that cross it. `getThresholdCrossingCounts` returns those counts bucketed over time for one or more devices, filtered to a single object class.

The `dailyResetTimeMinute` field sets the minute of the day (0–1439, in the device's local time) at which the running daily count resets — useful for aligning counts to a business day rather than midnight.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
one_week_ago_ms = now_ms - (7 * 24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/report/getThresholdCrossingCounts",
    headers=headers,
    json={
        "devices": ["YOUR_CAMERA_UUID"],
        "startTimeMs": one_week_ago_ms,
        "endTimeMs": now_ms,
        "crossingObject": "HUMAN",
        "dailyResetTimeMinute": 0
    }
)

data = response.json()
for entry in data.get("counts", []):
    print(f"[{entry.get('timestampMs')}] crossings={entry.get('count')}")
```

```javascript JavaScript
const now = Date.now();
const oneWeekAgo = now - (7 * 24 * 60 * 60 * 1000);

const response = await fetch('https://api2.rhombussystems.com/api/report/getThresholdCrossingCounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    devices: ['YOUR_CAMERA_UUID'],
    startTimeMs: oneWeekAgo,
    endTimeMs: now,
    crossingObject: 'HUMAN',
    dailyResetTimeMinute: 0
  })
});

const data = await response.json();
for (const entry of data.counts || []) {
  console.log(`[${entry.timestampMs}] crossings=${entry.count}`);
}
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/report/getThresholdCrossingCounts \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "devices": ["YOUR_CAMERA_UUID"],
    "startTimeMs": 1712016000000,
    "endTimeMs": 1712620800000,
    "crossingObject": "HUMAN",
    "dailyResetTimeMinute": 0
  }'
```

### Request fields

- **`devices` (body, string\[])**: One or more camera UUIDs to include in the counts.
- **`startTimeMs` (body, integer)**: Start of the range, in epoch milliseconds.
- **`endTimeMs` (body, integer)**: End of the range, in epoch milliseconds.
- **`crossingObject` (body, string)**: The object class to count. Common values are `HUMAN` and `VEHICLE`. See the [`CrossingObject` table](https://developer.rhombus.com/implementations/reports-analytics#crossingobject).
- **`dailyResetTimeMinute` (body, integer)**: Minute of the day (0–1439) at which the daily count resets, in the device's local time.

### Response fields

- **`counts` (object\[])**: **ThresholdCrossingCountType**

  - **`count` (integer)**: Number of crossings in this bucket.
  - **`timestampMs` (integer)**: Bucket start in epoch milliseconds.

## Export report data as CSV

The Export Webservice streams report data as **CSV** (`text/csv`) rather than JSON. There is **no PDF export** — CSV is the only export format.

> **Warning**: Export endpoints (and the audit/diagnostic feeds) require the **`REPORT_ADMINISTRATION`** permission on your API key. Without it, these calls are rejected.

[`/api/export/countReports`](https://developer.rhombus.com/api-reference/export-webservice/download-count-report) mirrors the count report parameters but returns a CSV stream. Note that export takes a **single** `type` field, not a `types` array, and both `startTimeMs` and `endTimeMs` are required.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
thirty_days_ago_ms = now_ms - (30 * 24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/export/countReports",
    headers=headers,
    json={
        "scope": "LOCATION",
        "uuidList": ["YOUR_LOCATION_UUID"],
        "type": "PEOPLE",
        "interval": "DAILY",
        "startTimeMs": thirty_days_ago_ms,
        "endTimeMs": now_ms,
        "timeZone": "America/Los_Angeles"
    }
)

# Response is CSV text, not JSON
with open("people_count_report.csv", "w") as f:
    f.write(response.text)
print("Exported report to people_count_report.csv")
```

```javascript JavaScript
const now = Date.now();
const thirtyDaysAgo = now - (30 * 24 * 60 * 60 * 1000);

const response = await fetch('https://api2.rhombussystems.com/api/export/countReports', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    scope: 'LOCATION',
    uuidList: ['YOUR_LOCATION_UUID'],
    type: 'PEOPLE',
    interval: 'DAILY',
    startTimeMs: thirtyDaysAgo,
    endTimeMs: now,
    timeZone: 'America/Los_Angeles'
  })
});

// Response is CSV text, not JSON
const csv = await response.text();
console.log(csv);
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/export/countReports \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "scope": "LOCATION",
    "uuidList": ["YOUR_LOCATION_UUID"],
    "type": "PEOPLE",
    "interval": "DAILY",
    "startTimeMs": 1710028800000,
    "endTimeMs": 1712620800000,
    "timeZone": "America/Los_Angeles"
  }' \
  -o people_count_report.csv
```

> **Note**: Because the response body is CSV, read it as text (`response.text` / `.text()`) or write it straight to a file with the `-o` flag in cURL. Do not attempt to parse it as JSON.

To export raw people-count readings instead of aggregated reports, use [`/api/export/peopleCountEvents`](https://developer.rhombus.com/api-reference/export-webservice/download-people-count-report) with a `startInterval` and `endInterval` (epoch milliseconds). It returns CSV the same way.

## Enum reference

Reports are described by a small set of fixed enums. There is no endpoint that lists these at runtime — the values below are the complete set.

### ReportType

Values accepted by the `types` array (V2) and single `type` field (v1 and export).

| Value             | Counts                              |
| ----------------- | ----------------------------------- |
| `CROWD`           | Crowd-density detections            |
| `PEOPLE`          | People detections / people counting |
| `FACES`           | Face detections                     |
| `MOTION`          | Motion events                       |
| `BANDWIDTH`       | Device bandwidth usage              |
| `VEHICLES`        | Vehicle detections                  |
| `LICENSEPLATES`   | License-plate reads                 |
| `ALERTS`          | Alert / policy events               |
| `AM_VERIFICATION` | Alarm-monitoring verifications      |
| `DWELL`           | Dwell-time detections               |

### ReportInterval

Bucket size for each data point.

| Value           | Bucket         |
| --------------- | -------------- |
| `MINUTELY`      | Per minute     |
| `QUARTERHOURLY` | Per 15 minutes |
| `HOURLY`        | Per hour       |
| `DAILY`         | Per day        |
| `WEEKLY`        | Per week       |
| `MONTHLY`       | Per month      |

### ReportScope

Aggregation level for a count report. Provide a matching `uuid` for `LOCATION`, `DEVICE`, or `REGION`; omit it for `ORG`.

| Value      | Aggregates over         |
| ---------- | ----------------------- |
| `ORG`      | The entire organization |
| `LOCATION` | A single location       |
| `DEVICE`   | A single device         |
| `REGION`   | A configured region     |

### CrossingObject

Object class for `getThresholdCrossingCounts`.

| Value        | Object                   |
| ------------ | ------------------------ |
| `HUMAN`      | People                   |
| `VEHICLE`    | Vehicles                 |
| `FACE`       | Faces                    |
| `LPR`        | License plates           |
| `POSE`       | Pose detections          |
| `CLIP_EMBED` | Visual-embedding matches |
| `UNKNOWN`    | Unclassified             |

## Use cases

- **Foot-traffic dashboards** — chart hourly or daily `PEOPLE` counts per location with `getCountReportV2` to compare stores or track trends over time.
- **Occupancy monitoring** — chart `getOccupancyCountsV2` buckets to show occupancy through the day and compare it with earlier periods.
- **Entrance and lane counting** — use `getThresholdCrossingCounts` with `crossingObject: "HUMAN"` or `"VEHICLE"` to count people through a doorway or cars through a lane.
- **Scheduled reporting** — run [`/api/export/countReports`](https://developer.rhombus.com/api-reference/export-webservice/download-count-report) on a cron and drop the CSV into a BI tool or data warehouse for weekly and monthly reporting.

## Advanced: AI Scene Query analytics

Rhombus also offers **Scene Query**, an AI feature that counts custom, natural-language-defined events (for example, "forklifts without a spotter"). Scene Query is configured through the `/api/scenequery/*` endpoints and reported via [`/api/report/getCustomEventsReport`](https://developer.rhombus.com/api-reference/report-webservice/get-custom-events-report). It is a distinct, licensed capability rather than part of the core reporting workflow above — treat it as an optional extension when standard report types don't capture what you need to measure.

## Troubleshooting

**Empty timeSeriesDataPoints or all-zero counts**

Confirm that the device actually produces the analytic you requested — a camera without people counting enabled returns no `PEOPLE` data. Also verify your time range is in **epoch milliseconds** (not seconds) and that the `scope`/`uuid` pair is correct.

**Buckets are offset from local time**

Set the `timeZone` field to the relevant IANA zone (for example, `America/Chicago`). Without it, bucket boundaries and `dateLocal` fall on UTC.

**Export request is rejected**

CSV export requires the `REPORT_ADMINISTRATION` permission. Check the API key's permissions in the Rhombus Console, and remember the export body uses a single `type` field rather than the `types` array used by `getCountReportV2`.

**Export response looks like garbage JSON**

The export endpoints return `text/csv`, not JSON. Read the response as text or write it to a file rather than calling `.json()`.

## Next steps

- **[Faces & People Counting](https://developer.rhombus.com/implementations/faces-people-counting)**: Enroll people, query face events, and dig deeper into people-counting analytics
- **[LPR & Vehicles](https://developer.rhombus.com/implementations/lpr-vehicle)**: Query license-plate reads and vehicle detections that feed the vehicle report types
- **[API Reference](https://developer.rhombus.com/api-reference/report-webservice/get-count-report-v2)**: Explore complete request and response schemas for every report and export endpoint
