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

# Reporting API

> Retrieve, manage, export, and share reports and dashboards in HubSpot.

export const BetaDisclaimerBanner = () => <Warning>
        This functionality is currently in beta. By participating in this beta, you agree to HubSpot's <a href="https://legal.hubspot.com/developer-terms">Developer Terms</a> and <a href="https://legal.hubspot.com/developerbetaterms">Developer Beta Terms</a>. Note that the functionality is still under active development and is subject to change based on testing and feedback.
    </Warning>;

export const ScopesList = ({scopes = [], description = "This API requires one of the following scopes:"}) => {
  if (!scopes || scopes.length === 0) {
    return null;
  }
  const sortedScopes = scopes.sort((a, b) => a.localeCompare(b));
  return <div>
      <div className="text-sm mb-2">{description}</div>
      <div>
        {sortedScopes.map((scope, index) => <div key={index}>
            <code>
              <span className="text-xs">{scope}</span>
            </code>
          </div>)}
      </div>
    </div>;
};

<Accordion title="Scope requirements">
  <ScopesList
    scopes={[
'reporting.full.read',
'reporting.full.edit',
'reporting.full.write',
'reporting.full.admin'
]}
  />
</Accordion>

<Warning>
  This functionality is currently in public beta. You can opt into this beta via the associated [product update](https://app.hubspot.com/l/product-updates/?rollout=327896) in your HubSpot account.

  By participating in this beta, you agree to HubSpot's <a href="https://legal.hubspot.com/developer-terms">Developer Terms</a> and <a href="https://legal.hubspot.com/developerbetaterms">Developer Beta Terms</a>. Note that the functionality is still under active development and is subject to change based on testing and feedback.
</Warning>

Use the reporting API to manage the reports and dashboards in your account. For example, batch-update the owner of a set of dashboards, share a report with your team by email, or clone an existing dashboard for a new business unit.

Learn more about using [reports](https://knowledge.hubspot.com/reports/create-custom-reports) and [dashboards](https://knowledge.hubspot.com/dashboards/manage-your-dashboards) in HubSpot.

## What's supported in this beta

This beta supports managing the *metadata* of reports and dashboards, including reading, searching, updating, restoring, archiving, exporting, and managing ownership, permissions, and dashboard widgets.

It does **not** yet support a report's *configuration* — the underlying query and visualization that define what a report shows. As a result, you cannot currently:

* Create a report (a report can't be created without its configuration).
* Read or update a report's configuration.

Reading and updating report *metadata* (such as its name, description, owner, and permissions) is fully supported.

The reporting team is actively working toward a unified, publicly consumable way to declare a report's configuration. This is a high priority, and support for report creation and configuration is expected to be added incrementally over the course of this beta.

## Scope requirements

This API uses the following scopes:

* `reporting.full.read` permits all read operations, including retrieving, searching, and exporting reports and dashboards.
* `reporting.full.edit` permits update operations, including updating report and dashboard metadata and adding reports to or removing reports from dashboards. It does not permit creating or deleting objects.
* `reporting.full.write` permits all write operations, including creating, archiving, restoring, and batch operations. It is a superset of `reporting.full.edit`.
* `reporting.full.admin` permits all operations and additionally grants administrative access. For apps with user-level access, it lets users who are reporting or super admins exercise their full administrative access. For example, this gives them access to all reporting objects, including ones that would otherwise be private. It isn't relevant for apps with account-level access, since those already act as administrators within the bounds of their authorized scopes.

A request needs only one scope that covers the operation being performed.

<Warning>
  **Please note:**

  * Updating an object requires at least `reporting.full.edit`. However, from an app with user-level access, if the requesting user isn't the object's owner, updating the `ownerUserId`, `permissions`, or `businessUnitId` requires `reporting.full.write` or `reporting.full.admin` instead.
  * Export endpoints are available to apps with **user-level access only**. Calling an `/export` endpoint from an app with account-level access (including legacy apps) will fail, because a real user is required to trigger an export.
</Warning>

## Reading dashboards and reports

The easiest way to get familiar with the API is to retrieve existing data from your account. You can retrieve a single object by ID or search for a page of objects.

### Retrieve a single dashboard or report

To retrieve a single dashboard, make a `GET` request to `/analytics/reporting/2027-03-beta/dashboards/{dashboardId}`. To retrieve a single report, make a `GET` request to `/analytics/reporting/2027-03-beta/reports/{reportId}`.

By default, a core set of properties is returned. To include additional properties that aren't returned by default, add a `properties` query parameter.

| Parameter    | Type    | Description                                                                                                                                                                |
| ------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `properties` | Array   | Additional properties to include in the response. For dashboards: `tags`, `widgets`, `permissions`. For reports: `tags`, `permissions`. Ignored when `archived` is `true`. |
| `archived`   | Boolean | When `true`, retrieves the object from your archived (soft-deleted) objects instead of your active ones. Defaults to `false`.                                              |

Retrieving an archived object requires you to be the object's owner, a reporting admin, or a super admin.

For example, to retrieve a dashboard along with its widgets and permissions, your request URL would look like `/analytics/reporting/2027-03-beta/dashboards/4815162342?properties=widgets&properties=permissions`.

### Search for dashboards or reports

To search for dashboards, make a `GET` request to `/analytics/reporting/2027-03-beta/dashboards`. To search for reports, make a `GET` request to `/analytics/reporting/2027-03-beta/reports`. Both endpoints return a paginated collection with a total count.

<Note>
  Search is eventually consistent. Changes to reporting objects may take a few seconds to be reflected in search results.
</Note>

You can include the following query parameters to filter, sort, and paginate the results.

| Parameter                        | Type     | Description                                                                                            |
| -------------------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `q`                              | String   | Free-text search by name.                                                                              |
| `ids`                            | Array    | Filter to specific object IDs.                                                                         |
| `tagIds`                         | Array    | Filter by tag IDs.                                                                                     |
| `ownerUserIds`                   | Array    | Filter by owner user IDs.                                                                              |
| `businessUnitIds`                | Array    | Filter by business unit IDs.                                                                           |
| `createdAfter` / `createdBefore` | DateTime | Filter by creation date range.                                                                         |
| `updatedAfter` / `updatedBefore` | DateTime | Filter by last-updated date range.                                                                     |
| `onlyFavorites`                  | Boolean  | When `true`, only returns objects you've favorited.                                                    |
| `sort`                           | String   | Sort field and direction. One of `name`, `updatedAt`, or `lastViewedAt`. Defaults to `-updatedAt`.     |
| `limit`                          | Number   | Page size. Defaults to `25`, maximum `100`.                                                            |
| `after`                          | String   | Pagination cursor returned in the previous response's `paging` object.                                 |
| `properties`                     | Array    | Additional properties to include for each object.                                                      |
| `archived`                       | Boolean  | When `true`, searches archived objects. Defaults to `false`. Additional restrictions apply, see below. |

The `/reports` endpoint also supports two additional filters:

| Parameter     | Type    | Description                                                                |
| ------------- | ------- | -------------------------------------------------------------------------- |
| `dashboardId` | String  | Filter to reports on a specific dashboard.                                 |
| `onDashboard` | Boolean | Filter to reports that are (`true`) or are not (`false`) on any dashboard. |

For example, to retrieve the first 50 dashboards owned by a specific user, sorted by name, your request URL would look like `/analytics/reporting/2027-03-beta/dashboards?ownerUserIds=11045285&sort=name&limit=50`.

<Note>
  When searching archived objects with `archived=true`, only `limit` and `after` are respected — filtering, custom sorting, and additional property projection are ignored. Results are always ordered from most recently to least recently deleted, and only your own archived objects are returned unless you're a reporting or super admin.
</Note>

## Creating a dashboard

To create a dashboard, make a `POST` request to `/analytics/reporting/2027-03-beta/dashboards`, and provide a `name` and `permissions` object in the body of your request.

<Note>
  There is no endpoint to create a report in this beta, because a report can't be created without its configuration. See [What's supported in this beta](#whats-supported-in-this-beta).
</Note>

You can include the following properties in the request body.

| Property         | Type   | Description                                                                                                |
| ---------------- | ------ | ---------------------------------------------------------------------------------------------------------- |
| `name`           | String | The name of the dashboard.                                                                                 |
| `permissions`    | Object | The dashboard's permission configuration. See [Managing permissions](#managing-permissions).               |
| `description`    | String | An optional description.                                                                                   |
| `businessUnitId` | String | The business unit the dashboard belongs to. If omitted, the account's default business unit is used.       |
| `reportIdsToAdd` | Array  | IDs of existing reports to attach to the dashboard as widgets. Widgets are appended in the order provided. |

For example, to create a private dashboard with two existing reports attached, your request body would look like:

```json theme={null} theme={null}
{
  "name": "Q3 revenue overview",
  "description": "Pipeline and revenue reports for Q3",
  "permissions": {
    "permissionType": "PRIVATE"
  },
  "reportIdsToAdd": ["8675309", "5551212"]
}
```

<Note>
  Attaching reports at creation time is best-effort — some or all reports may fail to attach, but the dashboard is still created and no error is returned. A maximum of 50 reports can appear on a dashboard. Check the `widgets` field in the response to confirm which reports were attached.
</Note>

## Cloning a dashboard

To clone an existing dashboard, make a `POST` request to `/analytics/reporting/2027-03-beta/dashboards/{dashboardId}/clone`, and provide a `name` and `permissions` object in the body of your request.

| Property       | Type    | Description                                                                                                                                     |
| -------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`         | String  | The name of the cloned dashboard.                                                                                                               |
| `permissions`  | Object  | The cloned dashboard's permission configuration. See [Managing permissions](#managing-permissions).                                             |
| `cloneReports` | Boolean | Whether to also clone the reports on the dashboard (`true`) or reference the same report objects as the original (`false`). Defaults to `true`. |

## Updating a dashboard or report

To update a dashboard, make a `PATCH` request to `/analytics/reporting/2027-03-beta/dashboards/{dashboardId}`. To update a report, make a `PATCH` request to `/analytics/reporting/2027-03-beta/reports/{reportId}`. Only the properties you include in the request body are updated.

<Note>
  Updating an object requires at least `reporting.full.edit`. However, when requesting from an app with user-level access, if the requesting user is not the object's owner, attempting to update the `ownerUserId`, `permissions`, or `businessUnitId` will require `reporting.full.write` or `reporting.full.admin` instead.
</Note>

| Property         | Type    | Description                                                                                                      |
| ---------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
| `name`           | String  | The object's name.                                                                                               |
| `description`    | String  | The object's description. Send an explicit `null` to clear it.                                                   |
| `ownerUserId`    | String  | The user ID of the object's owner.                                                                               |
| `permissions`    | Object  | The object's permission configuration. See [Managing permissions](#managing-permissions).                        |
| `businessUnitId` | String  | The object's business unit. Send an explicit `null` to reset to the account's default.                           |
| `archived`       | Boolean | Archives (`true`) or restores (`false`) the object. When present, this must be the only property in the request. |

For example, to rename a report and reassign its owner, your request body would look like:

```json theme={null}
{
  "name": "Weekly closed-won deals",
  "ownerUserId": "11045285"
}
```

## Archiving and restoring

Archiving is a soft delete. Archived objects can be retrieved and restored, but are **permanently purged 90 days after they're archived**. There are two ways to archive or restore an object.

### Archive or restore a single object

Set the `archived` field in a `PATCH` request to the object. When `archived` is present, it must be the only property in the request body, or the request returns an error.

Attempting to archive an already-archived object, or restore an already-active object, is a no-op and returns a successful response.

```json theme={null}
{
  "archived": true
}
```

### Archive or restore in batch

To archive multiple objects at once, make a `POST` request to `/analytics/reporting/2027-03-beta/dashboards/batch/archive` or `/analytics/reporting/2027-03-beta/reports/batch/archive`. To restore, use the corresponding `/batch/restore` endpoints. In the request body, include the object IDs.

The maximum batch size is `500`. Batch archive silently ignores already-archived objects, and batch restore silently ignores already-active ones.

For example, to archive three dashboards, your request body would look like:

```json theme={null}
{
  "inputs": ["4815162342", "8675309", "5551212"]
}
```

## Managing dashboard widgets

A dashboard's reports are represented as widgets.

<Note>
  A dashboard can hold up to `50` reports. If a request would take a dashboard beyond that limit, it fails.
</Note>

### Add a single report

To add one report to a dashboard, make a `PUT` request to `/analytics/reporting/2027-03-beta/dashboards/{dashboardId}/widgets/{reportId}`. No request body is required. The widget is appended to the end of the dashboard's layout.

If the report is already on the dashboard, doesn't exist, or you don't have permission to access it, the request does nothing. The dashboard in the response reflects whether the report was added.

### Add reports in batch

To add multiple reports to a dashboard, make a `POST` request to `/analytics/reporting/2027-03-beta/dashboards/{dashboardId}/batch/widgets`, and include the report IDs in the request body. Widgets are appended in the order provided, and any reports that are already on the dashboard, don't exist, or aren't accessible are skipped. The dashboard in the response reflects which reports were added.

```json theme={null}
{
  "inputs": ["8675309", "5551212"]
}
```

### Remove a report

To remove one report from a dashboard, make a `DELETE` request to `/analytics/reporting/2027-03-beta/dashboards/{dashboardId}/widgets/{reportId}`. No request body is required.

## Batch updating owners and permissions

You can reassign ownership or update permissions for many objects in a single request. All batch endpoints have a maximum batch size of `500`.

### Batch update owners

To update the owner of multiple objects, make a `POST` request to `/analytics/reporting/2027-03-beta/dashboards/owners/batch/update` or `/analytics/reporting/2027-03-beta/reports/owners/batch/update`. In the request body, include the object IDs and the `ownerUserId` of the new owner.

```json theme={null}
{
  "inputs": ["4815162342", "8675309"],
  "ownerUserId": "11045285"
}
```

### Batch update permissions

To update permissions for multiple objects, make a `POST` request to `/analytics/reporting/2027-03-beta/dashboards/permissions/batch/update` or `/analytics/reporting/2027-03-beta/reports/permissions/batch/update`. In the request body, include the object IDs and a `permissions` object. See [Managing permissions](#managing-permissions) for the shape of the permissions object.

<a id="managing-permissions" />

## Managing permissions

The object's owner and any super or reporting admin always have full permissions. For everyone else, access is governed by the `permissionType` field in the permissions object.

| `permissionType` | Description                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------- |
| `PRIVATE`        | Only the owner can access the object.                                                    |
| `EVERYONE_VIEW`  | Everyone can view the object.                                                            |
| `EVERYONE_EDIT`  | Everyone can edit the object.                                                            |
| `SPECIFIC`       | Only specified users and/or teams can access the object. Requires `specificPermissions`. |

When `permissionType` is `SPECIFIC`, include a `specificPermissions` configuration. Each configuration has a `permissionType` of `VIEW` or `EDIT` and a `grants` array, where each grant has a `grantType` of `USER` or `TEAM` and a `granteeId`.

Dashboards and reports differ in how many specific configurations they accept:

* **Dashboards** accept up to two configurations — one `VIEW` and/or one `EDIT`. Each user or team receives the level of the configuration they're listed in.
* **Reports** accept exactly one configuration — either `VIEW` or `EDIT`, not both.

For example, to grant edit access to a specific team and view access to a specific user on a dashboard, the `permissions` object would look like:

```json theme={null}
{
  "permissionType": "SPECIFIC",
  "specificPermissions": [
    {
      "permissionType": "EDIT",
      "grants": [
        { "grantType": "TEAM", "granteeId": "7742" }
      ]
    },
    {
      "permissionType": "VIEW",
      "grants": [
        { "grantType": "USER", "granteeId": "11045285" }
      ]
    }
  ]
}
```

<Note>
  When a report is on one or more dashboards, it inherits the aggregate permissions of all dashboards it's on instead of having its own permissions. You cannot set a report's permissions if it is on any dashboard, and attempting to do so will result in an error.
</Note>

## Exporting dashboards and reports

To export a dashboard, make a `POST` request to `/analytics/reporting/2027-03-beta/dashboards/{dashboardId}/export`. To export a report, make a `POST` request to `/analytics/reporting/2027-03-beta/reports/{reportId}/export`. The export is delivered to one or more HubSpot users through standard channels, primarily email.

<Note>
  Export endpoints are available to apps with **user-level access only**, and there is no status tracking — a successful request returns a `204 No Content`. If a recipient doesn't have access to reporting or doesn't have CRM export permissions, they receive a notification describing the error rather than a successful export.
</Note>

You can include the following fields in the request body.

| Property           | Type   | Description                                                                                                                    |
| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `exportType`       | String | The export format. See the supported formats below.                                                                            |
| `recipientUserIds` | Array  | The HubSpot users to send the export to. If empty, the export is sent to the requesting user.                                  |
| `subject`          | String | The subject line for the export. If absent or whitespace, the name of the dashboard or report is used. Maximum 100 characters. |
| `message`          | String | An optional message to include with the export. Maximum 5,000 characters.                                                      |

Dashboard exports also support a `reportIds` array — if non-empty, only the specified reports are included in the export.

Supported `exportType` values differ by object:

| Object    | Supported `exportType` values                            |
| --------- | -------------------------------------------------------- |
| Dashboard | `SCREENSHOT`, `PDF`, `PPTX`, `ZIP`, `CSV`, `XLS`, `XLSX` |
| Report    | `SCREENSHOT`, `CSV`, `XLS`, `XLSX`                       |

For dashboards, `SCREENSHOT` produces individual report screenshots, while `PDF`, `PPTX`, and `ZIP` bundle those screenshots into a single file. The `CSV`, `XLS`, and `XLSX` types each produce a ZIP containing one or more data files per report.

For example, to email a PDF of a dashboard to two users, your request body would look like:

```json theme={null}
{
  "exportType": "PDF",
  "recipientUserIds": ["11045285", "9586504"],
  "subject": "Q3 revenue overview",
  "message": "Here's the latest on Q3 pipeline and revenue."
}
```
