Le SDK des scripts
Les huit modules du SDK api_manager — datasources, HTTP, logs, fichiers téléversés, secrets, notifications, workflows et rapports — avec des exemples Python.
Dans un script, l'app entière est à portée d'un import. Le SDK s'appelle
api_manager et s'importe module par module :
from api_manager import db, http, log, files, secrets, notify, workflow, reports
Ces huit noms sont toute la surface publique du SDK — ce qui n'est pas là n'existe pas à l'exécution :
| Module | Pour quoi |
|---|---|
db |
Interroger les datasources de l'app (Oracle, PostgreSQL, MySQL/MariaDB, SQL Server, …). |
http |
Requêtes HTTP vers des services externes, avec JSON automatique. |
log |
Écrire des lignes dans les logs de l'exécution. |
files |
Lire les fichiers envoyés par téléversement dans une API. |
secrets |
Lire les secrets de l'app, déchiffrés sur le serveur. |
notify |
Notifications in-app et e-mails aux utilisateurs de l'app. |
workflow |
Démarrer des processus, réveiller des attentes et trancher des tâches humaines. |
reports |
Générer les rapports de l'app en PDF ou Excel. |
L'éditeur complète tout cela — les noms, les paramètres et la documentation de chaque fonction apparaissent pendant que vous écrivez.
Dica
Le bouton Prompt pour LLM dans la barre de l'éditeur ouvre le guide complet du SDK, prêt à copier avec Tout copier. Collez-le dans Claude, ChatGPT ou un autre assistant, avec ce que vous voulez que le script fasse — l'assistant connaît alors le contrat exact et n'invente pas de fonctions qui n'existent pas.

