App Builder
Create custom ERP data apps with natural language
What is the App Builder?
The App Builder is a specialized agent in Operator that lets you create small, focused ERP data applications — called Operator Apps — using natural language. Instead of writing code manually, you describe what you want—and the App Builder generates a fully functional app that integrates directly with ERP.net.
What Are Operator Apps?
Operator Apps are single-page applications (SPAs) designed for internal corporate users. They are lightweight, browser-based tools that run entirely on the client side — no server deployment or infrastructure is required.
Each app is purpose-built for a specific task: a dashboard, a data entry form, a lookup screen, a status monitor, or any other focused interface that works with your ERP data in real-time. They are not intended as public-facing websites or customer portals — they are internal productivity tools for your organization's employees.
ERP.net as the Default Data Model
The App Builder is built around the ERP.net data model. Every app you create can immediately work with real business objects — customers, products, sales orders, invoices, projects, and any other entity in your ERP.net instance — because the App Builder already understands ERP.net relationships, types, and conventions.
This means:
- Out-of-the-box data — apps are designed against proper ERP.net business objects, not anonymous spreadsheets or custom schemas.
- Zero setup integration — your apps connect to the same live ERP.net instance you are already using in Operator, so they work immediately for the current enterprise company and user permissions.
- Business-ready design — the AI knows how to build forms, dashboards, and workflows that match ERP.net entities and best practices.
At the same time, you are not limited to ERP.net. You can build any Operator App you want — including apps that use external APIs, combine ERP data with third-party services, or do not touch ERP.net at all.
Support Policy
The App Builder follows a self-service model. AI-powered assistance inside Operator replaces traditional support — AI-generated apps are not covered by ERP.net or Operator support. You build and run them at your own discretion.
By using the App Builder you accept the following principles:
- User Responsibility — You are solely responsible for testing, securing, and maintaining your app. Verify behavior with real data before relying on it for business-critical work.
- AI Error Liability — AI-generated code may contain mistakes, omissions, or unexpected behavior. You accept all risks arising from such errors. Always review the generated code and the live preview before deploying.
- Security — You must configure security (permission policies, sharing scope, data exposure) to protect your data. The AI does not guarantee a secure configuration by default. Apps that read or modify ERP data inherit the permissions of the user running them — design accordingly.
- Code Ownership — You own the code your apps generate. You can view it in the Code tab and export it to deploy elsewhere if you outgrow the App Builder.
If you need a guaranteed, supported solution, build it as a traditional ERP.net customization or engage an ERP.net partner. The App Builder is intended for fast, internal, self-managed productivity tools.
How It Works
The App Builder uses a conversational workflow:
Describe your app — Tell the App Builder what you want in plain language. For example: "Create a dashboard showing overdue invoices grouped by customer".
Iterative design — The App Builder generates the app and shows you a live preview. You can refine it by asking for changes: "Add a filter for date range" or "Make the table sortable by amount".
Deploy — Once satisfied, deploy your app through one of the available deployment modes.
Getting Started
Who can use the App Builder
The App Builder is available to:
- Global Power Users and above — always available, regardless of which instance (if any) is connected.
- Instance Power Users and above — available while an ERP.net instance where you have Power User or Admin role is connected. Disconnecting that instance (or switching to one where you only have Normal/External access) hides the App Builder.
Starting the App Builder
- Open the agent selector in the header bar (next to the Operator logo).
- Select App Builder from the dropdown.
- You'll enter Creator Mode — a clean workspace ready for a new app.
- Describe your app in the chat and watch it come to life.
The Agent Selector
The agent/app dropdown in the header serves dual purposes:
- Select App Builder — Switches to the App Builder agent in Creator Mode, ready to design a new app from scratch.
- Select an existing app — Opens the app in Execution Mode for day-to-day use. From there, you can switch to Design Mode to make changes.
Interface Modes
The App Builder interface has several modes, accessible via the tab bar:
| Mode | Description |
|---|---|
| Create | Shown when no app is selected. Describe a new app to build it. |
| Execute | Run and interact with your app as an end user would. |
| Design | A split view (chat + preview) for iterating on your app's design and functionality. |
| Code | View and inspect the generated source code. |
| Properties | Configure app metadata: name, slug, description, and categories. |
| Deploy | Configure how and where your app is deployed. |
Design Mode
Design mode provides a 30/70 split view — the chat panel on the left and a live app preview on the right. As you describe changes, the preview updates in real-time. Use the Refresh preview button (right side of the tab bar) to force a reload if needed.
Execution Mode
Execution mode shows your app full-width, exactly as end users will see it. This is the default view when selecting an existing app from the agent dropdown. Operator opens existing apps directly in Execute mode for faster startup, and only loads the related design conversation when you switch to Design, Code, Properties, or Deploy.
What Apps Can Do
Apps built with the App Builder have access to the ERP.net Domain API (OData v4), enabling them to:
- Query data — Fetch and display records from any ERP entity
- Create records — Build forms that create new entries in the system
- Update records — Modify existing data with validation
- Display dashboards — Visualize data with charts, tables, and summary cards
- Filter and search — Add interactive controls for data exploration
- Respond to context — React to the connected instance and enterprise company
- Call AI — Invoke Pure AI for one-shot completions or run full Operator agents (built-in or user-defined) with tools and capabilities. See AI in Apps.
Data Volume Limits
App Builder apps are lightweight client-side applications with strict data limits to ensure performance and stability:
- Maximum 1,000 rows per request — Each API call can return at most 1,000 rows. Always apply filters to keep result sets within this limit.
- Maximum 10,000 rows in memory — The app should never accumulate more than 10,000 rows across all loaded data sets combined.
If your use case requires processing larger volumes of data, consider these strategies:
- Narrower filters — Use date ranges, status filters, or other criteria to reduce the data set.
- Paginated views — Load one page of data at a time without keeping all pages in memory.
- Summary queries — Use aggregate or summary data instead of loading raw detail rows.
- Server-side processing — Offload heavy data processing to the ERP system rather than pulling everything into the browser.
Domain API Transactions
The ERP.net Domain API allows only one open transaction per session. If an app opens a transaction (for a multi-step write that must be atomic) and fails to close it after an error, the session becomes unusable for any further transactional work until it expires — every later attempt fails with "Maximum allowed transactions for this session have been reached." The App Builder is instructed to avoid transactions when a single write would do, and — when one is genuinely needed — to wrap it in a try / catch / finally block so the transaction is always rolled back on error. You don't need to ask for this behavior; the App Builder applies it automatically. If you ever see this error from an older app, regenerate or patch it in the App Builder to pick up the safer pattern.
Error Handling for ERP.net Calls
When window.operator.fetch(...) fails, the rejected Error carries the exact ERP.net server message, so apps should surface it to the user instead of a generic "failed" toast. The error object includes:
error.message— the ERP.net server message (already in the instance's language, e.g. *"Вие нямате достъп до обект Gen_Products(**73d7a)").error.isErpError—truewhen ERP.net rejected the request,falsefor transport/authentication failures.error.status— HTTP status returned by ERP.net (e.g.403,500).error.statusText— friendly label (e.g. "ERP.net permission denied", "ERP.net rejected the request").error.code,error.type,error.details— when ERP.net provides them.
Apps generated by the App Builder follow this pattern automatically:
try {
await window.operator.fetch('/Production_Technologies_RecipeIngredients', {
method: 'POST',
body: payload,
});
} catch (e) {
if (e.isErpError) {
toast.error('ERP.net rejected the change', { description: e.message });
} else {
toast.error('Could not reach ERP.net', { description: e.message });
}
}
Most "the app can't save to ERP.net" reports are in fact ERP.net permission errors — the connected user lacks read or write access on a referenced entity. The full error is also recorded in the app's runtime logs (visible to the App Builder agent), which usually points to the exact missing permission.
The same e.message also carries proxy-side reasons such as "Your connection to ERP.net expired and could not be refreshed. Please reconnect the instance." or "No ERP.net instance is currently selected." — apps do not need any special-case code; surfacing e.message is always meaningful. These improvements apply live to every existing app on the next page reload — no rebuild or App Builder edit is required.
Automatic request serialization
ERP.net rejects concurrent calls from the same session with HTTP 429. To prevent this, Operator automatically serializes every window.operator.fetch() call inside the app — even if the app code accidentally fires them in parallel (e.g. Promise.all([...])), Operator queues them and sends one at a time. App authors should still write sequential await code for clear loading and error states, but this safety net protects existing apps from accidental 429s with no rebuild needed.
Connection validation on app start
When a user opens an app at /app/:slug, Operator validates the active ERP.net connection before mounting the app iframe. If the access token is expired, Operator silently attempts to refresh it. If the refresh fails, the app does not load — instead, Operator displays a full-screen "Session expired for {instance}" overlay with a Reconnect button that walks the user through the ERP.net sign-in flow. As soon as sign-in completes, the iframe boots with a fresh token and the app runs normally. App authors do not need to handle this case in code.
External API Access
App Builder apps can call external REST APIs directly using the standard browser fetch() API — no special SDK needed. This is useful for integrating third-party services, public data sources, or custom backends alongside your ERP data.
How It Works
Use fetch() as you would in any web application:
const response = await fetch("https://api.example.com/data", {
headers: { "Authorization": "Bearer YOUR_TOKEN" }
});
const data = await response.json();
CORS Restrictions
Apps run in a browser sandbox, so standard CORS (Cross-Origin Resource Sharing) rules apply:
- If the external API includes an
Access-Control-Allow-Originheader, you can call it directly withfetch(). - If it does not (e.g. EU VIES, many government APIs), the browser blocks the response. For these cases use
window.operator.corsFetch(url, options)— Operator forwards the request server-side and returns the response wrapped in CORS headers your app can read.
const res = await window.operator.corsFetch(
'https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ countryCode: 'BG', vatNumber: '121084091' }),
}
);
if (res.ok) console.log(res.json());
corsFetch is not an open proxy:
- Only signed-in Operator users can call it.
- Limited to 60 requests / minute / user.
- Sensitive headers (
Authorization,Cookie) are stripped before forwarding, so never use it for APIs that require a secret API key — build a dedicated backend function so the secret stays server-side. - Internal hosts (
*.erp.net,*.supabase.co,*.lovable.app,localhost, private IPs) are blocked.
Tips
- Public APIs with CORS (weather, exchange rates) — call directly with
fetch(). - Public APIs without CORS — call through
window.operator.corsFetch(). - APIs that need a secret key — neither path is safe in client code; ask for a dedicated server function.
- ERP.net data should always be accessed through
window.operator.fetch(), which handles authentication and proxying automatically. - Sequential requests — As with ERP API calls, avoid concurrent requests to external APIs. Use
awaitfor each call to prevent rate limiting or ordering issues.
Best Practices
- Start simple — Describe the core function first, then iterate with refinements.
- Be specific — Mention exact entity names, fields, and filters you need.
- Test with real data — Connect your instance and verify the app works with actual records.
- Use descriptive names — Give your app a clear name and description for easy discovery.
Next Steps
- Learn about Deployment Options for publishing your apps
- Manage your apps on the Apps Page
Conversations Tab
Each app supports multiple conversations. Open the Conversations tab in the app panel to see every conversation you've had for the current app, across all your instances.
- Switch to any conversation on your currently selected instance with one click. Conversations bound to other instances are shown for reference but can only be opened after switching to that instance.
- New conversation starts a fresh thread for the same app. Use a separate conversation for each feature, screen, or sub-project — it keeps context focused and makes it easier to come back to a specific area later.
- Archive conversations you no longer need from the row menu. Archived conversations are hidden by default; toggle Show archived to view or Restore them. Archive is a soft-hide — no data is lost.