KEPLIN Docs

Das SDK der Skripte

Die acht Module des SDK api_manager — Datenquellen, HTTP, Logs, hochgeladene Dateien, Secrets, Benachrichtigungen, Workflows und Berichte — mit Python-Beispielen.

Innerhalb eines Skripts ist die ganze App nur einen import entfernt. Das SDK heißt api_manager und wird Modul für Modul importiert:

from api_manager import db, http, log, files, secrets, notify, workflow, reports

Diese acht Namen sind die gesamte öffentliche Oberfläche des SDK — was hier nicht steht, existiert zur Laufzeit nicht:

Modul Wofür
db Die Datenquellen der App abfragen (Oracle, PostgreSQL, MySQL/MariaDB, SQL Server, …).
http HTTP-Anfragen an externe Dienste, mit automatischem JSON.
log Zeilen in die Logs der Ausführung schreiben.
files Über eine API hochgeladene Dateien lesen.
secrets Geheimnisse der App lesen, auf dem Server entschlüsselt.
notify In-App-Benachrichtigungen und E-Mails an die Benutzer der App.
workflow Prozesse starten, Wartezustände aufwecken und menschliche Aufgaben entscheiden.
reports Die Berichte der App als PDF oder Excel erzeugen.

Der Editor vervollständigt all das automatisch — die Namen, die Parameter und die Dokumentation jeder Funktion erscheinen, während Sie schreiben.

Dica

Die Schaltfläche Prompt für LLM in der Leiste des Editors öffnet den vollständigen Leitfaden zum SDK, fertig zum Kopieren mit Alles kopieren. Fügen Sie ihn in Claude, ChatGPT oder einen anderen Assistenten ein, zusammen mit dem, was das Skript tun soll — der Assistent kennt dann den genauen Vertrag und erfindet keine Funktionen, die es nicht gibt.

Der Prompt für LLM — der gesamte Vertrag des SDK als Text, fertig zum Einfügen in einen KI-Assistenten.
Der Prompt für LLM — der gesamte Vertrag des SDK als Text, fertig zum Einfügen in einen KI-Assistenten.

db — die Datenquellen der App

Die in der App konfigurierten Datenquellen werden über ihren Namen referenziert. Zwei Funktionen:

from api_manager import db

crm = db("Dados CRM")

linhas = crm.query(          # Liste von Dicts (Spalte -> Wert)
    "select id, nome from contas where cidade = ?", ["Lisboa"]
)
conta = crm.query_one(       # die erste Zeile, oder None
    "select * from contas where id = ?", [42]
)

params ist eine positionelle Liste. Die Platzhalter sind die der Engine der Datenquelle:

Engine Platzhalter Beispiel
PostgreSQL $1, $2, … where id = $1
MySQL / MariaDB ? where id = ?
Oracle :1, :2, … where id = :1

Existiert die Datenquelle nicht, wirft der Aufruf einen Fehler (und die Ausführung endet mit Fehler, wenn Sie ihn nicht abfangen).

Atenção

Datumsangaben in Oracle: Übergeben Sie sie besser nicht als Parameter — der Transport per JSON macht den Typ mehrdeutig. Bevorzugen Sie das Literal im SQL: TO_DATE('2026-06-25','YYYY-MM-DD').

http — Anfragen an externe Dienste

Fünf Verben, alle mit automatischem JSON — ein body als Dict oder Liste geht als JSON hinaus, und eine JSON-Antwort kommt bereits dekodiert an:

from api_manager import http, log

r = http.get("https://api.exemplo.com/clientes")
# r = {"status": int, "ok": bool, "body": <dekodiertes JSON oder Text>}
if r["ok"]:
    log("empfangen", len(r["body"]), "Kunden")

r = http.post(
    "https://api.exemplo.com/leads",
    body={"nome": "Vininha & Filhos", "origem": "keplin"},
    headers={"Authorization": "Bearer abc123"},
)

Es gibt auch http.put(url, body, headers), http.patch(url, body, headers) und http.delete(url, headers). r["ok"] ist wahr für 2xx-Antworten — ein 404 oder 500 wirft keine Ausnahme, prüfen Sie den Status selbst.

log — die Logs der Ausführung

from api_manager import log

log("wird verarbeitet", 42, {"fase": "inicial"})

Es nimmt mehrere Argumente entgegen, wie print — und jeder Aufruf ist eine Zeile in den Logs der Ausführung, sichtbar im Panel Ergebnis der Ausführung und in der Historie Ausführungen. Das klassische print wird ebenfalls erfasst, aber log ist die kanonische Form des SDK.

Das Detail einer Ausführung — das zurückgegebene Ergebnis und die vom Skript geschriebenen Log-Zeilen.
Das Detail einer Ausführung — das zurückgegebene Ergebnis und die vom Skript geschriebenen Log-Zeilen.

