API REST v1
Lire stocks et factures avec une clé d'API personnelle et de simples requêtes HTTP — référence engendrée depuis la description OpenAPI.
L'API REST v1 lit les mêmes données que le
connecteur (chaque point d'API sert
le même outil, avec les mêmes contrôles), mais avec de simples requêtes
GET et une clé d'API personnelle : pas de
navigateur, pas d'OAuth. Elle est en lecture seule. Une clé
vaut pour une personne dans une exploitation.
Connecteur MCP ou API REST : lequel choisir ?
- API REST : un script, un tableur, un serveur, une
tâche planifiée — un programme qui tourne seul. Une clé dans une variable
d'environnement, un
GET, du JSON. - Connecteur MCP : un assistant IA (Claude, ChatGPT) ou une application dont chaque utilisateur se connecte lui-même à Mon Chai, avec un écran de consentement et des jetons qui se renouvellent.
Les deux lisent la même chose ; aucun des deux n'écrit.
Créer et révoquer une clé
- Dans Mon Chai : Administration › Connecteur IA & clés d'API, onglet « Clés d'API (vos programmes) ». Réservé au propriétaire et aux administrateurs de l'exploitation.
- Donnez un nom à la clé (celui de votre programme), cochez les portées utiles, choisissez sa durée de vie (30, 90, 365 jours). Au plus 5 clés actives par personne et par exploitation.
- La clé s'affiche une seule fois : copiez-la tout de suite dans un gestionnaire de mots de passe ou dans la configuration de votre programme. Mon Chai n'en garde qu'une empreinte et ne pourra plus vous la montrer.
Forme d'une clé : mc_live_<préfixe : 10 caractères>_<secret : 43 caractères>. Le préfixe n'est pas
secret : c'est lui que l'écran affiche pour reconnaître la clé. Pour
révoquer une clé, même écran, bouton « Révoquer » : l'effet est
immédiat. Une clé expirée ou révoquée ne se réactive pas ; on en crée une
nouvelle.
S'authentifier
Chaque requête porte la clé dans l'en-tête Authorization, et
uniquement là :
Authorization: Bearer mc_live_…
- Une clé dans l'adresse (
?cle=…,?api_key=…, ou collée dans le chemin) est refusée en400cle_dans_l_adresse, sans être lue : une adresse finit dans les journaux et l'historique. Si c'est déjà arrivé, révoquez la clé. - Toujours en HTTPS (
https://tester.monchai.fr) : une clé envoyée une seule fois enhttp://a circulé en clair, révoquez-la. - Depuis un serveur ou un script uniquement : l'API n'envoie pas d'en-têtes CORS, et une clé servie à un navigateur est lisible par tous.
Portées et droits
| Portée | Ce qu'elle ouvre |
|---|---|
stocks:lecture | Stocks : bouteilles, formats, cuvées, vrac |
factures:lecture | Factures et chiffre d'affaires : factures de vente et d'achat, impayés, chiffre d'affaires |
Les deux sont exigés à chaque requête : la portée de la clé
et le droit actuel de la personne dans l'exploitation. Un droit retiré
coupe la clé (403) sans la révoquer ; une personne qui quitte
l'exploitation perd ses clés (401).
Réponses et erreurs
Une réponse 200 est l'objet JSON de l'outil correspondant du
connecteur, avec son champ etat (ok ou
empty). Les montants sont des chaînes décimales
("125.50"), les dates au format AAAA-MM-JJ. Toute
erreur a la même forme :
{"erreur": {"code": "portee_insuffisante", "message": "Cette clé ne permet pas de lire …"}}
Testez le code (stable) dans votre programme ; le
message est une phrase en français qui dit quoi faire.
details s'ajoute parfois (paramètres acceptés, candidats).
| Statut | Codes | Cause, et quoi faire |
|---|---|---|
400 | cle_dans_l_adresse, parametre_inconnu, parametre_invalide, ambigu | Clé dans l'adresse ; paramètre mal orthographié (la liste acceptée est dans details) ou hors bornes ; nom de cuvée ambigu (candidats dans details). |
401 | cle_absente, authentification_non_supportee, cle_mal_formee, cle_inconnue, cle_revoquee, cle_expiree, acces_retire | Pas d'en-tête, autre schéma que Bearer, clé tronquée ou inconnue, révoquée, expirée, ou la personne n'a plus accès à l'exploitation. |
403 | portee_insuffisante, permission_refusee, module_indisponible | La clé n'a pas la portée ; le rôle de la personne ne le permet plus ; module fermé pour l'exploitation. |
404 | introuvable, route_inconnue | Cuvée ou facture inconnue ; adresse qui n'existe pas (sans barre finale). |
405 | methode_non_autorisee | Autre méthode que GET : l'API est en lecture seule. |
429 | trop_de_requetes, trop_d_echecs | Limite atteinte : attendez la durée de l'en-tête Retry-After (secondes). |
503 | service_indisponible | Le compteur de limitation ne répond pas : réessayez après Retry-After. |
Limites
- 120 requêtes par minute et par clé ;
- 300 requêtes par minute et par adresse IP ;
- 20 essais de clé refusés par 5 minutes et par adresse IP, après quoi l'adresse attend.
Chaque réponse authentifiée porte X-RateLimit-Limit,
X-RateLimit-Remaining et X-RateLimit-Reset (secondes
avant la nouvelle fenêtre). Les listes sont plafonnées comme au connecteur
(par exemple 50 factures au plus).
Référence des points
Cette référence est engendrée depuis la description
OpenAPI 1.0.0 que sert Mon Chai :
https://tester.monchai.fr/api/v1/openapi.json (publique, sans clé). Tous
les points sont des GET.
GET /api/v1/moi
Exploitation, utilisateur, rôle et portées de la clé. À appeler en premier.
Aucune portée exigée.
Outil du connecteur équivalent : contexte_connexion.
Réponses : 200, 401, 403, 405, 429.
GET /api/v1/stocks
Résumé des stocks : vrac en litres, bouteilles au total, par format et par cuvée.
Portée : stocks:lecture.
Outil du connecteur équivalent : stock_resume.
Réponses : 200, 401, 403, 405, 429.
GET /api/v1/stocks/vrac
Vin en vrac (cuves, lots techniques) en litres, par lot.
Portée : stocks:lecture.
Outil du connecteur équivalent : stock_vrac.
Réponses : 200, 401, 403, 405, 429.
GET /api/v1/stocks/cuvees/{cuvee}
Stock d'une cuvée par son nom (exact, sinon « contient ») : bouteilles par format et lots commerciaux.
Portée : stocks:lecture.
Outil du connecteur équivalent : stock_par_cuvee.
Réponses : 200, 400, 401, 403, 404, 405, 429.
| Paramètre | Où | Description |
|---|---|---|
cuvee obligatoire | chemin | Nom de la cuvée (encodé dans l'adresse). |
GET /api/v1/stocks/formats/{format_ml}
Lots commerciaux d'un format en millilitres (750 bouteille, 1500 magnum, 375 demi).
Portée : stocks:lecture.
Outil du connecteur équivalent : stock_par_format.
Réponses : 200, 400, 401, 403, 405, 429.
| Paramètre | Où | Description |
|---|---|---|
format_ml obligatoire | chemin | Format en millilitres, entre 50 et 30000. |
GET /api/v1/factures
Factures (ventes par défaut, achats avec sens=purchase), filtrables par statut, période et client ; triées par date d'émission décroissante ; au plus 50.
Portée : factures:lecture.
Outil du connecteur équivalent : factures_lister.
Réponses : 200, 400, 401, 403, 405, 429.
| Paramètre | Où | Description |
|---|---|---|
statut | requête | Statut de la facture. Valeurs : draft, sent, validated, executed, issued, paid, cancelled. |
du | requête | Début de période (date d'émission), AAAA-MM-JJ. |
au | requête | Fin de période (date d'émission), AAAA-MM-JJ. |
client | requête | Partie du nom du client (100 caractères au plus). |
sens | requête | sale (ventes) ou purchase (achats). Valeurs : sale, purchase. Par défaut : sale. |
limite | requête | Nombre de factures rendues (1 à 50). Par défaut : 20. |
GET /api/v1/factures/impayees
Factures de vente émises non soldées (avoirs déduits), total dû et échues.
Portée : factures:lecture.
Outil du connecteur équivalent : factures_impayees.
Réponses : 200, 400, 401, 403, 405, 429.
| Paramètre | Où | Description |
|---|---|---|
au | requête | Fin de période (date d'émission), AAAA-MM-JJ. |
GET /api/v1/factures/{numero}
Détail d'une facture ou d'un avoir par son numéro : en-tête, lignes, règlements.
Portée : factures:lecture.
Outil du connecteur équivalent : facture_detail.
Réponses : 200, 400, 401, 403, 404, 405, 429.
| Paramètre | Où | Description |
|---|---|---|
numero obligatoire | chemin | Numéro (ou référence) de la facture ou de l'avoir. |
GET /api/v1/chiffre-affaires
Chiffre d'affaires sur une période : factures et avoirs de vente émis ou payés, net HT et TTC.
Portée : factures:lecture.
Outil du connecteur équivalent : chiffre_affaires.
Réponses : 200, 400, 401, 403, 405, 429.
| Paramètre | Où | Description |
|---|---|---|
du | requête | Début de période (date d'émission), AAAA-MM-JJ. |
au | requête | Fin de période (date d'émission), AAAA-MM-JJ. |
Exemples complets
Ils lisent la clé dans la variable d'environnement MON_CHAI_CLE —
jamais écrite dans le code. Dans un terminal (bash, zsh, Git Bash) :
export MON_CHAI_CLE="mc_live_…" # collez votre clé ; PowerShell : $env:MON_CHAI_CLE = "…"
Mes stocks (curl)
curl -s "https://tester.monchai.fr/api/v1/stocks" \
-H "Authorization: Bearer $MON_CHAI_CLE"
Mes impayés (curl)
curl -s "https://tester.monchai.fr/api/v1/factures/impayees" \
-H "Authorization: Bearer $MON_CHAI_CLE"
Mes stocks et mes impayés (Python)
Installation : pip install httpx — lancement : python api_mes_donnees.py.
Fichier api_mes_donnees.py, à copier tel quel :
"""Mes stocks et mes impayés par l'API REST v1 de Mon Chai — exemple complet en Python.
Avant de lancer :
1. dans Mon Chai, Administration › Connecteur IA & clés d'API › « Clés d'API
(vos programmes) », créez une clé avec les portées « Stocks » et
« Factures et chiffre d'affaires » ; copiez-la, elle ne sera plus affichée ;
2. installez le client HTTP : pip install httpx
3. donnez la clé au script par une variable d'environnement, jamais dans le code :
macOS / Linux : export MON_CHAI_CLE="mc_live_…"
Windows PowerShell : $env:MON_CHAI_CLE = "mc_live_…"
Lancement :
python api_mes_donnees.py
Autre adresse Mon Chai : MON_CHAI_URL=https://… python api_mes_donnees.py
"""
import os
import httpx
MON_CHAI = os.environ.get("MON_CHAI_URL", "https://tester.monchai.fr").rstrip("/")
CLE = os.environ.get("MON_CHAI_CLE", "")
def lire(client: httpx.Client, chemin: str) -> dict:
"""Un GET sur l'API ; en cas de refus, le code et le message de Mon Chai."""
reponse = client.get(chemin)
corps = reponse.json()
if reponse.status_code != 200:
erreur = corps["erreur"]
attente = reponse.headers.get("Retry-After")
raise SystemExit(f"{reponse.status_code} {erreur['code']} : {erreur['message']}"
+ (f" (réessayer dans {attente} s)" if attente else ""))
return corps
def main() -> None:
if not CLE:
raise SystemExit("Définissez la variable d'environnement MON_CHAI_CLE (votre clé d'API).")
entetes = {"Authorization": f"Bearer {CLE}"}
with httpx.Client(base_url=MON_CHAI + "/api/v1", headers=entetes, timeout=30) as client:
moi = lire(client, "/moi")
print("Exploitation :", moi["exploitation"], "— portées :", ", ".join(moi["portee"]))
stocks = lire(client, "/stocks")
print("Bouteilles :", stocks["bouteilles_total"], "— vrac :", stocks["vrac_litres"], "L")
for ligne in stocks["par_cuvee"]:
print(f" {ligne['cuvee']} : {ligne['bouteilles']} bouteilles")
impayes = lire(client, "/factures/impayees")
if impayes["etat"] == "empty":
print("Aucune facture impayée.")
return
print(f"{impayes['nombre']} facture(s) impayée(s), {impayes['nombre_echues']} échue(s)"
f" — total dû : {impayes['montant_du_total']} €")
for facture in impayes["factures"]:
print(f" {facture['numero']} {facture['client']} reste dû {facture['reste_du']} €"
f" échéance {facture['date_echeance'] or '—'}")
if __name__ == "__main__":
main()
Mes stocks et mes impayés (TypeScript)
Installation : aucune dépendance (fetch de Node.js 18+) ; tsx pour exécuter le TypeScript — lancement : npx tsx api_mes_donnees.ts.
Fichier api_mes_donnees.ts, à copier tel quel :
/**
* Mes stocks et mes impayés par l'API REST v1 de Mon Chai — exemple complet en TypeScript.
*
* Avant de lancer :
* 1. dans Mon Chai, Administration › Connecteur IA & clés d'API › « Clés d'API
* (vos programmes) », créez une clé avec les portées « Stocks » et
* « Factures et chiffre d'affaires » ; copiez-la, elle ne sera plus affichée ;
* 2. donnez la clé au script par une variable d'environnement, jamais dans le code :
* macOS / Linux : export MON_CHAI_CLE="mc_live_…"
* Windows PowerShell : $env:MON_CHAI_CLE = "mc_live_…"
*
* Lancement (Node.js 18 ou plus récent, aucune dépendance : fetch est intégré) :
* npx tsx api_mes_donnees.ts (ou, avec Node.js 23.6+ : node api_mes_donnees.ts)
* Autre adresse Mon Chai : MON_CHAI_URL=https://… npx tsx api_mes_donnees.ts
*
* À exécuter côté serveur ou dans un script : jamais dans une page web, la clé
* y serait lisible par tous (et l'API n'envoie pas d'en-têtes CORS : un
* navigateur refuserait de toute façon la réponse).
*/
const MON_CHAI = (process.env.MON_CHAI_URL ?? "https://tester.monchai.fr").replace(/\/+$/, "");
const CLE = process.env.MON_CHAI_CLE ?? "";
/** Un GET sur l'API ; en cas de refus, le code et le message de Mon Chai. */
async function lire(chemin: string): Promise<any> {
const reponse = await fetch(`${MON_CHAI}/api/v1${chemin}`, {
headers: { Authorization: `Bearer ${CLE}` },
});
const corps = await reponse.json();
if (!reponse.ok) {
const attente = reponse.headers.get("Retry-After");
throw new Error(`${reponse.status} ${corps.erreur.code} : ${corps.erreur.message}`
+ (attente ? ` (réessayer dans ${attente} s)` : ""));
}
return corps;
}
async function main() {
if (!CLE) throw new Error("Définissez la variable d'environnement MON_CHAI_CLE (votre clé d'API).");
const moi = await lire("/moi");
console.log(`Exploitation : ${moi.exploitation} — portées : ${moi.portee.join(", ")}`);
const stocks = await lire("/stocks");
console.log(`Bouteilles : ${stocks.bouteilles_total} — vrac : ${stocks.vrac_litres} L`);
for (const ligne of stocks.par_cuvee) {
console.log(` ${ligne.cuvee} : ${ligne.bouteilles} bouteilles`);
}
const impayes = await lire("/factures/impayees");
if (impayes.etat === "empty") {
console.log("Aucune facture impayée.");
return;
}
console.log(`${impayes.nombre} facture(s) impayée(s), ${impayes.nombre_echues} échue(s) — total dû : ${impayes.montant_du_total} €`);
for (const facture of impayes.factures) {
console.log(` ${facture.numero} ${facture.client} reste dû ${facture.reste_du} € échéance ${facture.date_echeance ?? "—"}`);
}
}
main().catch((erreur) => {
console.error(erreur.message ?? erreur);
process.exit(1);
});
Ces commandes et ces deux fichiers sont ceux que Mon Chai exécute contre un serveur réel, avec une clé de test, pour vérifier cette page.