Skip to main content

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.read scope (or modelapps.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.

OperationRequestReturns
List tablesGET /environments/{environmentId}/reporting/entitiesThe tables your role may read (logical name, display name, plural name).
Describe a table's columnsGET /environments/{environmentId}/reporting/entities/{entity}/schemaEach column: logical name, display name, data type, whether it is the primary field, and a lookup's target table.
Read a datasetPOST /environments/{environmentId}/reporting/entities/{entity}/datasetcolumns and rows, plus totalCount, pageNumber and pageSize.
Run a saved queryPOST /environments/{environmentId}/saved-queries/{logicalName}/runThe 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 carry reporting.read or modelapps.read. Ask for the scope by name when you request the token: a token requested without scope carries 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, filter or order field is not on the table. Use a field from the table's schema.
  • Register an External App.
  • Bind a Report to an Entity (maker).