files — hochgeladene Dateien

Wenn eine API der App ein Argument vom Typ Upload hat, schickt der Client eine Datei und das Skript erhält sie in input["args"] als Handle — die Bytes liegen auf der Festplatte, nicht im Speicher. Das Modul files arbeitet auf diesem Handle:

from api_manager import files


def main(input):
    f = input["args"]["ficheiro"]      # das Handle des Uploads
    texto = files.read_text(f)         # str (utf-8)
    dados = files.read(f)              # bytes
    caminho = files.path(f)            # absoluter Pfad auf der Festplatte
    files.save(f, "ultimo-recebido.csv")   # kopiert in den Ordner des Skripts
    return {"nome": f["filename"], "tamanho": f["size"]}

Das Handle bringt filename, mimeType und size mit — nützlich, um vor der Verarbeitung zu prüfen.

secrets — Geheimnisse der App

Tokens und Schlüssel liegen verschlüsselt auf der Seite Secrets der App (seitlicher Baum, Gruppe App). Das Skript liest sie über den Schlüssel:

from api_manager import secrets

token = secrets.get("STRIPE_KEY")   # -> str | None
if token is None:
    raise RuntimeError("Secret STRIPE_KEY in dieser App nicht konfiguriert.")

Der Wert wird auf dem Server entschlüsselt, im Moment der Ausführung — er erscheint nie im Browser und bleibt nicht im Code.

Die Seite Secrets der App — die Schlüssel, die die Skripte mit secrets.get lesen.
Die Seite Secrets der App — die Schlüssel, die die Skripte mit secrets.get lesen.

Atenção

Machen Sie kein log(token). Die Logs bleiben in der Ausführungshistorie — ein Geheimnis, das in ein Log geschrieben wurde, ist kein Geheimnis mehr.

notify — Benachrichtigungen und E-Mails

Die App hat zwei feste Kanäle — einen In-App-Kanal und einen für E-Mail —, aktivierbar in den Einstellungen für Benachrichtigungen der App (dort wohnen die SMTP-Konfiguration und der Absender). users sind Benutzernamen von Benutzern der App; roles weiten sich auf alle Mitglieder der Rolle aus.

In-App, in Echtzeit:

from api_manager import notify

r = notify.send(
    "Bericht fertig",
    subtitle="Monatsberichte",
    body="Der Monatsbericht ist verfügbar.",
    users=["joao"],
    roles=None,
    data={"url": "/relatorios/42"},
)
# r = {"recipients": int, "delivered": int}

delivered zählt die in Echtzeit Zugestellten, an alle, die verbunden sind; die übrigen bleiben im Posteingang und erhalten sie beim Anmelden.

E-Mail:

r = notify.email(
    "Bestandswarnung",
    to=["chefe@empresa.pt"],      # freie Adressen
    users=None,
    roles=["admin"],              # und/oder Benutzer und Rollen der App
    text="Bestand unter dem Minimum.",
    html=None,
)
# r = {"accepted": [emails], "skipped": [usernames ohne E-Mail]}

Der SMTP-Versand geschieht asynchron auf dem Server — das Skript wartet nicht. attachments nimmt Anhänge mit dem Inhalt in base64 entgegen (siehe reports weiter unten für den typischen Fall).

workflow — die Prozesse der App

Ein Skript kann einen Prozess starten, jene aufwecken, die auf ein Ereignis warten, und menschliche Aufgaben entscheiden. Der Prozess wird über seinen Namen referenziert (oder über seine stabile Kennung); key ist der Schlüssel des Datensatzes, auf dem er läuft:

from api_manager import workflow

r = workflow.start("Aprovação de despesa", 42, data={"valor": 1200})
# r = {"instanceId": int}

r = workflow.signal("visto", key=42)
# r = {"woken": int}

abertas = workflow.tasks("joao")
# offene Aufgaben dieses Benutzers der App, mit den möglichen Entscheidungen

r = workflow.complete(task=17, outcome="aprovar", as_user="joao",
                      data={"nota": "ok"})
# r = {"ok": bool}

Regeln, die zählen:

  • workflow.start gibt zurück, sobald die Maschine die erste Wartestelle erreicht — es wartet nie darauf, dass der Prozess endet.
  • workflow.signal ohne key weckt alle Prozesse auf, die bei diesem Ereignis stehen; {"woken": 0} ist kein Fehler — es kann sein, dass niemand wartet.
  • Bei workflow.complete ist as_user verpflichtend (wer entscheidet, bleibt in der Historie) und die Aufgabe muss diesem Benutzer zugewiesen sein. Die Werte von outcome sind die Ausgänge des Aufgabenknotens — dieselben, die die Person als Schaltflächen sieht. Erfinden Sie keine Namen.

