> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.navipartner.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.navipartner.com/_mcp/server.

# BI

The BI endpoints expose any Business Central table as JSON, so a reporting or analytics tool can read NP Retail data without a dedicated endpoint being built for each table first.

There are two endpoints. `GET /bi/{tableNo}` returns the records of a table. `GET /tablemetadata` and `GET /tablemetadata/{tableNo}` describe what can be read and what the records look like.

## Enabling a table

The BI endpoint can reach every table in the database, so access is not granted by the permission set alone. Each table has to be enabled for the caller, one table at a time.

In Business Central, open **NaviPartner API Keys**, select the key your integration uses, and choose **BI API Allowed Tables**. Add a row per table you want the integration to read. If your integration authenticates with an Entra ID application that was not created from a NaviPartner API Key, use the **BI API Allowed Tables** action on the Entra Application card instead.

An application that belongs to a NaviPartner API Key always uses the list of that key. Rows added for such an application directly are ignored.

The list is held once per tenant rather than per company, so enabling a table enables it in every company the caller can already reach. Which companies those are is decided separately, by where the BI permission set is assigned: a caller that does not hold it in a given company cannot read anything there, whatever the list says.

Reading a table that has not been enabled returns `403` with the error code `bi_table_not_allowed`.

## Discovering tables and fields

`GET /tablemetadata` lists every table that can be exposed, with its table number, its English and translated name, whether you are allowed to read it, and whether it supports replication.

`GET /tablemetadata/{tableNo}` describes one table field by field. For each field it gives the field number, the English and translated caption, the data type, the length for text fields, whether the field belongs to the primary key, and `jsonName`: the property name that field has in the records returned by `/bi/{tableNo}`.

Both metadata endpoints are open to every caller that holds the BI permission set. They return no business data, and they work whether or not the table has been enabled. Only the data endpoint is gated by the allowlist.

## Which fields you get

The records contain every field that is stored in the database, including the system fields: `id` (the system id of the record), `systemCreatedAt`, `systemCreatedBy`, `systemModifiedAt` and `systemModifiedBy`.

Calculated fields are not returned, because they are not stored: FlowFields and FlowFilters are left out. Binary and filter fields are left out as well: Blob, Media, MediaSet and TableFilter.

Enum and option fields carry the value name rather than the translated caption, so `NP API Key` rather than `NaviPartner API Key`. That name is the same whatever language the caller asks for, which makes it safe to store and compare. Dates, times, durations and date formulas are written in the invariant format for the same reason.

Property names are the camelCase form of the English field name, so `No.` becomes `no` and `VAT Bus. Posting Group` becomes `vatBusPostingGroup`. The names `id` and `rowVersion` are reserved; a field whose name would collide with a reserved name, or with a field that comes earlier in the table, gets its field number appended, for example `id_1`. Always take the names from `jsonName` rather than deriving them yourself.

## Paging and incremental loads

Paging and replication behave exactly as they do elsewhere in this API. See [Pagination](/apis/pagination) and [Replication](/apis/replication).

The default page size of the BI endpoint is 1000 records rather than the usual maximum, because a BI record carries every field of its table and some tables have several hundred. You can raise it up to 20000 with `pageSize`.

Every read is incremental. There is no separate mode to switch on: records always come back ordered by row version, and every record carries its `rowVersion`. To read a table for the first time, call it without `lastRowVersion` and follow `nextPageKey` to the end. To pick up changes later, pass the highest `rowVersion` you received as `lastRowVersion`. Sending `sync=false` is rejected, so you cannot end up with a read you are unable to resume.

A read stops just below the oldest write that is still in progress, so a record is never handed out before every record older than it is also available. That is what makes storing the highest `rowVersion` you received safe: a write that commits out of order cannot slip in behind your watermark. It also means a page can come back empty, or shorter than `pageSize`, while a write is in flight. Ask again in a moment rather than treating that as the end of the data.

Because reads are ordered by row version, the table needs a database index that starts with it. Most tables do not have one until it is added to NP Retail, so a table nobody has read this way before answers `400` with the error code `bi_missing_rowversion_index`. The message names the table; send it to NaviPartner and the index will be added.

`GET /tablemetadata` tells you in advance which tables are ready, through the `syncSupported` flag. A table with `syncSupported: false` cannot be read through `/bi` at all yet.

## Language

The metadata endpoints return both an English and a translated caption for every table and field. The translated caption follows the `Accept-Language` header, then the language of the user behind the API key, and finally English. The language that was used is reported back in the `language` property of the response.

Field tooltips are not returned. Business Central does not make them available at runtime.

## Errors

| Code                          | Status | Meaning                                                                                                                      |
| ----------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `bi_principal_not_resolved`   | 403    | The caller is not an Entra ID application registered in Business Central, so no allowlist can be applied.                    |
| `bi_table_not_allowed`        | 403    | The table has not been enabled for this API key or Entra ID application.                                                     |
| `bi_missing_rowversion_index` | 400    | The table has no index on the row version, so it cannot be read yet.                                                         |
| `invalid_input`               | 400    | The table number in the path is not a number, a query parameter is not a number or a boolean, or `sync=false` was requested. |
| `resource_not_found`          | 404    | No such table, or the table is a system table or has been removed.                                                           |