Products
TL;DR : un
native_productest une fiche produit (nom, slug, type, currency, status, images, catégorie). Crée-le depuis Settings → Commerce → Products, puis enchaîne sur ses variantes.

Concept
Un product dans Trackily, c'est la fiche d'identité d'un article que tu vends en direct. Il vit dans la table native_products et il est totalement indépendant des products Shopify-synchronisés (autre table, autre pipeline). Pour ne pas mélanger les outils MCP, les commandes natives sont préfixées native_ (list_native_products, get_native_product_detail) alors que les outils Shopify gardent leurs noms d'origine (list_products, update_product…).
Un produit n'a aucun prix sur lui-même : le prix vit sur ses variantes. Même si tu vends "un seul truc à un seul prix", Trackily crée automatiquement une variante par défaut qui porte le prix. Ça uniformise la pipeline checkout et te permet de monter en complexité (taille, couleur, abonnement) sans casser ce que tu avais déjà.
Anatomie d'un produit
| Champ | Type | Rôle |
|---|---|---|
id |
SERIAL | identifiant interne |
name |
TEXT | nom affiché ("Memo Mind — 30 jours") |
slug |
TEXT UNIQUE | utilisé dans /p/<slug> |
description |
TEXT | description longue (markdown autorisé sur la page publique) |
type |
TEXT | physical ou digital |
currency |
TEXT | ISO-3 (USD, EUR, GBP…) |
status |
TEXT | draft (masqué partout) ou active (vendable) |
category |
TEXT | catégorie libre — alimente les pills /catalog |
images |
JSONB | tableau d'objets {url, alt} |
tax_rate |
NUMERIC(5,2) | TVA en pourcent (0 = pas de tax) |
shipping_cost |
NUMERIC(12,2) | shipping fallback si aucune zone ne match |
free_shipping_above |
NUMERIC(12,2) | seuil au-dessus duquel le shipping passe à 0 |
allowed_domains |
JSONB | whitelist de hosts (vide = aucune restriction) |
default_payment_account_ids |
JSONB | comptes paiement utilisés sur /p/<slug> |
default_cod_enabled |
BOOLEAN | active COD sur /p/<slug> |
checkout_fields_schema |
JSONB | personnalisation des champs du formulaire checkout |
deleted_at |
TIMESTAMPTZ | soft-delete (le produit disparaît mais ses commandes restent) |
type=physical vs type=digital
- physical — le visiteur doit fournir une adresse de livraison, le shipping est calculé, la commande passe par fulfillment (tracking number, carrier). COD (cash on delivery) est autorisé.
- digital — pas d'adresse, pas de shipping, pas de COD. Une fois
payment_status='paid', la commande passe directement enfulfillment_status='fulfilled'. C'est aussi le bon choix pour les services (abonnements, accès logiciel) et les livraisons hors-Trackily (tu factures, tu gères la livraison à part).
Comment faire (UI + MCP)
Via l'admin UI
- Va dans Commerce → Products (ou tape
/admin#native-productsdirectement). - Clique sur + Add Product en haut à droite.
- Remplis :
- Name (obligatoire) — par exemple "Memo Mind 30-day Brain Booster".
- Slug — auto-généré depuis le name, éditable. Sert dans
/p/<slug>. - Type — Physical ou Digital.
- Currency — USD par défaut, ajustable par produit.
- Description — markdown court. Apparaît sur
/p/<slug>et alimente le prompt degenerate_landing_for_product. - Category — texte libre. Sera proposé en pill sur
/catalog. Voir categories.md. - Status — laisse en Draft tant que tu n'as pas validé tes variantes et tes paiements.
- Clique sur Create — Trackily crée la ligne
native_products+ une variante par défaut (que tu pourras remplacer ensuite). - Tu atterris sur la page détail. Onglets disponibles : Variants, Images, Shipping & Tax, Checkout fields, Domains, Stats.
- Upload tes images depuis l'onglet Images (drag-drop ou URL externe — stockées en
native_product_assets, max 10 MB par fichier). - Quand tout est prêt, passe le status à Active. Le produit devient vendable.
Via MCP
L'outil MCP commerce-native pour la création de produit est create_product (côté ecommerce-tools — même tool sert pour Shopify et le natif, distingué par le payload). Pour le natif tu peux passer par le proxy admin REST :
{
"name": "create_product",
"arguments": {
"platform": "native",
"name": "Memo Mind — 30 jours",
"slug": "memo-mind",
"type": "physical",
"currency": "EUR",
"description": "Booster de mémoire formulé pour les adultes 40+. Cure de 30 jours, 1 gélule par jour.",
"category": "Suppléments cognitifs",
"status": "draft",
"tax_rate": 20.00,
"shipping_cost": 4.90,
"free_shipping_above": 50.00
}
}
Lister les produits existants :
{
"name": "list_native_products",
"arguments": {
"status": "active",
"type": "physical",
"search": "memo",
"limit": 50
}
}
Réponse type (extrait) :
{
"status": "ok",
"count": 1,
"products": [
{
"id": 12,
"name": "Memo Mind — 30 jours",
"slug": "memo-mind",
"type": "physical",
"currency": "EUR",
"status": "active",
"images": [{ "url": "/product-assets/47/box.webp", "alt": "Memo Mind box" }],
"variant_count": 3,
"price_from": "29.90",
"price_to": "79.90",
"total_stock": 412,
"has_unlimited_stock": false
}
]
}
Récupérer le détail complet (avec ses options + variantes) :
{
"name": "get_native_product_detail",
"arguments": {
"product_id": 12
}
}
Mettre à jour un champ :
{
"name": "update_product",
"arguments": {
"platform": "native",
"product_id": 12,
"patch": {
"status": "active",
"tax_rate": 20.00,
"free_shipping_above": 39.00
}
}
}
Supprimer (soft-delete — les commandes historiques sont conservées, le produit disparaît de toutes les listes et de la pipeline checkout) :
{
"name": "delete_product",
"arguments": {
"platform": "native",
"product_id": 12
}
}
Note Tier-2.
update_productetdelete_productsont des outils Tier-2 : le premier appel renvoie unconfirm_token, le deuxième (avec le token) exécute. C'est la garantie qu'un agent MCP ne peut pas casser ton catalogue sans validation explicite. Voirdocs/mcp/pour le détail du système Tier.
Génération AI d'une landing à partir d'un produit
Une fois ton produit en status='active', l'outil le plus utile pour aller vite, c'est generate_landing_for_product. Il lit le produit (nom, description, type, variantes, prix), construit le prompt, génère une landing complète et la sauvegarde en mode draft avec product_id déjà rempli + les payment_account_ids que tu lui passes.
{
"name": "generate_landing_for_product",
"arguments": {
"product_id": 12,
"language": "fr",
"vertical": "supplements",
"framework": "pas",
"traffic_temperature": "warm",
"payment_account_ids": [3, 5],
"cod_enabled": true
}
}
Réponse première étape (preview + confirm_token) :
{
"status": "confirmation_required",
"tool": "generate_landing_for_product",
"preview": {
"product": { "id": 12, "name": "Memo Mind — 30 jours", "variants_count": 3 },
"ai_inputs": {
"vertical": "supplements",
"language": "fr",
"framework": "pas",
"traffic_temperature": "warm",
"description_preview": "Product \"Memo Mind — 30 jours\" (physical good). Seller-provided description: Booster de mémoire …"
},
"checkout_links": { "payment_account_ids": [3, 5], "cod_enabled": true },
"cost_note": "This call will consume LLM tokens (~5-8k output) and create one new landing_pages row in draft status."
},
"confirm_token": "tk_abc123…"
}
Tu rejoues l'appel avec confirm_token pour exécuter. La landing créée pointe sur ton produit, les boutons Stripe / PayPal / COD sont déjà câblés sous le formulaire.
Anti-leak avec allowed_domains
Par défaut, n'importe qui peut cloner le HTML de ta landing publique, l'héberger sur son propre domaine et garder le bloc checkout pointé sur ton Stripe — ce qui revient à te faire vendre pour son compte. Pour s'en protéger :
{
"name": "update_product",
"arguments": {
"platform": "native",
"product_id": 12,
"patch": {
"allowed_domains": ["lp.monsite.com", "store.monsite.com"]
}
}
}
Effet : le bloc checkout et la route /p/memo-mind ne rendent plus quand la requête arrive depuis un host hors whitelist. Le formulaire de soumission de reviews /api/reviews applique la même règle (voir reviews.md) pour qu'un concurrent ne puisse pas non plus polluer tes avis. Laisse le tableau vide pour ne pas restreindre.
Page publique /p/<slug>
Chaque produit en status='active' a sa page autonome à /p/<slug> (par exemple https://lp.monsite.com/p/memo-mind). Elle :
- affiche images, titre, prix, description, sélecteur de variante, formulaire checkout, bloc de reviews ;
- utilise
default_payment_account_ids+default_cod_enableddu produit (pas besoin d'une landing dédiée) ; - respecte
allowed_domains; - est listée sur
/catalog(la grille des produits actifs).

C'est l'option la plus rapide pour vendre un produit "tel quel" sans construire une landing AI. Pour des angles marketing variés, garde le produit + génère plusieurs landings.
Exemples concrets
1. Supplément 30 jours, marché FR
{
"name": "create_product",
"arguments": {
"platform": "native",
"name": "Memo Mind — 30 jours",
"type": "physical",
"currency": "EUR",
"description": "Booster de mémoire pour adultes 40+. 30 gélules.",
"category": "Suppléments cognitifs",
"status": "draft",
"tax_rate": 20.00,
"shipping_cost": 4.90,
"free_shipping_above": 50.00
}
}
Suite logique : ajouter 3 variantes "30 jours / 60 jours / 90 jours" (voir variants.md), créer une zone shipping FR + une zone rest-of-world (shipping.md), connecter Stripe (stripe-connect.md), passer status='active'.
2. Ebook digital, prix unique
{
"name": "create_product",
"arguments": {
"platform": "native",
"name": "Cold Email Playbook 2026",
"type": "digital",
"currency": "USD",
"description": "120 pages. 14 frameworks éprouvés. Templates inclus.",
"category": "Ebooks",
"status": "active"
}
}
Pas de shipping, pas de COD. Une seule variante à $29, lien de download envoyé via la séquence email post-purchase (voir email/sequences.md).
3. Mini-store sur un domaine custom
Tu as 5 produits, tu veux un mini-shop à l'adresse store.monsite.com. Workflow :
- Crée les 5 produits avec catégories cohérentes (par exemple "Boissons", "Snacks", "Compléments").
- Set
allowed_domainsà["store.monsite.com"]sur chacun. - Set
default_payment_account_ids+default_cod_enabledsur chacun. - Configure le domaine
store.monsite.comavecroot_behavior='catalog'(voirdocs/campaigns/domains.md). - Attache les 5 produits au domaine via
domain_native_products. - Le visiteur qui arrive sur
store.monsite.com/voit la grille catalog, clique sur un produit, achète. Toujours pas de Shopify.
Erreurs courantes
- "Le slug est déjà pris" —
slugest UNIQUE. Si tu refais un test, soft-delete l'ancien produit (il libère le slug ? non, l'unique tient même sur les soft-deleted) → préfixe avec une date (memo-mind-2026-05). - "Le produit n'apparaît pas sur
/catalog" — il est encore enstatus='draft', ou il n'a aucune variante active, ou la requête tape un host non whitelisté. - "Mes images sont rejetées" —
validateUploadplafonne à 10 MB et n'accepte qu'image/* + application/pdf. Compresse les hero shots en WebP (voirdocs/landings/image-providers.md). - "J'ai passé
type='digital'mais le checkout demande l'adresse" — vérifie que ta landing utilise bien leproduct_idmodifié (le bloc checkout est généré à la création de la landing, mais il se reconfigure à la volée à l'affichage en fonction du type courant du produit). - "
generate_landing_for_productme dit que payment_account_ids n'existent pas" — l'outil refuse silencieusement les IDs invalides ou inactifs. Liste d'abordlist_payment_accounts(admin REST) pour vérifier. - "J'ai mis
tax_rate=20mais le checkout affichetax=0" — la tax est appliquée après le discount. Si le code promo couvre tout le subtotal, la tax tombe à 0. Sinon, contrôle que le produit a bien letax_rate(et pas la variante — la tax vit sur le produit, pas par variante).
Voir aussi
- variants.md — décliner ton produit en plusieurs SKUs.
- categories.md — l'utilisation de
categorydans la page/catalog. - shipping.md — zones et tarifs de livraison.
- stripe-connect.md — connecter Stripe en un clic.
- orders.md — vie d'une commande après le checkout.
- Landings → AI builder — la pipeline
generate_landing_for_producten détail.