Fulfillment
TL;DR : marque une commande "préparée / expédiée / livrée" et attache éventuellement un tracking number + carrier via
fulfill_native_order(Tier-2). Bulk viaauto_fulfill_ready_orders(côté Shopify uniquement).fulfillment_statusest orthogonal àpayment_status— une commande peut être paid + unfulfilled, ou pending + fulfilled (rare).

Concept
Le fulfillment, c'est tout ce qui se passe après l'encaissement côté merchant : préparer le colis, le remettre au transporteur, suivre la livraison, gérer un retour. Pour Trackily, c'est juste une transition d'état + un tracking optionnel — la logistique physique se passe ailleurs (en entrepôt, chez ton fournisseur dropship, etc.).
Les 6 statuts (fulfillment_status)
┌───────────────┐
│ unfulfilled │ ← default
└───────┬───────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ partial │ │ fulfilled │ │ cancelled │
│ (1+ items │ │ (préparé, │ │ (la commande │
│ parti, pas │ │ pas forcé- │ │ est annulée │
│ tout) │ │ ment ship- │ │ cancelled_at│
└──────┬───────┘ │ pé) │ │ non NULL) │
│ └──────┬───────┘ └──────────────┘
│ │
└──────────┬────────┘
▼
┌──────────────┐
│ shipped │ ← tracking attaché
│ (parti, en │
│ transit) │
└──────┬───────┘
│
▼
┌──────────────┐
│ delivered │ ← reçu par le client
└──────┬───────┘
│
▼ (optionnel : retour)
┌──────────────┐
│ returned │ ← précède souvent un
│ │ refund total
└──────────────┘
| Status | Quand l'utiliser |
|---|---|
unfulfilled |
Default. Rien n'a bougé. |
partial |
Multi-items, certains partis, d'autres pas (rupture sur 1 variante par ex.). |
fulfilled |
Préparé / picked. Utile pour les digitaux où "préparé" = "envoyé par email". Aussi utile en step intermédiaire avant shipped. |
shipped |
Tracking number attaché, colis chez le transporteur. |
delivered |
Confirmé livré. Peut être setté manuellement ou via un webhook transporteur (pas inclus out-of-box, à câbler). |
returned |
Le colis est revenu. Étape pré-refund total typiquement. |
Orthogonal au payment_status
payment_status |
fulfillment_status |
Cas typique |
|---|---|---|
paid |
unfulfilled |
Commande à préparer |
paid |
fulfilled |
Préparée, pas encore expédiée |
paid |
shipped |
Cas nominal "tout va bien" |
paid |
delivered |
Livraison confirmée |
pending |
unfulfilled |
COD en attente cash, ou checkout pas finalisé |
pending |
fulfilled |
COD préparée avant cash (rare — risque commerçant) |
refunded |
returned |
Return-then-refund |
refunded |
delivered |
Refund après-vente sans retour (geste commercial) |
partial_refund |
shipped |
Partiel sur un produit livré OK |
Tu peux librement combiner. C'est ta business logic qui décide ce qui a du sens.
Tracking : carrier + number
Deux colonnes optionnelles sur orders :
tracking_carrier(TEXT) — nom du transporteur ("DHL", "FedEx", "Bpost", "Colissimo")tracking_number(TEXT) — le numéro
Côté affichage, l'admin UI et les emails post-shipping peuvent générer un lien de suivi vers le site du transporteur en fonction de tracking_carrier. Mapping géré dans les templates (non couvert ici).
Comment faire (UI + MCP)
Via l'admin UI
- Commerce → Orders → clique sur la commande.
- Bouton Fulfill en haut à droite.
- Modal :
- Status — pick dans le dropdown (
unfulfilled,partial,fulfilled,shipped,delivered,returned). - Tracking carrier — optionnel mais recommandé pour
shipped. - Tracking number — optionnel.
- Notify customer — si coché, déclenche l'email "Your order has shipped" (templates dans Settings → Email templates).
- Status — pick dans le dropdown (
- Save.
Si tu passes en shipped sans tracking number, l'UI te demande confirmation ("Shipped without tracking?"). Pas bloquant, juste un nudge — beaucoup d'opérateurs n'utilisent pas de tracking pour les petits colis (Letterbox, lettres simples).
Via MCP
Outil principal : fulfill_native_order (Tier-2).
Passer en fulfilled simple :
{
"name": "fulfill_native_order",
"arguments": {
"order_id": 87,
"fulfillment_status": "fulfilled"
}
}
Passer en shipped avec tracking :
{
"name": "fulfill_native_order",
"arguments": {
"order_id": 87,
"fulfillment_status": "shipped",
"tracking_carrier": "Colissimo",
"tracking_number": "1Z999AA10123456784"
}
}
Tier-2 — première réponse preview :
{
"status": "confirmation_required",
"tool": "fulfill_native_order",
"preview": {
"order_id": 87,
"order_number": "TR-000087",
"current_fulfillment_status": "unfulfilled",
"new_fulfillment_status": "shipped",
"tracking_number": "1Z999AA10123456784",
"tracking_carrier": "Colissimo"
},
"confirm_token": "tk_…"
}
Re-soumets avec le token pour exécuter.
Passer en delivered (confirmation manuelle) :
{
"name": "fulfill_native_order",
"arguments": {
"order_id": 87,
"fulfillment_status": "delivered"
}
}
Passer en returned (étape pré-refund total) :
{
"name": "fulfill_native_order",
"arguments": {
"order_id": 87,
"fulfillment_status": "returned"
}
}
Le tool ne déclenche aucune action côté provider de paiement. Si tu veux refunder à la suite, appelle
refund_native_orderséparément.
Bulk fulfillment : auto_fulfill_ready_orders
Important : auto_fulfill_ready_orders existe uniquement côté Shopify (cf. autopilot-ecommerce-orchestration-tools.js). Pour le natif, pas d'équivalent prêt à l'emploi. Pour automatiser en bulk natif, ton agent MCP peut chainer list_native_orders + N appels fulfill_native_order :
// Étape 1 : récupérer les commandes à fulfill
{
"name": "list_native_orders",
"arguments": {
"payment_status": "paid",
"fulfillment_status": "unfulfilled",
"limit": 100
}
}
Puis pour chaque order_id retourné, l'agent appelle fulfill_native_order en deux temps (preview + confirm). C'est intentionnellement plus verbeux que le bulk Shopify : la fulfillment native est souvent contextuelle (chaque colis a son propre tracking), automatiser sans review humaine est risqué.
Côté Shopify, le bulk auto-fulfill est utile parce que Shopify a la notion de "ready to fulfill" (paid + inventory available + no flag de fraude). Trackily n'a pas (encore) cette notion pour les natifs.
Shopify bulk : auto_fulfill_ready_orders
Pour rappel, côté Shopify :
{
"name": "auto_fulfill_ready_orders",
"arguments": {
"store_id": 3,
"limit": 50
}
}
Preview :
{
"status": "confirmation_required",
"summary": "AUTO-FULFILL 22 ready orders on store \"My Store\". IRREVERSIBLE.",
"preview": { "store_id": 3, "orders_to_fulfill": 22, "limit": 50 }
}
Confirm → loop sur les 22 commandes, appelle l'API Shopify pour chacune. Idempotent : ré-exécuter ne refait pas les fulfillments déjà créés (fulfillment_status='fulfilled' filtre les commandes déjà traitées).
Exemples concrets
1. Workflow standard : paid → shipped en deux étapes
Tu prépares le colis le matin, tu l'expédies l'après-midi :
// Matin — confirme la préparation (mais pas encore expédié)
{ "name": "fulfill_native_order", "arguments": { "order_id": 87, "fulfillment_status": "fulfilled" } }
// Après-midi — colis chez Colissimo
{
"name": "fulfill_native_order",
"arguments": {
"order_id": 87,
"fulfillment_status": "shipped",
"tracking_carrier": "Colissimo",
"tracking_number": "8L00012345678"
}
}
L'email "Your order has shipped" est déclenché par la séquence email branchée sur l'event order.shipped (voir email/sequences.md).
2. Digital product : paid direct → fulfilled
Le produit est un ebook PDF. Au paiement, ta séquence email envoie immédiatement le lien de download. Tu fulfilles juste pour la propreté du statut :
{
"name": "fulfill_native_order",
"arguments": {
"order_id": 87,
"fulfillment_status": "fulfilled"
}
}
Pas de tracking (digital). Pas de shipping cost non plus (calcul shipping skip sur type='digital').
3. Return-then-refund
Le client renvoie le produit, tu reçois le colis le 25/05 :
// 1. Marquer returned
{ "name": "fulfill_native_order", "arguments": { "order_id": 87, "fulfillment_status": "returned" } }
// 2. Refund total
{ "name": "refund_native_order", "arguments": { "order_id": 87 } }
payment_status passe à refunded, stock restocké (parce que c'est un full refund), fulfillment_status reste à returned.
4. Partial shipping (multi-item)
Commande de 3 items, dont 1 en rupture. Tu envoies les 2 disponibles :
// Étape 1 — passer en partial
{ "name": "fulfill_native_order", "arguments": { "order_id": 87, "fulfillment_status": "partial", "tracking_number": "8L00012345678", "tracking_carrier": "Colissimo" } }
// Étape 2 (3 semaines plus tard, stock rechargé) — envoie du dernier item
// Idéalement tu enverrais comme un 2e colis avec un 2e tracking number, mais
// Trackily n'a qu'un seul champ tracking_number par order — un soit-tu update
// le tracking, soit tu joins les deux dans le même champ ("8L0001 + 8L0002").
{ "name": "fulfill_native_order", "arguments": { "order_id": 87, "fulfillment_status": "shipped" } }
Multi-tracking par commande n'est pas supporté natif. Si c'est un cas fréquent pour toi, ouvre une issue.
5. Delivered manuel après confirmation client
Le client te dit par email "Bien reçu, merci !". Tu confirmes :
{ "name": "fulfill_native_order", "arguments": { "order_id": 87, "fulfillment_status": "delivered" } }
Utile pour les rapports "lead time entre paid et delivered" → diagnose les lenteurs côté logistique.
Stratégies fulfillment
- Toujours attacher un tracking sur
shipped— la conversion d'un email "shipped + tracking link" est 2× supérieure à un email "shipped" simple. Le client peut suivre, il ne contacte pas le support. - Email automatique sur chaque transition —
unfulfilled → fulfilled,fulfilled → shipped,shipped → delivered. 3 emails, 3 occasions de garder le client engagé. Voir email/sequences.md. - Auto-fulfill les digitaux — crée une rule Automizer :
payment_status changed to paid AND product.type='digital'→fulfill_native_order(fulfillment_status='fulfilled'). Te libère 100 % du fulfillment digital. - Auto-mark delivered J+10 sur
shipped— règle Automizer :fulfillment_status='shipped' AND fulfilled_at < NOW() - INTERVAL '10 days'→fulfill_native_order(fulfillment_status='delivered'). Pas optimal (un colis peut prendre + de 10 jours) mais clean tes stats. - Track ta vitesse
paid → shipped— KPI "time to shipping" en heures. > 48h, ton service client va manger des réclamations. < 24h, c'est très bien.
Erreurs courantes
- "fulfillment_status must be one of: unfulfilled, partial, fulfilled, shipped, delivered, returned" — tu as passé une valeur hors enum.
- "Cannot fulfill a cancelled order" —
cancelled_atest non-NULL. Tu ne peux pas fulfiller une commande annulée. Crée une nouvelle commande ou revert le cancel (en SQL direct — pas de tool pour ça parce que c'est sale). - "Stock négatif après fulfill" — pas possible via l'UI, mais possible si tu update directement en SQL. Trackily ne vérifie pas le stock à la fulfill, parce que le stock a été décrémenté à la création de la commande, pas à la fulfill.
- "Le client n'a pas reçu l'email shipping" — vérifie que la séquence email "order shipped" est attachée à la campagne, que
post_purchase_list_idest setté, et que le buyer n'est pas dansemail_suppression. - "Tracking number copié-collé contient un saut de ligne" — l'UI trim côté front, mais via MCP c'est de ta responsabilité.
String.prototype.trim()avant envoi. - "Carrier "DHL" affiche un lien tracking cassé" — le mapping carrier → tracking URL est dans les templates email. Vérifie l'orthographe ("dhl", "DHL", "Dhl" peuvent ne pas tous matcher selon ta normalisation).
- "Auto-fulfill natif n'existe pas" — confirmé, à automatiser via boucle MCP côté agent (voir section bulk ci-dessus).
Voir aussi
- orders.md — la state machine complète et
payment_statusvsfulfillment_status. - refunds.md — la suite logique après
fulfillment_status='returned'. - Email → Sequences — séquences "order shipped" / "delivery confirmed".
- Automizer → Rules — auto-fulfill via rules natives +
fulfill_native_orderenchainés. - Code source :
autopilot-native-orders-tools.jstoolFulfillNativeOrder,autopilot-ecommerce-orchestration-tools.jstoolAutoFulfillReadyOrders(Shopify uniquement).