Introduction

L'API SwissLivraisonPro permet d'integrer votre systeme de commandes (site web, application, plateforme tierce) avec notre service de livraison. Vous pouvez :

  • Envoyer automatiquement des commandes via webhooks
  • Suivre les statuts de livraison en temps reel
  • Gerer les livreurs et les courses via l'API REST
  • Recevoir des callbacks de statut sur votre serveur

Base URL : https://hub.swisslivraisonpro.ch

Authentification

Tokens Webhook

Chaque integration possede un token unique genere automatiquement. Ce token identifie votre restaurant et sa configuration. Il est inclus directement dans l'URL du webhook :

POST /webhook/{votre_token}

Le token est genere lors de la creation de l'integration dans le dashboard, section Integrations.

Cle API (Bearer Token)

Pour les endpoints d'administration, l'authentification se fait via une session admin ou un Bearer token :

Authorization: Bearer {api_key}

Authentification Livreur

Les livreurs s'authentifient via Firebase Auth (email + mot de passe). Deux flows :

  • PWA Web : firebase.auth().signInWithEmailAndPassword(email, pwd) cote client, puis POST /api/driver/firebase-login avec {idToken} qui retourne un cookie de session Firebase (HTTP-only, 14j).
  • APK Android native : Firebase SDK natif, puis Authorization: Bearer <Firebase idToken> sur chaque appel API.

Les anciens endpoints /api/driver/login (phone/password), /api/driver/forgot-password et /api/driver/reset-password ont ete retires en Phase 3 de la migration. Le reset password est desormais natif Firebase via sendPasswordResetEmail().

Webhook - Reception de commandes

POST /webhook/{token}

Point d'entree universel pour recevoir des commandes depuis n'importe quelle plateforme (Just Eat, Uber Eats, WooCommerce, Shopify, ou systeme personnalise).

Fonctionnement

  1. Creez une integration dans le dashboard pour obtenir un token
  2. Configurez le mapping des champs si necessaire
  3. Envoyez vos commandes en POST JSON vers /webhook/{token}
  4. La commande est normalisee, creee et automatiquement dispatchee aux livreurs

Reponse succes

{
  "ok": true,
  "order_id": 42,
  "source_order_id": "ORD-12345",
  "fleet_id": null,
  "dispatch_result": "internal"
}

Valeurs principales de dispatch_result : internal (dispatch automatique lance), manual (attribution manuelle — aucune flotte liee, mode manuel, ou aucun livreur trouve apres cascade), waiting_for_ready / waiting_for_prep (en attente que la commande soit prete en cuisine), scheduled (pre-commande : heure souhaitee eloignee — dispatch differe automatiquement a l'approche), paused (restaurant en pause : commande enregistree et mise en attente, dispatchee automatiquement a la reprise). Une commande deja recue renvoie {"ok": true, "status": "duplicate"} avec l'id existant (pas de dispatch_result).

Plateformes supportees

PlateformeMappingStatut callback
Just EatAutomatiqueOui
Uber EatsAutomatiqueOui
WooCommerceAutomatiqueOui
ShopifyAutomatiqueOui
ShipdayAutomatiqueSync natif Shipday (sortant)
ZipZestAutomatiqueOui (statut interne brut)
Chataigne (chatbot WhatsApp)AutomatiqueOui (statut interne brut)
Wix (snippet Velo ou Automation no-code)AutomatiqueNon (suivi client SLP)
PersonnaliseConfigurableConfigurable

Format du payload de commande

Pour les integrations personnalisees, le mapping des champs est configure a la creation de l'integration (aucun mapping n'est implicite). Le format canonique recommande — celui configure par defaut — est le suivant :

{
  "id": "ORD-12345",
  "restaurantName": "Pizza Roma",
  "restaurantAddress": "Rue du Marche 12, 1204 Geneve",
  "customerName": "Jean Dupont",
  "customerPhoneNumber": "+41 79 123 45 67",
  "customerEmail": "jean@example.com",
  "deliveryAddress": "Avenue de la Gare 5, 1003 Lausanne",
  "deliveryLatitude": 46.5197,
  "deliveryLongitude": 6.6323,
  "comments": "3e etage, sonner interphone",
  "total": 42.50,
  "itemsDescription": "1x Pizza Margherita, 2x Coca 33cl",
  "expectedTime": "2026-08-09T18:30:00Z"
}

Champs

