KEPLIN Docs

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.

Le Prompt pour LLM — tout le contrat du SDK dans un texte prêt à coller dans un assistant IA.
Le Prompt pour LLM — tout le contrat du SDK dans un texte prêt à coller dans un assistant IA.

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.

Le détail d'une exécution — le résultat renvoyé et les lignes de log écrites par le script.
Le détail d'une exécution — le résultat renvoyé et les lignes de log écrites par le script.

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.

La page Secrets de l'app — les clés que les scripts lisent avec secrets.get.
La page Secrets de l'app — les clés que les scripts lisent avec secrets.get.

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.start rend la main dès que le moteur atteint la première attente — il n'attend jamais que le processus se termine.
  • workflow.signal sans key ré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_user est obligatoire (qui décide reste dans l'historique) et la tâche doit être attribuée à cet utilisateur. Les valeurs d'outcome sont 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 main doit ê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.