db — les datasources de l'app
Les datasources configurés dans l'app se référencent par leur nom. Deux fonctions :
from api_manager import db
crm = db("Dados CRM")
linhas = crm.query( # liste de dicts (colonne -> valeur)
"select id, nome from contas where cidade = ?", ["Lisboa"]
)
conta = crm.query_one( # la première ligne, ou None
"select * from contas where id = ?", [42]
)
params est une liste positionnelle. Les placeholders sont ceux du moteur
du datasource :
| Moteur | Placeholders | Exemple |
|---|---|---|
| PostgreSQL | $1, $2, … |
where id = $1 |
| MySQL / MariaDB | ? |
where id = ? |
| Oracle | :1, :2, … |
where id = :1 |
Si le datasource n'existe pas, l'appel lève une erreur (et l'exécution se termine en Erreur, si vous ne la rattrapez pas).
Atenção
Les dates sous Oracle : évitez de les passer en paramètre — le transport en
JSON rend le type ambigu. Préférez le littéral dans le SQL :
TO_DATE('2026-06-25','YYYY-MM-DD').
http — requêtes vers des services externes
Cinq verbes, tous avec JSON automatique — un body dict ou liste part en
JSON, et une réponse JSON arrive déjà décodée :
from api_manager import http, log
r = http.get("https://api.exemplo.com/clientes")
# r = {"status": int, "ok": bool, "body": <json décodé ou texte>}
if r["ok"]:
log("reçus", len(r["body"]), "clients")
r = http.post(
"https://api.exemplo.com/leads",
body={"nome": "Vininha & Filhos", "origem": "keplin"},
headers={"Authorization": "Bearer abc123"},
)
Il existe aussi http.put(url, body, headers), http.patch(url, body, headers) et http.delete(url, headers). r["ok"] est vrai pour les réponses
2xx — un 404 ou un 500 ne lève pas d'exception, vérifiez le statut
vous-même.
log — les logs de l'exécution
from api_manager import log
log("traitement en cours", 42, {"fase": "inicial"})
Il accepte plusieurs arguments, comme le print — et chaque appel est une
ligne dans les logs de l'exécution, visible dans le panneau Résultat de
l'exécution et dans l'historique Exécutions. Le print classique est
capturé lui aussi, mais log est la forme canonique du SDK.

files — fichiers envoyés par téléversement
Quand une API de l'app a un argument de type Upload, le client envoie un
fichier et le script le reçoit dans input["args"] sous la forme d'un
handle — les octets restent sur disque, pas en mémoire. Le module files
opère sur ce handle :
from api_manager import files
def main(input):
f = input["args"]["ficheiro"] # le handle du téléversement
texto = files.read_text(f) # str (utf-8)
dados = files.read(f) # bytes
caminho = files.path(f) # chemin absolu sur disque
files.save(f, "ultimo-recebido.csv") # copie vers le dossier du script
return {"nome": f["filename"], "tamanho": f["size"]}
Le handle apporte filename, mimeType et size — utiles pour valider avant
de traiter.
secrets — les secrets de l'app
Les tokens et les clés se rangent dans la page Secrets de l'app (arborescence latérale, groupe App), chiffrés. Le script les lit par leur clé :
from api_manager import secrets
token = secrets.get("STRIPE_KEY") # -> str | None
if token is None:
raise RuntimeError("Secret STRIPE_KEY non configuré dans cette app.")
La valeur est déchiffrée sur le serveur, au moment de l'exécution — elle n'apparaît jamais dans le navigateur et ne reste pas dans le code.

Atenção
Ne faites pas log(token). Les logs restent dans l'historique des
exécutions — un secret écrit dans un log a cessé d'être un secret.
notify — notifications et e-mails
L'app a deux canaux fixes — un in-app et un d'e-mail — activables dans
les paramètres de Notifications de l'app (c'est là que vit la
configuration SMTP et l'expéditeur). users sont des noms d'utilisateur
de l'app ; roles se déploient vers tous les membres du rôle.
In-app, en temps réel :
from api_manager import notify
r = notify.send(
"Rapport prêt",
subtitle="Rapports mensuels",
body="Le rapport mensuel est disponible.",
users=["joao"],
roles=None,
data={"url": "/relatorios/42"},
)
# r = {"recipients": int, "delivered": int}
delivered compte ceux qui ont été livrés en temps réel, à qui est connecté ;
les autres restent dans la boîte de réception et les reçoivent à la connexion.
E-mail :
r = notify.email(
"Alerte de stock",
to=["chefe@empresa.pt"], # adresses libres
users=None,
roles=["admin"], # et/ou utilisateurs et rôles de l'app
text="Stock sous le minimum.",
html=None,
)
# r = {"accepted": [emails], "skipped": [usernames sans e-mail]}
L'envoi SMTP se fait de façon asynchrone sur le serveur — le script n'attend
pas. attachments accepte des pièces jointes avec le contenu en base64 (voir
reports ci-dessous pour le cas typique).
workflow — les processus de l'app
Un script peut démarrer un processus, réveiller ceux qui attendent un
événement, et trancher des tâches humaines. Le processus se référence par son
nom (ou par son identifiant stable) ; key est la clé de l'enregistrement sur
lequel il tourne :
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")
# tâches ouvertes de cet utilisateur de l'app, avec les décisions possibles
r = workflow.complete(task=17, outcome="aprovar", as_user="joao",
data={"nota": "ok"})
# r = {"ok": bool}
Les règles qui comptent :
workflow.startrend la main dès que le moteur atteint la première attente — il n'attend jamais que le processus se termine.workflow.signalsanskeyréveille tous les processus arrêtés sur cet événement ;{"woken": 0}n'est pas une erreur — il se peut que personne n'attende.- Dans
workflow.complete,as_userest obligatoire (qui décide reste dans l'historique) et la tâche doit être attribuée à cet utilisateur. Les valeurs d'outcomesont les sorties du nœud de la tâche — les mêmes que la personne voit sous forme de boutons. N'inventez pas de noms.
reports — générer les rapports de l'app
Le document est généré sur le serveur, avec les données de l'app, à partir d'un rapport existant :
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(
"Factures de mars",
to=["financeiro@empresa.pt"],
text="Veuillez trouver le document en pièce jointe.",
attachments=[{"filename": r["filename"], "content": r["base64"]}],
)
Le troisième argument facultatif est le format : "pdf" (par défaut) ou
"xlsx". Les paramètres sont ceux que le rapport déclare. Dans le résultat,
bytes arrive prêt à écrire sur disque et base64 prêt à joindre à un
e-mail.
Un exemple complet
Du genre du script atualizar_indicadores de l'app Gestion des Clients —
il lit le pipeline, écrit des logs utiles et prévient quand des clôtures
approchent :
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és /", abertas["total"], "EUR")
if fechos:
notify.send(
"Clôtures cette semaine",
body=f"{len(fechos)} opportunités dont la clôture est prévue avant le {limite}.",
roles=["comercial"],
)
return {
"oportunidades_abertas": abertas["n"],
"valor_pipeline": abertas["total"],
"fechos_proximos_7_dias": len(fechos),
}
Limites et bonnes pratiques
- Le résultat de
maindoit être sérialisable en JSON ; maximum 32 Mo. - Chaque exécution respecte le Temps limite du script (30 secondes à 10 minutes) — une fois dépassé, le processus est arrêté et reste en Timeout.
- Les exécutions sont isolées : ne dépendez pas des variables d'une exécution précédente. Persistez dans des fichiers du dossier du script ou dans un datasource.
- Faites
log(...)à chaque étape qui compte — c'est ce que vous lirez quand quelque chose échouera à 7h00 un dimanche. - Rattrapez les erreurs récupérables et enregistrez-les ; laissez remonter les fatales — une exception non rattrapée marque l'exécution en Erreur, et c'est bien ce que vous voulez voir dans l'historique quand quelque chose va vraiment mal.
- N'imprimez pas de secrets, et lisez-les toujours depuis
secrets— ne les écrivez jamais dans le code.
Questions fréquentes
from api_manager import db échoue sur mon ordinateur.
C'est attendu : le module api_manager n'existe qu'à l'exécution sur la
plateforme — c'est là qu'il est injecté. Dans l'éditeur, vous avez
l'autocomplétion complète du SDK ; le fichier api_manager.py que vous voyez
dans le dossier du script n'existe que pour cela.
db("nome") dit que le datasource n'existe pas.
Le nom doit être exactement celui du datasource dans l'app (ex. : Dados CRM, avec les majuscules et l'espace). Voyez la liste dans la section
Datasources de l'arborescence.
notify.email a renvoyé accepted mais l'e-mail n'est pas arrivé.
L'envoi SMTP est asynchrone — accepted signifie que l'e-mail est parti vers
le canal e-mail de l'app. Vérifiez la configuration SMTP dans les paramètres
de Notifications de l'app et le dossier de spam du destinataire.
Puis-je utiliser le SDK dans la validation d'une fenêtre ?
Oui — la validation s'exécute dans le même environnement que le script
principal, avec le même input et le même SDK. Voir
Planifications.