Skip to main content

Incremental loads and deletions

Reading a whole table on every refresh gets slow as Business Central grows. BI Endpoints gives you two things to load incrementally instead:

  1. systemModifiedAt on every endpoint — the records created or changed since your last load.
  2. deletedEntries — the records deleted since your last load.

Together they keep a copy in a warehouse or lakehouse exactly in step with Business Central.

Changed records: systemModifiedAt​

Every endpoint includes systemModifiedAt, when the record was created or last modified. Filter on it to read only what changed since your last extraction:

GET .../companies({companyId})/glEntries?$filter=systemModifiedAt ge 2026-01-01T00:00:00.000Z

A typical loop:

  1. Keep the time of your last successful extraction in a control table.
  2. Before you start, note the current time.
  3. Read each endpoint with $filter=systemModifiedAt ge <last extraction>.
  4. Upsert the rows by the endpoint's business key — for example entryNo for glEntries, or documentNo and lineNo for salesInvoiceLines.
  5. Read and apply the deleted entries for the same period (below).
  6. When everything succeeded, store the time from step 2 as the new last extraction.

Using ge and the time the load started means a record changed while the load ran is read again on the next run rather than missed; upserting by business key makes the overlap harmless.

Deleted entries​

Deleted records are tracked by default for every BI endpoint and served from a separate endpoint, deletedEntries (Premium):

GET .../companies({companyId})/deletedEntries?$filter=entitySetName eq 'glEntries' and systemModifiedAt ge 2026-01-01T00:00:00.000Z

Each row is one deleted record:

FieldMeaning
entryNoSequence number of the log entry.
entitySetNameThe endpoint the record belonged to, for example glEntries.
tableId, tableNameThe Business Central source table.
key1Name … key6Name, key1Value … key6ValueThe record's business key, one field per part, in the order of the endpoint's business key.
primaryKeyText, primaryKeyJsonThe key as text and as JSON.
keyTruncatedtrue when a key value was too long to store in full.
deletedSystemIdThe Business Central SystemId of the deleted record.
deletedAt, deletedByWhen and by whom it was deleted.
systemModifiedAtWhen the log entry was written. Filter on it as you do on the data endpoints.

Business key values are rendered in the API output language, so they match the values the data endpoint returned.

Delete the matching keys from your copy. You don't need to build the URL yourself: each endpoint's Deleted Records URL — on its page in Business Central, or deletedRecordsUrl in biEndpoints — already filters on the endpoint and selects exactly the key columns it uses:

.../deletedEntries?$filter=entitySetName eq 'salesInvoiceLines'&$select=entryNo,tableName,key1Name,key1Value,key2Name,key2Value,primaryKeyText,deletedAt,deletedBy,systemModifiedAt

Add and systemModifiedAt ge <last extraction> to its filter.

Turning delete tracking off​

Tracking can be switched off for every endpoint, or per endpoint, and old log entries are removed by a retention policy. See BI Endpoints Setup. Make sure your loads run more often than the retention period, or a deletion can be purged before you read it.

With Cloud Connector​

Cloud Connector sets this up for you: adding a BI Endpoints entity set as a dataset creates a full read, an incremental read on systemModifiedAt, and a delete read of deletedEntries, and carries deletions through to SQL Server or Fabric.