L'API Commerce permet à n'importe quelle boutique d'envoyer ses événements vers Kanal : commandes, paniers abandonnés, clients et expéditions. Ces données alimentent ensuite vos automatisations WhatsApp, sans passer par Shopify.
Contrairement à l'API Messaging, elle ne nécessite pas d'abonnement actif. La référence complète est sur developers.getkanal.com.
store_id, le nombre qui suit /stores/ dans l'exemple cURL de l'onglet Guide, et une clé d'ingestion limitée à cette boutique, affichée en entier une seule fois. Voir Connecter une boutique hors Shopify (boutique personnalisée).Toutes les URL commencent par https://api.getkanal.com/api/v1/stores/{store_id}/.
Vous n'avez pas à savoir si un enregistrement existe déjà dans Kanal. Vous envoyez l'objet complet, et Kanal le crée ou le met à jour. Chaque ressource est identifiée par le couple (store_id, external_id).
external_id est votre identifiant : numéro de commande, jeton de panier, identifiant client. Renvoyez le même external_id et Kanal met à jour l'enregistrement existant au lieu de créer un doublon.
201 Created : Kanal voyait cet external_id pour la première fois.
200 OK : un enregistrement existant a été mis à jour.
Les deux retournent le même corps JSON : vous pouvez les traiter de la même manière.
POST rejoué sur une commande ou un panier peut redéclencher les automatisations attachées à cette ressource.POST /api/v1/stores/{store_id}/ordersDéclenche vos automatisations post-achat. Champs requis : external_id et total_price.
Champs optionnels :
placed_at (format ISO 8601) : sans ce champ, Kanal utilise l'heure de réception de la commande.
customer : l'objet client, avec phone, external_id, first_name, last_name, email, locale, tags, lifetime_value et orders_count.
currency, financial_status, fulfillment_status, line_items, tags, metadata.
customer.phone est bien enregistrée, mais elle ne déclenche aucune automatisation. Envoyez toujours le téléphone du client si la commande doit donner lieu à un message.curl -X POST https://api.getkanal.com/api/v1/stores/123/orders \
-H "Authorization: Bearer VOTRE_CLE_API" \
-H "Content-Type: application/json" \
-d '{
"external_id": "ORDER-1001",
"placed_at": "2026-05-18T10:00:00Z",
"total_price": 49.90,
"currency": "EUR",
"customer": { "phone": "+33612345678", "first_name": "Marie" }
}'Une commande ne crée pas de fiche client. Pour synchroniser le profil de vos clients (tags, valeur vie, nombre de commandes), envoyez-les aussi via POST /customers.
Pour ne modifier que le statut d'une commande existante, utilisez PATCH /orders/{external_id}. Seuls financial_status, fulfillment_status, tags et metadata sont modifiables. Une commande inexistante retourne une erreur 404.
curl -X PATCH https://api.getkanal.com/api/v1/stores/123/orders/ORDER-1001 \
-H "Authorization: Bearer VOTRE_CLE_API" \
-H "Content-Type: application/json" \
-d '{ "financial_status": "paid" }'POST /api/v1/stores/{store_id}/checkoutsEnregistre vos paniers abandonnés dans Kanal. Champs requis : external_id, abandoned_at, total_price et l'objet customer avec son phone.
Champs optionnels : recovery_url, currency, line_items, tags, metadata, ainsi que les autres champs de l'objet customer (external_id, first_name, last_name, email, locale, tags).
Le champ recovery_url est recommandé : c'est le lien qui permet au client de reprendre sa commande.
curl -X POST https://api.getkanal.com/api/v1/stores/123/checkouts \
-H "Authorization: Bearer VOTRE_CLE_API" \
-H "Content-Type: application/json" \
-d '{
"external_id": "CHECKOUT-7700",
"abandoned_at": "2026-05-18T09:40:00Z",
"recovery_url": "https://shop.example.com/checkout/recover/7700",
"total_price": 49.90,
"currency": "EUR",
"customer": { "phone": "+33612345678", "first_name": "Marie" }
}'Le PATCH /checkouts/{external_id} permet de faire évoluer le statut du panier, avec les valeurs active, abandoned, recovered, completed ou expired. Il accepte aussi recovery_url, tags et metadata. Pensez à passer un panier en recovered dès qu'il est converti, pour garder des données à jour.
POST /api/v1/stores/{store_id}/customersSynchronise les profils clients, leurs tags et leur valeur vie. Seul phone est requis. La clé naturelle est external_id, mais si vous l'omettez, c'est le phone qui sert de clé. Les champs lifetime_value et orders_count doivent être des nombres entiers.
curl -X POST https://api.getkanal.com/api/v1/stores/123/customers \
-H "Authorization: Bearer VOTRE_CLE_API" \
-H "Content-Type: application/json" \
-d '{
"external_id": "CUST-42",
"phone": "+33612345678",
"first_name": "Marie",
"tags": ["vip"],
"lifetime_value": 1299,
"orders_count": 7
}'Pour honorer une demande d'effacement RGPD venant de votre boutique, utilisez DELETE /customers/{external_id}. La réponse est un 204 sans corps. L'appel est idempotent : supprimer un client déjà supprimé ou inconnu retourne également 204.
POST /api/v1/stores/{store_id}/shipmentsDéclenche vos notifications de livraison. Champs requis : external_id et order_external_id, qui rattache l'expédition à la commande correspondante.
Champs optionnels : tracking_number, tracking_url, carrier, metadata, et status parmi pending, in_transit, delivered, failed ou returned.
tracking_number et tracking_url, si elle n'a pas de status, ou si la commande liée est inconnue de Kanal ou n'a pas de contact. Dans ces cas, l'expédition est quand même enregistrée, sans erreur.curl -X POST https://api.getkanal.com/api/v1/stores/123/shipments \
-H "Authorization: Bearer VOTRE_CLE_API" \
-H "Content-Type: application/json" \
-d '{
"external_id": "SHIP-9001",
"order_external_id": "ORDER-1001",
"tracking_number": "1Z999AA10123456784",
"tracking_url": "https://track.example.com/1Z999AA10123456784",
"carrier": "Colissimo",
"status": "in_transit"
}'Il n'existe pas de PATCH pour les expéditions : renvoyez un POST avec le même external_id pour mettre à jour le suivi ou le statut.
L'API Commerce est limitée à 500 requêtes par minute et par boutique. Ce budget est partagé entre tous les endpoints d'une même boutique. En cas de dépassement, l'API retourne un 429 sans en-tête Retry-After : le délai d'attente est indiqué dans le corps de la réponse, retry_after: 60 (en secondes). Des en-têtes X-RateLimit-* sont présents sur chaque réponse pour suivre votre consommation.
Trois principes pour rester sous la limite : envoyez un upsert complet plutôt que plusieurs PATCH partiels, poussez vos données au moment où elles changent au lieu d'interroger l'API en boucle, et appliquez un backoff exponentiel sur les 429. Si vous prévoyez un volume soutenu supérieur à 500 requêtes par minute, par exemple un import historique ou une vente flash, prévenez votre contact Kanal en amont pour faire relever la limite.
Les erreurs prennent deux formes. Les problèmes détectés avant validation (clé, boutique, limite) retournent une simple chaîne error. Les payloads invalides retournent un 422 avec un message et une carte errors détaillée par champ, en notation pointée pour les objets imbriqués. Exemple pour un panier envoyé sans téléphone :
{
"message": "The customer.phone field is required.",
"errors": {
"customer.phone": ["The customer.phone field is required."]
}
}Code | Signification | Rejouer ? |
|---|---|---|
| Mise à jour ou création réussie | Sans objet |
| Suppression effectuée, sans corps | Sans objet |
| Paramètre store_id manquant dans l'URL | Non |
| Clé absente, invalide ou non autorisée | Non |
| Boutique inconnue ou en pause, ou enregistrement introuvable | Non |
| Validation échouée : corrigez le payload | Non |
| Limite de débit dépassée | Oui, après 60 secondes ( |
| Erreur serveur passagère | Oui, avec backoff exponentiel |