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:

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:

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:

  1. Describe your app — Tell the App Builder what you want in plain language. For example: "Create a dashboard showing overdue invoices grouped by customer".

  2. 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".

  3. 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:

Starting the App Builder

  1. Open the agent selector in the header bar (next to the Operator logo).
  2. Select App Builder from the dropdown.
  3. You'll enter Creator Mode — a clean workspace ready for a new app.
  4. 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:

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:

Data Volume Limits

App Builder apps are lightweight client-side applications with strict data limits to ensure performance and stability:

If your use case requires processing larger volumes of data, consider these strategies:

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:

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:

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:

Tips

Best Practices

Next Steps

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.