KEPLIN Docs

L'SDK degli script

Gli otto moduli dell'SDK api_manager — datasource, HTTP, log, file di upload, secrets, notifiche, workflow e report — con esempi in Python.

Dentro uno script, l'app intera è a portata di un import. L'SDK si chiama api_manager e si importa modulo per modulo:

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

Questi otto nomi sono l'intera superficie pubblica dell'SDK — quello che non è qui non esiste in esecuzione:

Modulo A cosa serve
db Interrogare i datasource dell'app (Oracle, PostgreSQL, MySQL/MariaDB, SQL Server, …).
http Richieste HTTP a servizi esterni, con JSON automatico.
log Scrivere righe nei log dell'esecuzione.
files Leggere file inviati tramite upload in un'API.
secrets Leggere i segreti dell'app, decifrati sul server.
notify Notifiche in-app ed email agli utenti dell'app.
workflow Avviare processi, svegliare attese e decidere attività umane.
reports Generare i report dell'app in PDF o Excel.

L'editor completa automaticamente tutto questo — i nomi, i parametri e la documentazione di ogni funzione compaiono mentre scrivi.

Dica

Il pulsante Prompt per LLM nella barra dell'editor apre la guida completa dell'SDK, pronta da copiare con Copia tutto. Incollala in Claude, ChatGPT o un altro assistente, insieme a quello che vuoi che lo script faccia — l'assistente arriva a conoscere il contratto esatto e non inventa funzioni che non esistono.

Il Prompt per LLM — tutto il contratto dell'SDK in un testo pronto da incollare in un assistente di IA.
Il Prompt per LLM — tutto il contratto dell'SDK in un testo pronto da incollare in un assistente di IA.

db — i datasource dell'app

I datasource configurati nell'app si richiamano per nome. Due funzioni:

from api_manager import db

crm = db("Dados CRM")

linhas = crm.query(          # elenco di dict (colonna -> valore)
    "select id, nome from contas where cidade = ?", ["Lisboa"]
)
conta = crm.query_one(       # la prima riga, oppure None
    "select * from contas where id = ?", [42]
)

params è un elenco posizionale. I segnaposto sono quelli del motore del datasource:

Motore Segnaposto Esempio
PostgreSQL $1, $2, … where id = $1
MySQL / MariaDB ? where id = ?
Oracle :1, :2, … where id = :1

