Query Entity Data for Reporting
Goal
Read a table's data from one environment through the read-only reporting data source, so an external reporting or analytics integration can list the tables it may read, discover a table's columns, pull pages of rows, and run a saved query, without any write access.
Prerequisites
- An external application registration granted the
reporting.readscope (ormodelapps.read, which also grants reporting reads) and a role that may read the tables it needs. See Register an External App. - An access token obtained for that registration with the scope requested by name.
- The identifier of the environment whose data you want to read.
The reporting surface
All operations are scoped to one environment and are read-only. Two things decide what you get: the scope opens the surface at all, and the application's role decides which tables and which rows, exactly as it would for a person using an app. A token with the scope and no role reaches no rows.
| Operation | Request | Returns |
|---|---|---|
| List tables | GET /environments/{environmentId}/reporting/entities | The tables your role may read (logical name, display name, plural name). |
| Describe a table's columns | GET /environments/{environmentId}/reporting/entities/{entity}/schema | Each column: logical name, display name, data type, whether it is the primary field, and a lookup's target table. |
| Read a dataset | POST /environments/{environmentId}/reporting/entities/{entity}/dataset | columns and rows, plus totalCount, pageNumber and pageSize. |
| Run a saved query | POST /environments/{environmentId}/saved-queries/{logicalName}/run | The saved query's records or groups. |
A dataset request
The body is a query: select (the columns you want), filter (a connector and conditions,
each a field, an operator and a value), order (fields, each ascending or descending) and
page (number and size):
{
"select": ["nw_name", "nw_company"],
"filter": { "connector": "And",
"conditions": [{ "field": "nw_company", "operator": "Equals", "value": "Analytical Engines" }] },
"order": [{ "field": "nw_name", "descending": false }],
"page": { "number": 1, "size": 50 }
}
With select, columns describes exactly those columns in that order. A choice column comes
back as its number and, beside it, its label under <column>__text. Read totalCount to know
how many pages there are.
A saved query
A query a maker designed and saved can be run by its logical name. The body is optional and can
only narrow it: an extra filter is added to the saved one, never put in its place, and
page picks a page. A grouped query answers with its groups and their measures, so an
integration can ask for totals instead of adding rows up itself.
What happens behind the scenes
The reporting data source has no create, update or delete. Each request checks the token's scope first, then reads through the same path the platform's own screens use, so the role's permissions and their policies decide the rows. Only the environment named in the route is read.
Troubleshooting
- 403 with
modelapps.reporting.forbidden. The token does not carryreporting.readormodelapps.read. Ask for the scope by name when you request the token: a token requested withoutscopecarries none. - 403 with
modelapps.record.forbidden. The scope is there, but the application's role may not read that table. Grant the role Read on it in this environment. - A table is missing from the list. The role may not read it, or it does not exist in this environment.
- 400 for an unknown field. A
select,filterororderfield is not on the table. Use a field from the table's schema.
Related scenarios
- Register an External App.
- Bind a Report to an Entity (maker).