Aller au contenu

Démarrer avec l'API v2

En bref

L'API v2 du connecteur SenSaaS expose Sage 100c en HTTP/JSON : 728 opérations sur 398 chemins, réparties en 108 groupes. Toute réponse en succès est emballée dans une enveloppe { success, data, meta } ; toute erreur sort en ProblemDetails RFC 9457. L'authentification se fait par jeton JWT porteur.

Cette page s'adresse à l'intégrateur : elle donne le minimum pour appeler le connecteur depuis un programme. Le détail opération par opération est dans les groupes, les règles de format dans les conventions.

URL de base et contrat

Le connecteur écoute en HTTPS sur le port 443. Toutes les adresses de cette section sont relatives à l'hôte du connecteur, noté https://<votre-connecteur> dans les exemples.

Ressource Adresse Accès
Contrat OpenAPI v2 (3.0.4) GET /swagger/v2/swagger.json anonyme, toujours exposé
Contrat OpenAPI v1 (surface historique) GET /swagger/v1/swagger.json anonyme, toujours exposé
Outil de test intégré GET /test seulement si l'option SwaggerUI est active

Le contrat JSON est toujours servi, même quand l'outil de test est désactivé : il ne contient aucune donnée métier, seulement la description des routes. C'est lui qui alimente l'outillage, dont le serveur MCP « sensaas-connecteur ».

L'outil de test embarqué permet d'essayer une opération depuis un navigateur : on colle le jeton dans « Authorize », puis on déclenche l'appel.

Capture à venir

Outil de test de l'API SenSaaS, liste des opérations v2 — identifiant connecteur/swagger-ui. L'image sera ajoutée lors de la prochaine campagne de captures.

S'authentifier

Obtenir un jeton

POST /v2/auth/login prend un corps JSON { "username": …, "password": … } et renvoie un jeton dans l'enveloppe de réponse.

curl -s -X POST "https://<votre-connecteur>/v2/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username": "integration", "password": "<mot-de-passe>"}'
{
  "success": true,
  "data": {
    "userName": "integration",
    "serverName": "SENSAAS-DEMO",
    "success": true,
    "message": "",
    "token": "eyJhbGciOiJIUzUxMiIs…",
    "tokenExpiration": "2026-09-11T10:20:00",
    "refreshToken": "…",
    "refreshTokenExpiration": "2026-09-18T10:00:00",
    "b2b": false
  },
  "meta": { "correlationId": "…", "durationMs": 42, "dossiers": null }
}

Utiliser le jeton

Le jeton se place dans l'en-tête Authorization de chaque appel :

GET /v2/tiersbanque?targets=DEMO&limit=50 HTTP/1.1
Host: votre-connecteur
Authorization: Bearer <jeton>
Accept: application/json
Point Valeur
Signature HMAC-SHA512, clé propre à l'installation
Durée de vie du jeton 20 minutes en production, 4 heures en débogage
Renouvellement POST /v2/auth/refresh-token ; le jeton de renouvellement vit 7 jours
Stockage du renouvellement cookie HttpOnly, révocable côté connecteur
Limite de débit 30 requêtes par minute et par adresse IP sur les routes d'authentification

Pas de CORS

Aucune politique CORS n'est enregistrée, volontairement : l'API vise des clients natifs et des services, pas des pages web tierces. Un appel depuis un navigateur en cross-origin sera refusé par le navigateur.

Dix-sept opérations /v2 sont anonymes et n'exigent pas de jeton : les entrées d'authentification (/v2/auth/login, /v2/auth/login-token, /v2/auth/refresh-token, /v2/auth/register, /v2/auth/ping), les sondes du serveur (/v2/serveur/ping, /v2/serveur/check, /v2/serveur/sync-confirm), les routes réseau /v2/reseau/… et le service d'images /v2/images/{imageHash}/{maxWidth}/{maxHeight}. Les pages de groupe le signalent opération par opération.

L'enveloppe de réponse

Toute réponse 2xx de l'API v2 a la même forme, en camelCase :

