{"openapi":"3.1.0","info":{"title":"API MenuiserieAI","version":"1.0.0","description":"Lancer des simulations de menuiserie depuis votre logiciel, et en\nrécupérer les rendus.\n\nCette spécification est ENGENDRÉE depuis le code : les codes d'erreur,\nles portées et les champs publiés en sont extraits à chaque appel.\nElle ne peut donc pas décrire un comportement que l'API n'a pas.","contact":{"name":"Support MenuiserieAI","url":"https://menuiserieai.fr/contact"}},"servers":[{"url":"https://menuiserieai.fr/api/v1","description":"Production"}],"security":[{"ApiKey":[]}],"tags":[{"name":"Simulations","description":"Lancer un rendu et en suivre l’avancement."},{"name":"Interface intégrée","description":"Ouvrir les écrans MenuiserieAI depuis votre logiciel."},{"name":"Référentiels","description":"Catégories, matériaux, coloris, modèles."},{"name":"Prospects","description":"Transmettre et relire des demandes entrantes."},{"name":"Compte","description":"Consommation et agences."}],"paths":{"/simulations":{"post":{"tags":["Simulations"],"summary":"Lancer une simulation","description":"Répond **202** : le rendu n’est pas prêt. Suivez `GET /jobs/{id}`,\nou attendez la notification `simulation.succeeded`.\n\nL'en-tête `Idempotency-Key` est **obligatoire** : sans lui, une\nrelance réseau consommerait un second crédit.","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":255},"description":"Identifiant unique de la tentative. Rejouer la même clé renvoie le même job."},{"name":"X-MAI-On-Behalf-Of","in":"header","required":false,"schema":{"type":"string","format":"email"},"description":"Adresse du commercial à imputer. Exige `act_on_behalf` ET l'autorisation sur la clé. Sans cet en-tête, la simulation est imputée au porteur de la clé."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["category","site_photo_url"],"properties":{"category":{"type":"string","description":"Voir `GET /meta`. Une valeur inconnue est refusée.","example":"portail"},"site_photo_url":{"type":"string","format":"uri","description":"**HTTPS obligatoire** — une adresse en clair est refusée (protection contre les requêtes forgées)."},"mode":{"type":"string","enum":["replacement","add"],"default":"replacement"},"material":{"type":"string","example":"aluminium"},"color":{"type":"string","example":"gris-anthracite"},"style":{"type":"string"},"numero_devis":{"type":"string","description":"Votre référence de devis. Republiée telle quelle."},"name_chantier":{"type":"string","description":"Adresse du chantier."},"external_ref":{"type":"string","description":"Votre identifiant de dossier. Republié dans les notifications."},"product_reference_url":{"type":"string","format":"uri","description":"Image du modèle à reproduire. C’est ELLE qui pilote le rendu."}}}}}},"responses":{"200":{"description":"Rejeu d’une `Idempotency-Key` déjà vue. Aucun crédit supplémentaire."},"202":{"description":"Simulation acceptée. Le rendu suivra.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"job_cbae3c0b6d3c4666b859ae1132a4bb4c"},"object":{"type":"string","example":"job"},"status":{"type":"string","enum":["pending","processing","succeeded","failed"]}}}}}},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"402":{"description":"Quota épuisé (`QUOTA_EXCEEDED`). **Non rejouable** — rien ne sert de réessayer.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"422":{"description":"Corps invalide (`VALIDATION_ERROR`). `details` nomme le champ fautif.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}},"get":{"tags":["Simulations"],"summary":"Lister les simulations","description":"Pagination par CURSEUR signé, jamais par décalage : une simulation créée pendant votre parcours ne décale pas les pages.","security":[{"ApiKey":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Reprendre après `next_cursor`."},{"name":"updated_since","in":"query","schema":{"type":"string","format":"date-time"},"description":"Pour une relance périodique : ne relire que ce qui a changé."}],"responses":{"200":{"description":"Liste paginée.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","example":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Simulation"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/simulations/{id}":{"get":{"tags":["Simulations"],"summary":"Lire une simulation","description":"Publie des champs ABSENTS de la liste, dont `numero_devis` — la liste est une projection volontairement réduite.","security":[{"ApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"La simulation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Simulation"}}}},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"404":{"description":"Inconnue, ou hors du périmètre de votre clé (`NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/jobs/{id}":{"get":{"tags":["Simulations"],"summary":"Suivre un rendu","description":"À interroger toutes les 5 à 10 secondes, pas plus vite.\n\nQuand `status` vaut `succeeded`, lisez `GET /simulations/{id}`\npour obtenir l'adresse du rendu.","security":[{"ApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"État du travail.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","example":"job"},"status":{"type":"string","enum":["pending","processing","succeeded","failed"]},"simulation_id":{"type":["string","null"]},"error":{"type":["object","null"]}}}}}},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"404":{"description":"Travail inconnu (`NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/embed_sessions":{"post":{"tags":["Interface intégrée"],"summary":"Ouvrir un écran MenuiserieAI","description":"Renvoie une adresse à ouvrir dans un onglet : l'utilisateur voit\nl'interface réelle de MenuiserieAI, sans se reconnecter.\n\nL'adresse vaut une session de 30 minutes. Ne la journalisez pas,\nne la mettez pas en cache, ouvrez-la immédiatement.","security":[{"ApiKey":[]}],"parameters":[{"name":"X-MAI-On-Behalf-Of","in":"header","required":false,"schema":{"type":"string","format":"email"},"description":"Le commercial pour qui la session est ouverte."}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"destination":{"type":"string","enum":["dashboard","simulate","studio","library","videos","catalog","products","hdr","upscale","brighten","assistant","prospects","prospection","team","settings"],"default":"simulate","description":"L'écran à ouvrir. Liste FERMÉE : un chemin libre serait refusé (422). Omis, l’assistant de simulation s’ouvre."},"quote_ref":{"type":"string","description":"Pré-remplit le numéro de devis (écran `simulate` seulement)."},"site_label":{"type":"string","description":"Pré-remplit l’adresse du chantier."},"external_ref":{"type":"string","description":"Votre identifiant de dossier."}}}}}},"responses":{"201":{"description":"Session ouverte.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","example":"embed_session"},"url":{"type":"string","format":"uri","description":"L'adresse à ouvrir. Usage unique."},"expires_in":{"type":"integer","example":1800},"commercial":{"type":"object","properties":{"id":{"type":"string"}}}}}}}},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"422":{"description":"Destination inconnue (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"503":{"description":"Fonctionnalité non configurée sur cet environnement.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/uploads":{"post":{"tags":["Simulations"],"summary":"Obtenir une adresse de dépôt","description":"Pour envoyer une photo qui n’est pas déjà accessible en HTTPS. Déposez le fichier sur l’adresse renvoyée, puis passez son `public_url` à `POST /simulations`.","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content_type"],"properties":{"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp"]},"bytes":{"type":"integer","description":"Taille annoncée. Aide au refus précoce d’un fichier trop lourd."}}}}}},"responses":{"201":{"description":"Adresse de dépôt signée, valable quelques minutes."},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"422":{"description":"Format refusé (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/meta":{"get":{"tags":["Référentiels"],"summary":"Catégories, matériaux, styles, coloris","description":"La SOURCE des valeurs acceptées par `POST /simulations`. Ne les recopiez pas : rapatriez-les, une fois par jour suffit. Une valeur écrite en dur finira par être refusée.","security":[{"ApiKey":[]}],"parameters":[{"name":"section","in":"query","schema":{"type":"string","enum":["categories","colors"]}}],"responses":{"200":{"description":"Les référentiels."},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/products":{"get":{"tags":["Référentiels"],"summary":"Le catalogue de modèles","description":"Les modèles de la bibliothèque. Leur `image_url` est ce qui pilote le rendu — les libellés, eux, n’alimentent que le devis.","security":[{"ApiKey":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":20}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Liste paginée de modèles."},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/leads":{"get":{"tags":["Prospects"],"summary":"Lister les prospects","security":[{"ApiKey":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":20}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Liste paginée."},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}},"post":{"tags":["Prospects"],"summary":"Transmettre un prospect","description":"Le routage vers l’agence se fait sur le CODE POSTAL, selon la configuration du réseau. `Idempotency-Key` obligatoire.","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["telephone"],"properties":{"nom":{"type":"string"},"email":{"type":"string","format":"email"},"telephone":{"type":"string"},"code_postal":{"type":"string","description":"Détermine l’agence destinataire."},"external_ref":{"type":"string"}}}}}},"responses":{"201":{"description":"Prospect enregistré et routé."},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"422":{"description":"Corps invalide (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/usage":{"get":{"tags":["Compte"],"summary":"Consommation et quota","description":"À interroger avant de lancer un lot : mieux vaut savoir que le quota est épuisé AVANT de faire attendre un commercial.","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Consommation courante.","content":{"application/json":{"schema":{"type":"object","properties":{"simulations":{"type":"object","properties":{"used":{"type":"integer"},"reserved":{"type":"integer","description":"En cours de génération, crédit retenu."},"quota":{"type":"integer"},"remaining":{"type":"integer"},"bonus_credits":{"type":"integer"}}}}}}}},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}},"/agencies":{"get":{"tags":["Compte"],"summary":"Lister les agences","description":"Utile pour relier vos établissements aux agences MenuiserieAI.","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Les agences visibles par votre clé."},"401":{"description":"Clé absente, invalide ou révoquée (`INVALID_API_KEY`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"403":{"description":"Clé valide mais portée insuffisante (`SCOPE_MISSING`).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"429":{"description":"Cadence dépassée (`RATE_LIMITED`). Respectez `Retry-After`.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}},"500":{"description":"Incident de notre côté (`INTERNAL`). Rejouable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}}}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","description":"En-tête `Authorization: Bearer mai_live_…`\n\nLa clé porte une PORTÉE et un PÉRIMÈTRE : une clé d’agence ne voit\nque sa propre agence, quoi qu’elle demande."}},"schemas":{"Simulation":{"type":"object","description":"Champs publiés par les listes. Le détail unitaire en expose davantage.","properties":{"id":{"type":"string"},"object":{"type":"string","example":"simulation"},"status":{"type":"string","enum":["pending","processing","succeeded","failed"]},"job_id":{"type":["string","null"]},"external_ref":{"type":["string","null"]},"category":{"type":["string","null"]},"name_chantier":{"type":["string","null"]},"numero_devis":{"type":["string","null"],"description":"Détail unitaire seulement."},"source":{"type":["string","null"]},"generated_image_url":{"type":["string","null"],"description":"Adresse du rendu. Nulle tant que la simulation n’a pas abouti."},"commercial":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Code stable. C'est LUI qu'il faut brancher, pas le message."},"message":{"type":"string","description":"Formulation lisible, susceptible de changer."},"request_id":{"type":"string","description":"À citer dans tout signalement."},"details":{"type":"object","description":"Champ fautif et motif, quand la nature de l’erreur le permet."}}}}}}},"x-scopes":[{"name":"simulations:read","summary":"Lire GET /v1/simulations et GET /v1/simulations/{id}."},{"name":"simulations:write","summary":"Créer POST /v1/simulations (202) et supprimer DELETE /v1/simulations/{id}."},{"name":"leads:read","summary":"Lire GET /v1/leads."},{"name":"leads:write","summary":"Créer POST /v1/leads."},{"name":"uploads:write","summary":"Obtenir une URL d'upload signée via POST /v1/uploads."},{"name":"agencies:read","summary":"Lire GET /v1/agencies (le réseau vu par la clé)."},{"name":"usage:read","summary":"Lire GET /v1/usage (quotas, consommation, date de renouvellement)."},{"name":"act_on_behalf","summary":"Employer l'en-tête X-MAI-On-Behalf-Of pour imputer une action à un commercial précis (et donc à son plafond individuel `TeamMember.allocated_credits`)."}],"x-errors":[{"code":"API_VERSION_UNSUPPORTED","status":400,"retryable":false,"message":"Version d'API inconnue ou retirée. Mettez à jour le connecteur ; voir l'en-tête Sunset."},{"code":"IDEMPOTENCY_KEY_REQUIRED","status":400,"retryable":false,"message":"L'en-tête Idempotency-Key est obligatoire sur cet endpoint. Générez un UUID v4 par tentative métier et réutilisez-le sur les rejeux."},{"code":"IDEMPOTENCY_KEY_TOO_LONG","status":400,"retryable":false,"message":"La clé d'idempotence dépasse 255 caractères."},{"code":"INVALID_CURSOR","status":400,"retryable":false,"message":"Le curseur de pagination est invalide ou ne correspond plus aux filtres. Relancez la pagination depuis le début, sans curseur."},{"code":"MALFORMED_JSON","status":400,"retryable":false,"message":"Le corps de la requête n'est pas un JSON valide. Corrigez la sérialisation."},{"code":"INVALID_API_KEY","status":401,"retryable":false,"message":"Clé d'API absente ou invalide. Vérifiez l'en-tête « Authorization: Bearer mai_live_… ». Ne réessayez pas : prévenez l'administrateur du compte."},{"code":"KEY_EXPIRED","status":401,"retryable":false,"message":"Cette clé d'API a expiré. Faites-la renouveler avant sa date d'expiration."},{"code":"KEY_REVOKED","status":401,"retryable":false,"message":"Cette clé d'API a été révoquée. Arrêtez les tâches planifiées et demandez une nouvelle clé au siège."},{"code":"MEMBER_ALLOCATION_EXCEEDED","status":402,"retryable":false,"message":"Le plafond individuel de ce commercial est atteint. Son responsable d'agence peut le réajuster."},{"code":"PLAN_REQUIRED","status":402,"retryable":false,"message":"Cette fonctionnalité n'est pas incluse dans l'offre du compte."},{"code":"QUOTA_EXCEEDED","status":402,"retryable":false,"message":"Quota de simulations épuisé pour la période en cours. Ne réessayez pas avant le renouvellement (voir « resets_at » de GET /v1/usage)."},{"code":"SUBSCRIPTION_INACTIVE","status":402,"retryable":false,"message":"L'abonnement du compte n'est pas actif. Prévenez la comptabilité ; ne réessayez pas avant régularisation."},{"code":"VIDEO_QUOTA_EXCEEDED","status":402,"retryable":false,"message":"Quota de vidéos épuisé pour la période en cours."},{"code":"ACTOR_OUT_OF_AGENCY_SCOPE","status":403,"retryable":false,"message":"Le commercial indiqué n'appartient pas à l'agence de cette clé. Corrigez la correspondance entre son compte et son membre MenuiserieAI."},{"code":"AGENCY_OUT_OF_SCOPE","status":403,"retryable":false,"message":"Le paramètre « agency_id » est hors du périmètre de cette clé. Retirez le filtre, ou utilisez la clé de siège."},{"code":"BROWSER_ORIGIN_FORBIDDEN","status":403,"retryable":false,"message":"Cette API est serveur-à-serveur. Une clé « mai_live_ » exposée dans un navigateur doit être considérée comme compromise : retirez l'appel du front et faites tourner la clé."},{"code":"ON_BEHALF_NOT_ALLOWED","status":403,"retryable":false,"message":"Cette clé n'est pas autorisée à agir au nom d'un commercial (en-tête X-MAI-On-Behalf-Of). Demandez l'activation au siège."},{"code":"SCOPE_MISSING","status":403,"retryable":false,"message":"Cette clé d'API ne porte pas le droit requis pour cette opération. Demandez au siège d'ajouter le scope indiqué dans « details »."},{"code":"NOT_FOUND","status":404,"retryable":false,"message":"Ressource introuvable."},{"code":"METHOD_NOT_ALLOWED","status":405,"retryable":false,"message":"Méthode HTTP non autorisée sur cette ressource. Voir l'en-tête Allow."},{"code":"IDEMPOTENCY_CONFLICT","status":409,"retryable":false,"message":"Cette clé d'idempotence a déjà été utilisée avec un corps différent. Une clé = une tentative métier : générez-en une neuve."},{"code":"IDEMPOTENCY_IN_PROGRESS","status":409,"retryable":true,"message":"Une requête portant cette clé est en cours de traitement."},{"code":"JOB_NOT_CANCELABLE","status":409,"retryable":false,"message":"Ce job n'est plus annulable : il est déjà terminé."},{"code":"WEBHOOK_URL_ALREADY_REGISTERED","status":409,"retryable":false,"message":"Cette URL est déjà enregistrée pour cette organisation."},{"code":"PAYLOAD_TOO_LARGE","status":413,"retryable":false,"message":"Le corps de la requête dépasse la limite acceptée. Utilisez le flux d'upload en deux temps (POST /v1/uploads)."},{"code":"UNSUPPORTED_MEDIA_TYPE","status":415,"retryable":false,"message":"Type de contenu non supporté. « application/json » est attendu."},{"code":"ACTOR_NOT_ACTIVE","status":422,"retryable":false,"message":"Le commercial indiqué n'a pas d'accès actif (invitation en attente, refusée ou révoquée). Faites accepter l'invitation, ou réassignez."},{"code":"ACTOR_NOT_IN_ORGANIZATION","status":422,"retryable":false,"message":"Le commercial indiqué n'appartient pas à cette organisation. Corrigez le mapping ; voir GET /v1/members."},{"code":"ACTOR_REQUIRED","status":422,"retryable":false,"message":"Aucun commercial n'a été indiqué et cette clé n'a pas d'acteur par défaut. Renseignez l'en-tête X-MAI-On-Behalf-Of."},{"code":"IMAGE_UNREACHABLE","status":422,"retryable":false,"message":"L'URL de la photo de chantier n'est pas accessible depuis nos serveurs. Passez par POST /v1/uploads."},{"code":"INVALID_CATEGORY","status":422,"retryable":false,"message":"Catégorie de menuiserie inconnue. Utilisez un slug de GET /v1/meta/categories."},{"code":"INVALID_MODE","status":422,"retryable":false,"message":"Le mode doit valoir « add » ou « replacement »."},{"code":"INVALID_STATUS","status":422,"retryable":false,"message":"Statut de lead invalide. Valeurs : nouveau, contacte, devis, signe, pose."},{"code":"NO_IMAGE","status":422,"retryable":false,"message":"Cette simulation n'a pas de rendu : la vidéo est impossible. Attendez « status: succeeded »."},{"code":"VALIDATION_ERROR","status":422,"retryable":false,"message":"Le corps de la requête est invalide. Chaque entrée de « details » porte le champ concerné, le motif et la valeur reçue."},{"code":"WEBHOOK_URL_FORBIDDEN","status":422,"retryable":false,"message":"URL de webhook refusée : HTTPS sur le port 443 obligatoire, adresses privées interdites."},{"code":"CONCURRENCY_LIMIT_REACHED","status":429,"retryable":true,"message":"Trop de générations simultanées pour cette clé ou cette organisation. Réessayez après le délai indiqué, sans augmenter le parallélisme."},{"code":"DAILY_CAP_REACHED","status":429,"retryable":false,"message":"Le plafond quotidien de générations de ce compte est atteint. Reprenez demain, ou demandez un relèvement au siège."},{"code":"RATE_LIMITED","status":429,"retryable":true,"message":"Trop de requêtes. Réessayez après le délai indiqué par l'en-tête Retry-After."},{"code":"INTERNAL","status":500,"retryable":true,"message":"Erreur interne. Réessayez une fois avec la même Idempotency-Key, puis transmettez la référence « request_id » au support."},{"code":"DEPENDENCY_UNAVAILABLE","status":503,"retryable":true,"message":"Un service dont dépend l'API est momentanément indisponible. Réessayez après le délai indiqué, avec la même Idempotency-Key."},{"code":"PROVIDER_UNAVAILABLE","status":503,"retryable":true,"message":"Le moteur de rendu est indisponible. Aucun crédit n'a été débité ; réessayez après le délai indiqué."},{"code":"SCHEMA_UNAVAILABLE","status":503,"retryable":false,"message":"Incident de configuration côté MenuiserieAI. Ne réessayez pas en boucle : alertez le support avec la référence « request_id »."}]}