Se il datasource non esiste, la chiamata solleva un errore (e l'esecuzione termina in Errore, se non lo intercetti).

Atenção

Date in Oracle: evita di passarle come parametro — il trasporto in JSON rende il tipo ambiguo. Preferisci il letterale nell'SQL: TO_DATE('2026-06-25','YYYY-MM-DD').

http — richieste a servizi esterni

Cinque verbi, tutti con JSON automatico — un body dict o lista parte come JSON, e una risposta JSON arriva già decodificata:

from api_manager import http, log

r = http.get("https://api.exemplo.com/clientes")
# r = {"status": int, "ok": bool, "body": <json decodificato o testo>}
if r["ok"]:
    log("ricevuti", len(r["body"]), "clienti")

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

Esistono anche http.put(url, body, headers), http.patch(url, body, headers) e http.delete(url, headers). r["ok"] è vero per le risposte 2xx — un 404 o un 500 non solleva un'eccezione, lo stato lo controlli tu.

log — i log dell'esecuzione

from api_manager import log

log("in elaborazione", 42, {"fase": "inicial"})

Accetta più argomenti, come il print — e ogni chiamata è una riga nei log dell'esecuzione, visibile nel pannello Esito dell'esecuzione e nella cronologia Esecuzioni. Anche il print classico viene catturato, ma log è la forma canonica dell'SDK.

Il dettaglio di un'esecuzione — il risultato restituito e le righe di log scritte dallo script.
Il dettaglio di un'esecuzione — il risultato restituito e le righe di log scritte dallo script.

files — file inviati tramite upload

Quando un'API dell'app ha un argomento di tipo Upload, il client invia un file e lo script lo riceve in input["args"] come handle — i byte restano su disco, non in memoria. Il modulo files opera su quell'handle:

from api_manager import files


def main(input):
    f = input["args"]["ficheiro"]      # l'handle dell'upload
    texto = files.read_text(f)         # str (utf-8)
    dados = files.read(f)              # bytes
    caminho = files.path(f)            # percorso assoluto su disco
    files.save(f, "ultimo-recebido.csv")   # copia nella cartella dello script
    return {"nome": f["filename"], "tamanho": f["size"]}

L'handle porta filename, mimeType e size — utili per convalidare prima di elaborare.

secrets — segreti dell'app

Token e chiavi si conservano nella pagina Secrets dell'app (albero laterale, gruppo App), cifrati. Lo script li legge tramite la chiave:

from api_manager import secrets

token = secrets.get("STRIPE_KEY")   # -> str | None
if token is None:
    raise RuntimeError("Secret STRIPE_KEY non configurato in questa app.")

Il valore viene decifrato sul server, nel momento dell'esecuzione — non compare mai nel browser né resta nel codice.

La pagina Secrets dell'app — le chiavi che gli script leggono con secrets.get.
La pagina Secrets dell'app — le chiavi che gli script leggono con secrets.get.

Atenção

Non fare log(token). I log restano nella cronologia delle esecuzioni — un segreto scritto in un log ha smesso di essere un segreto.

notify — notifiche ed email

L'app ha due canali fissi — uno in-app e uno di email — attivabili nelle impostazioni di Notifiche dell'app (è lì che vive la configurazione SMTP e il mittente). users sono username di utenti dell'app; roles si espandono a tutti i membri del ruolo.

In-app, in tempo reale:

from api_manager import notify

r = notify.send(
    "Report pronto",
    subtitle="Report mensili",
    body="Il report mensile è disponibile.",
    users=["joao"],
    roles=None,
    data={"url": "/relatorios/42"},
)
# r = {"recipients": int, "delivered": int}

delivered conta quelli consegnati in tempo reale, a chi è collegato; i restanti restano nella posta in arrivo e li ricevono al momento dell'accesso.

Email:

r = notify.email(
    "Avviso di stock",
    to=["chefe@empresa.pt"],      # indirizzi liberi
    users=None,
    roles=["admin"],              # e/o utenti e ruoli dell'app
    text="Stock sotto il minimo.",
    html=None,
)
# r = {"accepted": [emails], "skipped": [username senza email]}

L'invio SMTP avviene in modo asincrono sul server — lo script non resta in attesa. attachments accetta allegati con il contenuto in base64 (vedi reports qui sotto per il caso tipico).

workflow — i processi dell'app

Uno script può avviare un processo, svegliare chi sta aspettando un evento, e decidere attività umane. Il processo si richiama per nome (o tramite l'identificatore stabile); key è la chiave del record su cui viene eseguito:

from api_manager import workflow

r = workflow.start("Approvazione spesa", 42, data={"valor": 1200})
# r = {"instanceId": int}

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

abertas = workflow.tasks("joao")
# attività aperte di quell'utente dell'app, con le decisioni possibili

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

Regole che contano:

  • workflow.start restituisce appena il motore arriva alla prima attesa — non aspetta mai che il processo finisca.
  • workflow.signal senza key sveglia tutti i processi fermi su quell'evento; {"woken": 0} non è un errore — può non esserci nessuno in attesa.
  • In workflow.complete, as_user è obbligatorio (chi decide resta nella cronologia) e l'attività deve essere assegnata a quell'utente. I valori di outcome sono le uscite del nodo dell'attività — gli stessi che la persona vede come pulsanti. Non inventare nomi.

reports — generare i report dell'app

Il documento viene generato sul server, con i dati dell'app, a partire da un report esistente:

from api_manager import notify, reports

r = reports.render("Fatture del mese", {"mes": "2026-03"})
# r = {"filename", "pages", "format", "mime", "bytes", "base64"}

notify.email(
    "Fatture di marzo",
    to=["financeiro@empresa.pt"],
    text="In allegato.",
    attachments=[{"filename": r["filename"], "content": r["base64"]}],
)

Il terzo argomento opzionale è il formato: "pdf" (predefinito) o "xlsx". I parametri sono quelli che il report dichiara. Nel risultato, bytes arriva pronto da salvare su disco e base64 pronto da allegare a un'email.

Un esempio completo

Sulla falsariga dello script atualizar_indicadores dell'app Gestione Clienti — legge il pipeline, scrive log utili e avvisa quando ci sono chiusure vicine:

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"], "opportunità /", abertas["total"], "EUR")
    if fechos:
        notify.send(
            "Chiusure di questa settimana",
            body=f"{len(fechos)} opportunità con chiusura prevista entro {limite}.",
            roles=["comercial"],
        )

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

Limiti e buone pratiche

  • Il risultato di main deve essere serializzabile in JSON; massimo 32 MB.
  • Ogni esecuzione rispetta il Limite di tempo dello script (da 30 secondi a 10 minuti) — al superamento, il processo viene terminato e resta Timeout.
  • Le esecuzioni sono isolate: non dipendere da variabili di un'esecuzione precedente. Persisti in file della cartella dello script o in un datasource.
  • Fai log(...) a ogni fase rilevante — è quello che leggerai quando qualcosa fallirà alle 7:00 di una domenica.
  • Intercetta gli errori recuperabili e registrali; lascia salire quelli fatali — un'eccezione non gestita segna l'esecuzione come Errore, ed è questo che vuoi vedere nella cronologia quando qualcosa va davvero male.
  • Non stampare segreti, e leggili sempre da secrets — non scriverli mai nel codice.

Domande frequenti

from api_manager import db fallisce sul mio computer. È previsto: il modulo api_manager esiste solo in esecuzione sulla piattaforma — è lì che viene iniettato. Nell'editor hai il completamento automatico completo dell'SDK; il file api_manager.py che vedi nella cartella dello script esiste solo per quello.

db("nome") dice che il datasource non esiste. Il nome deve essere esattamente quello del datasource nell'app (es.: Dados CRM, con le maiuscole e lo spazio). Guarda l'elenco nella sezione Datasource dell'albero.

notify.email ha restituito accepted ma l'email non è arrivata. L'invio SMTP è asincrono — accepted significa che l'email è passata al canale email dell'app. Controlla la configurazione SMTP nelle impostazioni di Notifiche dell'app e la casella di spam del destinatario.

Posso usare l'SDK nella convalida di una finestra? Sì — la convalida viene eseguita nello stesso ambiente dello script principale, con lo stesso input e lo stesso SDK. Vedi Pianificazioni.