Vos flux récurrents — navettes parc ↔ concession, restitutions de fin de contrat, rotations entre agences — se ressemblent d’une semaine à l’autre. Plutôt que de reconstruire un POST /transports complet, dupliquez une mission existante : le serveur recopie le trajet, le véhicule, les contacts, les consignes et les documents que vous choisissez, puis crée une nouvelle mission aux dates que vous indiquez, tarifée sur votre grille du moment.

Aperçu sans engagement

POST /transports/{id}/duplicate/preview renvoie le prix et les documents recopiés, sans rien créer.

Création idempotente

POST /transports/{id}/duplicate crée la mission ; rejouable sans doublon avec Idempotency-Key.
Les deux routes sont scopées au groupe de votre clé : une mission qui n’est pas la vôtre renvoie 404. La création exige le scope transports:write.
Variables utilisées dans les exemples :

Ce qui est recopié, ce qui ne l’est pas

Le prix n’est jamais recopié : il est recalculé sur la grille de votre groupe au moment de la duplication. Si l’enlèvement demandé est proche, la majoration express s’applique comme pour une création classique (price.express: true).
Une mission ne se duplique qu’une seule fois. Une seconde tentative sur la même source renvoie 409 Conflict avec code: "already_duplicated" et l’extension duplicated_to (identifiants de la mission déjà créée). Pour une troisième occurrence, dupliquez la mission créée, pas la source d’origine.

1. Obtenir un aperçu

L’aperçu applique exactement les mêmes contrôles que la création (dates, options, documents, règle « une seule fois ») mais n’a aucun effet de bord : ni mission, ni événement, ni idempotence.
Réponse 200

Les dates, jambe par jambe

dates est obligatoire et doit couvrir chaque jambe de la source : enlevement-aller, livraison-aller, et, si la source comporte un retour, enlevement-retour et livraison-retour. Chaque jambe porte un jour (YYYY-MM-DD, heure de Paris) et un créneau start_time / end_time (HH:mm). Pour une jambe sans créneau connu, envoyez need_appointment: true avec la date seule : la mission naît alors en attente de rendez-vous. Le serveur refuse (422) une date passée, des jambes désordonnées, un créneau inversé, et un enlèvement sous le délai minimal de commande — dans ce dernier cas la réponse porte code: "too_early" et min_date, première date acceptée.

Options et documents

  • options.aller / options.retour : soit un tableau des libellés des options de la source à conserver (tels qu’ils apparaissent sur la source ; absent = toutes conservées ; un libellé inconnu renvoie 422 unknown_option), soit un objet { "keep": [...], "add": [...] } pour ajouter des options au prix de votre grille dès la duplication. Chaque entrée de add porte option_key (clé du catalogue, cf. Options) et, selon l’option, ses obligations dans params : loueur + lien ou code PIN pour un PV dématérialisé, liste d’accessoires à vérifier. Une obligation manquante renvoie 422 requirements_not_met avec le champ précis (errors[].field = "options.aller.add.0.renter"). Un seul type de PV par jambe : ajouter un PV dématérialisé retire le PV papier conservé. Un créneau need_appointment ajoute d’office l’option « Prise de rendez-vous ».
  • documents.copy : identifiants Document.id (cf. GET /transports/{id}/documents) des documents de la source à recopier. Absent = tous les documents copiables (ceux que vous avez déposés vous-même ; les PV signés, inspections et pièces générées par MED ne sont jamais recopiés). 20 documents au plus par duplication.
  • documents.pending : clés des documents que vous déposerez juste après la création (POST /transports/{id}/documents), par exemple PV_PAPIER quand vous ajoutez l’option « PV papier » sans PV recopié — sans cette annonce ni PV copié, l’ajout est refusé (422 requirements_not_met, documents: ["PV_PAPIER"]).
La réponse (aperçu comme création) rend options.aller / options.retour : les options finales de chaque jambe, clés {nom} - {prix} au prix appliqué.

2. Créer le duplicata

Même corps que l’aperçu. Ajoutez un en-tête Idempotency-Key : un rejeu (timeout, double envoi) renvoie la même réponse sans créer de seconde mission.
Réponse 201
La nouvelle mission devient lisible via GET /transports/-OabcDEF123 dès que la synchronisation est faite, en général en moins de quelques secondes (son champ duplicated_from pointe vers la source) ; la source expose alors is_duplicated: true. Un 404 juste après le 201 signifie simplement que la synchronisation n’est pas encore passée : réessayez brièvement plutôt que de conclure à un échec.
documents.failed liste les fichiers qui n’ont pas pu être recopiés : la mission est créée malgré tout. Reposez-les avec POST /transports/{id}/documents.

3. Webhooks

Deux événements sont émis : Le duplicata suit ensuite le cycle de vie habituel (transport.assigned, transport.collected, …). Voir le catalogue des événements.

4. Erreurs

Timeout ou 502 après un appel de création : la copie des documents peut dépasser le délai du relai alors que la mission a bien été créée. Relisez la source (GET /transports/{id}) : si is_duplicated vaut true, retrouvez le duplicata via GET /events?type=transport.duplicated (data.duplicated_to). Rejouer l’appel avec la même Idempotency-Key est sans risque : la source ne peut pas être dupliquée deux fois.