ERP.net with Operator Apps
Custom apps on top of your live ERP.net data
App Builder apps are single-page apps you describe in natural language. When an ERP.net instance is connected, those apps can read and write your live business data — without you handling a single credential.
How an app reaches ERP.net
The app calls the ERP.net Domain API through Operator:
// Read
const res = await operator.fetch('/Crm_Sales_SalesOrders?$top=5&$select=Number,DocumentDate,DisplayText');
console.log(res.value);
// Write
await operator.fetch('/General_Products_Products', {
method: 'POST',
body: { Name: { en: 'Test product' } }
});
What Operator does around that call:
- Attaches your ERP.net token server-side. The app code never sees it, so a shared app cannot leak your session.
- Runs as the signed-in user. Two people opening the same app see their own permitted data.
- Serialises requests. ERP.net rejects bursts of concurrent calls, so Operator queues them for you — even if the app fires them in parallel.
- Surfaces real errors. When ERP.net refuses a write, the app receives the server's own message (already localized) plus the status code, so it can show something meaningful instead of "something went wrong".
Summaries and totals in an app
When a screen shows a KPI, a chart, or a per-group breakdown, the app asks ERP.net to do the grouping instead of downloading rows and adding them up in the browser. The totals then cover every matching record, not just the first page, and one request replaces thousands of rows.
// Revenue per customer since the start of the year
const res = await operator.aggregate({
entity: 'Crm_Sales_SalesOrderLines',
group_by: ['SalesOrder/Customer'],
expand_group: { 'SalesOrder/Customer': ['DisplayText'] },
aggregate: [{ op: 'sum', field: 'LineAmount/Value', alias: 'Revenue' }],
filter: [{ field: 'SalesOrder/DocumentDate', op: 'ge', value: '2026-01-01' }],
top: 200
});
const top5 = res.value.sort((a, b) => b.Revenue - a.Revenue).slice(0, 5);
Good to know:
- Amounts and quantities stay per measure. Two currencies come back as two lines; they are never silently added together.
- Groups come back unordered. For a "top 5" view the app sorts the returned groups itself.
- Server-side summaries need ERP.net 27 or later. On an older instance the call fails with a readable message naming the instance version, and the App Builder designs the screen around narrow filtered queries instead — it knows the version of the connected instance before it starts building.
Knowing who is using the app
An app can ask Operator who it is running for — the Operator account, the ERP.net user of the current instance, and the instance itself. That is how apps filter to "my activities" or stamp a record with the responsible person.
Building apps against your model
The App Builder agent has the same ERP.net understanding as other agents: it discovers entities, reads your custom attributes, and honours your Entity Instructions while it writes the app. Describe the app in business terms — "a board of open service cases grouped by responsible person, with a button to close a case" — and it resolves the entities for you.
If the data you describe does not fit an existing ERP.net entity, the builder will say so and ask, rather than silently inventing storage. For app-owned data that genuinely does not belong in ERP.net, use Pro Apps table and file storage.
Where these apps run
The same app can run inside the Operator workspace, full-screen on its own address, embedded in the ERP.net Web Client, or installed on a phone as a PWA. See Deployment Options.
Practical patterns
- Operational dashboards — live KPIs straight from the Domain API, no export step.
- Focused data entry — a form with three fields instead of a full ERP.net screen, for people who only ever do one thing.
- Web Client extensions — an app pinned next to an ERP.net record, showing context the standard screen does not.
- Field tools — a phone app for stock checks or delivery confirmations.
Limits worth knowing
- Apps talk to ERP.net through Operator only; they cannot open a direct connection with their own credentials.
- Requests to arbitrary third-party APIs go through Operator's proxy, which blocks calls back into ERP.net, Operator infrastructure, and private networks — deliberate, to prevent credential-free back doors.
- An app is bound to the instance it is opened on, exactly like a conversation.