{
  "success": true,
  "data": [],
  "meta": {
    "correlationId": "8f2a…",
    "durationMs": 128,
    "dossiers": ["DEMO", "DEMO2"]
  }
}
Champ Contenu
success true pour toute réponse 2xx
data la charge utile : objet, tableau, ou résultat indexé par dossier
meta.correlationId identifiant de corrélation, égal à l'en-tête X-Correlation-Id de la réponse
meta.durationMs durée de traitement côté connecteur
meta.dossiers dossiers Sage réellement interrogés ; null pour les opérations mono-objet

meta.pagination et meta.warnings existent dans le contrat mais ne sont pas encore alimentés.

Corrélation

Envoyez votre propre X-Correlation-Id : le connecteur le reprend, l'inscrit dans ses journaux et le renvoie dans meta.correlationId. C'est la clé pour faire relier un appel côté client à une ligne de journal côté serveur.

Paramètres transverses

Trois paramètres de requête sont reconnus au-delà de ceux propres à chaque opération.

Paramètre Rôle Détail
targets dossier(s) Sage visé(s) Forme canonique v2, valeurs séparées par des virgules : ?targets=DEMO,DEMO2. Sans valeur, tous les dossiers autorisés à l'utilisateur sont interrogés. Alias hérités acceptés : target, target_base, base, bases (séparateur , ou \|).
limit nombre maximum de lignes 0 = illimité. S'applique par dossier.
offset décalage pagination, à combiner avec limit.

Les autorisations de l'utilisateur filtrent toujours targets : un dossier non autorisé est écarté.

Écritures : un seul dossier

Une écriture (POST, PUT, DELETE) doit viser un dossier unique. Plusieurs valeurs dans targets renvoient l'erreur DOSSIER.AMBIGUOUS (400).

Les lectures de listes (GET /v2/<objet>) renvoient un résultat indexé par dossier et renseignent meta.dossiers.

Les erreurs

Une réponse ≥ 400 ne passe pas par l'enveloppe : elle sort en application/problem+json, au format ProblemDetails (RFC 9457), enrichi de deux extensions SenSaaS.

{
  "type": "https://sensaas.fr/errors/sage-not-found",
  "title": "Enregistrement introuvable",
  "status": 404,
  "detail": "…",
  "instance": "/v2/tiers/clients/C0001",
  "code": "SAGE.NOT_FOUND",
  "correlationId": "8f2a…"
}

code appartient à une taxonomie stable (AUTH.*, VALIDATION.*, DOSSIER.*, SAGE.*, LICENCE.*, QUERY.*, INTERNAL) : c'est sur lui qu'il faut brancher la logique du client, jamais sur title ou detail. Le détail complet est dans les conventions.

Codes les plus courants à traiter :

Code Statut Situation
DOSSIER.NOT_AUTHORIZED 403 l'utilisateur n'a pas accès au dossier demandé
DOSSIER.AMBIGUOUS 400 écriture visant plusieurs dossiers
SAGE.NOT_FOUND 404 clé inexistante dans Sage
SAGE.READONLY 409 objet en lecture seule côté interop
SAGE.COM.UNAVAILABLE 503 écritures v2 désactivées sur le connecteur
VALIDATION.MODEL 400 corps vide ou invalide

Premier appel de bout en bout

HOTE="https://<votre-connecteur>"