reports — Berichte der App erzeugen

Das Dokument wird auf dem Server erzeugt, mit den Daten der App, ausgehend von einem bestehenden Bericht:

from api_manager import notify, reports

r = reports.render("Facturas do mês", {"mes": "2026-03"})
# r = {"filename", "pages", "format", "mime", "bytes", "base64"}

notify.email(
    "Rechnungen für März",
    to=["financeiro@empresa.pt"],
    text="Anbei das Dokument.",
    attachments=[{"filename": r["filename"], "content": r["base64"]}],
)

Das dritte, optionale Argument ist das Format: "pdf" (Standard) oder "xlsx". Die Parameter sind die, die der Bericht deklariert. Im Ergebnis kommt bytes fertig zum Schreiben auf die Festplatte und base64 fertig zum Anhängen an eine E-Mail.

Ein vollständiges Beispiel

In der Art des Skripts atualizar_indicadores der App Kundenverwaltung — es liest die Pipeline, schreibt nützliche Logs und meldet sich, wenn Abschlüsse bevorstehen:

from datetime import date, timedelta

from api_manager import db, log, notify


def main(input):
    crm = db("Dados CRM")

    abertas = crm.query_one(
        "select count(*) as n, coalesce(sum(valor), 0) as total "
        "from oportunidades where fase not in ('fechada_ganha', 'fechada_perdida')"
    )

    limite = (date.today() + timedelta(days=7)).isoformat()
    fechos = crm.query(
        "select titulo, data_fecho from oportunidades "
        "where fase not in ('fechada_ganha', 'fechada_perdida') "
        "and data_fecho <= ? order by data_fecho",
        [limite],
    )

    log("Pipeline:", abertas["n"], "Verkaufschancen /", abertas["total"], "EUR")
    if fechos:
        notify.send(
            "Abschlüsse in dieser Woche",
            body=f"{len(fechos)} Verkaufschancen mit geplantem Abschluss bis {limite}.",
            roles=["comercial"],
        )

    return {
        "oportunidades_abertas": abertas["n"],
        "valor_pipeline": abertas["total"],
        "fechos_proximos_7_dias": len(fechos),
    }

Grenzen und gute Praxis

  • Das Ergebnis von main muss JSON-serialisierbar sein; maximal 32 MB.
  • Jede Ausführung achtet auf das Zeitlimit des Skripts (30 Sekunden bis 10 Minuten) — bei Überschreitung wird der Prozess beendet und bleibt auf Timeout.
  • Die Ausführungen sind isoliert: Verlassen Sie sich nicht auf Variablen einer vorherigen Ausführung. Speichern Sie dauerhaft in Dateien des Skript-Ordners oder in einer Datenquelle.
  • Setzen Sie in jeder relevanten Phase ein log(...) — das ist es, was Sie lesen werden, wenn etwas an einem Sonntag um 7:00 Uhr fehlschlägt.
  • Fangen Sie die behebbaren Fehler ab und protokollieren Sie sie; lassen Sie die fatalen durch — eine nicht abgefangene Ausnahme markiert die Ausführung als Fehler, und genau das wollen Sie in der Historie sehen, wenn wirklich etwas im Argen liegt.
  • Geben Sie keine Geheimnisse aus und lesen Sie sie immer über secrets — schreiben Sie sie nie in den Code.

Häufige Fragen

from api_manager import db schlägt auf meinem Rechner fehl. Das ist zu erwarten: Das Modul api_manager existiert nur bei der Ausführung auf der Plattform — dort wird es eingespeist. Im Editor haben Sie die vollständige Autovervollständigung des SDK; die Datei api_manager.py, die Sie im Ordner des Skripts sehen, existiert nur dafür.

db("nome") sagt, dass die Datenquelle nicht existiert. Der Name muss exakt dem der Datenquelle in der App entsprechen (z. B.: Dados CRM, mit Großbuchstaben und Leerzeichen). Die Liste sehen Sie im Abschnitt Datenquellen des Baums.

notify.email hat accepted zurückgegeben, aber die E-Mail kam nicht an. Der SMTP-Versand ist asynchron — accepted bedeutet, dass die E-Mail an den E-Mail-Kanal der App weitergereicht wurde. Prüfen Sie die SMTP-Konfiguration in den Einstellungen für Benachrichtigungen der App und den Spam-Ordner des Empfängers.

Kann ich das SDK in der Validierung eines Fensters verwenden? Ja — die Validierung läuft in derselben Umgebung wie das Hauptskript, mit demselben input und demselben SDK. Siehe Zeitpläne.