Enneo

Apps

Apps

Eigene HTML-Anwendungen erstellen und in Enneo integrieren

Warning

Vorschau-Feature — Apps befindet sich derzeit in der Vorschau-Phase. Funktionalität und API können sich noch ändern. Wir freuen uns über Feedback!

Tip

Apps ermöglichen es, eigene HTML-Anwendungen direkt in Enneo zu erstellen und auszuführen. Nutzen Sie Apps für individuelle Dashboards, Berichte, Formulare oder Tools — alles im vertrauten Enneo-Interface.

Überblick

Apps sind benutzerdefinierte Anwendungen, die HTML zurückgeben und direkt in Enneo als eigene Seite angezeigt werden. Jede App wird in einer Sandbox ausgeführt und hat Zugriff auf die Enneo-API.

Typische Anwendungsfälle:

  • Individuelle Dashboards — Echtzeitdaten aus dem ERP oder CRM visualisieren
  • Berichte und Auswertungen — Maßgeschneiderte Reports mit eigener Logik
  • Interne Tools — Formulare, Kalkulatoren oder Workflow-Helfer
  • Datenabfragen — Vertragsdaten, Kundenstatus oder Statistiken anzeigen

Aufbau einer App

Jede App besteht aus:

BestandteilBeschreibung
NameAnzeigename in der Seitenleiste und in den Einstellungen
SlugEindeutiger technischer Bezeichner (z.B. mein-dashboard). Wird in der URL verwendet
BeschreibungOptionale Beschreibung des Zwecks der App
AnbieterOptional: Wer die App erstellt hat
ExecutorDer Code, der bei Aufruf ausgeführt wird (Python, Node.js oder PHP)

Executor

Der Executor verwendet das Standard-Executor-Format von Enneo (gleicher Aufbau wie bei Einstellungen und KI-Agenten). Er unterstützt aktuell den Typ Quellcode mit den Sprachen Python 3.11, Node.js 20 und PHP 8.2.

Die Ausgabe auf stdout wird als HTML an den Browser zurückgegeben.

# Einfache App: Begrüßungsseite
print("<h1>Willkommen</h1>")
print("<p>Dies ist meine erste Enneo-App.</p>")
// Einfache App: Begrüßungsseite
console.log("<h1>Willkommen</h1>");
console.log("<p>Dies ist meine erste Enneo-App.</p>");
<?php
echo "<h1>Willkommen</h1>";
echo "<p>Dies ist meine erste Enneo-App.</p>";

Eingabeparameter

Bei jedem Aufruf erhält der Code automatisch Metadaten als Parameter über STDIN:

{
  "_metadata": {
    "userId": 1,
    "clientId": "1",
    "appId": "a1b2c3d4-...",
    "appRevision": 3,
    "userAuthToken": "Bearer ..."
  }
}

Zusätzliche GET- oder POST-Parameter werden automatisch übergeben und stehen im Code zur Verfügung.

Umgebungsvariablen

Folgende Umgebungsvariablen stehen im App-Code zur Verfügung:

VariableBeschreibung
ENNEO_API_URLBasis-URL der Enneo-API
ENNEO_SESSION_TOKENSession-Token für API-Aufrufe
ENNEO_USER_AUTH_HEADERAuth-Header des aufrufenden Benutzers
ENNEO_APP_IDUUID der App
ENNEO_APP_SLUGSlug der App
ENNEO_APP_VERSIONVersion der App (z.B. 1.0.0)
SDKPfad zur SDK-Datei

Apps nutzen

Seitenleiste

Sobald mindestens eine App existiert und der Benutzer die Berechtigung readApps hat, erscheint in der Seitenleiste der Menüpunkt Apps. Hier werden alle verfügbaren Apps als Tabs angezeigt.

Die App-URL im Browser verwendet den Slug der App:

/apps/mein-dashboard

App aufrufen

Jede App wird in einem iFrame innerhalb von Enneo ausgeführt. Die API-URL akzeptiert sowohl UUID als auch Slug:

/api/mind/app/{slug}/run
/api/mind/app/{appId}/run

GET-Parameter können direkt angehängt werden:

/api/mind/app/mein-dashboard/run?param1=wert1&param2=wert2

App Storage

Apps können persistente Daten speichern und lesen. Der App Storage ist ein Key-Value-Store, der pro App isoliert ist. Die Werte werden als JSON gespeichert.

Warning

Der App Storage ist gemeinsamer Speicher der App, kein persönlicher Speicher ihrer Benutzer. Es gibt keine Trennung nach Benutzer und keinen Eigentümer je Schlüssel: Jeder Code der App liest und überschreibt alle Schlüssel dieser App. Legen Sie dort keine Passwörter, Tokens oder personenbezogenen Daten ab — Zugangsdaten gehören in die Secrets und werden von dort über {{secret.KEY}} referenziert.

Zugriffswege

