Mono-transporteur (cas nominal)
C'est le scénario le plus simple, et le plus courant : un seul transporteur, du chargement à la livraison, sans relais ni sous-traitance. Un TMS externe crée le document ; sur le terrain, trois signatures s'enchaînent — l'expéditeur signe la collecte, le chauffeur signe lui-même l'acceptation de cette collecte (sa propre signature de transporteur, case 23 du CMR papier), puis le destinataire signe la livraison — et le TMS est notifié en temps réel de chaque changement de statut via webhook — il n'a jamais besoin d'interroger l'API en polling.
Points clés à retenir :
- La souscription au webhook se fait une seule fois, en amont, indépendamment de chaque transport.
- Le chauffeur peut précharger le document (mode offline) avant même l'arrivée sur site.
- Chaque signature de collecte ou de livraison déclenche à la fois une transition de statut et une notification webhook — les deux sont synchrones du point de vue du chauffeur, asynchrones du point de vue du TMS. La signature du chauffeur (
COLLECTION_ACCEPTANCE) n'a en revanche aucun effet sur le statut ni sur les webhooks : elle s'enchaîne automatiquement juste après la collecte, sur le même appareil. - La livraison est bloquée tant que le chauffeur n'a pas signé sa propre acceptation de collecte (
signature.collection_acceptance_required, 409, si elle est tentée avant) — voir Signature électronique. Dans ce scénario nominal, l'application chauffeur enchaîne les deux automatiquement et vous n'avez rien à orchestrer pour cela. - La consultation du PDF signé se fait à la demande, a posteriori — ce n'est jamais l'API qui pousse le PDF.
sequenceDiagram
participant TMS as TMS externe
participant API as API e-CMR
participant Chauffeur as App chauffeur
Note over TMS,API: Onboarding préalable (une fois)
TMS->>API: Souscription webhook (callback URL)
API-->>TMS: 201 Subscription créée
Note over TMS,API: Création du transport
TMS->>API: POST /freightdocuments (externalIdentifier, parties, marchandises)
API-->>TMS: 201 Created, status=DRAFT
TMS->>API: POST /freightdocuments/id/issue
API-->>TMS: 200 OK, status=ISSUED
API-)TMS: Webhook STATUS=ISSUED
Note over API,Chauffeur: Préchargement terrain
API-)Chauffeur: Notification push (mission assignée)
Chauffeur->>API: GET /freightdocuments/id (mode offline)
API-->>Chauffeur: Document complet + config signature
Note over Chauffeur,API: Collecte
Chauffeur->>API: POST events (arrivée, chargement)
Chauffeur->>API: POST signature collecte (expéditeur)
API-->>Chauffeur: 200 OK, status=TRANSIT
API-)TMS: Webhook STATUS=TRANSIT
Chauffeur->>API: POST signature collecte-chauffeur (COLLECTION_ACCEPTANCE)
API-->>Chauffeur: 200 OK, signature enregistrée (statut inchangé)
Note over Chauffeur,API: Livraison
Chauffeur->>API: POST events (arrivée, déchargement)
Chauffeur->>API: POST signature livraison
API-->>Chauffeur: 200 OK, status=DELIVERED
API-)TMS: Webhook STATUS=DELIVERED
Note over TMS,API: Consultation a posteriori
TMS->>API: GET /freightdocuments/id/pdf
API-->>TMS: PDF e-CMR signé
Assigner le chauffeur par son numéro de téléphone
Votre TMS sait déjà quel chauffeur roule : c'est lui qui l'a planifié. À la création du document, le rôle CARRIER accepte un champ optionnel driver_phone_number — le numéro tel que vous le stockez dans votre propre fiche chauffeur. Nous résolvons (ou créons) le sous-compte chauffeur correspondant et l'attachons immédiatement au rôle : le document revient déjà assigné, sans qu'un opérateur ait à intervenir dans le portail et sans que vous ayez à connaître d'identifiant de sous-compte chez nous.
driver_phone_number n'est jamais obligatoire. Omis, le document se crée exactement comme avant : tous les sous-comptes du compte titulaire du rôle y ont accès, et l'assignation peut se faire plus tard depuis le portail ou via PATCH /v1/freightdocuments/{id}/roles/{role_id}/assign-subaccount.
Le cycle complet, en un appel
Une commande planifiée chez vous (référence de commande, lieux, marchandise, plaque du tracteur, chauffeur assigné) devient un e-CMR déjà ciblé sur ce chauffeur :
{
"external_identifier": "CMD-2026-4711",
"transport_reference": "TR-4711",
"goods_lines": [
{
"package_count": 20,
"packaging_method": "Palettes",
"nature_of_goods": "Grain de café",
"gross_weight_kg": 2000
}
],
"roles": [
{
"role_type": "CARRIER",
"account_id": "<votre propre account_id>",
"party_snapshot": { "name": "Lotrans SA" },
"vehicle_plate": "VD-123456",
"driver_phone_number": "+41787083851",
"driver_name": "Jane Driver"
},
{
"role_type": "PLACEOFTAKINGOVER",
"email": "quai@expediteur.example",
"party_snapshot": {
"name": "Expéditeur SA",
"address": { "street": "Rue 1", "postal_code": "1000", "city": "Lausanne", "country": "CH" }
}
},
{
"role_type": "PLACEOFDELIVERY",
"email": "reception@destinataire.example",
"party_snapshot": { "name": "Destinataire SA" }
}
]
}
La réponse 201 renvoie le rôle CARRIER avec son assigned_subaccount_id renseigné — c'est le sous-compte du chauffeur, résolu ou créé par cet appel :
{
"id": "...",
"status": "DRAFT",
"roles": [
{
"role_type": "CARRIER",
"vehicle_plate": "VD-123456",
"assigned_subaccount_id": "8141560d-8878-43c6-888b-db25383ef0ef"
}
]
}
Enchaînez ensuite normalement avec POST /freightdocuments/{id}/issue : à partir de là, le flux est celui du schéma ci-dessus, à ceci près que la mission n'est visible que par ce chauffeur-là, et non par tous les sous-comptes de votre compte.
Ce qu'il faut savoir avant de brancher ce champ
Envoyez toujours l'indicatif pays. Les formes nationales sont acceptées, mais interprétées dans la région par défaut de la plateforme (CH en standard) : 0787083851, 0041787083851 et +41787083851 désignent bien le même mobile suisse, en revanche un numéro national espagnol comme 612345678 sera compris comme le suisse +41612345678 — un numéro valide, mais pas le bon, et aucune erreur ne sera renvoyée. En E.164 (+34612345678), il n'y a aucune ambiguïté possible.
Un numéro non résolvable est rejeté explicitement, avec freight_document.invalid_driver_phone_number (400) : rien n'est créé, aucun SMS n'est envoyé, et votre external_identifier reste libre — vous pouvez corriger le numéro et rejouer le même appel à l'identique.
Le chauffeur reçoit une invitation SMS, une seule fois. Un numéro que nous n'avons jamais vu sous votre compte déclenche l'envoi d'un lien d'inscription au chauffeur (il choisit lui-même son mot de passe ; recevoir et ouvrir ce lien est ce qui prouve le numéro). Un numéro déjà connu — d'un transport précédent, d'une fiche créée par un opérateur, ou d'une signature passée — est simplement réutilisé, sans nouveau SMS. Cet envoi est soumis à une limite de débit par compte : un flux d'ordres portant chacun un nouveau numéro finira par recevoir un 429, comme sur l'endpoint d'invitation du portail. Les ordres portant un chauffeur déjà connu ne consomment jamais ce quota.
Uniquement sur votre propre rôle CARRIER. Le champ n'est valide que sur un rôle CARRIER dont l'account_id est le vôtre — un sous-traitant assigne ses propres chauffeurs, depuis son propre compte. Il n'est accepté qu'à la création du document : sur l'ajout de rôle post-création (POST /freightdocuments/{id}/roles), il est refusé explicitement plutôt qu'ignoré silencieusement.
Un changement de numéro crée un nouveau chauffeur. Un chauffeur qui change de numéro donne un nouveau sous-compte, distinct de l'ancien : il n'y a aucun rapprochement automatique entre les deux. Ses signatures passées restent attachées au sous-compte qui les a réellement produites — c'est ce lien qui fait leur valeur de preuve.
N'essayez pas de créer le sous-compte vous-même en amont. POST /v1/accounts/me/subaccounts (et son équivalent /invite) n'accepte que la session d'un utilisateur connecté au portail : avec une clé API il renvoie 401, avec un jeton OAuth2 client_credentials un 403 auth.insufficient_scope. driver_phone_number est aujourd'hui la seule voie d'accès machine à la gestion d'un chauffeur.