ChampTypeRequisDescription
idstringOuiIdentifiant unique de la commande (seul champ obligatoire — 400 no_order_id sinon)
restaurantNamestringRecommandeNom du restaurant — sans lui, le point de collecte s'affiche sans nom pour le livreur
restaurantAddressstringNonAdresse de collecte (defaut : adresse du commercant)
customerNamestringRecommandeNom du client
customerPhoneNumberstringRecommandeTelephone du client (contact livreur + suivi SMS)
customerEmailstringNonEmail du client (suivi de commande)
deliveryAddressstringFortement recommandeAdresse de livraison — sans adresse ni coordonnees, la course ne peut pas etre attribuee
deliveryLatitudefloatNonLatitude de livraison (sinon geocodage serveur de l'adresse)
deliveryLongitudefloatNonLongitude de livraison (sinon geocodage serveur de l'adresse)
commentsstringNonInstructions de livraison (etage, code d'entree...) — affichees au livreur
totalfloatNonMontant de la commande (CHF)
itemsDescriptionstringNonResume des articles (max 1000 caracteres)
requestedTimestringNonHeure de livraison demandee par le client, ISO 8601 avec fuseau (ex. 2026-08-20T20:00:00+02:00 ou ...18:00:00Z) — declaration explicite, prise telle quelle. Proche (< 4 h, meme jour, ex. commande a 17 h pour 20 h) : la commande est creee immediatement et la course est calee sur cette heure (livraison visee = heure demandee, pas de livreur mobilise en avance). Eloignee (>= 4 h ou un autre jour) : cree une pre-commande programmee (dispatch_result = "scheduled", dispatch differe a l'approche de l'heure). Absente = livraison au plus vite (ASAP). Sans fuseau ou illisible : ignoree. Recommande des que votre systeme connait l'heure voulue par le client
expectedTimestringNonHeure de livraison estimee/annoncee par votre plateforme, ISO 8601 avec fuseau. Si elle porte la signature d'un vrai creneau choisi par le client (minutes rondes, >= 75 min dans le futur), elle est traitee comme requestedTime ci-dessus ; sinon elle est informative. Preferez requestedTime quand vous connaissez la demande du client. Valeur illisible ignoree

Mapping personnalise

Si votre payload utilise des noms de champs differents, configurez un field_mapping lors de la creation de l'integration :

{
  "field_mapping": {
    "order_id": "order_number",
    "restaurant_name": "shop.name",
    "customer_name": "client.fullName",
    "delivery_address": "client.address.street",
    "delivery_lat": "client.address.lat",
    "delivery_lng": "client.address.lng"
  }
}

Les chemins supportent la notation pointee pour les objets imbriques (ex: client.address.street) et les index de liste (ex: items.0.name).

Cles de mapping disponibles : order_id, restaurant_name, restaurant_address, customer_name, customer_name_last (nom de famille, concatene au prenom), customer_phone, customer_email, delivery_address, delivery_lat, delivery_lng, delivery_instructions, total, items_description, expected_time, pickup_lat, pickup_lng, order_number (numero de ticket AFFICHE au livreur/restaurant quand il differe de order_idorder_id reste l'identifiant unique qui deduplique).

Statuts de livraison

Chaque commande passe par les statuts suivants :

StatutDescriptionDeclencheur
receivedCommande recue, preparation en cuisineSysteme / Resto
pendingPrete, en attente d'un livreur (dispatch)Systeme
acceptedLivreur a accepte la courseLivreur
to_restaurantEn route vers le restaurantLivreur
at_restaurantArrive au restaurantLivreur
collectedCommande recupereeLivreur
to_customerEn route vers le clientLivreur
deliveredCommande livreeLivreur
Statuts non nominaux
on_holdEn attente (cuisine pas prete ou aucun livreur dispo)Systeme
rejectedCourse refusee par le livreur (re-dispatch)Livreur
cancelledCommande annuleeResto / Systeme
returnedColis retourne au restaurantLivreur
failedEchec de livraisonSysteme

Callbacks de statut

Si vous configurez un status_push_url sur votre integration, SwissLivraisonPro enverra un POST a cette URL a chaque changement de statut :

POST {votre_status_push_url}
Content-Type: application/json
Authorization: Bearer {votre_api_key}

{
  "order_id": "ORD-12345",
  "status": "InDelivery",
  "internal_status": "collected"
}

Le champ status est traduit selon le mapping de votre plateforme. Le champ internal_status contient toujours le statut interne SwissLivraisonPro.

Rejeu en cas d'echec

Si votre endpoint ne repond pas (timeout 10 s) ou repond en erreur (HTTP ≥ 400), le callback est rejoue automatiquement : jusqu'a 4 nouvelles tentatives espacees de 1, 5, 15 puis 60 minutes (~1 h 20 de couverture), puis abandon. Seul le dernier statut de chaque commande est rejoue — vous ne recevrez jamais un statut plus ancien apres un plus recent ; des etapes intermediaires peuvent etre sautees. Prevoyez un traitement idempotent (le meme statut peut arriver deux fois en cas de timeout).

Environnement de test (sandbox)

Un compte de test isole est disponible sur demande pour developper votre integration sans toucher a l'operationnel :

  • Isolement total : le commercant de test n'est rattache a aucune flotte — aucun livreur reel n'est jamais sollicite, aucune notification operationnelle.
  • Livraison simulee : chaque commande envoyee au webhook progresse automatiquement dans le cycle de vie reel (acceptedto_restaurantat_restaurantcollectedto_customerdelivered, ~5 minutes bout-en-bout) — vos callbacks status_push_url sont declenches a chaque etape, exactement comme en production — rejeu automatique compris (voir Rejeu en cas d'echec). Une heure demandee (requestedTime, ou expectedTime detectee comme creneau client) est respectee : la simulation se cale pour livrer a l'heure demandee (une commande « pour 20 h » reste silencieuse jusqu'a ~19 h 55).
  • Aucune communication client : les emails et SMS de suivi sont coupes pour le compte de test — vous pouvez utiliser des coordonnees fictives sans risque.
  • Passage en production : meme format, meme mecanique — seul le token webhook change.

Demandez votre token sandbox a l'equipe SwissLivraisonPro, puis gerez tout en autonomie depuis l'Espace developpeur : configuration de vos callbacks (URL + Bearer), envoi de commandes de test et suivi de leur progression en direct.

API Livreur

Tous les endpoints livreur utilisent le prefixe /api/driver et l'authentification Firebase (ID token en Authorization: Bearer pour l'app native, ou cookie de session Firebase pour le web — cf. section Authentification). Ces endpoints sont internes aux apps livreur SwissLivraisonPro.

Commandes

MethodeEndpointDescription
GET/api/driver/ordersListe des commandes assignees
GET/api/driver/orders/{id}Detail d'une commande
POST/api/driver/orders/{id}/acceptAccepter une course
POST/api/driver/orders/{id}/rejectRefuser une course
POST/api/driver/orders/{id}/to-restaurantEn route vers le restaurant
POST/api/driver/orders/{id}/at-restaurantArrive au restaurant
POST/api/driver/orders/{id}/collectedCommande recuperee
POST/api/driver/orders/{id}/to-customerEn route vers le client
POST/api/driver/orders/{id}/deliverCommande livree
POST/api/driver/orders/{id}/scan-qrValider le ramassage par scan QR
POST/api/driver/orders/{id}/problemSignaler un probleme
POST/api/driver/orders/{id}/request-replacementDemander un remplacant (panne / transfert sur place)
POST/api/driver/orders/{id}/forgotten-itemsSignaler un oubli d'article (cree une commande complement)

Localisation & statut

MethodeEndpointDescription
POST/api/driver/locationMettre a jour la position GPS
PUT/api/driver/statusChanger le statut (online/offline)
GET/api/driver/statsStatistiques du livreur
GET/api/driver/historyHistorique des courses
GET/api/driver/optimized-routeRoute optimisee multi-arrets
POST/api/driver/dispatch-readySignaler la disponibilite au dispatch (retour en ligne / GPS frais)
GET/api/driver/pending-returnsColis a retourner au restaurant

Chat & notifications

Le chat (livreur ↔ client ↔ restaurant, et chat de flotte) est temps reel via Firebase RTDB : l'endpoint d'autorisation pose l'ACL et renvoie le nom du canal, puis le client lit/ecrit directement dans RTDB (live). Les notifications push utilisent FCM (Firebase Cloud Messaging) cote app native. Le Web Push (VAPID) a ete retire.

MethodeEndpointDescription
GET/api/driver/orders/{id}/chatAutorise le canal de la commande + renvoie le nom du canal RTDB
POST/api/driver/orders/{id}/chatEnvoyer un message (ecrit dans RTDB)
GET/api/driver/chat/fleetAutorise le canal de la flotte (chat livreurs)
POST/api/driver/chat/fleetEnvoyer un message dans le chat flotte
POST/api/driver/push/fcm-registerEnregistrer le token FCM (notifications push, app Android)

API Integrations

Gestion des integrations (necessite authentification admin).

MethodeEndpointDescription
GET/api/integrationsLister les integrations
POST/api/integrationsCreer une integration
PUT/api/integrations/{id}Modifier une integration
DELETE/api/integrations/{id}Supprimer une integration
POST/api/integrations/{id}/regenerate-tokenRegenerer le token

Creer une integration

POST /api/integrations
Content-Type: application/json

{
  "tenant_id": "rest_abc123",
  "platform": "custom",
  "name": "Mon Site Web",
  "status_push_url": "https://monsite.ch/api/delivery-status",
  "field_mapping": {
    "order_id": "id",
    "restaurant_name": "restaurant.name",
    "customer_name": "customer.name",
    "delivery_address": "customer.address"
  }
}

Reponse

{
  "ok": true,
  "integration_id": 7,
  "webhook_url": "/webhook/abc123def456",
  "webhook_token": "abc123def456"
}

Stock & Disponibilite

La disponibilite des articles suit le modele HubRise : un article se met en rupture temporaire (snooze) sans le retirer du catalogue. Le catalog est le catalogue synchronise (HubRise) ; les articles sont references par sku_ref.

MethodeEndpointDescription
GET/api/tenant-admin/catalogCatalogue synchronise (articles + variantes)
POST/api/tenant-admin/catalog/items/{sku_ref}/snoozeMettre un article en rupture (indispo temporaire)
POST/api/tenant-admin/catalog/items/{sku_ref}/unsnoozeRemettre un article disponible
PUT/api/tenant-admin/catalog/items/{sku_ref}/restrictionsRestrictions de l'article (par canal)
PUT/api/tenant-admin/catalog/items/{sku_ref}/channel-pricePrix specifique par canal
DELETE/api/tenant-admin/catalog/items/{sku_ref}/channel-price/{variant_ref}Supprimer un prix par canal

Clients

Base clients du restaurant, synchronisee avec HubRise (import depuis la liste clients HubRise). Authentifiee tenant-admin.

MethodeEndpointDescription
GET/api/tenant-admin/customersListe des clients
POST/api/tenant-admin/customers/pullImporter / synchroniser les clients depuis HubRise
GET/api/tenant-admin/customers/{id}Detail d'un client
POST/api/tenant-admin/customers/{id}/deleteSupprimer un client

Exemples d'integration

Envoyer une commande (curl)

curl -X POST https://hub.swisslivraisonpro.ch/webhook/VOTRE_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "id": "CMD-001",
    "restaurantName": "Pizzeria Bella",
    "restaurantAddress": "Rue de Lausanne 42, 1201 Geneve",
    "customerName": "Marie Martin",
    "customerPhoneNumber": "+41 78 900 11 22",
    "deliveryAddress": "Chemin des Fleurs 8, 1202 Geneve",
    "deliveryLatitude": 46.2108,
    "deliveryLongitude": 6.1467
  }'

Creer une integration (curl)

curl -X POST https://hub.swisslivraisonpro.ch/api/integrations \
  -H "Content-Type: application/json" \
  -H "Cookie: session=VOTRE_SESSION" \
  -d '{
    "tenant_id": "rest_abc123",
    "platform": "custom",
    "name": "Mon Site Web"
  }'

Webhook depuis WooCommerce

Dans WooCommerce, allez dans Reglages > Avance > Webhooks :

  • Statut : Actif
  • Sujet : Commande creee
  • URL : https://hub.swisslivraisonpro.ch/webhook/VOTRE_TOKEN
  • Version API : WP REST API v3

Webhook depuis Shopify

Dans Shopify, allez dans Parametres > Notifications > Webhooks :

  • Evenement : Creation de commande
  • URL : https://hub.swisslivraisonpro.ch/webhook/VOTRE_TOKEN
  • Format : JSON

Codes d'erreur

Code HTTPErreurDescription
400invalid_jsonLe corps de la requete n'est pas un JSON valide
400no_order_idAucun identifiant de commande trouve
400tenant_id requiredLe champ tenant_id est requis
401invalid_tokenToken webhook invalide ou integration desactivee
403tenant_disabledLe commercant est desactive — nouvelles commandes refusees
401unauthorizedAuthentification requise
200duplicateLa commande existe deja (retourne l'ID existant)

Format de reponse d'erreur

{
  "error": "invalid_token"
}

Format de reponse succes

{
  "ok": true,
  ...
}