WegWerWofür gedacht
SDK (AppStorage) aus dem App-CodeDie App selbst, unter dem Konto des ExecutorsDer vorgesehene Weg
REST-Endpunkte /api/mind/app/{appId}/data…Benutzer mit der Berechtigung manageAppData — standardmäßig nur AdminEinsehen und Korrigieren von Hand

Das SDK benötigt manageAppData nicht: Es greift unter dem Konto des Executors auf den Storage zu, nicht unter dem des angemeldeten Benutzers. Ein Agent kann den Storage also über die App nutzen, ihn aber nicht direkt über die API auslesen oder verändern.

SDK-Zugriff

# Wert speichern
sdk.AppStorage.save("counter", 42)
sdk.AppStorage.save("config", {"theme": "dark", "lang": "de"})

# Wert lesen
counter = sdk.AppStorage.get("counter")  # → 42
config = sdk.AppStorage.get("config")    # → {"theme": "dark", "lang": "de"}

# Wert mit Metadaten lesen
meta = sdk.AppStorage.get_meta("counter")
# → {"key": "counter", "value": 42, "createdAt": "...", "modifiedAt": "...", "lastAccessAt": "..."}

# Alle Schlüssel auflisten
keys = sdk.AppStorage.list_keys()
# → [{"key": "counter", "createdAt": "...", ...}, ...]

# Wert löschen
sdk.AppStorage.delete("counter")
<?php
// Wert speichern
AppStorage::save("counter", 42);
AppStorage::save("config", ["theme" => "dark", "lang" => "de"]);

// Wert lesen
$counter = AppStorage::get("counter");  // → 42
$config = AppStorage::get("config");    // → {"theme": "dark", "lang": "de"}

// Wert mit Metadaten lesen
$meta = AppStorage::getMeta("counter");

// Alle Schlüssel auflisten
$keys = AppStorage::listKeys();

// Wert löschen
AppStorage::delete("counter");

App-Info

Über die Klasse AppInfo können App-Metadaten abgerufen werden:

app_id = sdk.AppInfo.get_id()        # UUID der App
app_slug = sdk.AppInfo.get_slug()    # Slug der App
app_version = sdk.AppInfo.get_version()  # Version (z.B. "1.0.0")
<?php
$appId = AppInfo::getId();
$appSlug = AppInfo::getSlug();
$appVersion = AppInfo::getVersion();

Unterstützte Werttypen

TypBeispiel
String"hello world"
Zahl42, 3.14
Booleantrue, false
Nullnull
Objekt{"key": "value"}
Array[1, "two", 3.0]

Benutzerspezifische Daten und geschützte Werte

Soll eine App Daten je Benutzer trennen oder Werte nur bestimmten Benutzern zeigen, lässt sich das über den App Storage nicht lösen — er kennt keine Benutzer. Legen Sie die Regel stattdessen in eine benutzerdefinierte Funktion (UDF), die die App aufruft:

  1. Die App ruft die UDF auf. Der Aufruf läuft unter dem angemeldeten Benutzer.
  2. Die UDF ermittelt den Aufrufer, indem sie die Enneo-API mit dessen Autorisierung abfragt.
  3. Erst danach greift die UDF — unter dem Konto des Executors — auf den Storage zu und gibt nur zurück, was dieser Benutzer sehen darf.
# In der UDF: Aufrufer über die API ermitteln, nicht aus den Parametern
profil = sdk.ApiEnneo.get('/api/mind/profile')   # läuft unter dem aufrufenden Benutzer
benutzer_id = profil['id']
berechtigungen = profil['permissions']

# Erst danach auf den gemeinsamen Storage zugreifen
notizen = sdk.ApiEnneo.get(
    f'/api/mind/app/{app_id}/data/notizen',
    authorizeAs='serviceWorker',
)['value']

return notizen.get(str(benutzer_id), {})

Warning

Zwei Punkte entscheiden darüber, ob diese Prüfung trägt:

  • Ermitteln Sie den Aufrufer ausschließlich über den API-Aufruf, nie aus den Parametern. Werte wie _metadata.userId stammen aus dem aufrufenden Code und lassen sich beliebig setzen. ENNEO_USER_ID steht nur Apps zur Verfügung, nicht UDFs.
  • Die UDF muss für die betroffenen Benutzer ausführbar sein, sonst erreichen sie sie nicht. Die gesamte Rechteprüfung liegt damit im Code der UDF — behandeln Sie ihn entsprechend.

Apps verwalten

Die Verwaltung erfolgt unter Einstellungen → Erweiterte Einstellungen → Apps.

Neue App erstellen

  1. Navigieren Sie zu Einstellungen → Apps
  2. Klicken Sie auf App erstellen
  3. Geben Sie Name, Slug und optional Beschreibung ein
  4. Schreiben Sie den Executor-Code
  5. Nutzen Sie die Vorschau, um die Ausgabe zu prüfen
  6. Klicken Sie auf Speichern

App bearbeiten

