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.

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.

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.

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.startrestituisce appena il motore arriva alla prima attesa — non aspetta mai che il processo finisca.workflow.signalsenzakeysveglia 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 dioutcomesono 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
maindeve 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.