Votre boutique en ligne enregistre toutes ses ventes, mais Google Analytics 4 (GA4) n'en voit qu'une partie. Avec l'API des ventes, votre serveur envoie chaque vente à takotak au moment où elle est payée. Vous n'avez plus de fichier à déposer dans Mes ventes, et takotak travaille sur vos vraies ventes : bilan, courbe de Mes actions, panier moyen, marge, et comparaison avec ce que déclarent Google Ads et Meta.
Cette page s'adresse au développeur qui branche l'envoi. L'appel se place au même endroit que l'envoi de la vente à GA4 par le Measurement Protocol, décrit dans Remontée des ventes : le webhook du prestataire de paiement, le back-office ou la base de commandes. Faites les deux au même endroit. takotak a vos ventes tout de suite, et GA4 finit par les voir toutes.
La clé et l'adresse
Créez la clé dans votre espace client : Mes ventes › Envoi automatique › Créer une clé. Elle désigne votre site : l'adresse de l'API est la même pour tous.
https://app.takotak.ai/api/v1/sales
Chaque appel porte la clé dans l'en-tête Authorization: Bearer tks_…. Gardez-la côté serveur, dans une variable d'environnement, jamais dans une page. Elle permet d'écrire vos ventes. Elle est distincte des clés de l'API des articles et de la newsletter.
« Remplacer la clé » invalide l'ancienne aussitôt : il n'y a pas de période où les deux marchent. Déployez la nouvelle clé juste après, et renvoyez les lots refusés entre-temps (401) : la reference évite les doublons. « Arrêter l'envoi automatique » retire la clé. Les jours déjà reçus restent couverts par vos ventes ; les jours suivants reviennent à GA4.
Envoyer des ventes
POST https://app.takotak.ai/api/v1/sales
Authorization: Bearer tks_…
Content-Type: application/json
{
"sales": [
{
"reference": "CMD-2026-10481",
"date": "2026-10-21T14:03:00+02:00",
"amount": 89.9,
"currency": "EUR",
"margin": 31.5,
"gclid": "Cj0KCQjw…"
}
]
}
Un lot contient de 0 à 1 000 ventes. Envoyez la vente au passage en « payée », une par appel ou par petits lots. Pour l'historique d'avant le premier appel, déposez plutôt un export dans Mes ventes (voir « La période couverte »).
Réponse 200 :
{
"site": "ma-boutique.fr",
"version": "2026-10-22",
"received": 1,
"added": 1,
"updated": 0,
"removed": 0,
"refunded": 0,
"unknown": 0,
"ignoredFields": [],
"linked": 1,
"coverage": { "since": "2026-10-21", "until": "2026-10-21", "lastCallAt": "2026-10-21T12:03:04.512Z", "received": 1 }
}
| Champ | Sens |
|---|---|
added, updated |
ventes créées, et ventes déjà connues mises à jour |
removed |
ventes retirées (annulées, remboursées en entier) |
refunded |
commandes remboursées en partie (vente reçue ou achat vu par GA4), désormais comptées pour leur montant net |
unknown |
annulations ou remboursements d'une référence que takotak ne connaît ni en vente ni en achat vu par GA4 : sans effet |
ignoredFields |
champs que takotak ne connaît pas : ignorés, jamais stockés. Vérifiez qu'il ne s'agit pas d'une faute de frappe. |
linked |
paires vente ↔ achat GA4 après ce lot, sur tout le site ; null quand le lot ne change aucune vente (lot vide) : le rapprochement n'est pas refait |
coverage |
la période où takotak tient vos ventes pour complètes (voir plus bas) |
Les champs d'une vente
| Champ | Obligatoire | Valeur |
|---|---|---|
reference |
oui | votre numéro de commande, le même que le transaction_id envoyé à GA4 : c'est lui qui relie la vente à l'achat vu par GA4, et qui identifie la vente d'un appel à l'autre. |
date |
oui | l'heure du paiement en ISO 8601 avec son fuseau : 2026-10-21T14:03:00+02:00 ou 2026-10-21T12:03:00Z. takotak range la vente au jour du fuseau de votre site (celui de vos dépôts, Paris par défaut) : 2026-10-21T23:30:00-04:00 est une vente du 22 à Paris. Une date seule (2026-10-21) est acceptée, telle quelle. Pas de date avant 2000. |
amount |
oui | ce que le client a payé, toutes taxes et frais de port compris, en nombre (89.9, point décimal). |
currency |
non | code ISO 4217 sur trois lettres ; EUR pour une vente nouvelle, la devise déjà reçue pour une mise à jour. |
ga4Amount |
non | le montant envoyé à GA4 en value, s'il diffère (hors taxes, sans frais de port) : il aide le rapprochement. |
margin |
non | ce qui vous reste sur la vente, en montant dans la devise de la vente (31.5), pas en pourcentage. Sans elle, takotak prend le montant × la marge de vos réglages. |
status |
non | paid par défaut ; pending, cancelled ou refunded retirent la vente, sauf refunded avec un refundAmount partiel, qui la garde pour son net (voir plus bas). |
refundAmount |
non | avec status: "refunded" seulement : le total remboursé à ce jour sur la commande, pour un remboursement partiel (voir plus bas). |
channel |
non | d'où vient la vente, si votre site le sait : google ads, meta ads, newsletter, partenaire… |
gclid, gbraid, wbraid |
non | l'identifiant du clic Google Ads gardé avec la commande. |
utmSource, utmMedium |
non | les paramètres utm_source et utm_medium de la visite qui a mené à la commande. |
fbclid, ttclid |
non | l'identifiant du clic Facebook ou TikTok. |
Le canal de la vente
Le canal est ce qui permet à takotak de vérifier chaque régie publicitaire : comparer les conversions que Google Ads ou Meta s'attribuent aux ventes qui viennent vraiment de leurs clics. Sans canal, takotak ne connaît la provenance que des ventes que GA4 a vues, et la comparaison avec chaque régie en devient moins sûre.
Au moment de la commande, gardez avec elle l'identifiant du clic et les utm de la visite (paramètres de l'adresse d'arrivée, à conserver en session ou en cookie jusqu'au paiement). takotak déduit le canal dans cet ordre :
channel, s'il est envoyé ;gclid,gbraidouwbraid: Google Ads ;utmSourceetutmMedium:facebook / cpc, par exemple ;fbclidouttclid: Facebook ou TikTok, payant ou non (ces identifiants sont aussi posés sur les clics des publications ordinaires).
Seul le canal déduit est gardé : les identifiants de clic ne sont pas stockés.
Mises à jour, annulations, remboursements
Une vente est identifiée par sa reference, sans tenir compte d'un # initial ni des majuscules (#CMD-1042 et cmd-1042 sont la même commande). La renvoyer met la vente à jour : envoyer deux fois la même vente ne la compte donc jamais deux fois, et un webhook rejoué ne pose aucun problème.
- Mise à jour (montant corrigé) : renvoyez la vente avec
reference,dateet le nouveauamount. Les champs facultatifs absents gardent leur valeur : le canal et la devise reçus à la commande restent. Unedatesans heure qui change le jour de la vente efface l'heure gardée. - Remboursement partiel :
{ "reference": "CMD-2026-10481", "status": "refunded", "refundAmount": 20 }.refundAmountest le total remboursé à ce jour sur la commande, pas le montant de ce remboursement-ci : un second remboursement de 15 € s'envoie avec35. Un webhook rejoué ou arrivé en retard ne compte donc rien deux fois : le plus grand total reçu fait foi. Il se compte sur la même base queamount(taxes et port compris). La vente compte pour son montant moins ce total, et le garde si vous la renvoyez ensuite enpaid. À 95 % ou plus du montant, elle est retirée comme un remboursement complet. - Annulation ou remboursement complet :
{ "reference": "CMD-2026-10481", "status": "refunded" }suffit (oucancelled). La vente disparaît de vos ventes. - L'achat vu par GA4 suit : un remboursement ou une annulation s'applique aussi à l'achat que GA4 a reçu sous ce numéro de commande, même si takotak n'a jamais reçu la vente (une commande d'avant votre premier appel, par exemple). Il ne compte plus, ou compte pour son net ; si vous avez envoyé
ga4Amount, le remboursement lui est rapporté dans la même proportion. Envoyez le remboursement avec la vente (dans le même lot) ou après elle, jamais dans un lot qui la précède. - Commande en attente de paiement : ne l'envoyez pas, ou envoyez-la en
pending. Elle ne compte qu'une fois envoyée enpaid. Un achat que GA4 aurait reçu sous ce numéro n'est pas touché.
La période couverte
takotak tient vos ventes pour complètes du jour du premier appel jusqu'au jour du dernier, jours comptés à l'heure de Paris. Tant que l'envoi est actif (un appel depuis moins de deux jours), la journée en cours est couverte aussi : un jour sans appel est un jour sans vente. Sur cette période, elles remplacent GA4, comme un dépôt de fichier : un achat vu par GA4 sans vente en face n'y compte jamais, vos ventes font foi. Envoyez l'heure de chaque vente (date avec heure et fuseau) : l'onglet Aujourd'hui compte alors la journée sur vos ventes, sans le retard de GA4 ; une seule vente sans heure dans la journée la renvoie à GA4.
Le début de la période ne recule jamais : une correction tardive d'une commande de mars met la vente à jour, mais ne rend pas « complets » les mois où rien n'était envoyé. Une vente antérieure au premier appel ne compte que sur une période couverte par un dépôt.
Trois conséquences :
- Une journée sans vente s'annonce par un lot vide :
{ "sales": [] }, par exemple chaque soir avant minuit (heure de Paris) par une tâche planifiée. Sans appel, takotak ne sait pas si vous n'avez rien vendu ou si l'envoi s'est arrêté : il revient alors à GA4 pour ces jours-là. - Après deux jours sans appel, Mes actions affiche « Relancer l'envoi automatique de vos ventes ».
- Pour reprendre votre historique, déposez un export de vos ventes dans Mes ventes › Déposer mes ventes : il couvre sa propre période, du premier au dernier jour du fichier.
Avec les dépôts de fichier
Les deux se combinent. Un fichier déposé dans Mes ventes remplace toutes les ventes de sa période, du premier au dernier jour du fichier, y compris celles reçues par l'API. Une vente envoyée ensuite par l'API, avec un numéro de commande déjà déposé, met à jour la vente déposée.
Aucune donnée personnelle
takotak ne reçoit ni nom, ni e-mail, ni téléphone, ni adresse, ni identifiant client. Un champ de ce genre (email, phone, name, firstName, address, customer, client…), ou une valeur qui contient une adresse e-mail, dans n'importe quel champ, refuse tout le lot. Le numéro de commande suffit.
Erreurs
Un lot est accepté en entier ou refusé en entier : sur une erreur, rien n'est enregistré.
| Réponse | Cas |
|---|---|
400 |
Corps illisible : JSON attendu, de la forme { "sales": [ … ] }. |
401 |
Clé absente, fausse, remplacée ou retirée. |
413 |
Plus de 1 000 ventes, ou plus de 1 Mo. |
422 |
Une vente au moins est refusée : errors donne pour chacune sa position dans le lot (index, à partir de 0), sa référence et la raison (20 au plus). |
{
"error": "Lot refusé : rien n'a été enregistré. Corrigez les ventes indiquées et renvoyez le lot.",
"errors": [
{ "index": 3, "reference": "CMD-2026-10484", "error": "« date » : précisez le fuseau horaire (Z ou +02:00), sinon l'heure est ambiguë." }
]
}
Sur une erreur réseau ou une réponse 5xx, renvoyez le même lot plus tard : la reference évite les doublons. Gardez sur la commande la trace d'un envoi réussi, et alertez-vous quand une commande payée n'en a pas.
Vérifier la clé
GET https://app.takotak.ai/api/v1/sales
Authorization: Bearer tks_…
La réponse donne le site de la clé et la période couverte, sans rien enregistrer :
{
"site": "ma-boutique.fr",
"version": "2026-10-22",
"coverage": { "since": "2026-10-21", "until": "2026-10-22", "lastCallAt": "2026-10-22T19:00:02.118Z", "received": 37 }
}
since et until sont null tant qu'aucun lot n'a été reçu avec cette clé.
Exemples
curl :
curl -X POST https://app.takotak.ai/api/v1/sales \
-H "Authorization: Bearer $TAKOTAK_SALES_KEY" \
-H "Content-Type: application/json" \
-d '{"sales":[{"reference":"CMD-2026-10481","date":"2026-10-21T14:03:00+02:00","amount":89.9,"gclid":"Cj0KCQjw"}]}'
Le remboursement de 20 € d'une partie de cette commande :
curl -X POST https://app.takotak.ai/api/v1/sales \
-H "Authorization: Bearer $TAKOTAK_SALES_KEY" \
-H "Content-Type: application/json" \
-d '{"sales":[{"reference":"CMD-2026-10481","status":"refunded","refundAmount":20}]}'
Node.js, au passage de la commande en « payée » :
async function sendSaleToTakotak(order) {
const res = await fetch("https://app.takotak.ai/api/v1/sales", {
method: "POST",
headers: { authorization: `Bearer ${process.env.TAKOTAK_SALES_KEY}`, "content-type": "application/json" },
body: JSON.stringify({
sales: [{
reference: order.number, // le transaction_id envoyé à GA4
date: order.paidAt.toISOString(), // heure du paiement, en UTC
amount: order.total,
currency: order.currency,
margin: order.margin ?? undefined,
gclid: order.gclid ?? undefined, // gardés avec la commande à l'arrivée sur le site
utmSource: order.utmSource ?? undefined,
utmMedium: order.utmMedium ?? undefined,
}],
}),
});
if (!res.ok) throw new Error(`takotak ${res.status} : ${await res.text()}`);
}
PHP, pour WooCommerce (WordPress), au passage de la commande en « payée » :
$paid = $order->get_date_paid() ?: $order->get_date_created();
$payload = ['sales' => [[
// Le même numéro que le transaction_id envoyé à GA4 : selon l'extension GA4, get_order_number() ou get_id().
'reference' => (string) $order->get_order_number(),
'date' => $paid->format(DATE_ATOM),
'amount' => (float) $order->get_total(),
'currency' => $order->get_currency(),
'gclid' => $order->get_meta('_gclid') ?: null,
]]];
$response = wp_remote_post('https://app.takotak.ai/api/v1/sales', [
'headers' => ['Authorization' => 'Bearer ' . TAKOTAK_SALES_KEY, 'Content-Type' => 'application/json'],
'body' => wp_json_encode($payload),
'timeout' => 10,
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
// À renvoyer plus tard : la commande garde la trace d'un envoi manqué.
$order->update_meta_data('_takotak_sent', 'no');
} else {
$order->update_meta_data('_takotak_sent', 'yes');
}
$order->save();
Dans cet exemple, le gclid n'existe que si votre site l'a gardé avec la commande, ici dans une méta _gclid. Vérifiez quel numéro votre extension GA4 envoie en transaction_id : beaucoup envoient l'identifiant interne (get_id()) et non le numéro affiché. Pour PrestaShop ou un site sur mesure, l'appel est le même : un POST JSON au passage en « payée ».
La liste de contrôle
- Clé créée dans Mes ventes › Envoi automatique, gardée côté serveur.
- Un appel au passage en « payée », au même endroit que l'envoi
purchaseà GA4, avec le même numéro de commande. dateavec fuseau,amount, et si possiblemarginet le canal (channel,gclid,utm).status: "cancelled"à l'annulation,"refunded"au remboursement : sans montant s'il est complet, avecrefundAmount(le total remboursé à ce jour) s'il est partiel.- Un lot vide chaque jour, pour que takotak sache qu'il ne manque rien.
- Historique d'avant le premier appel repris par un dépôt CSV dans Mes ventes.
- Aucune donnée personnelle dans l'envoi.