Conventions de payload v2¶
En bref
Une seule enveloppe pour tous les succès, un seul format pour toutes les erreurs, une seule façon de cibler un dossier Sage. Ces trois règles sont le contrat stable du protocole v2 : elles ne dépendent d'aucune opération particulière et ne changent pas sans préavis.
Ce sont les règles à implémenter une fois dans votre client. Le détail opération par opération est dans les groupes.
Enveloppe des réponses en succès¶
Toute réponse 2xx est emballée par le connecteur ; aucun contrôleur ne construit l'enveloppe à la
main, la forme est donc rigoureusement identique partout.
{
"success": true,
"data": {},
"meta": {
"correlationId": "8f2a…",
"dossiers": ["DEMO"],
"pagination": null,
"durationMs": 128,
"warnings": null
}
}
Champ de meta |
Type | Contenu |
|---|---|---|
correlationId |
string |
identifiant de corrélation, égal à l'en-tête X-Correlation-Id de la réponse |
dossiers |
string[] |
dossiers Sage réellement interrogés ; null pour les opérations mono-objet |
pagination |
objet | { limit, offset, total } — présent dans le contrat, pas encore alimenté |
durationMs |
entier | durée de traitement côté connecteur |
warnings |
tableau | dégradations non bloquantes : { code, message } — pas encore alimenté |
warnings remplacera les échecs silencieux de l'ancien protocole : un dossier écarté, un indicateur
partiel, y seront signalés sans faire échouer l'appel.
PascalCase métier, camelCase sur le fil¶
La règle prête à confusion, elle mérite d'être dite précisément :
- côté connecteur, les propriétés des DTO sont déclarées en PascalCase (
Reference,CorrelationId) — c'est la convention du code métier ; - sur le fil, le sérialiseur les expose en camelCase (
reference,correlationId). La convention est épinglée explicitement dans la configuration du connecteur, elle ne dépend pas d'un réglage d'environnement.
Ce choix est aligné sur les erreurs : le format ProblemDetails impose des champs en minuscules, et forcer le PascalCase aurait cassé cette conformité.
Deux exceptions, assumées
POST /v2/queryetPOST /v2/search: le contenu dedataest produit par un sérialiseur interne et ses clés sont les noms de colonnes SQL Sage bruts (CT_Num,AR_Ref…), non transformés — c'est un retour fidèle du schéma Sage. Seule l'enveloppe qui les entoure est en camelCase.- Le corps de
POST /v2/querysuit le modèle hérité, en minuscules etsnake_case(model,columns,where,order_by,target_base,limit…).
Corps des requêtes¶
Écritures par DTO¶
Les écritures d'objets métier (/v2/tiers, /v2/articles, /v2/entites/{entite}, et toutes les
routes /v2/<objet>) attendent un JSON partiel du DTO.
| Règle | Conséquence pour l'intégrateur |
|---|---|
| Reconnaissance par nom de propriété DTO, insensible à la casse | {"reference": …} et {"Reference": …} sont équivalents |
| Les noms de colonnes SQL Sage ne sont pas reconnus | {"AR_Ref": …} est ignoré : utilisez reference |
| Liaison partielle sûre | seules les propriétés présentes dans le corps sont marquées comme modifiées ; les autres champs Sage ne sont pas écrasés |
| Aller-retour garanti | un objet lu en camelCase peut être réécrit tel quel |
C'est le point le plus important pour éviter les dégâts : n'envoyez que les champs que vous voulez modifier. Envoyer un objet complet relu puis modifié fonctionne aussi, mais réécrit tout.
Modèles hérités¶
| Route | Modèle attendu |
|---|---|
POST /v2/query |
champs en minuscules / snake_case : model, alias, columns, columns_array, where, join, order_by, aggregate, computed_columns, limit, offset, unfilter, parameters, target_base, mediaSize, escapeDate, sqlQuery |
POST /v2/search |
models, searchTerm, limit |
La liaison est insensible à la casse, ce qui rend ces modèles tolérants. Ils sont partagés avec la surface historique : leur casse n'est pas alignée sur le reste de la v2, c'est une dette assumée jusqu'au retrait de celle-ci.
Drapeau des écritures¶
Les écritures v2 (POST, PUT, DELETE) sont derrière un drapeau du connecteur
(AutoriserEcrituresV2). Tant qu'il n'est pas activé, toute écriture répond 503 avec le code
SAGE.COM.UNAVAILABLE. Un objet exposé en lecture seule par la couche d'interopérabilité répond
409 (SAGE.READONLY).
Paramètres transverses¶
| Paramètre | Rôle | Détail |
|---|---|---|
targets |
dossier(s) Sage cible | Forme canonique v2, séparateur virgule : ?targets=DEMO,DEMO2. Sans valeur, tous les dossiers autorisés. Alias hérités acceptés : target, target_base, base, bases (séparateurs , ou \|). Toujours filtré par les autorisations de l'utilisateur. |
limit |
nombre maximum de lignes | 0 = illimité |
offset |
décalage | pagination, à combiner avec limit |
Ils sont résolus au même endroit pour toute l'API, et honorés par /v2/query, /v2/search, les
indicateurs, la balance, le cadencier, les tarifs, et les listes d'objets (GET /v2/<objet>),
dont le résultat est alors indexé par dossier avec meta.dossiers renseigné.
Écriture = un seul dossier
Une écriture doit viser un dossier unique. Plusieurs valeurs dans targets renvoient
DOSSIER.AMBIGUOUS (400) ; aucune valeur alors que l'utilisateur a plusieurs dossiers
autorisés renvoie DOSSIER.REQUIRED (400).
Erreurs¶
Format¶
Une réponse ≥ 400 ne passe pas par l'enveloppe. Elle sort en application/problem+json
(ProblemDetails, RFC 9457), avec deux extensions SenSaaS : code et correlationId.
{
"type": "https://sensaas.fr/errors/sage-readonly",
"title": "Objet en lecture seule",
"status": 409,
"detail": "…",
"instance": "/v2/tierstype",
"code": "SAGE.READONLY",
"correlationId": "8f2a…"
}
Les erreurs venant d'un processus métier Sage portent en plus une extension sageDetail : le
message renvoyé par Sage lui-même (par exemple « L'article X existe déjà ! »).
Aucune trace d'exécution n'est exposée ; sur une erreur 5xx le détail est masqué et ne se retrouve
que dans les journaux du connecteur, repérable par le correlationId.
Branchez-vous sur code, pas sur title
title et detail sont des textes destinés à l'humain et peuvent évoluer. code appartient à
une taxonomie stable : un code peut être ajouté, jamais renommé ni supprimé.
Taxonomie CodesErreur¶
| Code | Situation |
|---|---|
AUTH.INVALID_CREDENTIALS |
identifiants refusés |
AUTH.TOKEN_EXPIRED |
jeton expiré : renouveler par POST /v2/auth/refresh-token |
AUTH.FORBIDDEN |
action interdite à cet utilisateur |
| Code | Situation |
|---|---|
VALIDATION.MODEL |
corps absent, vide ou invalide (400) |
VALIDATION.PARAMETER |
paramètre de requête invalide |
| Code | Situation |
|---|---|
DOSSIER.UNKNOWN |
dossier inexistant |
DOSSIER.NOT_AUTHORIZED |
dossier non autorisé à l'utilisateur (403) |
DOSSIER.AMBIGUOUS |
plusieurs dossiers alors qu'un seul est attendu (400) |
DOSSIER.REQUIRED |
écriture sans cible explicite alors que plusieurs dossiers sont autorisés (400) |
DOSSIER.IGNORED |
cible écartée parmi des cibles valides — avertissement, pas une erreur |
| Code | Situation |
|---|---|
SAGE.NOT_FOUND |
référence, pièce ou clé introuvable (404) |
SAGE.LOCK.RECORD |
enregistrement verrouillé (409) |
SAGE.LOCK.CONTROLLER |
contrôleur Sage verrouillé |
SAGE.COM.PROCESS |
échec d'un processus métier : transformation, lettrage, attribution de lots (422) |
SAGE.COM.UNAVAILABLE |
Sage injoignable, ou écritures v2 désactivées (503) |
SAGE.READONLY |
écriture sur un objet exposé en lecture seule (409) |
| Code | Situation |
|---|---|
LICENCE.EXPIRED |
licence du connecteur expirée |
LICENCE.QUOTA |
quota de licence atteint |
QUERY.INVALID |
requête /v2/query invalide |
QUERY.FORBIDDEN |
requête interdite par les filtres |
INTERNAL |
erreur non prévue ; le détail est dans les journaux, repérable par correlationId |
Les URI du champ type sont construites sur la base https://sensaas.fr/errors/.
SageErreur, côté connecteur¶
Les erreurs métier remontées par la passerelle Sage voyagent dans une exception dédiée qui porte
déjà son code de taxonomie et son statut HTTP. Le gestionnaire d'exceptions global la traduit
directement en ProblemDetails. Concrètement, pour l'intégrateur : le statut HTTP et le code sont
décidés au plus près de Sage, ils sont donc fiables et stables d'une version à l'autre.
| Fabrique | Code | Statut |
|---|---|---|
| Introuvable | SAGE.NOT_FOUND |
404 |
| Verrou d'enregistrement | SAGE.LOCK.RECORD |
409 |
| Processus métier en échec | SAGE.COM.PROCESS |
422 (+ extension sageDetail) |
| Sage indisponible | SAGE.COM.UNAVAILABLE |
503 |
| Lecture seule | SAGE.READONLY |
409 |
| Dossier non autorisé | DOSSIER.NOT_AUTHORIZED |
403 |
| Dossier ambigu | DOSSIER.AMBIGUOUS |
400 |
| Dossier requis | DOSSIER.REQUIRED |
400 |
Deux voies d'accès aux objets métier¶
L'API v2 offre deux chemins complémentaires vers la même surface Sage.
Un contrôleur par objet métier, rangé par catégorie et étiqueté Catégorie.Objet — c'est ce
qui donne les groupes de cette documentation (Catalogue.Article, Tiers.TiersBanque,
Comptabilite.Journal…).
Routes : GET | POST | PUT | DELETE /v2/<objet>, plus POST /v2/<objet>/save,
POST /v2/<objet>/recherche et GET | POST /v2/<objet>/count.
C'est la voie privilégiée : routes stables, clé dans l'URL pour les objets majeurs
(/v2/articles/{reference}, /v2/tiers/clients/{ctNum}), DTO typé.
Repli universel pour la longue traîne et la découverte.
| Route | Rôle |
|---|---|
GET /v2/entites |
catalogue des objets accessibles — aucun accès à Sage, idéal pour explorer |
GET /v2/entites/{entite}/{cle} |
lecture par clé, pour les objets qui l'exposent (Article, Tiers client et fournisseur, Document achat et vente) ; sinon 400, passer par /v2/query |
POST /v2/entites/{entite} |
création ou mise à jour, derrière le drapeau des écritures |
Un objet en lecture seule renvoie 409 à l'écriture.