➜︎ Retour - Menu API, webhooks et mailhooks
Webhook et mailhook « Vente : validation »
Menu de la documentation
Cette page décrit le webhook et le mailhook « Vente : validation », qui se règlent dans le menu
Réglages → API & Make.
À chaque vente validée et encaissée, Melkal envoie le détail complet de la vente, comme sur un ticket de caisse :
les lignes, les prix, la TVA, les remises, les totaux, la ventilation de la TVA, les paiements, le numéro de ticket ou de facture, et le client.
Réglages → API & Make.
Cela comprend les ventes de la caisse, les commandes e-commerce importées, les abonnements facturés automatiquement, les factures encaissées, et les annulations (qui sont des ventes inverses, avec des montants négatifs). Les devis, les bons de livraison et les notes non encaissées ne déclenchent pas cet envoi.
Pour le fonctionnement commun (activation, délai, relances, doublons, formats), voir Webhooks et mailhooks : fonctionnement.
Exemple#
Un ticket de deux lignes, payé en espèces avec rendu de monnaie :{
"evenement": "vente.validee",
"idEvenement": 5871,
"dateEnvoi": "2026-10-13 10:43:02",
"vente": {
"id": 24449,
"type": "ticket",
"numero": 1042,
"numeroFacture": 0,
"date": "2026-10-13 10:42:07",
"caisse": 1,
"initiale": "T",
"numTable": 0,
"couverts": 0,
"numeroAddition": 0,
"idTypeVente": 5,
"typeVente": "VENTE SUR PLACE",
"idZone": 0,
"zone": "",
"wooId": 0,
"typeEcom": 0,
"numeroCommandeEcom": "",
"idSousTypePiece": 4,
"sousTypePiece": "TICKET",
"idTypeOperation": 1,
"typeOperation": "VENTE",
"vendeur": "Camille",
"magasin": {
"nom": "Papeterie du Port",
"nomCommercial": "Papeterie du Port",
"raisonSociale": "SARL Papeterie du Port",
"formeJuridique": "SARL",
"capital": "8000",
"adresse1": "12 quai des Pêcheurs",
"adresse2": "",
"cp": "29000",
"cpVille": "29000 Quimper",
"pays": "France",
"rcs": "RCS Quimper 123 456 789",
"numTVA": "FR12123456789",
"siren": "123456789",
"siret": "12345678900012",
"naf": "4761Z",
"tvaSurDebit": "1",
"entete": "",
"pied": "Merci de votre visite"
},
"client": {
"id": 312,
"numero": 312,
"anonyme": false,
"uuid": "38D13BD6-4836-4115-B829-B55AC2537B09",
"codebarre": "20003128",
"idTypeClient": 2,
"typeClient": "PROFESSIONNEL",
"idTypologie": 2,
"typologie": "ENTREPRISE, OU ASSO STRUCTUREE (B2B)",
"fidelite": [
{
"id": 57,
"nom": "Cagnotte fidélité",
"idType": 4,
"type": "CAGNOTTE EN EUROS",
"nbPassageUtilise": 0,
"nbPassage": 0,
"dateCreation": "2026-03-02 09:15:00",
"dateFin": "2027-03-02",
"cagnotte": "12.50"
}
],
"email": "achats@dupont-sarl.fr",
"tel": "06 12 34 56 78",
"remarque": "",
"nom": "Dupont",
"prenom": "Martin",
"raisonSociale": "Dupont Services",
"siren": "987654321",
"siret": "98765432100019",
"numTVA": "FR55987654321",
"adresse": {
"ligne1": "3 rue des Écoles",
"ligne2": "",
"cp": "29000",
"cpVille": "29000 Quimper",
"pays": "France"
}
},
"lignes": [
{
"id": 88120,
"idArticle": 1875,
"code": "1875",
"nom": "Cahier A5",
"nomLong": "Cahier A5 petits carreaux 96 pages",
"declinaison": "",
"promo": "",
"refFabricant": "CAH-A5",
"codebarre": "20018753",
"wooId": 4412,
"famille": "PAPETERIE",
"idFamille": 3,
"sousFamille": "CAHIERS",
"idSousFamille": 14,
"idSousTypeArticle": 3,
"sousTypeArticle": "PRODUIT COMPLET (à l'unité, avec stock et achat possibles)",
"idVendeur": 2,
"vendeur": "Camille",
"idTypePrix": 1,
"typePrix": "TARIF NORMAL",
"idTypeArticle": 1,
"quantite": "2",
"unite": "",
"prixUnitaireNet": "3.60",
"totalNet": "7.20",
"remisePourcent": "0.00",
"remiseMontant": "0.00",
"offert": false,
"idTVA": 1,
"lettreTVA": "B",
"tauxTVA": "20.00",
"ht": "6.00",
"tva": "1.20",
"ttc": "7.20",
"ecoParticipation": "0.00",
"stock": 18
},
{
"id": 88121,
"idArticle": 2210,
"code": "2210",
"nom": "Baguette",
"nomLong": "Baguette tradition",
"declinaison": "",
"promo": "",
"refFabricant": "",
"codebarre": "20022107",
"wooId": 0,
"famille": "BOULANGERIE",
"idFamille": 5,
"sousFamille": "PAINS",
"idSousFamille": 21,
"idSousTypeArticle": 3,
"sousTypeArticle": "PRODUIT COMPLET (à l'unité, avec stock et achat possibles)",
"idVendeur": 2,
"vendeur": "Camille",
"idTypePrix": 1,
"typePrix": "TARIF NORMAL",
"idTypeArticle": 1,
"quantite": "1",
"unite": "",
"prixUnitaireNet": "1.20",
"totalNet": "1.20",
"remisePourcent": "0.00",
"remiseMontant": "0.00",
"offert": false,
"idTVA": 2,
"lettreTVA": "C",
"tauxTVA": "5.50",
"ht": "1.14",
"tva": "0.06",
"ttc": "1.20",
"ecoParticipation": "0.00",
"stock": 6
}
],
"totaux": {
"ht": "7.14",
"ttc": "8.40",
"remise": "0.00",
"tva": "1.26",
"aPayer": "8.40"
},
"ventilationTVA": [
{
"idTVA": 1,
"lettre": "B",
"taux": "20.00",
"ht": "6.00",
"tva": "1.20",
"ttc": "7.20"
},
{
"idTVA": 2,
"lettre": "C",
"taux": "5.50",
"ht": "1.14",
"tva": "0.06",
"ttc": "1.20"
}
],
"paiements": [
{
"idMoyen": 1,
"moyen": "ESPECES",
"quantite": 1,
"montant": "10.00",
"total": "10.00",
"date": "2026-10-13 10:42:07"
},
{
"idMoyen": 1,
"moyen": "ESPECES",
"quantite": 1,
"montant": "-1.60",
"total": "-1.60",
"date": "2026-10-13 10:42:07"
}
]
}
}
Le message : première couche#
| Propriété | Type | Explications |
|---|---|---|
| evenement | string | Toujours "vente.validee". |
| idEvenement | integer | L'identifiant de cet envoi. Il ne change pas quand Melkal réessaie : servez-vous-en pour ignorer les doublons. |
| dateEnvoi | string | La date et l'heure où le message a été préparé. |
| vente | object | Toute la vente : voir ci-dessous. |
vente : la pièce#
| Propriété | Type | Explications |
|---|---|---|
| id | integer | L'identifiant de la vente dans Melkal. |
| type | string | "ticket", "facture", "avoir" (facture d'avoir) ou "autre". |
| numero | integer | Le numéro de la pièce. C'est le numéro du ticket. |
| numeroFacture | integer | Le numéro de facture quand la vente a été facturée, 0 sinon. |
| date | string | La date et l'heure de la vente. |
| caisse | integer | Le numéro de la caisse. |
| initiale | string | La lettre qui précède le numéro sur le ticket (par exemple « T »). |
| numTable | integer | Le numéro de table, 0 s'il n'y en a pas. |
| couverts | integer | Le nombre de couverts, 0 s'il n'y en a pas. |
| numeroAddition | integer | Le numéro d'addition, 0 s'il n'y en a pas. |
| idTypeVente typeVente |
integer string |
Le type de vente (« Vente sur place », « À emporter »...). Les types sont ceux que vous avez définis dans Melkal. |
| idZone zone |
integer string |
La zone de vente. 0 et texte vide s'il n'y en a pas. |
| wooId | integer | L'identifiant de la commande dans WooCommerce, 0 si la vente ne vient pas du site e-commerce. |
| typeEcom | integer | 0 : vente en magasin. 1 : Click & Collect. 2 : e-commerce. |
| numeroCommandeEcom | string | Le numéro de commande tel que votre client le connaît (celui de son email de confirmation). Vide pour une vente en magasin. |
| idSousTypePiece sousTypePiece |
integer string |
Le sous-type de pièce : 1 DEVIS, 2 PRO FORMA, 3 NOTE, 4 TICKET, 5 FACTURE SUR TICKET, 6 BL, 7 AVOIR SUR TICKET, 8 JUSTIFICATIF DE PAIEMENT, 9 NOTE INDIVIDUELLE. |
| idTypeOperation typeOperation |
integer string |
Le type d'opération : 1 VENTE, 2 ANNULATION, 3 RETOUR PARTIEL, 4 LIVRAISON, 5 PROPOSITION, 6 RECAPITULATIF, 7 JUSTIFICATIF DE PAIEMENT. |
| idPieceOrigine | integer | Absente si la vente n'en annule ni n'en corrige aucune. Sinon, l'identifiant de la pièce d'origine (pour une annulation). |
| numeroAnnule | integer | Absente si la vente n'en annule aucune. Sinon, le numéro de la pièce annulée. |
| numeroAnnulation | integer | Absente si la vente n'est pas annulée. Sinon, le numéro de la pièce qui l'annule. |
| vendeur | string | Le nom du vendeur. |
| magasin | object | Les coordonnées de votre entreprise : voir ci-dessous. |
| client | object | Le client : voir ci-dessous. |
| lignes | array | Les lignes de la vente. |
| totaux | object | Les totaux. |
| ventilationTVA | array | La ventilation de la TVA, une ligne par taux. |
| paiements | array | Les paiements, une ligne par règlement. |
vente.magasin : votre entreprise#
Les coordonnées de l'entreprise, telles qu'elles étaient sur la pièce au moment de la vente (celles du haut du ticket). Une propriété sans valeur est un texte vide.| Propriété | Type | Explications |
|---|---|---|
| nom | string | Le nom commercial du magasin, tel qu'inscrit sur la pièce. |
| nomCommercial | string | Le nom commercial. |
| raisonSociale | string | La raison sociale. |
| formeJuridique | string | La forme juridique (SARL, SAS...). |
| capital | string | Le capital social. |
| adresse1 adresse2 |
string | Les deux lignes d'adresse. |
| cp cpVille |
string | Le code postal seul, puis le code postal suivi de la ville. |
| pays | string | Le pays. |
| rcs | string | Le numéro d'immatriculation au RCS. |
| numTVA | string | Le numéro de TVA intracommunautaire. |
| siren siret |
string | Le SIREN et le SIRET. |
| naf | string | Le code NAF (APE). |
| tvaSurDebit | string | "1" : TVA sur les débits. "0" : TVA sur les encaissements. |
| entete pied |
string | Le texte libre imprimé en haut et en bas du ticket. |
vente.client : le client#
Pour une vente sans client précisé, c'est le client anonyme de Melkal (anonyme vaut alors true). Les noms, la raison sociale, le SIREN, le SIRET et l'adresse sont ceux inscrits sur la pièce au moment de la vente.| Propriété | Type | Explications |
|---|---|---|
| id | integer | L'identifiant du client dans Melkal. |
| numero | integer | Le numéro du client, tel qu'il s'affiche dans Melkal. |
| anonyme | boolean | true pour le client anonyme (vente au comptoir sans fiche client). |
| uuid | string | L'identifiant unique universel de la fiche client, le même que dans l'API. |
| codebarre | string | Le code-barre interne de la fiche client (EAN 8). |
| idTypeClient typeClient |
integer string |
Le type de client, ceux que vous avez définis dans Melkal. |
| idTypologie typologie |
integer string |
La typologie de la fiche : 1 PARTICULIER (B2C), 2 ENTREPRISE (B2B), 3 ORGANISME PUBLIC (B2G). |
| string | L'adresse email principale de la fiche. | |
| tel | string | Le téléphone mobile, ou à défaut le téléphone fixe. |
| remarque | string | Les indications saisies sur la fiche client. |
| nom prenom |
string | Le nom et le prénom. |
| raisonSociale | string | La raison sociale, pour une entreprise. |
| siren siret |
string | Le SIREN et le SIRET, pour une entreprise. |
| numTVA | string | Le numéro de TVA intracommunautaire. |
| adresse | object | ligne1, ligne2 (deux lignes), cp (code postal), cpVille (code postal et ville), pays. |
| fidelite | array | Les cartes de fidélité en cours du client à la date de la vente : voir ci-dessous. Liste vide si le client n'en a pas. |
vente.client.fidelite : les cartes de fidélité#
| Propriété | Type | Explications |
|---|---|---|
| id | integer | L'identifiant de la carte. |
| nom | string | Le nom de la carte. |
| idType type |
integer string |
Le type de fidélité : 1 remise sur le Xème passage (services), 2 remise sur le cumul produit des X passages précédents, 3 remise sur le Xème article, 4 cagnotte en euros, 5 cagnotte en points. |
| nbPassageUtilise nbPassage |
integer | Pour les cartes à passages ou à articles : le nombre déjà utilisé, et le nombre total. |
| dateCreation | string | La date de création de la carte. |
| dateFin | string | La date de fin de validité. |
| cagnotte | string | Absente si la carte n'est pas une cagnotte. Pour une cagnotte en euros : le montant, en euros ("12.50"). Pour une cagnotte en points : le nombre de points ("120"). |
vente.lignes : les lignes de la vente#
Les lignes annulées, les lignes privées et les lignes que vous n'imprimez pas sur le ticket sont exclues : la liste contient ce qu'un ticket imprimé contient.| Propriété | Type | Explications |
|---|---|---|
| id | integer | L'identifiant de la ligne. |
| idArticle | integer | L'identifiant de l'article dans Melkal. |
| code | string | Le code de l'article. |
| nom | string | Le nom de l'article. |
| nomLong | string | Le nom long de l'article. |
| declinaison | string | La déclinaison (taille, couleur...). Plusieurs valeurs sont séparées par « - ». Vide pour un produit non décliné. |
| promo | string | Le nom de la promotion appliquée à la ligne, vide s'il n'y en a pas. |
| refFabricant | string | La référence fabricant. |
| codebarre | string | Le code-barre interne de l'article. |
| wooId | integer | L'identifiant de l'article dans WooCommerce (la déclinaison, pour un article décliné). 0 s'il n'est pas relié au site e-commerce. |
| idFamille famille |
integer string |
La famille de l'article. |
| idSousFamille sousFamille |
integer string |
La sous-famille de l'article. |
| idSousTypeArticle sousTypeArticle |
integer string |
Le sous-type de l'article : 1 produit générique, 2 produit simplifié, 3 produit complet, 4 produit avec déclinaisons, 5 prestation sur mesure, 6 prestation de service, 7 forfait de prestations, 8 abonnement à la séance, 9 commentaire, 10 cadeau, 11 rabais en euros, 12 remise globale en pourcentage, 13 passage abonné, 14 abonnement au temps passé, 15 abonnement avec tarif préférentiel, 16 pack / lot / panier, 17 service de cashback. |
| idVendeur vendeur |
integer string |
Le vendeur de la ligne (il peut différer de celui de la vente). |
| idTypePrix typePrix |
integer string |
Le type de prix appliqué (votre tarif normal, un tarif client...). |
| idTypeArticle | integer | Le type de ligne : 1 produit, 2 prestation, 3 commentaire, 4 cadeau, 6 rabais en euros, 7 rabais en pourcentage, 12 passage abonné. Une ligne « commentaire » n'a ni prix ni total utiles. |
| quantite | string | La quantité vendue, avec les décimales de l'unité. Négative sur une annulation. |
| unite | string | L'initiale de l'unité (« K » pour un kilo...), vide pour une vente à l'unité. |
| prixUnitaireNet | string | Le prix unitaire TTC après remise. Pour un pack, le prix du pack. |
| totalNet | string | Le total TTC de la ligne après remise. |
| remisePourcent | string | Le pourcentage de remise de la ligne, "0.00" s'il n'y en a pas. |
| remiseMontant | string | Le montant total de la remise de la ligne, en euros. |
| offert | boolean | true si la ligne a été offerte. |
| idTVA lettreTVA |
integer string |
Le taux de TVA de la ligne, et la lettre imprimée en face sur le ticket. |
| tauxTVA | string | Le taux de TVA, en pourcentage ("20.00"). |
| ht tva ttc |
string | Le total de la ligne, hors taxe, la TVA, et toutes taxes comprises. Ils tiennent compte des remises globales du ticket. |
| ecoParticipation | string | La part d'éco-participation comprise dans le prix, en euros. |
| stock | integer | Le stock de l'article au moment de l'envoi. Absente si aucun stock n'est enregistré pour l'article. |
vente.totaux : les totaux#
| Propriété | Type | Explications |
|---|---|---|
| ht | string | Le total hors taxe de la vente. |
| tva | string | Le total de la TVA. |
| ttc | string | Le total toutes taxes comprises. |
| remise | string | Le total des remises accordées. |
| aPayer | string | Le « TOTAL » imprimé sur le ticket, c'est-à-dire le total TTC à payer. |
vente.ventilationTVA : la TVA par taux#
Une ligne par taux de TVA présent sur la vente, comme dans le tableau de TVA du ticket.| Propriété | Type | Explications |
|---|---|---|
| idTVA lettre |
integer string |
Le taux de TVA, et sa lettre sur le ticket. |
| taux | string | Le taux, en pourcentage. |
| ht tva ttc |
string | Le total hors taxe, la TVA, et le total toutes taxes comprises pour ce taux. |
vente.paiements : les paiements#
Une ligne par règlement, dans l'ordre du ticket. Le rendu de monnaie est une ligne comme les autres, avec un montant négatif : dans l'exemple ci-dessus, 10,00 € donnés en espèces, puis 1,60 € rendus.| Propriété | Type | Explications |
|---|---|---|
| idMoyen moyen |
integer string |
Le moyen de paiement (ESPECES, CB...). Ceux que vous avez définis dans Melkal. |
| quantite | integer | Le nombre de règlements identiques (par exemple 3 chèques-déjeuner de même valeur). |
| montant | string | Le montant unitaire. Négatif pour un rendu de monnaie. |
| total | string | Le total de la ligne : quantité x montant. |
| date | string | La date et l'heure du règlement. |
Dernière mise à jour : 13 octobre 2026