Jede Änderung erstellt automatisch eine neue Revision. So können Sie jederzeit zu einer früheren Version zurückkehren.

Ungespeicherte Änderungen werden durch ein Entwurf-Tag in der Seitenleiste angezeigt. Beim Navigieren mit ungespeicherten Änderungen erscheint eine Warnung.

Export und Import

Apps können als JSON exportiert und importiert werden. Der Export-Button befindet sich im Tab Spezifikationen. Dies ermöglicht den Transfer zwischen Umgebungen.

Revisionen und Rollback

Apps unterstützen Versionierung:

  • Jede Speicherung erstellt eine neue Revision
  • Über die Revisions-Übersicht können Sie zu jeder früheren Version zurückkehren
  • Die aktive Revision wird den Benutzern angezeigt

Berechtigungen

BerechtigungBeschreibungStandard-Rollen
readAppsApps ansehen und ausführenAdmin, Agent
appCreatorApps erstellen, bearbeiten und VorschauAdmin
manageAppsApps importieren und löschenAdmin
manageAppDataApp Storage direkt über die API lesen, schreiben und löschenAdmin

Beispiele

Dashboard mit Vertragsdaten

import importlib.util, os, json, sys

# SDK laden
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)

# Eingabedaten laden
input_data = sdk.load_input_data()
metadata = input_data.get('_metadata', {})

# Vertragsdaten laden (Beispiel)
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', 'Unbekannt')
except Exception:
    name = "Nicht gefunden"
    status = "-"

# HTML ausgeben
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>Vertragsübersicht</h1>
    <div class="card">
        <strong>Name:</strong> {name}<br>
        <strong>Status:</strong> {status}<br>
        <strong>Vertragsnummer:</strong> {contract_id}
    </div>
</body>
</html>
""")

App mit persistentem Zähler

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()

# Zähler aus App Storage laden
try:
    counter = sdk.AppStorage.get("page_views")
except Exception:
    counter = 0

# Zähler erhöhen und speichern
counter += 1
sdk.AppStorage.save("page_views", counter)

print(f"""
<!DOCTYPE html>
<html>
<body style="font-family: Arial, sans-serif; padding: 20px;">
    <h1>Besucherzähler</h1>
    <p>Diese Seite wurde <strong>{counter}</strong> mal aufgerufen.</p>
    <p><em>Der Wert wird im App Storage gespeichert und bleibt über Seitenaufrufe hinweg erhalten.</em></p>
</body>
</html>
""")

Statische Info-Seite

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>Wichtige Informationen</h1>
    <div class="info">
        <strong>Servicezeiten:</strong> Mo-Fr, 8:00 - 18:00 Uhr
    </div>
    <br>
    <div class="warning">
        <strong>Hinweis:</strong> Wartungsarbeiten am Wochenende geplant.
    </div>
</body>
</html>
""")

Tip

Apps haben Zugriff auf das gleiche Enneo SDK, das auch für regelbasierte KI-Agenten verwendet wird. Damit können Sie Verträge abfragen, Tickets lesen, Einstellungen abrufen und externe APIs aufrufen. Weitere Informationen finden Sie in der SDK-Dokumentation.

API-Referenz

App-Verwaltung

MethodeEndpunktBeschreibung
GET/api/mind/appAlle Apps auflisten
POST/api/mind/appNeue App erstellen
GET/api/mind/app/{appId}App-Details abrufen (UUID oder Slug)
PATCH/api/mind/app/{appId}App aktualisieren (neue Revision)
DELETE/api/mind/app/{appId}App löschen
GET/api/mind/app/{appId}/revisionsAlle Revisionen anzeigen
POST/api/mind/app/{appId}/rollback/{revision}Auf Revision zurücksetzen
POST/api/mind/app/{appId}/previewVorschau ausführen
POST/api/mind/app/importApp importieren
GET/POST/api/mind/app/{appId}/runApp ausführen (HTML-Antwort)

App Storage

MethodeEndpunktBeschreibung
GET/api/mind/app/{appId}/dataAlle Schlüssel auflisten
GET/api/mind/app/{appId}/data/{key}Wert lesen
GET/api/mind/app/{appId}/data/{key}/metaWert mit Metadaten lesen
PUT/api/mind/app/{appId}/data/{key}Wert speichern/aktualisieren
DELETE/api/mind/app/{appId}/data/{key}Schlüssel löschen

Alle Endpunkte akzeptieren sowohl die App-UUID als auch den Slug als {appId}.

Diese fünf Endpunkte erfordern die Berechtigung manageAppData. Aus dem App-Code heraus verwenden Sie stattdessen das SDK (AppStorage) — es benötigt die Berechtigung nicht.

Datenformat (executorUser)

Das Executor-Objekt in data.executorUser folgt dem Standard-Executor-Format:

{
  "id": 1,
  "code": "print('<h1>Hello</h1>')",
  "type": "sourceCode",
  "language": "python311",
  "packages": null,
  "parameters": [],
  "parameterDefinitions": []
}