API REST et serveur MCP
Pilotez votre comptabilité depuis un autre logiciel : une boutique en ligne qui enregistre chaque vente, un agent IA qui saisit les opérations, un outil de reporting qui lit le bilan. Tout ce que fait l'application est accessible, avec les mêmes contrôles comptables.
https://votre-site/api/v1Serveur MCP :
https://votre-site/mcp
Démarrer en trois étapes
Créez une clé
Dans l'application : menu API et intégrations → Créer une clé. Copiez-la : elle ne s'affiche qu'une fois.
Faites un premier appel
Lisez les informations de la société avec la clé dans l'en-tête Authorization.
Ou branchez un agent
Donnez l'adresse /mcp et la clé à Claude, Cursor ou votre agent : il découvre seul les outils.
curl https://votre-site/api/v1/societe \ -H "Authorization: Bearer clt_VOTRE_CLE"
Clés et droits
Une clé donne accès à une seule société. Elle se transmet à chaque requête dans l'en-tête
Authorization: Bearer clt_…. Seul l'administrateur de la société peut en créer (20 au plus) ou en révoquer ;
la révocation est immédiate. ComptaLT ne conserve qu'une empreinte de la clé : perdue, elle ne peut pas être retrouvée,
il faut en créer une nouvelle.
| Droits | Permet |
|---|---|
| Lecture seule | Toutes les requêtes GET : écritures, rapports, factures, clients, stock, commandes, fournisseurs, banque, exports PDF. |
| Lecture et écriture | En plus, des modifications, selon la portée choisie à la création de la clé. |
Portée d'une clé d'écriture
Une clé d'écriture reçoit une portée à sa création (champ portee de POST /api/cles-api,
écran Réglages > API et intégrations). Donnez à chaque logiciel la plus étroite qui lui suffit : une clé collée
dans une boutique est stockée en clair dans la base de la boutique.
| Portée | Permet |
|---|---|
boutique | Ce qu'appellent les modules PrestaShop et WooCommerce : GET /societe, /canaux, /commandes (et leurs paiements, remboursements, annulations), /paiements, articles (PUT /stock/articles/reference/{reference}, POST /stock/articles/lot, lectures de /stock). Rien d'autre, pas même la lecture des écritures. |
integrations (par défaut) | Toutes les lectures, ce que permet boutique, et ce qu'appellent n8n, Zapier et Make et les caisses enregistreuses : POST /ecritures, POST /factures et /factures/{id}/emettre, /encaisser, /envoyer, POST /clients, POST /fournisseurs/factures et /fournisseurs/factures/{id}/payer, abonnements POST /webhooks, PUT et DELETE /webhooks/{id}, POST /caisse/clotures et /caisse/import. |
complete | Toute l'API publique : réglages de la société (IBAN, raison sociale), exercices (création, clôture, réouverture, suppression), ouverture, plan comptable, banque, TVA, justificatifs, devis, contrats, suppressions, secret, essai et renvoi des webhooks. À cocher explicitement ; c'est la portée d'un agent IA (MCP) qui doit tout faire. |
heritee | Clé créée avant les portées (04.10.2026) : tout ce qu'elle permettait, outils MCP compris, sauf les réglages de la société, la suppression d'un exercice, le secret, l'essai et le renvoi des webhooks. L'administrateur peut la restreindre en un clic (PUT /api/cles-api/{id}/portee avec { "portee": "integrations" }). |
Hors de sa portée, une clé reçoit 403 avec un message qui dit quelle portée demander. Un abonnement
webhook créé avec une clé lui est lié (cleApiId, cleApiNom dans GET /webhooks) :
quand la clé est révoquée, il est désactivé et ne reçoit plus rien. Quelle que soit sa portée, une clé ne modifie,
n'essaie, ne renvoie ni ne supprime que les abonnements qu'elle a créés : ceux créés dans l'application ou par une
autre clé lui répondent 403. Un abonnement désactivé ne se réactive par une clé que si c'est elle qui
l'a désactivé (desactiveParCle) : désactivé dans l'application, ou à la révocation d'une clé, seule
l'application le réactive. Un déclencheur n8n, Zapier ou Make abonné avant ce lien (champ source,
sans cleApiId) peut encore être supprimé, désactivé ou réactivé à l'identique par une clé (adresse,
événements et description inchangés), jamais modifié ni lié à elle. Le cycle des modules n'utilise que cela :
abonnement (POST) et désabonnement (DELETE) de Zapier et Make, création, réactivation et
suppression de ses propres abonnements par n8n.
Routes ouvertes à une clé
Une clé n'ouvre que les ressources documentées ici (liste blanche) : /societe, /settings,
/comptes, /exercices, /ouverture, /ecritures, /pieces,
/rapports, /tva, /exports, /factures, /clients,
/devis, /opportunites, /contrats, /stock, /canaux,
/commandes, /paiements, /fournisseurs, /banque, /caisse et
/webhooks, et ce qui est en dessous. Toute autre adresse répond 403 à une clé, même si
l'application l'utilise : par exemple /etat (comptabilité entière), /connexions,
/echanges/…, /exports/sauvegarde et /exports/ticket, ainsi que les utilisateurs et
les clés. Le refus porte le message « Cette opération n'est pas disponible par clé API : elle reste réservée à
l'application (voir /developpeurs). » ; une clé en lecture seule qui tente une modification reçoit
« Cette clé API est en lecture seule. » Les règles s'appliquent à l'adresse décodée, comme la lit le
serveur : /exports/sauvegard%65 est /exports/sauvegarde et reçoit le même refus.
Conventions
- Corps et réponses en JSON (
Content-Type: application/json), texte en UTF-8. - Dates au format
AAAA-MM-JJ. Montants en francs suisses, nombres à deux décimales (108.1). - Une opération réussie renvoie son résultat, par exemple
{ "ok": true, "ecriture": { … } }. - Une erreur renvoie toujours
{ "erreur": "message lisible", "code": "refus" }: le message en français, prêt à afficher, et un code stable pour vos programmes (le message peut changer, le code jamais). -
Une opération reçue (commande, paiement, remboursement, clôture de caisse, relevé bancaire, facture fournisseur) datée
d'une période TVA déjà déclarée n'est pas refusée : elle entre dans le décompte de la
période en cours (art. 72 LTVA). Son écriture est passée à la date du jour et la réponse le signale dans
avertissements(« Opération du 15.03.2026 passée au 04.10.2026 : la période TVA T1 2026 est déclarée… »). -
Les adresses sont sensibles à la casse : écrivez-les en minuscules, comme ici.
/API/v1/societe,/api/V1/societeou/api/v1/Societerépondent404. Une barre finale (/api/v1/societe/) est acceptée.
| Code | Signification |
|---|---|
200 / 201 | Succès. |
400 | Corps de requête invalide : un champ n'a pas le bon type (texte, nombre, liste, objet). La réponse donne le détail de chaque champ refusé, voir ci-dessous. |
401 | Clé absente, inconnue ou révoquée, ou société désactivée. |
403 | Clé en lecture seule pour une modification, ou adresse non ouverte aux clés (liste blanche). |
404 | Ressource introuvable, ou adresse mal écrite (casse comprise). |
409 | La comptabilité a changé depuis la révision envoyée dans If-Match (corps { "erreur": "…", "code": "revision" }) : rien n'a été modifié, relisez puis recommencez. Code conflit : un autre conflit (traitement trop long, inscription simultanée) ; code version_future : données enregistrées par une version plus récente de ComptaLT. |
413 | Corps ou fichier trop volumineux (pièce jointe : 20 Mo au plus). |
422 | Refus comptable (toujours 422, quel que soit son code) : écriture déséquilibrée, exercice clôturé, compte inconnu, stock insuffisant, montant illisible, drapeau decompteTva mal posé, écriture verrouillée (exercice clôturé ou période TVA déclarée) modifiée ou supprimée, réouverture sans motif… |
429 | Trop de requêtes avec des clés inconnues depuis votre adresse : attendez le nombre de secondes indiqué par Retry-After. |
500 | Erreur interne (un défaut de ComptaLT) : { "erreur": "Erreur interne (référence K7M2Q9XA).", "code": "interne", "reference": "K7M2Q9XA" }. Le détail technique n'est jamais renvoyé : communiquez la référence au support. |
503 | Service momentanément indisponible, en particulier trop d'exports en cours sur la plateforme : réessayez après le nombre de secondes indiqué par Retry-After. Sans Retry-After : l'export demande plus de mémoire que la limite d'un export sur ce serveur, un nouvel essai échouerait aussi (contactez le support). |
Corps refusé : le détail des champs (400)
Avant tout traitement, le corps des requêtes /api/v1 (et donc des outils MCP) est contrôlé : seuls les
types incohérents sont refusés, par exemple un libellé envoyé comme objet, une liste lignes qui n'est
pas une liste ou un client qui n'est pas un objet. Les champs inconnus sont ignorés, un nombre en texte
("125.50") et un champ vide restent acceptés ; les contrôles comptables (équilibre, exercice, compte…)
répondent toujours 422. Rien n'est enregistré ; la réponse donne le premier motif dans erreur et
jusqu'à 20 motifs dans details, chacun avec le chemin du champ (lignes.0.compteNo : premier
élément de lignes) :
# Réponse 400 à une écriture dont le libellé est un objet
{
"erreur": "Requête invalide : libelle : texte attendu.",
"code": "invalide",
"details": [
{ "champ": "libelle", "message": "texte attendu" }
]
}
Codes d'erreur
Chaque réponse en erreur porte un champ code, stable d'une version à l'autre : testez le code, pas le
message. Un refus de la comptabilité répond toujours 422 sur /api/v1 (et
200 avec { "res": { "ok": false, "message", "code" } } sur /api/compta), même quand
son code est introuvable ou invalide : le statut HTTP des intégrations existantes ne change pas,
le code précise la raison. Les autres erreurs ont le statut de leur code.
| Code | Statut | Signification |
|---|---|---|
refus | 422 | Règle comptable ou métier : écriture déséquilibrée, exercice clôturé, stock insuffisant… |
doublon_probable | 422 | Paiement déjà encaissé par le relevé bancaire, voir ci-dessous. |
invalide | 400 (422 pour un refus de la comptabilité) | Requête mal formée : champ manquant, type ou format incorrect. |
non_authentifie | 401 | Clé absente, inconnue ou révoquée. |
interdit | 403 | Clé en lecture seule, adresse non ouverte aux clés, société de démonstration. |
introuvable | 404 (422 pour un refus de la comptabilité) | Ressource ou adresse inexistante. |
conflit | 409 | Traitement concurrent : recommencez. |
revision | 409 | La comptabilité a changé depuis la révision envoyée dans If-Match. |
version_future | 409 | Données enregistrées par une version plus récente de ComptaLT. |
trop_volumineux | 413 | Corps ou fichier trop volumineux. |
trop_de_requetes | 429 | Trop de requêtes : attendez Retry-After secondes. |
indisponible | 503 | Service momentanément indisponible (exports, prestataire). |
interne | 500 | Défaut de ComptaLT : communiquez la reference au support. |
Renvoyer une requête sans risque de doublon
Ajoutez l'en-tête Idempotency-Key à toute modification (POST, PUT,
DELETE), sur /api/v1/… comme sur /api/compta/…, avec une valeur propre à
l'opération : le numéro de commande de votre boutique, un identifiant tiré au hasard. Si la même requête arrive à nouveau
dans les 24 heures (coupure réseau, nouvel essai automatique), ComptaLT ne l'exécute pas une deuxième fois : il
renvoie la réponse d'origine avec l'en-tête Idempotent-Replayed: true. Deux envois simultanés n'enregistrent
qu'une opération : le second attend la fin du premier.
- La clé est enregistrée avec l'opération elle-même : elle reste valable après un redémarrage du serveur.
- Elle vaut pour une clé API (ou un utilisateur), une méthode et une adresse : la même valeur envoyée à une autre adresse est une autre opération. Ne réutilisez jamais une valeur pour une opération différente sur la même adresse : elle serait prise pour un renvoi. 500 caractères au plus.
- Une requête refusée avant tout traitement (
400,403,409) n'est pas retenue : corrigée, elle peut être renvoyée avec la même clé. - Si la réponse d'origine était très volumineuse (plus de 16 Ko), le renvoi répond seulement
{ "ok": true }: relisez la ressource si vous en avez besoin.
Ne pas écraser une modification faite entre-temps
Chaque réponse porte la révision de la comptabilité dans l'en-tête ComptaLT-Revision (un nombre qui
augmente à chaque enregistrement). Elle est aussi dans l'ETag (fort, ETag: "12") : d'une
lecture, la révision lue ; d'une modification, la révision qu'elle a enregistrée (une modification refusée ou qui
n'enregistre rien n'a pas d'ETag). Pour une modification
qui dépend de ce que vous avez lu, envoyez cette révision dans l'en-tête facultatif If-Match
(If-Match: "12" ou If-Match: 12) : si la comptabilité a changé depuis (autre utilisateur,
autre intégration), la requête est refusée en 409 sans rien modifier ; relisez, puis refaites la
modification. Sans If-Match (ou avec If-Match: *), rien ne change : la modification
s'applique à la comptabilité du moment.
ETag d'une lecture se renvoie tel quel en If-Match. Les réponses de l'API ne sont pas
gardées en cache (Cache-Control: no-store). Avant le 04.10.2026, l'ETag d'une lecture était
une empreinte du contenu (W/"…"), toujours refusée en If-Match : prenez la révision dans
ComptaLT-Revision si vous visez aussi des serveurs plus anciens.
curl -X POST https://votre-site/api/v1/ecritures \
-H "Authorization: Bearer clt_VOTRE_CLE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: commande-1042" \
-H "If-Match: 12" \
-d '{ … }'
Réponses allégées : Prefer: return=minimal
Les réponses de /api/v1 ne contiennent que le résultat de l'opération ({ "ok": true, "ecriture": { … } }).
L'API de l'application, /api/compta/…, renvoie en plus, par défaut, toute la comptabilité de la société
après chaque modification ({ "res": { … }, "base": { … } }), ce qui pèse lourd pour une grosse société.
Avec l'en-tête Prefer: return=minimal (RFC 7240), une modification renvoie seulement son résultat et la
nouvelle révision, { "res": { … }, "revision": 42 }, avec l'en-tête Preference-Applied: return=minimal.
Une modification qui renverrait la comptabilité ou les réglages seuls (PUT /settings, sur
/api/v1 aussi) répond { "revision": 42 }. Les lectures et les réponses sans l'en-tête ne
changent pas ; un renvoi idempotent sans l'en-tête renvoie la réponse complète.
Requêtes simultanées
Vous pouvez envoyer plusieurs modifications en parallèle : pour une même société, ComptaLT les traite l'une après l'autre, sans en perdre aucune. Les lectures ne sont jamais bloquées.
Société et réglages
| Requête | Description |
|---|---|
GET /societe | Résumé : nom, forme juridique, TVA (assujettie, taux, comptes 1170 impôt préalable et 2200 TVA due), modules activés, exercices. Le bon point de départ. |
GET /settings | Réglages complets, dont facturation.prixTtcApiParDefaut : sens des prix des factures, devis et contrats reçus sans prixTTC ; facturation.termeConvenu : échéance convenue par contrat ou conditions générales (intérêts moratoires dès l'échéance, sinon dès la première relance, art. 102 CO). |
PUT /settings | Remplace les réglages : lisez-les, modifiez les champs voulus, renvoyez l'objet entier. Répond avec les réglages enregistrés. Types contrôlés (400 détaillé, « Réglages invalides : … ») : entreprise, tva, tva.numerosComptes, facturation, modules sont des objets, leurs champs des textes, nombres ou indicateurs. Clé API de portée complete seulement (portées). |
Plan comptable
| Requête | Description |
|---|---|
GET /comptes | Tous les comptes : no, libelle, classe (1 à 8), type (actif, passif, charge, produit), tvaDefaut. |
POST /comptes | Ajoute un compte : { "no": "3410", "libelle": "Ventes boutique en ligne", "classe": 3, "type": "produit" }. |
PUT /comptes/{no} | Modifie un compte (champs partiels). |
DELETE /comptes/{no} | Supprime un compte inutilisé. |
Exercices
| Requête | Description |
|---|---|
GET /exercices | Liste [{ "annee": 2026, "cloture": false }]. |
POST /exercices | Ouvre un exercice : { "annee": 2027 }. |
POST /exercices/avec-ouverture | Ouvre un exercice en reportant les soldes du bilan précédent. |
POST /exercices/bornes | Exercice décalé ou premier exercice long : { "annee": 2026, "debut": "2025-07-01", "fin": "2026-06-30", "ouverture": true } (ouverture : soldes du bilan précédent reportés). Les rapports et leurs PDF suivent ces bornes. |
POST /ouverture/{annee}/soldes | Premier exercice : soldes de départ { "soldes": [{ "compteNo": "1020", "montant": 8500 }] }. |
GET /ouverture/{annee} | Aperçu de l'écriture d'ouverture. |
POST /ouverture/{annee} | Génère l'écriture d'ouverture ; /ouverture/{annee}/regenerer la recalcule. |
DELETE /ouverture/{annee} | Supprime l'écriture d'ouverture. |
GET /exercices/{annee}/controles | Points bloquants et avertissements avant clôture. |
GET /exercices/{annee}/produits-avance | Produits encaissés d'avance : part des factures (de contrats) de l'exercice qui se rapporte à l'exercice suivant. POST sur la même adresse passe la régularisation au 31.12 (compte 2301), extournée au 01.01 de l'exercice suivant, hors du décompte TVA ; refusée (422) s'il n'y a rien à régulariser ou si c'est déjà fait. |
POST /exercices/{annee}/cloturer | Clôture (verrouille la saisie). |
POST /exercices/{annee}/rouvrir | Rouvre un exercice clôturé : { "motif": "Correction de la TVA du T4 demandée par la fiduciaire" }, motif obligatoire (5 caractères au moins, sinon 422), inscrit avec son auteur et sa date dans l'historique de l'exercice et imprimé dans le grand livre. /exercices/{annee}/reouverture est la même opération. Un exercice rouvert ne se supprime plus et une sauvegarde ne le réécrit plus. |
GET /exercices/{annee}/bilan-suppression | Ce qu'effacerait la suppression de l'exercice. |
POST /exercices/{annee}/supprimer | Suppression définitive, avec { "confirmation": "SUPPRIMER" }. |
Écritures
Toute opération est une écriture en partie double : la somme des débits égale la somme des crédits, et chaque ligne porte soit un débit, soit un crédit. La date doit tomber dans l'exercice, qui doit être ouvert.
| Requête | Description |
|---|---|
GET /ecritures?annee=2026 | Écritures de l'exercice, triées par date. Filtres facultatifs : depuis, jusqua (dates incluses), compte, recherche (dans le libellé), limite (les N plus récentes). |
GET /ecritures/{id} | Une écriture. |
POST /ecritures | Crée une écriture, ou la modifie si le corps contient son id. Répond { "ok": true, "ecriture": { "id", "no", … } }. Un paiement de client (argent au débit, débiteurs au crédit) que le relevé bancaire a déjà encaissé peut être refusé (422, voir ci-dessous). |
DELETE /ecritures/{id} | Supprime une écriture d'un exercice ouvert, hors période TVA déclarée. |
POST /ecritures/{id}/corriger | Correction par extourne : { "correction": { "libelle", "lignes": [ … ], "date"? } }, ou { "correction": null } pour une simple annulation. L'écriture d'origine reste intacte ; une extourne datée du jour l'annule, puis la nouvelle version est passée (datée du jour sauf autre date hors période verrouillée). Répond { "ok": true, "extourne": { … }, "ecriture": { … } }. |
POST /api/v1/ecritures
{
"annee": 2026,
"date": "2026-09-17",
"libelle": "Loyer de septembre",
"lignes": [
{ "compteNo": "6000", "debit": 1650, "credit": 0 },
{ "compteNo": "1020", "debit": 0, "credit": 1650 }
]
}
decompteTva: true est réservé au paiement, au remboursement ou à la provision de la TVA (écriture exclue du
décompte) : une écriture qui le porte ne touche que les comptes de TVA et l'argent. Posé sur une autre écriture (une
vente, par exemple, qui sortirait ainsi du décompte), il est refusé (422). Le paiement de la TVA d'une période terminée
(decompteTva: true, libellé qui nomme la période, par exemple « Décompte TVA T1 2026 ») la rend
déclarée.
Paiement déjà encaissé par le relevé bancaire (422)
Une nouvelle écriture qui encaisse un débiteur (un compte d'argent au débit, les débiteurs au crédit du même montant)
est refusée quand le relevé bancaire importé a déjà encaissé ce paiement : transaction du même compte et du même
montant, à 31 jours près, rapprochée d'une facture, d'une commande ou d'une écriture de débiteurs, sans autre
transaction de ce montant à traiter. L'enregistrer compterait l'argent deux fois. La réponse est 422 :
{ "erreur": "Ce paiement de 1081.00 CHF semble déjà enregistré : le relevé bancaire du 10.03.2026 l'a encaissé la facture 2026-0001 (Client SA). …",
"code": "doublon_probable",
"doublonProbable": { "transactionId": "…", "date": "2026-03-10", "montant": 1081, "compteNo": "1020" } }
S'il s'agit bien d'un autre paiement, renvoyez la même écriture avec "doublonConfirme": true ("true"
et 1 sont acceptés) ; sinon, ne la renvoyez pas : le paiement est déjà en comptabilité. Pour encaisser une
facture, préférez POST /factures/{id}/encaisser ou /paiements, qui tiennent compte du relevé.
Écritures verrouillées
Une écriture d'un exercice clôturé, ou qui entre dans le décompte d'une période TVA déclarée, ne se modifie
plus (date, montants, comptes, drapeau decompteTva) et ne se supprime plus : POST /ecritures avec
son id et DELETE /ecritures/{id} répondent 422 (« L'écriture n° 12 du 15.03.2026 ne
se modifie plus : la période TVA T1 2026 est déclarée. Corrigez-la par une extourne datée du jour, puis une nouvelle
écriture »). Seul son libellé se corrige. Corrigez-la par POST /ecritures/{id}/corriger. Une nouvelle
écriture datée d'une période déclarée est refusée de même (422) : passez-la à une date de la période en cours.
Les montants debit et credit reçus par l'API sont arrondis au centime (un calcul de TVA
par expression, par exemple 11.3894, devient 11.39). Si l'écriture reçue était équilibrée, l'écart
d'arrondi (un centime par ligne au plus) est repris sur la ligne que l'arrondi a le plus déplacée (à égalité, sur un
compte hors TVA) ; une écriture reçue déséquilibrée reste refusée (422). Un montant est un nombre JSON ou un texte décimal
simple ("125.50") ; tout autre valeur (true, "0x10", "1e3",
"12,50") est refusée (422). Envoyez de préférence des montants déjà arrondis.
Pièces jointes
| Requête | Description |
|---|---|
POST /pieces/{ecritureId} | Joint un justificatif : { "nom": "ticket.pdf", "donneesBase64": "…" }. |
GET /pieces/contenu/{pieceId} | Contenu en base64 : { "donneesBase64": "…" }. |
DELETE /pieces/{ecritureId}/{pieceId} | Retire la pièce. |
Rapports, TVA et exports
| Requête | Description |
|---|---|
GET /rapports/bilan?annee=2026 | Bilan (actifs, passifs, résultat). |
GET /rapports/pertes-profits?annee=2026 | Compte de résultat. |
GET /rapports/balance?annee=2026 | Débit, crédit et solde de chaque compte : le plus simple pour connaître la trésorerie. |
GET /rapports/grand-livre?annee=2026 | Grand livre (art. 1 al. 2 Olico) : comptes (mouvements avec contrepartie et solde progressif), journal chronologique et controles (numérotation, équilibre). |
GET /tva/decomptes?annee=2026 | Décomptes TVA de l'année (trimestres, ou semestres au TDFN), chacun avec formulaire : les chiffres du formulaire de l'AFC (chiffres : 200 à 299, impôt par taux avec sa base, 399, 400 et 405, 500 ou 510 ; parTaux : base et impôt par taux) et avertissements (points à vérifier avant de déclarer). Une période déclarée porte en plus declaration : { "le", "par", "source", "montantDeclare"?, "soldeRecalcule", "ecart", "chiffresDeclares"?, "correction"? } ; son soldeAPayer est alors le solde déclaré à l'AFC (montantDeclare, corrections déclarées ensuite comprises), jamais recalculé en silence. soldeRecalcule : décompte refait avec les écritures et les règles actuelles ; ecart : recalcul moins solde déclaré, à déclarer en correction (art. 72 LTVA) s'il est fondé (0 sous 5 centimes) ; chiffresDeclares : chiffres du formulaire au moment de la déclaration ; correction : écriture proposée dans la période en cours ({ "libelle", "lignes" }). Le PDF (/exports/tva) les imprime. |
GET /tva/declarations | Périodes TVA déclarées : [{ "annee", "periode", "debut", "fin", "le", "par", "source" }] (source : paiement, action ou migration). Leurs écritures du décompte sont verrouillées. |
POST /tva/declarations | Déclare une période terminée : { "annee": 2026, "periode": "T1" } (T1 à T4, ou S1, S2 au TDFN). |
POST /tva/declarations/{annee}/{periode}/annuler | Retire une période marquée déclarée par erreur : { "motif": "…" } (obligatoire, inscrit dans la piste d'audit). Une période réellement déclarée à l'AFC se corrige dans la période en cours. |
GET /exports/{type}?annee=2026 | Fichier PDF : bilan, pp, balance, grand-livre, tva (avec &trimestre=1) ; Excel : grand-livre.xlsx. |
GET /exports/facture?id=… | PDF d'une facture avec QR-facture du solde ouvert (règlements reçus imprimés sous le total) ; rappel de la dernière relance d'une facture encore ouverte (&relance=2 pour un niveau précis, &relance=0 sans rappel). Un avoir (« Avoir », facture d'origine, montant en faveur du client), un brouillon (« Brouillon », sans numéro) et une facture annulée ou payée n'ont pas de QR-facture. Fichier Facture-2026-0001.pdf, Avoir-2026-0002.pdf ou Facture-brouillon.pdf. /exports/devis?id=… pour un devis. |
GET /rapports/annexe?annee=2026 | Annexe minimale aux comptes annuels (art. 959c CO) : rubriques avec leur texte et leur référence légale ; /rapports/annexe/pdf?annee=2026 pour le PDF. |
Les rapports PDF, le grand livre Excel et les fichiers d'échange sont produits en arrière-plan, quelques-uns à la fois
pour toute la plateforme. Quand trop d'exports sont déjà en cours, ou si un export dépasse le délai maximal (deux minutes
par défaut), la réponse est 503 avec l'en-tête Retry-After (en secondes, par exemple
Retry-After: 60) et { "erreur": "Trop d'exports sont en cours sur la plateforme : réessayez dans une
minute." } : attendez ce délai avant de redemander le fichier, sans boucle de nouvelles tentatives immédiates.
Les PDF d'une facture ou d'un devis ne sont pas concernés.
Clients
| Requête | Description |
|---|---|
GET /clients | Fichier clients. /clients/{id} pour un seul. |
POST /clients | Crée (ou modifie avec id) : nom obligatoire, adresse, npaLocalite, pays (CH), email, telephone, notes. |
DELETE /clients/{id} | Supprime un client sans contrat en cours. |
Factures
Cycle : brouillon (sans numéro) → émise (numéro définitif, écriture débiteurs / produits) →
payée en partie (partielle) → payée (écriture argent / débiteurs), ou annulée. Une
facture émise se corrige par un avoir.
- Numéro attribué à l'émission : un brouillon a
"numero": "";POST /factures/{id}/emettrelui donne le numéro suivant de l'année (2026-0007), sans trou même si des brouillons sont supprimés. Lisez le numéro dans la réponse de l'émission, pas dans celle de la création. Un brouillon créé avant le 04.10.2026 garde le numéro qu'il avait déjà. - Règlements :
paiementsliste chaque règlement{ "id", "date", "montant", "nature", "ecritureId"?, "compteNo"?, "avoirId"?, "factureId"?, "montantRecu"? },naturevalantpaiement,escompte,perte(perte sur débiteur) ouavoir(avoir imputé) ; absent sur une facture payée avant le 04.10.2026 (encaissementIdetdatePaiementcomme avant). Par une clé API, chaque facture porte aussisolde: le solde ouvert (total moins les règlements ; 0 pour un brouillon ou une facture annulée). Une facturepartielleattend encore sonsolde: traitez-la comme une facture à encaisser. - Avoir : une facture au total négatif,
avoirDe(identifiant de la facture d'origine) etmotif, numérotée dans la même suite, émise aussitôt et imputée sur le solde ouvert de la facture d'origine ; le reste se rembourse au client. Seslignesont des quantités négatives. Une facture de total négatif sansavoirDereste refusée (422). - Relances :
relancesliste{ "niveau", "date", "soldeOuvert", "interets"? }; les intérêts moratoires ({ "taux", "depuis", "jusquau", "jours", "montant" }, art. 104 CO) ne sont pas comptabilisés.
| Requête | Description |
|---|---|
GET /factures?annee=2026 | Factures et avoirs (année facultative), avec leurs totaux : net, tva, brut, rabais, parTaux, et leur solde ouvert. statut : brouillon, emise, partielle, payee ou annulee. /factures/{id} pour une seule. |
POST /factures | Crée un brouillon (ou le modifie avec id), voir le guide. Une facture émise garde son montant et sa date sous son numéro : les changer répond 422 (code refus ; correction par un avoir, ou annulation d'une facture non payée) ; son adresse, son client, son objet, ses notes et son échéance restent modifiables. Sens des prix des lignes, à préciser : "prixTTC": false, prix hors TVA, la TVA s'ajoute par taux ; "prixTTC": true, prix TVA comprise, le total est la somme des lignes et la facture indique « dont TVA ». Les formes texte "true"/"false" et 1/0 sont acceptées, toute autre valeur est refusée (422). Sans prixTTC : selon le réglage de la société facturation.prixTtcApiParDefaut (GET /settings, modifiable par PUT /settings) — hors TVA pour une société créée depuis le 04.10.2026 ; TVA comprise pour une société au TDFN qui utilisait déjà l'API (même total qu'avant). Une facture modifiée garde son sens. |
POST /factures/{id}/emettre | Numéro définitif (attribué maintenant), totaux figés et passage en comptabilité. Répond { "ok": true, "facture": { "numero": "2026-0007", … } }. |
POST /factures/{id}/encaisser | Paiement du solde : { "compteArgentNo": "1020", "datePaiement": "2026-10-15" } (le total d'une facture émise, ce qui reste d'une facture payée en partie). |
POST /factures/{id}/paiements | Paiement complet ou partiel : { "compteArgentNo": "1020", "datePaiement": "2026-10-15", "montant": 500 } ; escompte (escompte accordé, TVA due corrigée), montantRecu (francs réellement reçus pour une facture en devise : différence de change). Sans montant, le solde. Un montant au-delà du solde est refusé (422). Répond { "ok", "facture", "paiement" }. |
DELETE /factures/{id}/paiements/{paiementId} | Retire un règlement erroné (et l'escompte qui lui est lié) ; refusé (422) pour un paiement rapproché du relevé bancaire : annulez d'abord le rapprochement. |
POST /factures/{id}/perte | Perte sur débiteur : { "date": "2026-12-31", "montant": 120 } (sans montant : tout le solde). |
POST /factures/{id}/avoir | Établit un avoir : { "date": "2026-10-20", "motif": "Prestation non réalisée" } pour toute la facture, ou avec lignes (montants positifs crédités) pour une partie. Répond { "ok", "avoir", "facture" }. |
POST /factures/{id}/rembourser | Rembourse au client le reste d'un avoir : { "compteArgentNo": "1020", "date": "2026-10-25" } (montant facultatif). |
POST /factures/{id}/relances | Relance une facture échue : { "date": "2026-10-04", "interets": true } (intérêts moratoires de 5 % l'an, art. 104 CO, ou taux convenu), courus dès la première relance (mise en demeure, art. 102 al. 1 CO), ou dès l'échéance si le réglage facturation.termeConvenu est vrai (échéance convenue, art. 102 al. 2 CO). Pour un rappel donné, "depuis": "echeance" (échéance convenue avec ce client) ou "depuis": "mise-en-demeure" remplace le réglage ; toute autre valeur est refusée (422). Le PDF de la facture imprime alors le rappel. |
GET /factures/relances?date=2026-10-04 | Factures échues à relancer : [{ "factureId", "numero", "client", "echeance", "joursRetard", "soldeOuvert", "niveau", "derniereRelance"?, "interets" }]. |
POST /factures/{id}/annuler | Annule une facture émise et non réglée (écriture extournée). Une facture payée ou payée en partie se corrige par un avoir (422). |
POST /factures/{id}/envoyer | Envoie le PDF par e-mail au client (si l'envoi est configuré). Un brouillon part comme tel (« Facture (brouillon) », PDF marqué « Brouillon », sans QR-facture) : émettez la facture avant de l'envoyer pour paiement. |
DELETE /factures/{id} | Supprime un brouillon. |
Devis et opportunités
| Requête | Description |
|---|---|
GET /devis?annee=2026 | Devis. |
POST /devis | Crée ou modifie : mêmes champs qu'une facture (dont prixTTC, facultatif), plus validiteJusquau et opportuniteId. |
POST /devis/{id}/statut | { "statut": "envoye" } : brouillon, envoye, accepte, refuse. |
POST /devis/{id}/facturer | Crée la facture brouillon correspondante. |
DELETE /devis/{id} | Supprime un devis. |
GET /opportunites | Suivi des ventes. |
POST /opportunites | titre, montantEstime, client ou clientId, etape, dateRelance, notes. |
POST /opportunites/{id}/etape | prospect, contact, devis, negociation, gagne, perdu. |
POST /opportunites/{id}/facturer | Facture une opportunité gagnée. |
DELETE /opportunites/{id} | Supprime une opportunité. |
Contrats récurrents
| Requête | Description |
|---|---|
GET /contrats | Contrats. |
POST /contrats | clientId, objet, lignes, periodicite (mensuel, trimestriel, semestriel, annuel), dateDebut, joursAvant, nombrePeriodes, envoiAuto, prixTTC (facultatif, comme pour une facture : prix des factures établies TVA comprise). |
POST /contrats/{id}/statut | actif, suspendu, termine. |
POST /contrats/generer | Établit tout de suite les factures arrivées à échéance (fait aussi automatiquement chaque heure). |
DELETE /contrats/{id} | Supprime un contrat. |
Stock
| Requête | Description |
|---|---|
GET /stock/articles | Articles avec quantité, valeur et coût moyen pondéré. |
POST /stock/articles | { "no": "SKU-1042", "libelle": "Bougie parfumée", "unite": "pce" } ; compteStock (1200) et compteAchat (4200) facultatifs. Répond avec l'article et son id. |
PUT /stock/articles/{id} | Modifie un article. |
DELETE /stock/articles/{id} | Supprime un article sans mouvement. |
GET /stock/articles/{id}/mouvements | Historique de l'article. |
POST /stock/mouvements | articleId, date, type (entree, sortie, inventaire), quantite, prixUnitaire, libelle, ecritureId facultatif. |
Canaux de vente
Un canal regroupe les réglages comptables d'une source de ventes : une boutique, une caisse, un prestataire de paiement. Son
code (minuscules, chiffres, ., _, -) est choisi par le logiciel qui l'alimente. Un canal
inconnu est créé avec les valeurs par défaut à la première commande.
| Requête | Description |
|---|---|
GET /canaux | Liste. /canaux/{code} pour un seul. |
PUT /canaux/{code} | Crée ou met à jour (champs partiels) : nom, type, mode, stock, comptes, actif. |
DELETE /canaux/{code} | Supprime un canal sans opération. |
PUT /api/v1/canaux/prestashop-1
{
"nom": "Boutique PrestaShop",
"type": "prestashop",
"mode": "ecritures",
"stock": "mouvements",
"comptes": {
"ventesNormal": "3200", "ventesReduit": "3201", "ventesHebergement": "3401", "ventesExonere": "3200",
"fraisPort": "",
"encaissement": { "*": "1020", "carte": "1030", "paypal": "1036", "twint": "1037" },
"frais": "6900", "debiteurs": "1100", "banque": "1020"
}
}
| Champ | Valeurs |
|---|---|
type | prestashop, woocommerce, shopify, stripe, sumup, wallee, postfinance-checkout, caisse, api. |
mode | ecritures (défaut : une écriture par vente) ou factures (une facture ComptaLT émise puis encaissée par commande). |
stock | mouvements : chaque vente sort les articles par référence, les retours les remettent ; quantites : la boutique envoie ses quantités ; aucun. N'envoyez jamais les quantités en mode mouvements (double décompte). |
comptes.encaissement | Moyen de paiement → compte d'argent ; * pour les autres. Moyens conseillés : carte, twint, paypal, postfinance, virement, facture, especes, autre. Un compte 1000–1099 absent est créé (« en transit »). |
Commandes
La façon la plus simple de relier une boutique : envoyez la commande telle quelle, ComptaLT calcule la TVA par taux, passe l'écriture
(ou la facture), l'encaissement, la commission et la sortie de stock. Une commande renvoyée n'est jamais comptée deux fois :
canal + reference l'identifient durablement.
curl -X POST https://votre-site/api/v1/commandes \ -H "Authorization: Bearer clt_VOTRE_CLE" -H "Content-Type: application/json" \ -d '{ "canal": "prestashop-1", "reference": "1042", "numero": "XKBKNABJK", "date": "2026-09-17", "prixTTC": true, "client": { "nom": "Marie Dupont", "email": "marie@exemple.ch", "npaLocalite": "1800 Vevey", "pays": "CH" }, "lignes": [ { "reference": "BOUG-01", "description": "Bougie parfumée", "quantite": 2, "prixUnitaire": 25.00, "tauxTva": 8.1 }, { "reference": "LIV-12", "description": "Livre de recettes", "quantite": 1, "prixUnitaire": 30.00, "tauxTva": 2.6 }, { "type": "port", "description": "Poste CH", "montant": 7.00, "tauxTva": 8.1 }, { "type": "rabais", "description": "Code BIENVENUE", "montant": 5.00, "tauxTva": 8.1 } ], "total": 82.00, "paiement": { "moyen": "carte", "frais": 1.50, "reference": "pi_3Nabc" } }' # → { "ok": true, "dejaEnregistree": false, "commande": { "statut": "payee", "totalTtc": 82, "totalTva": 4.66, # "ventilation": [{ "taux": 8.1, "compte": "3200", "ht": 48.1, "tva": 3.9, "ttc": 52 }, …], "avertissements": [] } }
| Champ | Règle |
|---|---|
reference | Unique par canal (≤ 100 caractères). PrestaShop : l'id_order ; WooCommerce : l'ID de commande. |
prixTTC | true par défaut : prixUnitaire et montant sont TVA comprise ; false : hors TVA, la TVA s'ajoute par taux. |
lignes[].type | article (défaut), port, frais, rabais (montant positif déduit). |
lignes[].montant | Total de la ligne après rabais ; à défaut quantite × prixUnitaire. |
lignes[].tva | Facultatif : TVA de la ligne telle que calculée par la boutique, reprise exactement si toutes les lignes l'ont. |
total | Montant payé par le client, toujours TVA comprise (quel que soit prixTTC). Contrôle : un écart d'arrondi ≤ 0.10 CHF est absorbé (avertissement), au-delà la commande est refusée (422). |
devise / tauxChange | Autre devise que le CHF : tauxChange (CHF pour 1 unité) obligatoire. |
paiement | Absent : commande à encaisser (débiteurs). frais = commission. Commande en devise : montantChf (francs réellement reçus, par exemple d'après le relevé bancaire) n'est jamais reconverti ; l'écart avec le montant au cours de la commande est une différence de change (gain 6950, perte 6900), à 10 % près (422 au-delà). Les paiements partiels ne sont pas pris en charge. |
Les avertissements ne bloquent pas (article inconnu du stock, stock insuffisant, exercice ouvert automatiquement, taux de
TVA étranger, écart d'arrondi) : affichez-les dans le journal de votre connecteur. Une date dans l'année qui suit le dernier exercice
ouvre cet exercice automatiquement ; un exercice clôturé refuse la commande.
| Requête | Description |
|---|---|
GET /commandes | Liste, plus récentes d'abord. Filtres : canal, statut (a-encaisser, payee, remboursee-partiellement, remboursee, annulee), depuis, jusqua, recherche, limite. |
GET /commandes/{canal}/{reference} | Une commande (404 si inconnue). |
POST /commandes/{canal}/{reference}/paiement | Solde une commande non payée : { "date": "2026-09-20", "moyen": "virement", "frais": 0 }. Commande en devise : { "date": "2026-09-20", "moyen": "virement", "montantChf": 935.20 }, francs reçus (différence de change passée). |
POST /commandes/{canal}/{reference}/remboursements | Remboursement total ou partiel, voir ci-dessous. |
POST /commandes/{canal}/{reference}/annuler | { "date", "motif", "retourStock": true } : créance extournée si non payée, solde remboursé sinon. Rejouable. |
POST /api/v1/commandes/prestashop-1/1042/remboursements
{
"reference": "avoir-17",
"date": "2026-09-25",
"montantTtc": 32.00,
"retours": [{ "reference": "BOUG-01", "quantite": 1 }],
"motif": "Article abîmé"
}
referencedu remboursement obligatoire, unique par commande : renvoyé, il répond"dejaEnregistre": true.- Montant, par priorité :
lignes(détail, TVA de chaque ligne) ;montantTtc(argent rendu, TVA comprise) ;montant(dans la base de la commande) ; sinon tout le solde. - Sans
lignes, la TVA suit les articles deretoursquand ils couvrent le montant (le surplus, par exemple les frais de port, est réparti sur le reste de la commande) ; sinon le montant est réparti au prorata des taux de la commande. Pour un avoir sans retour en stock sur une commande à plusieurs taux, envoyez leslignes. retoursremet des articles en stock (canal en modemouvements) ;retourStock: falsen'en remet aucun.
Paiements isolés et versements
Pour un prestataire de paiement sans détail d'articles (lien de paiement, terminal, facture payée en ligne).
POST /api/v1/paiements
{ "canal": "stripe", "reference": "pi_3Nabc", "date": "2026-09-17", "montant": 108.10, "frais": 3.35,
"moyen": "twint", "description": "Atelier du 20 septembre", "tauxTva": 8.1, "compte": "3400",
"factureNumero": "2026-0012" }
- Si
factureNumerodésigne une facture ComptaLT émise, la facture est encaissée (et la commission passée) ; sinon le paiement devient une vente TTC d'une ligne. - Il est rangé avec les commandes (
originepaiementoufacture) : remboursements par/commandes/{canal}/{reference}/remboursements.
POST /api/v1/paiements/versements
{ "canal": "stripe", "reference": "po_1Nxyz", "date": "2026-09-19", "montant": 1045.30, "frais": 0 }
# Écriture : banque du canal au débit, compte d'encaissement (en transit) du canal au crédit.
Catalogue par référence
| Requête | Description |
|---|---|
GET /stock/articles/reference/{reference} | L'article dont le numéro est la référence (SKU). |
PUT /stock/articles/reference/{reference} | Crée ou met à jour : libelle (obligatoire à la création), unite, seuilAlerte, compteStock, compteAchat, quantite (inventaire si elle diffère), prixAchat (valorisation), date. Répond { "ok", "cree", "article", "mouvement" }. |
POST /stock/articles/lot | { "articles": [{ "reference": "BOUG-01", "libelle": "…" }, …] }, 500 au plus. Répond un résultat par article. |
Fournisseurs et factures fournisseurs
| Requête | Description |
|---|---|
GET /fournisseurs | Fichier des fournisseurs. /fournisseurs/{id} pour un seul. |
POST /fournisseurs | Crée (ou modifie avec id) : nom, adresse, npaLocalite, pays, email, iban, ide, compteCharge, tauxTva, conditionsJours. |
GET /fournisseurs/factures | Factures reçues ; filtres statut (a-payer, payee, annulee), annee, fournisseur. |
POST /fournisseurs/factures | Enregistre et comptabilise (charges, impôt préalable, créanciers 2000). Le fournisseur inconnu est créé. pieceJointe: { nom, donneesBase64 } joint le justificatif. |
POST /fournisseurs/factures/{id}/payer | { "compteArgentNo": "1020", "datePaiement": "2026-10-10" }. /annuler-paiement et /annuler reviennent en arrière. |
POST /fournisseurs/factures/lire-qr | { "contenu": "SPC\n0200\n1\n…" } : décode une QR-facture suisse (y compris les informations Swico S1 : numéro, date, TVA, conditions) et renvoie une proposition de facture, sans rien enregistrer. |
POST /api/v1/fournisseurs/factures
{
"fournisseur": { "nom": "Swisscom SA", "iban": "CH93 0076 2011 6238 5295 7" },
"numero": "F-88123", "date": "2026-09-10", "echeance": "2026-10-10",
"lignes": [{ "description": "Abonnement internet", "montant": 64.90, "tauxTva": 8.1, "compteCharge": "6570" }],
"referencePaiement": "210000000003139471430009017"
}
lignes[].montant est TVA comprise : l'impôt préalable est extrait selon tauxTva (sauf méthode
forfaitaire). Une ligne avec articleId et quantite fait entrer la marchandise en stock (au coût HT). Le
paiement par le rapprochement bancaire solde la facture automatiquement quand la référence correspond.
Relevés bancaires et rapprochement
Formats reconnus : camt.053, camt.052 et camt.054 (ISO 20022, normes SIX), MT940 et CSV (PostFinance, UBS,
Raiffeisen, exports génériques ; correspondance des colonnes détectée ou fournie). Les fichiers s'envoient en texte (contenu)
ou en base64 (contenuBase64, UTF-8 ou Windows-1252). Un mouvement déjà importé est ignoré, y compris un paiement
QR reçu d'abord par un avis camt.054 puis par le relevé camt.053 (même compte, date, montant et référence).
| Requête | Description |
|---|---|
POST /banque/analyser | { "nom", "contenu", "iban"? } : format, IBAN, période, solde final, nombre de mouvements, de doublons et de doublons probables (doublonsProbables), aperçu, colonnes CSV. Un relevé camt qui contient plusieurs comptes donne comptes ([{ "iban", "mouvements" }]) et n'analyse que le compte iban demandé (par défaut le premier). N'enregistre rien. |
POST /banque/imports | { "nom": "releve.xml", "contenu", "compteNo": "1020", "iban"?, "correspondanceCsv"?, "rapprochementAuto"? } : importe et rapproche automatiquement les cas sûrs (référence QR d'une facture au bon montant, facture fournisseur, règles automatiques). Relevé camt à plusieurs comptes : seuls les mouvements du compte iban (par défaut celui déjà associé à compteNo, sinon le premier) ; un iban absent du fichier est refusé (422). Répond aussi ecartSolde (solde final du relevé moins solde comptable du compte à la même date) et doublonsProbables (mouvements probablement déjà reçus d'un autre fichier, jamais rapprochés automatiquement). |
GET /banque/transactions | Filtres statut, compte, depuis, jusqua, import ; pagination limite et decalage (transactions sautées). Les transactions a-traiter portent des suggestions ; doublonProbable signale un mouvement probablement déjà importé. |
GET /banque/transactions/page | Mêmes filtres, réponse { "transactions": [ … ], "total": 1240 } : 100 transactions par défaut, 500 au plus (limite), à partir de decalage. |
GET /banque/ecarts?annee=2026 | Pour chaque relevé importé avec un solde final (de l'exercice annee, ou de tous) : écart actuel avec la comptabilité, [{ "importId", "fichier", "compteNo", "date", "soldeReleve", "soldeComptable", "ecart" }]. L'écart non nul du dernier relevé d'un compte bloque la clôture d'un exercice ouvert depuis le 04.10.2026 (simple avertissement pour les exercices plus anciens). |
POST /banque/transactions/{id}/rapprocher | Une suggestion telle quelle, ou { "action": "creer-ecriture", "compteNo": "6570", "tauxTva": 8.1 }. Actions : encaisser-facture, payer-facture-fournisseur, encaisser-commande, lier-ecriture, creer-ecriture, virement. |
POST /banque/transactions/{id}/ignorer | Aucune écriture nécessaire. /retablir annule un rapprochement ou un « ignoré ». |
POST /banque/rapprochement-auto | Applique toutes les suggestions sûres. |
GET /banque/regles | Règles ; POST /banque/regles { "nom", "contient": "SWISSCOM", "sens": "sortie", "compteNo": "6570", "tauxTva": 8.1, "automatique": true } ; DELETE /banque/regles/{id}. |
GET /banque/imports | Relevés importés ; DELETE /banque/imports/{id} retire ses transactions non rapprochées. |
Caisse
| Requête | Description |
|---|---|
POST /caisse/clotures | Une journée : { "canal": "caisse", "date", "reference": "Z-0412", "ventes": [{ "tauxTva": 8.1, "montant": 1081 }], "encaissements": [{ "moyen": "especes", "montant": 400 }, { "moyen": "carte", "montant": 681 }] }. Écart encaissé − ventes : excédent (3600) ou manque (6700). Idempotent par canal et référence (ou date). |
GET /caisse/clotures?annee=2026 | Clôtures. DELETE /caisse/clotures/{id} retire aussi l'écriture. |
POST /caisse/import | { "contenu" | "contenuBase64", "canal" } : CSV date;reference;ventes_8.1;ventes_2.6;especes;carte;twint;notes (autres moyens : postfinance, paypal, facture, bons, encaisse_…). Un résultat par ligne. |
Boutiques en ligne : modules prêts à installer
PrestaShop
1.7.8 à 9. Télécharger le module, puis Modules → Téléverser. File d'attente avec nouvelles tentatives, avoirs, multiboutique, synchronisation du catalogue.
WooCommerce
WordPress 6.2+, HPOS. Télécharger l'extension, puis Extensions → Téléverser. Traitement en arrière-plan par Action Scheduler.
Shopify
Sans installation : créez une connexion directe dans l'application, puis les webhooks dans Paramètres → Notifications.
Les modules suivent exactement l'API ci-dessus : PUT /canaux/{code} à l'enregistrement de la configuration,
POST /commandes au paiement, /remboursements pour les avoirs, /annuler, PUT
/stock/articles/reference/{sku} pour le catalogue. Les erreurs 5xx, 429 et réseau sont réessayées ;
4xx et 422 attendent une correction puis un renvoi manuel.
Paiements en ligne et Shopify : connexions directes
Dans « API et intégrations », une connexion reçoit une adresse secrète https://votre-site/api/connecteurs/cx_…
que vous indiquez à la plateforme. ComptaLT vérifie la signature, comptabilise, et tient un journal ; un événement refusé par la
comptabilité (exercice manquant…) répond 200 à la plateforme et reste à rejouer après correction.
| Plateforme | Événements à abonner | Sécurité |
|---|---|---|
| Shopify | orders/paid, refunds/create, orders/cancelled, products/create, products/update, inventory_levels/update (mode quantites) | HMAC-SHA256 X-Shopify-Hmac-Sha256 avec la clé de signature des webhooks. |
| Stripe (cartes, TWINT…) | charge.succeeded, charge.captured, charge.refunded, payout.paid | Stripe-Signature (fenêtre de 5 min). Clé API restreinte facultative pour les commissions et les devises. |
| Wallee, PostFinance Checkout | Transaction à l'état FULFILL, Remboursement SUCCESSFUL | La notification ne sert que de signal : les données sont relues par l'API avec l'utilisateur d'application (authentification MAC). |
| SumUp | Checkouts : return_url = adresse de réception. Terminaux : synchronisation horaire des journées complètes en clôtures de caisse. | Données relues par l'API avec la clé SumUp. |
n8n, Zapier et Make
| Outil | Contenu | Installation |
|---|---|---|
| n8n | Nœud « ComptaLT » (écritures, factures, clients, commandes, paiements, articles, factures fournisseurs, rapports) et « ComptaLT Trigger » (webhook signé). | n8n auto-hébergé : Settings → Community Nodes → n8n-nodes-comptalt (paquet npm), ou installation manuelle depuis integrations/n8n/n8n-nodes-comptalt. |
| Zapier | Déclencheurs instantanés (nouvelle commande, facture émise, payée en partie, payée ou soldée, paiement retiré, avoir émis ou annulé, nouveau client, stock bas, facture fournisseur, transaction rapprochée), actions et recherches. | Application privée publiée avec la CLI Zapier (zapier push, dossier integrations/zapier/comptalt-zapier), puis invitation des utilisateurs. |
| Make | Application personnalisée : modules d'action et de recherche, déclencheurs instantanés. | Application personnalisée déployée avec l'extension Make pour VS Code (dossier integrations/make/comptalt-make). |
Tous se connectent avec l'adresse de ComptaLT et une clé API ; les déclencheurs créent et suppriment eux-mêmes leur webhook.
Webhooks : abonnements
| Requête | Description |
|---|---|
POST /webhooks | { "url": "https://…", "evenements": ["commande.enregistree"], "description", "source" } (["*"] = tous). Répond le webhook avec son secret, affiché cette fois seulement. |
GET /webhooks | Liste, sans les secrets. |
PUT /webhooks/{id} | url, evenements, description, actif. Par une clé API : ses propres abonnements seulement (voir portées des clés), et une réactivation seulement de ce qu'elle a désactivé. |
DELETE /webhooks/{id} | Supprime l'abonnement (les livraisons en attente sont abandonnées). |
POST /webhooks/{id}/secret | Génère un nouveau secret. |
POST /webhooks/{id}/test | Envoie un événement ping et répond le résultat. |
GET /webhooks/{id}/livraisons | 100 dernières livraisons (statut, essais, réponses). POST /webhooks/livraisons/{id}/renvoyer. |
GET /webhooks/evenements | Catalogue des types d'événements. |
Chaque livraison est un POST JSON ; une réponse 2xx en moins de 10 secondes la valide, sinon ComptaLT réessaie après
1 min, 5 min, 30 min, 2 h, 6 h et 24 h. L'adresse doit être en https et publique (les adresses internes sont refusées).
{
"id": "evt_Yd0…", "type": "facture.payee", "creeLe": "2026-09-17T08:00:00.000Z", "societe": "Boutique Léman",
"donnees": { … la facture … }
}
Événements
| Type | donnees |
|---|---|
ecriture.creee, ecriture.modifiee, ecriture.supprimee | L'écriture (pour une suppression : id, no, annee, date, libelle). |
facture.creee, facture.emise, facture.payee, facture.annulee | La facture client avec ses totaux (net, tva, brut) et son solde ouvert. facture.creee : brouillon, numero vide (le numéro arrive avec facture.emise). facture.emise : une seule fois, à l'émission, jamais pour un avoir. facture.payee : le paiement qui solde la facture, jamais un paiement partiel. |
facture.paiement_partiel | La facture, restée ouverte (statut partielle) après un règlement partiel : paiement, avoir imputé ou perte partielle (paiements[].nature) ; solde = ce qui reste à payer. |
facture.soldee | La facture, soldée sans paiement de son solde : avoir imputé ou perte sur débiteur (statut payee, mais rien n'a été encaissé pour ce solde). |
facture.paiement_annule | La facture dont un règlement est retiré (paiement erroné, rapprochement bancaire annulé, avoir annulé) : elle redevient emise ou partielle. |
avoir.emis, avoir.annule | L'avoir (total négatif) avec ses totaux, son solde (négatif : reste à rembourser), avoirDe et factureOrigine ({ "id", "numero" }). |
client.cree, client.modifie | Le client. |
commande.enregistree, commande.payee, commande.remboursee, commande.annulee | La commande (ou le paiement en ligne). |
article.cree, article.modifie, stock.mouvement, stock.alerte | L'article ; pour un mouvement { mouvement, article } ; l'alerte part quand un article passe sous son seuil. |
fournisseur.facture.enregistree, fournisseur.facture.payee | La facture fournisseur. |
banque.releve.importe, banque.transaction.rapprochee | L'import ou la transaction. |
caisse.cloture.enregistree | La clôture de caisse. |
exercice.cloture | { annee }. |
ping | { message } (test). |
Les événements naissent de toute modification : application, API, agent IA, connexion directe ou tâche planifiée.
facture.creee, facture.emise,
facture.payee ni facture.annulee (son total négatif déclencherait les actions d'une facture) ;
facture.emise n'est plus renvoyé quand un paiement est retiré (facture.paiement_annule) ;
facture.payee ne part qu'au paiement qui solde la facture. Un abonnement ["*"] reçoit aussi les
nouveaux types : ignorez ceux que votre intégration ne connaît pas.
Vérifier la signature
L'en-tête ComptaLT-Signature: t=1726567200,v1=5f3c… contient l'horodatage et
HMAC-SHA256(secret, "{t}.{corps brut}") en hexadécimal. Calculez-le sur le corps brut, comparez en temps constant et
refusez un horodatage de plus de 5 minutes. ComptaLT-Livraison identifie la livraison (utile pour ignorer un doublon).
import { createHmac, timingSafeEqual } from 'node:crypto'
export function signatureValide(corpsBrut, entete, secret) {
const parties = Object.fromEntries(entete.split(',').map((p) => p.split('=')))
if (Math.abs(Date.now() / 1000 - Number(parties.t)) > 300) return false
const attendue = createHmac('sha256', secret).update(`${parties.t}.${corpsBrut}`).digest('hex')
return attendue.length === parties.v1.length && timingSafeEqual(Buffer.from(attendue), Buffer.from(parties.v1))
}
$corps = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_COMPTALT_SIGNATURE'] ?? ''), $parties);
$attendue = hash_hmac('sha256', $parties['t'] . '.' . $corps, $secret);
$valide = abs(time() - (int) $parties['t']) <= 300 && hash_equals($attendue, $parties['v1'] ?? '');
import hmac, hashlib, time
def signature_valide(corps_brut: bytes, entete: str, secret: str) -> bool:
parties = dict(p.split("=", 1) for p in entete.split(","))
if abs(time.time() - int(parties["t"])) > 300:
return False
attendue = hmac.new(secret.encode(), f"{parties['t']}.".encode() + corps_brut, hashlib.sha256).hexdigest()
return hmac.compare_digest(attendue, parties.get("v1", ""))
Guide : facturer par l'API
# 1. Brouillon POST /api/v1/factures { "date": "2026-09-17", "annee": 2026, "client": { "nom": "Atelier Rossier Sàrl", "adresse": "Rue du Lac 4", "npaLocalite": "1800 Vevey", "pays": "CH", "email": "compta@rossier.ch" }, "objet": "Refonte du site", "lignes": [ { "description": "Conception", "quantite": 12, "prixUnitaire": 120, "tauxTva": 8.1, "compteProduit": "3400" } ] } # → { "ok": true, "facture": { "id": "…", "numero": "", "statut": "brouillon", … } } # Le brouillon n'a pas encore de numéro. # Sans "prixTTC" : selon facturation.prixTtcApiParDefaut (hors TVA pour une nouvelle société, 8.1 % en sus). # Précisez-le : "prixTTC": false, 120 hors TVA ; "prixTTC": true, 120 TVA comprise (« dont TVA »). # 2. Émission (le numéro est attribué ici), envoi, puis encaissement POST /api/v1/factures/{id}/emettre # → { "ok": true, "facture": { "numero": "2026-0012", "statut": "emise", … } } POST /api/v1/factures/{id}/envoyer POST /api/v1/factures/{id}/encaisser { "compteArgentNo": "1020", "datePaiement": "2026-10-10" } # Paiement partiel : la facture passe « partielle », solde = ce qui reste à payer POST /api/v1/factures/{id}/paiements { "compteArgentNo": "1020", "datePaiement": "2026-10-10", "montant": 500 } # Correction d'une facture émise : avoir (toute la facture, ou des lignes), puis remboursement du reste POST /api/v1/factures/{id}/avoir { "date": "2026-10-20", "motif": "Prestation non réalisée" } POST /api/v1/factures/{avoirId}/rembourser { "compteArgentNo": "1020", "date": "2026-10-25" } # PDF avec QR-facture du solde ouvert GET /api/v1/exports/facture?id={id}
Connecter un agent IA (MCP)
Le Model Context Protocol permet à un
assistant IA d'utiliser directement votre comptabilité : « enregistre les frais de ce ticket », « quel est mon résultat
à fin août ? », « facture 12 heures à l'Atelier Rossier ». Le serveur MCP de ComptaLT utilise le transport
Streamable HTTP et la même clé API que l'API REST : une clé en lecture seule ne montre à l'agent que les outils
de consultation, une clé d'écriture que les outils de sa portée (tous avec une clé
complete).
https://votre-site/mcpEn-tête :
Authorization: Bearer clt_VOTRE_CLE
claude mcp add --transport http comptalt https://votre-site/mcp \
--header "Authorization: Bearer clt_VOTRE_CLE"
Dans le fichier de configuration MCP de votre application (mcp.json, claude_desktop_config.json…) :
{
"mcpServers": {
"comptalt": {
"type": "http",
"url": "https://votre-site/mcp",
"headers": { "Authorization": "Bearer clt_VOTRE_CLE" }
}
}
}
Une application qui ne sait lancer que des serveurs locaux peut passer par le pont
mcp-remote : npx mcp-remote https://votre-site/mcp --header "Authorization: Bearer clt_VOTRE_CLE".
// TypeScript, @modelcontextprotocol/sdk import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' const transport = new StreamableHTTPClientTransport(new URL('https://votre-site/mcp'), { requestInit: { headers: { Authorization: 'Bearer ' + process.env.COMPTALT_CLE } } }) const client = new Client({ name: 'mon-agent', version: '1.0.0' }) await client.connect(transport) const { tools } = await client.listTools() const resultat = await client.callTool({ name: 'rapport', arguments: { type: 'balance', annee: 2026 } })
Outils disponibles
Chargement de la liste…
Protocole brut
Serveur sans session : chaque message JSON-RPC 2.0 est un POST /mcp qui reçoit sa réponse en JSON
(les lots de messages sont traités dans l'ordre). Versions de protocole acceptées : 2025-06-18,
2025-03-26, 2024-11-05.
curl -X POST https://votre-site/mcp \
-H "Authorization: Bearer clt_VOTRE_CLE" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "lister_ecritures",
"arguments": { "annee": 2026, "limite": 5 } } }'
Un outil refusé par la comptabilité répond normalement, avec "isError": true et le message d'erreur
en texte : l'agent peut corriger et réessayer.
Bonnes pratiques de sécurité
- Une clé par logiciel : vous saurez qui fait quoi (date de dernière utilisation) et pourrez en couper un seul.
- Le minimum de droits : un tableau de bord ou un agent d'analyse n'a besoin que de la lecture seule.
- Gardez la clé côté serveur (variable d'environnement, coffre-fort de secrets), jamais dans une page web, une application mobile ou un dépôt de code.
- Une clé exposée par erreur se révoque immédiatement dans API et intégrations, puis se remplace.
- Un agent IA avec droits d'écriture agit en votre nom : relisez ce qu'il propose avant les opérations importantes (suppression, clôture).
- Webhooks : vérifiez toujours la signature et répondez vite (traitez en arrière-plan) ; un secret se régénère à tout moment.
- Connexions directes : l'adresse
/api/connecteurs/cx_…est secrète ; supprimez la connexion si elle a fuité. Les secrets et clés des prestataires restent sur le serveur, chiffrés avec la comptabilité, et ne sont jamais renvoyés par l'API ni à l'application.