# 1. Authentification
JETON=$(curl -s -X POST "$HOTE/v2/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"integration","password":"<mot-de-passe>"}' \
  | python -c "import json,sys; print(json.load(sys.stdin)['data']['token'])")

# 2. Dossiers Sage visibles
curl -s "$HOTE/v2/sage/dossiers" -H "Authorization: Bearer $JETON"

# 3. Une liste, sur un dossier, limitée
curl -s "$HOTE/v2/tiersbanque?targets=DEMO&limit=10" \
  -H "Authorization: Bearer $JETON"

# 4. Un indicateur
curl -s "$HOTE/v2/kpi/clients-ca/ca-n?targets=DEMO" \
  -H "Authorization: Bearer $JETON"
"""Client minimal de l'API SenSaaS v2 : jeton, enveloppe, ProblemDetails."""
import requests

HOTE = "https://<votre-connecteur>"
VERIFIER_TLS = True  # False seulement face à un certificat auto-signé interne


class ErreurSenSaaS(Exception):
    """Erreur renvoyée par le connecteur, porteuse du code de la taxonomie."""

    def __init__(self, probleme: dict):
        self.code = probleme.get("code")
        self.statut = probleme.get("status")
        self.correlation = probleme.get("correlationId")
        super().__init__(f"{self.statut} {self.code}{probleme.get('detail') or probleme.get('title')}")


class ClientSenSaaS:
    def __init__(self, hote: str = HOTE, verifier_tls: bool = VERIFIER_TLS):
        self.hote = hote.rstrip("/")
        self.session = requests.Session()
        self.session.verify = verifier_tls

    def connexion(self, utilisateur: str, mot_de_passe: str) -> None:
        reponse = self.session.post(
            f"{self.hote}/v2/auth/login",
            json={"username": utilisateur, "password": mot_de_passe},
            timeout=30,
        )
        jeton = self._deballer(reponse)["token"]
        self.session.headers["Authorization"] = f"Bearer {jeton}"

    def appeler(self, methode: str, chemin: str, *, params=None, corps=None):
        reponse = self.session.request(
            methode, f"{self.hote}{chemin}", params=params, json=corps, timeout=120
        )
        return self._deballer(reponse)

    @staticmethod
    def _deballer(reponse):
        if reponse.status_code >= 400:
            raise ErreurSenSaaS(reponse.json())
        charge = reponse.json()
        # Les réponses 2xx de l'API v2 sont emballées ; on rend directement « data ».
        return charge.get("data") if isinstance(charge, dict) and "success" in charge else charge


client = ClientSenSaaS()
client.connexion("integration", "<mot-de-passe>")

dossiers = client.appeler("GET", "/v2/sage/dossiers")
banques = client.appeler("GET", "/v2/tiersbanque", params={"targets": "DEMO", "limit": 10})
ca = client.appeler("GET", "/v2/kpi/clients-ca/ca-n", params={"targets": "DEMO"})

Où aller ensuite

Besoin Page
Format exact des corps, taxonomie des erreurs Conventions de payload
Indicateurs clés (128 indicateurs, 18 familles) Indicateurs (KPI)
Faire découvrir l'API à une IA Serveur MCP « sensaas-connecteur »
Toutes les opérations, groupe par groupe Groupes de l'API v2

Points d'entrée utiles pour explorer :

  • GET /v2/entites — catalogue des objets métier accessibles, sans accès à Sage.
  • GET /v2/sage/dossiers — dossiers Sage visibles par l'utilisateur connecté.
  • POST /v2/query — requête de masse sur le schéma Sage (modèle hérité v1, champs en minuscules).
  • GET /v2/serveur/version — version du connecteur.

Questions fréquentes

Faut-il utiliser /v2 ou les anciennes routes ?

Les anciennes routes (sans /v2) restent servies et figurent dans le même contrat, mais elles sont figées et destinées à disparaître. Une intégration neuve part de /v2 : elle bénéficie de l'enveloppe, des ProblemDetails et de la corrélation.

Pourquoi mes écritures renvoient-elles 503 ?

Les écritures v2 sont derrière un drapeau du connecteur. Tant qu'il n'est pas activé, toute écriture répond 503 avec le code SAGE.COM.UNAVAILABLE.

Comment cibler plusieurs dossiers ?

?targets=DEMO,DEMO2 en lecture. En écriture, un seul dossier est accepté.

Le corps de ma requête est volumineux, y a-t-il une limite ?

Oui : la taille maximale d'un corps de requête est de 50 Mo. Les réponses JSON sont compressées en gzip quand le client l'accepte.

Voir aussi