Preview Feature — Apps is currently in the preview phase. Functionality and API can still
be changed. We appreciate feedback!
Apps allow you to create and run your own HTML applications directly in Enneo.
Use Apps for individual dashboards, reports, forms or tools — all within the familiar Enneo interface.
Overview
Apps are custom applications that return HTML and are displayed directly in Enneo as their own page.
Each app runs in a sandbox and has access to the Enneo API.
Typical use cases:
- Individual Dashboards — visualize real-time data from the ERP or CRM
- Reports and evaluations — tailor-made reports with own logic
- Internal tools — forms, calculators or workflow helpers
- Data queries — display contract data, customer status or statistics
App Structure
Each App consists of:
| Component | Description |
|---|
| Name | Display name in the sidebar and in the settings |
| Slug | Unique technical identifier (e.g., my-dashboard). Used in the URL |
| Description | Optional description of the purpose of the App |
| Provider | Optional: Who has created the App |
| Executor | The code that is executed when called (Python, Node.js or PHP) |
Executor
The executor uses Enneo’s standard executor format (same structure as for settings and AI agents).
It currently supports the Source code type with the languages Python 3.11, Node.js 20 and PHP 8.2.
The output to stdout is returned as HTML to the browser.
# Simple App: Welcome page
print("<h1>Welcome</h1>")
print("<p>This is my first Enneo app.</p>")
With each call, the code automatically receives metadata as a parameter via STDIN:
{
"_metadata": {
"userId": 1,
"clientId": "1",
"appId": "a1b2c3d4-...",
"appRevision": 3,
"userAuthToken": "Bearer ..."
}
}
Additional GET or POST parameters are automatically passed on and are available in the code.
Environment Variables
The following environment variables are available in the app code:
| Variable | Description |
|---|
ENNEO_API_URL | Base URL of the Enneo API |
ENNEO_SESSION_TOKEN | Session token for API calls |
ENNEO_USER_AUTH_HEADER | Auth Header of the calling user |
ENNEO_APP_ID | UUID of the App |
ENNEO_APP_SLUG | Slug of the App |
ENNEO_APP_VERSION | Version of the App (e.g., 1.0.0) |
SDK | Path to the SDK file |
Using Apps
As soon as at least one app exists and the user has the readApps permission, the menu item Apps appears
in the sidebar. All available apps are displayed as tabs here.
The app-url in the browser uses the Slug of the app:
Call App
Each app runs in an iFrame inside of Enneo. The API URL accepts both UUID and Slug:
/api/mind/app/{slug}/run
/api/mind/app/{appId}/run
GET parameters can be directly appended:
/api/mind/app/my-dashboard/run?param1=value1¶m2=value2
App Storage
Apps can store and read persistent data. The App Storage is a key-value store,
which is isolated per app. The values are stored as JSON.
SDK Access
# Save value
sdk.AppStorage.save("counter", 42)
sdk.AppStorage.save("config", {"theme": "dark", "lang": "de"})
# Read value
counter = sdk.AppStorage.get("counter") # → 42
config = sdk.AppStorage.get("config") # → {"theme": "dark", "lang": "de"}
# Read value with metadata
meta = sdk.AppStorage.get_meta("counter")
# → {"key": "counter", "value": 42, "createdAt": "...", "modifiedAt": "...", "lastAccessAt": "..."}
# List all keys
keys = sdk.AppStorage.list_keys()
# → [{"key": "counter", "createdAt": "...", ...}, ...]
# Delete value
sdk.AppStorage.delete("counter")
App Info
The AppInfo class can be used to retrieve app metadata:
app_id = sdk.AppInfo.get_id() # UUID of the App
app_slug = sdk.AppInfo.get_slug() # Slug of the App
app_version = sdk.AppInfo.get_version() # Version (e.g., "1.0.0")
Supported Value Types
| Type | Example |
|---|
| String | "hello world" |
| Number | 42, 3.14 |
| Boolean | true, false |
| Null | null |
| Object | {"key": "value"} |
| Array | [1, "two", 3.0] |
Manage Apps
Management is under Settings → Advanced Settings → Apps.
Create New App
- Navigate to Settings → Apps
- Click on Create App
- Enter name, slug and optional description
- Write the executor code
- Use the Preview to check the output
- Click on Save
Edit App
Each change automatically creates a new Revision. This way, you can always revert to a
previous version.
Unsaved changes are indicated by a Draft tag in the sidebar.
A warning will appear when navigating with unsaved changes.
Export and Import
Apps can be exported and imported as JSON. The export button is located
in the Specifications tab. This allows for transfer between environments.
Revisions and Rollback
Apps support versioning:
- Each save creates a new revision
- Through the revisions overview, you can revert to any previous version
- The active revision is displayed to the users
Permissions
| Permission | Description | Standard Roles |
|---|
readApps | View and run Apps | Admin, Agent |
appCreator | Create, edit and preview Apps | Admin |
manageApps | Import and delete Apps | Admin |
Examples
Dashboard with Contract data
import importlib.util, os, json, sys
# Load SDK
file_path = os.getenv('SDK', 'sdk.py')
spec = importlib.util.spec_from_file_location('sdk', file_path)
sdk = importlib.util.module_from_spec(spec)
spec.loader.exec_module(sdk)
# Load input data
input_data = sdk.load_input_data()
metadata = input_data.get('_metadata', {})
# Load contract data (example)
contract_id = input_data.get('contractId', '715559')
try:
contract = sdk.ApiEnneo.get_contract(contract_id)
name = f"{contract.get('firstname', '')} {contract.get('lastname', '')}"
status = contract.get('status', 'Unknown')
except Exception:
name = "Not found"
status = "-"
# Output HTML
print(f"""<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; padding: 20px; }}
.card {{ background: #f5f5f5; padding: 16px; border-radius: 8px; margin: 8px 0; }}
h1 {{ color: #333; }}
</style>
</head>
<body>
<h1>Contract Overview</h1>
<div class="card">
<strong>Name:</strong> {name}<br>
<strong>Status:</strong> {status}<br>
<strong>Contract Number:</strong> {contract_id}
</div>
</body>
</html>
""")
App with Persistent Counter
import importlib.util, os, json, sys, html
file_path = os.getenv('SDK', 'sdk.py')
spec = importlib.util.spec_from_file_location('sdk', file_path)
sdk = importlib.util.module_from_spec(spec)
spec.loader.exec_module(sdk)
input_data = sdk.load_input_data()
# Load the counter from the App Storage
try:
counter = sdk.AppStorage.get("page_views")
except Exception:
counter = 0
# Increase and save the counter
counter += 1
sdk.AppStorage.save("page_views", counter)
print(f"""
<!DOCTYPE html>
<html>
<body style="font-family: Arial, sans-serif; padding: 20px;">
<h1>Visitor Counter</h1>
<p>This page has been visited <strong>{counter}</strong> times.</p>
<p><em>The value is stored in the App Storage and remains persistent across page loads.</em></p>
</body>
</html>
""")
Static Info Page
print("""
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; padding: 20px; max-width: 800px; margin: 0 auto; }
.info { background: #e8f5e9; padding: 16px; border-radius: 8px;
border-left: 4px solid #4caf50; }
.warning { background: #fff3e0; padding: 16px; border-radius: 8px;
border-left: 4px solid #ff9800; }
</style>
</head>
<body>
<h1>Important Information</h1>
<div class="info">
<strong>Service times:</strong> Mon-Fri, 8:00 AM - 6:00 PM
</div>
<br>
<div class="warning">
<strong>Note:</strong> Maintenance work planned for the weekend.
</div>
</body>
</html>
""")
Apps have access to the same Enneo SDK that is also used for rule-based AI agents.
This allows contracts to be queried, tickets to be read, settings to be retrieved, and external APIs to be called.
More information can be found in the SDK Documentation.
API Reference
App Management
| Method | Endpoint | Description |
|---|
GET | /api/mind/app | List all apps |
POST | /api/mind/app | Create new app |
GET | /api/mind/app/{appId} | Retrieve app details (UUID or Slug) |
PATCH | /api/mind/app/{appId} | Update app (new revision) |
DELETE | /api/mind/app/{appId} | Delete app |
GET | /api/mind/app/{appId}/revisions | Show all revisions |
POST | /api/mind/app/{appId}/rollback/{revision} | Rollback to revision |
POST | /api/mind/app/{appId}/preview | Execute preview |
POST | /api/mind/app/import | Import app |
GET/POST | /api/mind/app/{appId}/run | Execute app (HTML-Response) |
App Storage
| Method | Endpoint | Description |
|---|
GET | /api/mind/app/{appId}/data | List all keys |
GET | /api/mind/app/{appId}/data/{key} | Read value |
GET | /api/mind/app/{appId}/data/{key}/meta | Read value with metadata |
PUT | /api/mind/app/{appId}/data/{key} | Save/update value |
DELETE | /api/mind/app/{appId}/data/{key} | Delete key |
All endpoints accept both the app UUID and the slug as {appId}.
The executor object in data.executorUser follows the standard executor format:
{
"id": 1,
"code": "print('<h1>Hello</h1>')",
"type": "sourceCode",
"language": "python311",
"packages": null,
"parameters": [],
"parameterDefinitions": []
}