{"openapi":"3.0.3","info":{"title":"API IWANA","version":"1.0.0","description":"L'API IWANA permet à vos systèmes de lire et d'écrire les mêmes enregistrements que ceux affichés par le tableau de bord :\nrendez-vous, clients et entreprises, sites, équipements, catalogue, équipe, devis,\nfactures, fiches d'intervention, chantiers, tâches, automatisations et configuration de l'espace.\nToute opération listée ici peut être appelée avec une clé API ; rien d'autre n'est listé.\n\n**Authentification.** Chaque appel porte une clé API en jeton bearer. Créez-en une dans\nRéglages → Développeurs. La clé embarque son espace : il n'y a donc aucun en-tête d'espace\nà envoyer, et une clé ne peut jamais viser un espace qui n'est pas le sien.\nUne clé de test (`iw_test_…`) lit l'espace réel et se voit refuser toute écriture\n(`403 test_key_read_only`). Les clés et les jetons OAuth ne peuvent pas invoquer les fonctions d'IA\n(rédaction d'automatisations par IA, extraction de PDF) : celles-ci restent dans l'application.\n\n```bash\ncurl -H \"Authorization: Bearer iw_live_...\" \\\\\n  https://iwana.fr/v3/api/v1/pro/clients?limit=5\n```\n\n**Listes.** Les listes n'ont pas toutes la même forme — la réponse 200 de chaque opération indique laquelle :\nune page `{ items, total }` (certaines renvoient aussi `limit` et `offset`) avec sa propre taille de page\npar défaut et maximale, `{ items }`, ou un tableau simple. `total` compte l'ensemble des résultats, pas\nla page. Une `limit` supérieure au maximum est ramenée à celui-ci sur la plupart des listes ; les rares qui\nla refusent par une 400 le précisent sur le paramètre.\n\n**Validation.** Les corps de requête (et les chaînes de requête dotées d'une liste de paramètres déclarée) sont stricts :\nun champ que le schéma ne déclare pas est refusé avec `400 validation_failed`, il n'est pas\nignoré silencieusement ; il en va de même pour un nombre qui n'en est pas un (`limit=abc`). Pour les réponses, c'est\nl'inverse — des champs sont ajoutés sans préavis : ignorez ceux que vous ne connaissez pas et ne\nfaites jamais échouer votre code à cause d'eux. Ordre des vérifications : l'identifiant, puis le refus de la clé de test, puis les permissions, puis\nle corps — une clé de test reçoit donc `403 test_key_read_only` sur chaque `POST`, `PUT`, `PATCH` et\n`DELETE` (aperçus en lecture seule compris) avant que son corps ne soit lu, et la validation du corps ne peut\nêtre testée qu'avec une clé live.\n\n**Limites de débit.** Toutes les clés API et tous les jetons OAuth d'un espace puisent dans un même budget :\n`X-RateLimit-Limit` / `-Remaining` / `-Reset` (secondes) décrivent la fenêtre d'une minute et les\nmêmes noms avec `-Day` la fenêtre d'une journée ; au-delà de l'une ou l'autre, `429 rate_limited` avec `Retry-After`.\nUn plancher par IP s'applique avant l'authentification, sous les mêmes noms sans suffixe. Un appel refusé\npar les contrôles d'identifiant et de permission (`401`, une `403` telle que `test_key_read_only`, une\n`402 erp_required` sur un chantier) ne porte que ces en-têtes par IP — pas ceux en `-Day` — et\nne consomme pas le budget de l'espace.\n\n**Nouveaux essais.** Envoyez `Idempotency-Key: <8–128 chars>` sur ces créations : `/appointments`, `/clients`, `/companies`, `/contacts`, `/contracts`, `/equipments`, `/interventions`, `/invoices`, `/invoices/{id}/credit-note`, `/invoices/{id}/payments`, `/invoices/from-contract/{contractId}`, `/invoices/from-intervention/{reportId}`, `/invoices/from-quote/{quoteId}`, `/projects`, `/projects/{id}/duplicate`, `/quotes`, `/quotes/{id}/duplicate`, `/quotes/dpgf`, `/sites`, `/tasks`.\nUn nouvel essai avec la même clé sur la même route dans les 48 h renvoie la première réponse\nau lieu de créer un second enregistrement ; une clé de moins de 8 ou de plus de 128 caractères renvoie\n`400 invalid_idempotency_key`. Les autres routes ignorent cet en-tête.\n\n**Vos identifiants.** `POST /pro/clients` crée un client sauf si le corps porte son `id` IWANA :\nil ne rapproche jamais un client existant par e-mail ou téléphone. Pour éviter les doublons, enregistrez\nla correspondance entre vos identifiants et ceux d'IWANA avec `PUT /pro/external-refs` et consultez-la\navec `GET /pro/external-refs` (qui répond `{ \"item\": null }` en l'absence de résultat) avant de créer.\n\n**Source.** Un rendez-vous écrit avec une clé ou un jeton est marqué `source: API` sauf si\nle corps envoie sa propre `source`.\n\n**Webhooks.** Abonnez une URL HTTPS dans Réglages → Développeurs. Chaque événement est un\n`POST` signé de `{ event, orgId, data: { id, … }, occurredAt }` ; `x-iwana-webhook-events`\nliste les types d'événements et `webhooks.event` décrit la livraison. `data` est volontairement réduit\n— identifiants, statut, dates ISO, montants en centimes — relisez donc l'enregistrement avec `GET`\nlorsque vous avez besoin de tout.\n\n**Compatibilité.** Supprimer un champ ou changer son type implique une nouvelle version majeure dans\nle chemin.","contact":{"name":"IWANA","url":"https://iwana.fr/v3"}},"servers":[{"url":"https://iwana.fr/v3/api/v1","description":"Production"}],"tags":[{"name":"Intégration","description":"Vérifier une clé : quel espace elle ouvre et ce qu'elle permet."},{"name":"Rendez-vous","description":"Les rendez-vous eux-mêmes (et les créneaux bloqués, `type=UNAVAILABILITY`) — création, replanification, changement de statut, annulation ; plus les modèles de lecture du calendrier et de la carte et le partage « je suis en route » du technicien."},{"name":"Disponibilités","description":"Horaires de travail (plannings par membre), modèles de semaine, jours de fermeture et jours fériés. `GET /availability/resolve` donne les plages de travail effectives par membre et par jour — pas les créneaux de réservation libres : les rendez-vous existants ne sont pas déduits."},{"name":"Contacts","description":"Le carnet d'adresses. `/clients` contient les personnes (`kind=INDIVIDUAL`) et les entreprises (`kind=COMPANY`) — une liste sans `kind` ne renvoie que les personnes. `/companies` est la vue entreprise des mêmes enregistrements, `/contacts` les personnes rattachées à un compte, `/companies/lists` les listes importées ou hébergées."},{"name":"IDs externes","description":"La correspondance entre vos identifiants et ceux d'IWANA — la façon dont une intégration évite de créer deux fois le même client."},{"name":"Sites","description":"Les adresses où se déroulent les travaux. Un site appartient à un client ou à une entreprise et porte les notes d'accès, la position GPS et les personnes sur place."},{"name":"Équipements","description":"Ce qui est installé sur un site (chaudières, pompes à chaleur…). Modules ERP."},{"name":"Contrats","description":"Contrats de maintenance : lignes, équipements couverts et leur cadence. Modules ERP."},{"name":"Catalogue","description":"Prestations réservables avec leurs variantes et catégories, lieux, promotions, questionnaires, qui peut réaliser quoi, modèles de contrat et bibliothèque de prestations partagée. Pas la bibliothèque de prix pour les devis, qui est `/catalogue` (tag Bibliothèque de prix). Le `price` d'une prestation est en euros — renvoyé sous forme de chaîne décimale, envoyé sous forme de nombre ; tout ce qui s'appelle `…Cents` est en centimes entiers."},{"name":"Équipe","description":"Membres de l'espace (collègues, PAS des clients), leurs rôles et permissions, et équipes."},{"name":"Chantiers","description":"Projets (chantiers) : structure, phases, pièces, tâches, réserves, métrés, documents, intervenants et entreprises partenaires."},{"name":"Tâches","description":"Tâches à faire, éventuellement rattachées à un enregistrement."},{"name":"Configuration","description":"Règles métier (délais de réservation, annulation…) et définitions des champs personnalisés."},{"name":"Workflows","description":"Automatisations : définitions, exécutions de test et exécutions en attente d'approbation. La rédaction d'un workflow par IA n'est pas disponible avec les clés."},{"name":"Communications","description":"Messages envoyés aux clients : historique, modèles, réglages de notification et identité de l'expéditeur."},{"name":"Statistiques","description":"Tuiles et indicateurs du tableau de bord, et flux d'activité de l'espace."},{"name":"Géo","description":"Autocomplétion d'adresse, géocodage et régions françaises."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Une clé API générée dans Réglages → Développeurs, envoyée sous la forme `Authorization: Bearer iw_live_…`. Le secret n'est affiché qu'une seule fois à la création et n'est stocké que sous forme de hash — en cas de perte, révoquez la clé et générez-en une autre. La révocation prend effet dès la requête suivante. Un jeton d'accès OAuth (`iwa_at_…`) est accepté de la même façon."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":8,"maxLength":128},"description":"Toute chaîne que vous générez pour chaque création logique (un UUID convient). La même clé sur la même route dans les 48 h rejoue la première réponse réussie au lieu de créer à nouveau ; une erreur n'est pas stockée, relancez-la donc avec la même clé."}},"responses":{"BadRequest":{"description":"Échec de validation (`validation_failed`, y compris un champ non déclaré par le schéma) ou règle métier ayant refusé l'appel (un code spécifique tel que `client_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"validation_failed","message":"Validation failed","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"Unauthenticated":{"description":"Identifiant manquant, mal formé ou révoqué (`unauthenticated`, `invalid_api_key`, `invalid_token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid api key","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"Forbidden":{"description":"Refusé : écriture avec une clé de test (`test_key_read_only`), rôle qui n'y est pas autorisé (jeton OAuth), action que les clés ne peuvent pas effectuer (`scope_required`), ou offre désactivée pour toute la plateforme (`offer_disabled`, par ex. ERP).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"test_key_read_only","message":"Test key read only","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"NotFound":{"description":"Aucun enregistrement de ce type dans cet espace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Not found","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"RateLimited":{"description":"Le budget de l'espace est épuisé (`rate_limited`) ; attendez `Retry-After` secondes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Rate limited","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"ErpRequired":{"description":"L'offre de l'espace n'inclut pas les modules ERP (`erp_required`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"erp_required","message":"Erp required","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}}},"schemas":{"Error":{"type":"object","description":"Toute erreur a cette structure.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"invalid_api_key","description":"Stable, lisible par une machine."},"message":{"type":"string","description":"Lisible par un humain ; peut changer."},"requestId":{"type":"string","description":"Indiquez-le lorsque vous nous interrogez sur un appel. Également envoyé dans `X-Request-Id` sur chaque réponse."},"details":{"description":"Présent lorsque le refus s'accompagne de données structurées."}},"required":["code","message"]}},"required":["error"]},"WebhookEvent":{"type":"object","description":"Le corps de chaque livraison de webhook.","properties":{"event":{"type":"string","enum":["appointment.created","appointment.updated","appointment.cancelled","client.created","client.updated","client.deleted","quote.created","quote.updated","quote.deleted","invoice.created","invoice.updated","invoice.deleted","payment.created","intervention.created","intervention.updated","intervention.deleted","project.created","project.updated","project.deleted"]},"orgId":{"type":"string","description":"L'espace dans lequel l'événement s'est produit."},"data":{"type":"object","description":"Contient toujours l'`id` de l'enregistrement ; le reste dépend de l'événement et reste réduit : identifiants liés, `status`, dates ISO, montants en centimes entiers. Les événements de devis, facture, paiement, intervention et chantier ne contiennent ni noms, ni notes, ni adresses ; `client.created` / `client.updated` contiennent le `fullName` et l'`email` du contact. Relisez l'enregistrement avec GET lorsque vous avez besoin de tout.","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"occurredAt":{"type":"string","format":"date-time"}},"required":["event","orgId","data","occurredAt"]},"Appointment":{"type":"object","description":"Un rendez-vous tel que le renvoie `GET /appointments/{id}` : toutes les colonnes plus le client, la prestation, le lieu, l'équipe, le site, la fiche et l'historique joints. Les éléments de liste sont une projection plus réduite (`AppointmentListItem`).","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["APPOINTMENT","UNAVAILABILITY"]},"title":{"type":"string","nullable":true},"startAt":{"type":"string","format":"date-time"},"endAt":{"type":"string","format":"date-time"},"timezone":{"type":"string","nullable":true},"status":{"type":"string","enum":["PENDING","AWAITING_CLIENT","CONFIRMED","IN_PROGRESS","COMPLETED","CANCELLED","NO_SHOW","RESCHEDULED","EXPIRED","REQUEST_REJECTED","OFFER_DECLINED"]},"source":{"type":"string","enum":["DASHBOARD","WIDGET","MARKETPLACE","API","AI_ASSISTANT","WORKFLOW","IMPORT","PLUGIN"],"description":"`API` pour un enregistrement écrit avec une clé ou un jeton, sauf si l'appel a envoyé sa propre `source`."},"clientId":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"serviceId":{"type":"string","nullable":true},"locationId":{"type":"string","nullable":true},"siteId":{"type":"string","description":"L'adresse enregistrée où cela se déroule.","nullable":true},"projectId":{"type":"string","nullable":true},"internalNotes":{"type":"string","nullable":true},"clientNotes":{"type":"string","nullable":true},"totalAmountCents":{"type":"integer","nullable":true},"depositAmountCents":{"type":"integer","nullable":true},"currency":{"type":"string","nullable":true},"customFields":{"type":"object","additionalProperties":true},"metadata":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"cancelledAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true},"noShowAt":{"type":"string","format":"date-time","nullable":true},"client":{"type":"object","additionalProperties":true,"nullable":true},"service":{"type":"object","additionalProperties":true,"nullable":true},"location":{"type":"object","additionalProperties":true,"nullable":true},"site":{"type":"object","additionalProperties":true,"nullable":true},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifiant d'adhésion."},"displayName":{"type":"string","nullable":true},"avatar":{"type":"string","nullable":true},"color":{"type":"string","nullable":true}},"required":["id","displayName","avatar","color"]}},"participants":{"type":"array","items":{"type":"object","additionalProperties":true}},"report":{"type":"object","additionalProperties":true,"description":"`{ id, status, signedAt, validatedAt }` de la fiche d'intervention, le cas échéant.","nullable":true},"photos":{"type":"array","items":{"type":"object","additionalProperties":true}},"activeContract":{"type":"object","additionalProperties":true,"nullable":true}}},"AppointmentListItem":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["APPOINTMENT","UNAVAILABILITY"]},"title":{"type":"string","nullable":true},"startAt":{"type":"string","format":"date-time"},"endAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["PENDING","AWAITING_CLIENT","CONFIRMED","IN_PROGRESS","COMPLETED","CANCELLED","NO_SHOW","RESCHEDULED","EXPIRED","REQUEST_REJECTED","OFFER_DECLINED"]},"clientId":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"serviceId":{"type":"string","nullable":true},"locationId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true},"source":{"type":"string"},"internalNotes":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":true},"client":{"type":"object","additionalProperties":true,"description":"`{ id, fullName, email }`","nullable":true},"service":{"type":"object","additionalProperties":true,"description":"`{ id, name, color, imageUrl }`","nullable":true},"location":{"type":"object","additionalProperties":true,"description":"`{ id, name, city }`","nullable":true},"participants":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"Client":{"type":"object","description":"Une personne (`kind=INDIVIDUAL`) ou une entreprise (`kind=COMPANY`) du carnet d'adresses. Les éléments de liste ajoutent des compteurs (`apptCount`, `nextApptAt`, `siteCount`, `contactCount`, `primaryContact`…) ; `view=list` renvoie une ligne plus courte.","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["INDIVIDUAL","COMPANY"]},"fullName":{"type":"string"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"companyId":{"type":"string","description":"L'entreprise à laquelle appartient une personne.","nullable":true},"types":{"type":"array","items":{"type":"string","enum":["CLIENT","SUPPLIER","PARTNER"]}},"specialties":{"type":"array","items":{"type":"string"}},"trade":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"siret":{"type":"string","nullable":true},"vatNumber":{"type":"string","nullable":true},"address":{"type":"object","additionalProperties":true,"nullable":true},"language":{"type":"string","nullable":true},"birthdate":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","additionalProperties":true,"description":"Indexé par la `key` du champ personnalisé (voir `GET /fields`)."},"noShowCount":{"type":"integer"},"cancellationCount":{"type":"integer"},"ltvCents":{"type":"integer"},"lastVisitAt":{"type":"string","format":"date-time","nullable":true},"mergedIntoId":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"contactCount":{"type":"integer"},"siteCount":{"type":"integer"},"primaryContact":{"type":"object","additionalProperties":true,"nullable":true}}},"ClientDetail":{"allOf":[{"$ref":"#/components/schemas/Client"},{"type":"object","properties":{"contactCount":{"type":"integer","description":"Personnes rattachées à ce compte (0 s'il n'y en a pas)."},"siteCount":{"type":"integer","description":"Sites dont le compte est propriétaire (0 s'il n'y en a pas)."},"primaryContact":{"type":"object","description":"La personne principale, sinon la première ; `null` lorsque le compte n'en a aucune.","properties":{"id":{"type":"string"},"fullName":{"type":"string"},"email":{"type":"string","nullable":true},"phone":{"type":"string","description":"Téléphone, sinon mobile.","nullable":true},"role":{"type":"string","nullable":true}},"required":["id","fullName","email","phone","role"],"nullable":true}},"required":["contactCount","siteCount","primaryContact"]}]},"Contact":{"type":"object","description":"Une personne rattachée à un compte (une entreprise ou un foyer).","properties":{"id":{"type":"string"},"clientId":{"type":"string"},"clientName":{"type":"string"},"clientKind":{"type":"string"},"civility":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"fullName":{"type":"string"},"role":{"type":"string","nullable":true},"jobTitle":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"mobile":{"type":"string","nullable":true},"isPrimary":{"type":"boolean"},"notes":{"type":"string","nullable":true},"position":{"type":"integer","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Company":{"type":"object","description":"Un client `kind=COMPANY` dans la présentation entreprise : `name` au lieu de `fullName`, champs d'adresse à plat.","properties":{"id":{"type":"string"},"name":{"type":"string"},"types":{"type":"array","items":{"type":"string"}},"specialties":{"type":"array","items":{"type":"string"}},"trade":{"type":"string","nullable":true},"siret":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"address":{"type":"string","nullable":true},"postcode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"contactCount":{"type":"integer"},"listName":{"type":"string","nullable":true},"listSource":{"type":"string","nullable":true}}},"CompanyList":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["COMPANY","CONTACT"]},"companyId":{"type":"string","nullable":true},"companyName":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"exposedInBooking":{"type":"boolean"},"protocol":{"type":"string","nullable":true},"url":{"type":"string","description":"Masqué.","nullable":true},"hasCredentials":{"type":"boolean"},"columnMapping":{"type":"object","additionalProperties":true,"nullable":true},"refreshInterval":{"type":"string","nullable":true},"active":{"type":"boolean"},"nextRunAt":{"type":"string","format":"date-time","nullable":true},"lastRunAt":{"type":"string","format":"date-time","nullable":true},"lastStatus":{"type":"string","nullable":true},"lastError":{"type":"string","nullable":true},"lastRowCount":{"type":"integer","nullable":true},"memberCount":{"type":"integer"}}},"Site":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string","description":"Null lorsqu'une entreprise possède le site (`companyId` est alors renseigné).","nullable":true},"clientName":{"type":"string","nullable":true},"companyId":{"type":"string","nullable":true},"label":{"type":"string","nullable":true},"address":{"type":"string","description":"L'adresse composée sur une ligne."},"line1":{"type":"string"},"line2":{"type":"string","nullable":true},"postcode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"accessNotes":{"type":"string","nullable":true},"contactName":{"type":"string","nullable":true},"contactPhone":{"type":"string","nullable":true},"active":{"type":"boolean"},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"role":{"type":"string","example":"TENANT","nullable":true},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"isPrimary":{"type":"boolean"}}}},"client":{"type":"object","additionalProperties":true,"description":"Détail uniquement : `{ id, fullName, email, phone }`.","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Equipment":{"type":"object","description":"Les lignes de liste ajoutent `clientName`, `clientKind`, `siteLabel`, `docCount`, `photoCount`, `thumbUrl`.","properties":{"id":{"type":"string"},"label":{"type":"string"},"brand":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"serialNumber":{"type":"string","nullable":true},"roomLabel":{"type":"string","nullable":true},"power":{"type":"string","nullable":true},"category":{"type":"string","nullable":true},"installedAt":{"type":"string","format":"date-time","nullable":true},"warrantyEndAt":{"type":"string","format":"date-time","nullable":true},"lastMaintenanceAt":{"type":"string","format":"date-time","nullable":true},"nextMaintenanceDueAt":{"type":"string","format":"date-time","nullable":true},"notes":{"type":"string","nullable":true},"active":{"type":"boolean"},"clientId":{"type":"string"},"siteId":{"type":"string","nullable":true},"photos":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"Contract":{"type":"object","description":"Les lignes de liste ajoutent `clientName`, `clientKind`, `coverageCount`.","properties":{"id":{"type":"string"},"reference":{"type":"string"},"planId":{"type":"string","nullable":true},"planName":{"type":"string","nullable":true},"clientId":{"type":"string"},"contactId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true},"status":{"type":"string"},"totalAnnualCents":{"type":"integer"},"currency":{"type":"string"},"vatRate":{"type":"number","nullable":true},"startsOn":{"type":"string"},"endsOn":{"type":"string","nullable":true},"cadenceMonths":{"type":"integer"},"autoRenew":{"type":"boolean"},"noticeDays":{"type":"integer"},"billingDay":{"type":"integer","nullable":true},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Service":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"categoryId":{"type":"string","nullable":true},"durationMinutes":{"type":"integer"},"bufferBeforeMin":{"type":"integer"},"bufferAfterMin":{"type":"integer"},"price":{"type":"string","description":"Euros, renvoyés sous forme de chaîne décimale exactement telle que stockée (`\"45\"`, `\"45.5\"`) — convertissez-la, ne la comparez pas comme du texte. Dans un corps de requête, envoyez un nombre (`45.5`). `null` : aucun prix défini.","example":"45.5","nullable":true},"currency":{"type":"string","nullable":true},"vatRate":{"type":"string","nullable":true},"atClientSite":{"type":"boolean"},"bookableOnline":{"type":"boolean"},"color":{"type":"string","nullable":true},"imageUrl":{"type":"string","nullable":true},"position":{"type":"integer"},"active":{"type":"boolean"},"customFields":{"type":"object","additionalProperties":true},"category":{"type":"object","additionalProperties":true,"nullable":true},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ServiceVariant"}},"locationIds":{"type":"array","items":{"type":"string"}}}},"ServiceVariant":{"type":"object","properties":{"id":{"type":"string"},"serviceId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"durationMinutes":{"type":"integer"},"price":{"type":"string","description":"Euros, renvoyés sous forme de chaîne décimale ; envoyés sous forme de nombre. `null` : le prix de la prestation s'applique.","nullable":true},"position":{"type":"integer"},"active":{"type":"boolean"}}},"ServiceCategory":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string","nullable":true},"position":{"type":"integer"},"active":{"type":"boolean"}}},"Location":{"type":"object","description":"Un lieu où se déroulent les prestations : une adresse, une zone ou en ligne.","properties":{"id":{"type":"string"},"name":{"type":"string"},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"postcode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"region":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"meetingUrl":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"active":{"type":"boolean"}}},"Member":{"type":"object","properties":{"id":{"type":"string","description":"Identifiant d'adhésion — ce que référence `memberIds` d'un rendez-vous."},"userId":{"type":"string"},"name":{"type":"string","nullable":true},"displayName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"role":{"type":"string","enum":["OWNER","ADMIN","MANAGER","COLLABORATOR","VIEWER","ARTISAN"]},"status":{"type":"string"},"color":{"type":"string","nullable":true},"baseAddress":{"type":"object","additionalProperties":true,"nullable":true},"deletedAt":{"type":"string","format":"date-time","nullable":true}}},"Quote":{"type":"object","properties":{"id":{"type":"string"},"reference":{"type":"string"},"status":{"type":"string","enum":["DRAFT","SENT","VIEWED","ACCEPTED","REJECTED","EXPIRED","CANCELLED"]},"clientId":{"type":"string","nullable":true},"clientName":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true},"appointmentId":{"type":"string","nullable":true},"interventionReportId":{"type":"string","nullable":true},"projectId":{"type":"string","nullable":true},"subtotalCents":{"type":"integer"},"vatCents":{"type":"integer"},"totalCents":{"type":"integer"},"currency":{"type":"string"},"validUntil":{"type":"string","format":"date-time","nullable":true},"sentAt":{"type":"string","format":"date-time","nullable":true},"acceptedAt":{"type":"string","format":"date-time","nullable":true},"rejectedAt":{"type":"string","format":"date-time","nullable":true},"notes":{"type":"string","nullable":true},"legalMentions":{"type":"string","nullable":true},"customFields":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"},"invoice":{"type":"object","additionalProperties":true,"description":"`{ id, reference, status }` de la facture qui en est issue.","nullable":true}}},"QuoteLine":{"type":"object","properties":{"id":{"type":"string"},"serviceId":{"type":"string","nullable":true},"description":{"type":"string"},"quantity":{"type":"number"},"unit":{"type":"string"},"unitPriceCents":{"type":"integer"},"discountPercent":{"type":"number","nullable":true},"vatRate":{"type":"number","nullable":true},"position":{"type":"integer"},"catalogRef":{"type":"string","description":"`__section__` / `__text__` pour les titres et le texte libre.","nullable":true},"purchasePriceCents":{"type":"integer","nullable":true},"marginPercent":{"type":"number","nullable":true},"ecotaxeCents":{"type":"integer","nullable":true}}},"Invoice":{"type":"object","properties":{"id":{"type":"string"},"reference":{"type":"string","description":"Attribuée à l'émission de la facture.","nullable":true},"status":{"type":"string","enum":["DRAFT","ISSUED","SENT","PAID","PARTIAL","OVERDUE","CANCELLED","REFUNDED"]},"clientId":{"type":"string","nullable":true},"clientName":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true},"appointmentId":{"type":"string","nullable":true},"quoteId":{"type":"string","nullable":true},"contractId":{"type":"string","nullable":true},"interventionReportId":{"type":"string","nullable":true},"projectId":{"type":"string","nullable":true},"subtotalCents":{"type":"integer"},"vatCents":{"type":"integer"},"totalCents":{"type":"integer"},"paidCents":{"type":"integer"},"vatBreakdown":{"type":"object","additionalProperties":true,"nullable":true},"currency":{"type":"string"},"issuedAt":{"type":"string","format":"date-time","nullable":true},"dueAt":{"type":"string","format":"date-time","nullable":true},"paidAt":{"type":"string","format":"date-time","nullable":true},"notes":{"type":"string","nullable":true},"legalMentions":{"type":"string","nullable":true},"customFields":{"type":"object","additionalProperties":true},"einvoice":{"type":"object","additionalProperties":true,"nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"InvoiceLine":{"type":"object","properties":{"id":{"type":"string"},"serviceId":{"type":"string","nullable":true},"description":{"type":"string"},"quantity":{"type":"number"},"unit":{"type":"string"},"unitPriceCents":{"type":"integer"},"discountPercent":{"type":"number","nullable":true},"vatRate":{"type":"number","nullable":true},"position":{"type":"integer"}}},"InterventionReport":{"type":"object","properties":{"id":{"type":"string"},"reference":{"type":"string","example":"FI-2026-0042"},"status":{"type":"string","enum":["DRAFT","PLANNED","IN_PROGRESS","COMPLETED","SIGNED","VALIDATED","INVOICED","ARCHIVED"]},"clientId":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"technicianId":{"type":"string","nullable":true},"equipmentIds":{"type":"array","items":{"type":"string"}},"appointmentId":{"type":"string","nullable":true},"projectId":{"type":"string","nullable":true},"workSummary":{"type":"string","nullable":true},"recommendations":{"type":"string","nullable":true},"arrivedAt":{"type":"string","format":"date-time","nullable":true},"departedAt":{"type":"string","format":"date-time","nullable":true},"signedAt":{"type":"string","format":"date-time","nullable":true},"validatedAt":{"type":"string","format":"date-time","nullable":true},"invoicedAt":{"type":"string","format":"date-time","nullable":true},"siteAddress":{"type":"string","nullable":true},"customFields":{"type":"object","additionalProperties":true},"photos":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"createdVia":{"type":"string","nullable":true}}},"InterventionTemplate":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","nullable":true},"name":{"type":"string"},"kind":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"sections":{"type":"array","items":{"type":"object","additionalProperties":true}},"validations":{"type":"array","items":{"type":"object","additionalProperties":true}},"requiredPhotoTypes":{"type":"array","items":{"type":"string"}},"serviceIds":{"type":"array","items":{"type":"string"}},"requiresClientSignature":{"type":"boolean"},"requiresTechnicianSignature":{"type":"boolean"},"version":{"type":"integer"},"isPreset":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}},"Document":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"filename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"integer"},"url":{"type":"string","description":"Une URL de téléchargement signée ; actualisez-la à son expiration."},"createdAt":{"type":"string","format":"date-time"}}},"Project":{"type":"object","properties":{"id":{"type":"string"},"reference":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","description":"Texte libre ; l'application utilise DRAFT, PENDING_RESPONSE, PLANNED, IN_PROGRESS, ON_HOLD, DONE, INVOICED, CLOSED, CANCELLED."},"kind":{"type":"string","enum":["CHANTIER","INTERVENTION","AUTRE"]},"collaborative":{"type":"boolean"},"clientId":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true},"leadMemberId":{"type":"string","nullable":true},"partyRoles":{"type":"array","items":{"type":"string"}},"startsOn":{"type":"string","nullable":true},"endsOn":{"type":"string","nullable":true},"budgetPlannedCents":{"type":"integer","nullable":true},"memberIds":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"}},"notes":{"type":"string","nullable":true},"address":{"type":"object","additionalProperties":true,"nullable":true},"customFields":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"}}},"ProjectListItem":{"type":"object","properties":{"id":{"type":"string"},"reference":{"type":"string"},"name":{"type":"string","nullable":true},"clientId":{"type":"string","nullable":true},"clientName":{"type":"string","nullable":true},"kind":{"type":"string"},"status":{"type":"string"},"city":{"type":"string","nullable":true},"budgetPlannedCents":{"type":"integer","nullable":true},"partners":{"type":"array","items":{"type":"object","additionalProperties":true}},"memberIds":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"}},"crewIds":{"type":"array","items":{"type":"string"}},"phaseCount":{"type":"integer"},"phasesDone":{"type":"integer"},"endsOn":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"Task":{"type":"object","properties":{"id":{"type":"string","example":"tsk_…"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","DONE","CANCELLED"]},"priority":{"type":"string","enum":["LOW","NORMAL","HIGH","URGENT"]},"dueAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true},"assigneeId":{"type":"string","nullable":true},"references":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"id":{"type":"string"},"label":{"type":"string","nullable":true}},"required":["type","id","label"]}},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","additionalProperties":true},"links":{"type":"array","items":{"type":"object","additionalProperties":true}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CustomField":{"type":"object","properties":{"id":{"type":"string"},"scope":{"type":"string"},"key":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"type":"string","enum":["TEXT","LONGTEXT","EMAIL","PHONE","URL","NUMBER","DATE","CHECKBOX","SELECT","MULTI_SELECT","TAG","ADDRESS"]},"config":{"type":"object","additionalProperties":true,"nullable":true},"group":{"type":"string","nullable":true},"position":{"type":"integer"},"required":{"type":"boolean"},"helpText":{"type":"string","nullable":true},"active":{"type":"boolean"}}},"ExternalRef":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"entityType":{"type":"string"},"localId":{"type":"string","description":"L'identifiant IWANA."},"externalId":{"type":"string","description":"Votre identifiant."},"shopId":{"type":"string","nullable":true},"payloadHash":{"type":"string","nullable":true},"lastSyncedAt":{"type":"string","format":"date-time","nullable":true}}},"CalendarBootstrap":{"type":"object","properties":{"range":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}},"required":["from","to"]},"closedDays":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"label":{"type":"string"},"kind":{"type":"string","enum":["holiday","closure"]}},"required":["date","label","kind"]}},"holds":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"memberId":{"type":"string","description":"Identifiant d'adhésion."},"serviceId":{"type":"string","nullable":true},"startAt":{"type":"string","format":"date-time"},"endAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"contactName":{"type":"string","nullable":true}},"required":["id","memberId","serviceId","startAt","endAt","expiresAt","contactName"]},"description":"Créneaux qu'un client est en train de réserver en ligne. Ce ne sont pas des rendez-vous : ils expirent d'eux-mêmes."},"appointments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["APPOINTMENT","UNAVAILABILITY"]},"title":{"type":"string","nullable":true},"startAt":{"type":"string","format":"date-time"},"endAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["PENDING","AWAITING_CLIENT","CONFIRMED","IN_PROGRESS","COMPLETED","CANCELLED","NO_SHOW","RESCHEDULED","EXPIRED","REQUEST_REJECTED","OFFER_DECLINED"]},"clientId":{"type":"string","nullable":true},"serviceId":{"type":"string","nullable":true},"locationId":{"type":"string","nullable":true},"internalNotes":{"type":"string","nullable":true},"projectId":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":true},"memberIds":{"type":"array","items":{"type":"string"},"description":"Identifiants d'adhésion de l'équipe."}},"required":["id","type","title","startAt","endAt","status","clientId","serviceId","locationId","internalNotes","projectId","metadata","memberIds"]},"description":"Tout ce qui chevauche la fenêtre, créneaux bloqués (`UNAVAILABILITY`) compris."},"interventions":{"type":"object","description":"Identifiant du rendez-vous → identifiant de sa fiche d'intervention, pour les rendez-vous de la fenêtre qui en ont une.","additionalProperties":{"type":"string"}},"clients":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fullName":{"type":"string"},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"address":{"type":"object","additionalProperties":true,"nullable":true},"tags":{"type":"array","items":{"type":"string"}}},"required":["id","fullName","email","phone","address","tags"]},"description":"Les clients des rendez-vous de la fenêtre et les 500 plus récents, chacun une seule fois."},"services":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string","nullable":true},"durationMinutes":{"type":"integer"},"atClientSite":{"type":"boolean"},"active":{"type":"boolean"},"calendarCode":{"type":"string","description":"Le nom interne court affiché par le calendrier.","nullable":true}},"required":["id","name","color","durationMinutes","atClientSite","active","calendarCode"]}},"locations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["FIXED","ZONE","VIRTUAL","PHONE"]},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"postcode":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"meetingUrl":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true}},"required":["id","name","kind","line1","line2","city","postcode","lat","lng","meetingUrl","phone"]}},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifiant d'adhésion."},"displayName":{"type":"string"},"color":{"type":"string","nullable":true},"avatarUrl":{"type":"string","nullable":true}},"required":["id","displayName","color","avatarUrl"]},"description":"Membres actifs, entreprises partenaires exclues."},"customStatuses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}},"required":["id","label","color"]},"description":"Les statuts de rendez-vous propres à l'espace, s'il en a défini."},"format":{"type":"object","properties":{"locale":{"type":"string"},"timezone":{"type":"string"}},"required":["locale","timezone"]},"currentMembershipId":{"type":"string","description":"L'adhésion de l'appelant ; `null` pour une clé API.","nullable":true},"userId":{"type":"string","description":"L'utilisateur appelant ; absent pour une clé API."}},"required":["range","closedDays","holds","appointments","interventions","clients","services","locations","members","customStatuses","format","currentMembershipId"]},"Promotion":{"type":"object","description":"À la création, `validFrom`, `validTo` et `maxRedemptions` sont absents : l'API ne les définit jamais.","properties":{"id":{"type":"string"},"code":{"type":"string","description":"Le code de réduction, le cas échéant.","nullable":true},"name":{"type":"string"},"description":{"type":"string","nullable":true},"type":{"type":"string","enum":["PERCENT","FIXED_AMOUNT","PACKAGE","LOYALTY"]},"amountCents":{"type":"integer","nullable":true},"percent":{"type":"integer","nullable":true},"serviceIds":{"type":"array","items":{"type":"string"}},"validFrom":{"type":"string","format":"date-time","nullable":true},"validTo":{"type":"string","format":"date-time","nullable":true},"maxRedemptions":{"type":"integer","nullable":true},"redeemed":{"type":"integer"},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"ContractTemplate":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"cadenceMonths":{"type":"integer"},"noticeDays":{"type":"integer"},"autoRenew":{"type":"boolean"},"totalAnnualCents":{"type":"integer","nullable":true},"currency":{"type":"string"},"vatRate":{"type":"string","description":"Une chaîne décimale, par ex. `\"20\"`.","nullable":true},"serviceIds":{"type":"array","items":{"type":"string"}},"bodyMarkdown":{"type":"string","nullable":true},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"VstItem":{"type":"object","description":"Un article fournisseur. Les prix sont en euros hors taxes, sous forme de nombres.","properties":{"id":{"type":"string"},"vendor":{"type":"string","enum":["vst","ims","covap"]},"editable":{"type":"boolean","description":"`false` sur un article de la plateforme : ses coefficients ne peuvent pas être modifiés (`403 vst_item_read_only`)."},"codeArticle":{"type":"string"},"designation":{"type":"string"},"prixBaseHt":{"type":"number","nullable":true},"prixAdherentHt":{"type":"number","nullable":true},"prixVenteHt":{"type":"number","nullable":true},"ecotaxeHt":{"type":"number","nullable":true},"activiteLabel":{"type":"string","nullable":true},"familleLabel":{"type":"string","nullable":true},"sousFamilleLabel":{"type":"string","nullable":true},"chapitreLabel":{"type":"string","nullable":true},"activiteCode":{"type":"string","nullable":true},"familleCode":{"type":"string","nullable":true},"sousFamilleCode":{"type":"string","nullable":true},"chapitreCode":{"type":"string","nullable":true},"classement1Code":{"type":"string","nullable":true},"classement2Code":{"type":"string","nullable":true},"classement3Code":{"type":"string","nullable":true},"classement4Code":{"type":"string","nullable":true},"classement5Code":{"type":"string","nullable":true},"classement1Label":{"type":"string","nullable":true},"classement2Label":{"type":"string","nullable":true},"classement3Label":{"type":"string","nullable":true},"classement4Label":{"type":"string","nullable":true},"classement5Label":{"type":"string","nullable":true},"refFournisseur":{"type":"string","nullable":true},"nomFournisseur":{"type":"string","nullable":true},"codeBarre":{"type":"string","nullable":true},"codeAlpha":{"type":"string","nullable":true},"unite":{"type":"string","nullable":true},"imageUrl":{"type":"string","nullable":true},"docUrl":{"type":"string","nullable":true},"articleDivers":{"type":"boolean"},"saisieCommentaire":{"type":"boolean"},"commentaireObligatoire":{"type":"boolean"},"dateImportTarif":{"type":"string","nullable":true},"multipleVente":{"type":"number","nullable":true},"uniteCalculateur":{"type":"string","nullable":true},"coeffUniteCalculateur":{"type":"number","nullable":true},"coeffAchat":{"type":"number"},"coeffRevente":{"type":"number"},"coeffVente":{"type":"number"}},"required":["id","vendor","editable","codeArticle","designation","prixBaseHt","prixAdherentHt","prixVenteHt","ecotaxeHt","activiteLabel","familleLabel","sousFamilleLabel","chapitreLabel","activiteCode","familleCode","sousFamilleCode","chapitreCode","classement1Code","classement2Code","classement3Code","classement4Code","classement5Code","classement1Label","classement2Label","classement3Label","classement4Label","classement5Label","refFournisseur","nomFournisseur","codeBarre","codeAlpha","unite","imageUrl","docUrl","articleDivers","saisieCommentaire","commentaireObligatoire","dateImportTarif","multipleVente","uniteCalculateur","coeffUniteCalculateur","coeffAchat","coeffRevente","coeffVente"]},"CommunicationsBootstrap":{"type":"object","properties":{"orgName":{"type":"string"},"logoUrl":{"type":"string","nullable":true},"locale":{"type":"string","nullable":true},"senderEmail":{"type":"string","nullable":true},"smsSenderId":{"type":"string","nullable":true},"canEdit":{"type":"boolean"},"history":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"channel":{"type":"string","enum":["EMAIL","SMS","WHATSAPP","PUSH","IN_APP","VOICE"]},"type":{"type":"string","description":"Quelle lettre c'était, par son nom dans Notifications."},"subject":{"type":"string","nullable":true},"excerpt":{"type":"string","description":"Le début du message en texte brut.","nullable":true},"toEmail":{"type":"string","nullable":true},"toPhone":{"type":"string","nullable":true},"status":{"type":"string","enum":["QUEUED","SENT","DELIVERED","OPENED","CLICKED","FAILED","BOUNCED","COMPLAINED","SUPPRESSED"]},"failureReason":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","channel","type","subject","excerpt","toEmail","toPhone","status","failureReason","createdAt"]},"description":"Les 50 derniers envois, du plus récent au plus ancien. Le message lui-même : `GET /communications/history/{id}`."},"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"channel":{"type":"string","enum":["EMAIL","SMS","WHATSAPP","PUSH","IN_APP","VOICE"]},"contextType":{"type":"string"},"status":{"type":"string","enum":["DRAFT","ACTIVE","ARCHIVED"]},"language":{"type":"string","nullable":true},"internalDescription":{"type":"string","nullable":true},"branding":{"type":"string","enum":["PLATFORM","CUSTOM"]},"locked":{"type":"boolean","description":"Texte propre à IWANA : lecture seule."},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","channel","contextType","status","language","internalDescription","branding","locked","updatedAt"]}},"campaigns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"channel":{"type":"string","enum":["EMAIL","SMS","WHATSAPP","PUSH","IN_APP","VOICE"]},"status":{"type":"string","enum":["DRAFT","SCHEDULED","RUNNING","COMPLETED","CANCELLED"]},"internalDescription":{"type":"string","nullable":true},"subject":{"type":"string","nullable":true},"body":{"type":"string"},"perRecipientDateField":{"type":"string","nullable":true},"recipientFilter":{"type":"object","additionalProperties":true,"nullable":true},"recipientIds":{"type":"array","items":{"type":"string"},"nullable":true},"scheduleKind":{"type":"string","enum":["IMMEDIATE","ONE_OFF","PER_RECIPIENT_DATE"]},"scheduledAt":{"type":"string","format":"date-time","nullable":true},"recipientKind":{"type":"string","enum":["ALL","MANUAL","FILTERED"]},"totalRecipients":{"type":"integer"},"sentCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","channel","status","internalDescription","subject","body","perRecipientDateField","recipientFilter","recipientIds","scheduleKind","scheduledAt","recipientKind","totalRecipients","sentCount","createdAt","updatedAt"]}}},"required":["orgName","logoUrl","locale","senderEmail","smsSenderId","canEdit","history","templates","campaigns"]},"NotificationSettings":{"type":"object","properties":{"canEdit":{"type":"boolean"},"brand":{"type":"object","properties":{"orgName":{"type":"string"},"name":{"type":"string","description":"Le nom d'expéditeur affiché par les lettres."},"logoUrl":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"mode":{"type":"string","enum":["custom","platform"]},"senderEmail":{"type":"string","nullable":true},"smsSenderId":{"type":"string","nullable":true}},"required":["orgName","name","logoUrl","color","mode","senderEmail","smsSenderId"]},"proNotes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"serviceId":{"type":"string","nullable":true},"enabled":{"type":"boolean"}},"required":["id","text","serviceId","enabled"]},"description":"Les notes ajoutées aux lettres de réservation, par prestation ou pour toutes (`serviceId: null`)."},"testRecipient":{"type":"object","properties":{"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true}},"required":["email","phone"],"description":"Destinataire par défaut d'un test : l'e-mail et le téléphone de l'utilisateur appelant, tous deux `null` pour une clé API."},"services":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"section":{"type":"string","enum":["booking","satisfaction","projects","custom"],"description":"`custom` : les lettres propres à l'espace."},"channel":{"type":"string","enum":["EMAIL","SMS"]},"enabled":{"type":"boolean"},"booking":{"type":"boolean","description":"Une lettre de réservation : les notes du pro s'y ajoutent."},"letter":{"type":"object","properties":{"source":{"type":"string","enum":["template","automation"]},"editable":{"type":"boolean"},"customized":{"type":"boolean"},"subject":{"type":"string"},"body":{"type":"string"},"variables":{"type":"array","items":{"type":"string"},"description":"Les `{{variables}}` que le texte peut utiliser."},"original":{"type":"object","description":"Le texte par défaut ; `null` s'il n'y en a pas.","properties":{"subject":{"type":"string"},"body":{"type":"string"}},"required":["subject","body"],"nullable":true}},"required":["source","editable","customized","subject","body","variables","original"]}},"required":["id","label","description","section","channel","enabled","booking","letter"]}}},"required":["canEdit","brand","proNotes","testRecipient","services","events"]},"DashboardStats":{"type":"object","properties":{"label":{"type":"string","description":"La période en toutes lettres, en français."},"currentRange":{"type":"string","enum":["7d","30d","90d","ytd","mtd","custom"]},"tiles":{"type":"object","properties":{"bookings":{"type":"object","description":"`delta` : variation par rapport à la période précédente de même durée, sous forme de ratio ; `null` lorsque cette période était vide.","properties":{"value":{"type":"integer"},"delta":{"type":"number","nullable":true}},"required":["value","delta"]},"revenue":{"type":"object","description":"Paiements réussis, en centimes.","properties":{"value":{"type":"integer"},"delta":{"type":"number","nullable":true}},"required":["value","delta"]},"completionRate":{"type":"number","description":"0–1."},"noShow":{"type":"integer"},"cancelled":{"type":"integer"},"noShowRate":{"type":"number","description":"0–1."},"clientsTotal":{"type":"integer"},"newClients":{"type":"integer"},"equipmentTotal":{"type":"integer"},"equipmentNew":{"type":"integer"},"activeContractCount":{"type":"integer"},"recurringRevenue":{"type":"integer","description":"Centimes annuels des contrats actifs."},"pendingQuoteCount":{"type":"integer"},"pendingQuoteAmount":{"type":"integer","description":"Centimes."},"outstandingInvoiceCount":{"type":"integer"},"overdueInvoiceCount":{"type":"integer"},"outstandingAmount":{"type":"integer","description":"Centimes restant dus."},"notifications":{"type":"integer","description":"Messages envoyés sur la période."}},"required":["bookings","revenue","completionRate","noShow","cancelled","noShowRate","clientsTotal","newClients","equipmentTotal","equipmentNew","activeContractCount","recurringRevenue","pendingQuoteCount","pendingQuoteAmount","outstandingInvoiceCount","overdueInvoiceCount","outstandingAmount","notifications"]},"revenueSeries":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]},"description":"Euros par jour — pas des centimes."},"bookingsSeries":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]}},"equipmentGrowthSeries":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]}},"clientGrowthSeries":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]}},"statusBreakdown":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Le statut."},"value":{"type":"integer"}},"required":["name","value"]}},"memberBreakdown":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifiant d'adhésion, ou `unassigned`."},"name":{"type":"string"},"image":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"value":{"type":"integer"}},"required":["id","name","image","color","value"]},"description":"Rendez-vous par membre ; une intervention avec deux membres compte pour les deux."},"sendSeries":{"type":"object","properties":{"email":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]}},"sms":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]}}},"required":["email","sms"]},"sendByType":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Le nom du modèle."},"points":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]}}},"required":["name","points"]}},"contractSeries":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date"},"value":{"type":"number"}},"required":["bucket","value"]},"description":"Contrats actifs chaque jour."},"contractStatusBreakdown":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Le statut."},"value":{"type":"integer"}},"required":["name","value"]}},"projectStatusBreakdown":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Le statut."},"value":{"type":"integer"}},"required":["name","value"]}},"quoteStatusBreakdown":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Le statut."},"value":{"type":"integer"}},"required":["name","value"]}},"invoiceStatusBreakdown":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Le statut."},"value":{"type":"integer"}},"required":["name","value"]}},"locationHeatmap":{"type":"array","items":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"},"value":{"type":"integer"},"label":{"type":"string"}},"required":["lat","lng","value","label"]}},"appointmentPins":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"lat":{"type":"number"},"lng":{"type":"number"},"title":{"type":"string"},"subtitle":{"type":"string","nullable":true},"at":{"type":"string","format":"date-time"},"status":{"type":"string"},"customStatusId":{"type":"string","nullable":true},"serviceId":{"type":"string","nullable":true},"memberIds":{"type":"array","items":{"type":"string"}}},"required":["id","lat","lng","title","subtitle","at","status","customStatusId","serviceId","memberIds"]},"description":"Une épingle par rendez-vous localisé, les 1 500 plus récents au maximum."},"customStatuses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"color":{"type":"string"}},"required":["id","label","color"]}},"hourHeatmap":{"type":"array","items":{"type":"object","properties":{"dow":{"type":"integer","description":"0 = dimanche."},"hour":{"type":"integer"},"value":{"type":"integer"}},"required":["dow","hour","value"]},"description":"Heure de début des rendez-vous ; les cases vides sont exclues."},"bookingActivity":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["created","modified","cancelled","confirmed"]},"dow":{"type":"integer","description":"0 = dimanche."},"hour":{"type":"integer"},"value":{"type":"integer"}},"required":["kind","dow","hour","value"]},"description":"Moment où les réservations ont été faites, modifiées ou annulées, dans le fuseau horaire de l'espace."},"bookedBy":{"type":"array","items":{"type":"object","properties":{"who":{"type":"string","enum":["online","dashboard","leo","sync"]},"value":{"type":"integer"}},"required":["who","value"]}},"serviceBreakdown":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifiant de prestation, ou `unknown`."},"name":{"type":"string"},"color":{"type":"string","nullable":true},"value":{"type":"integer"}},"required":["id","name","color","value"]}}},"required":["label","currentRange","tiles","revenueSeries","bookingsSeries","equipmentGrowthSeries","clientGrowthSeries","statusBreakdown","memberBreakdown","sendSeries","sendByType","contractSeries","contractStatusBreakdown","projectStatusBreakdown","quoteStatusBreakdown","invoiceStatusBreakdown","locationHeatmap","appointmentPins","customStatuses","hourHeatmap","bookingActivity","bookedBy","serviceBreakdown"]}}},"paths":{"/pro/activity-log":{"get":{"operationId":"getActivityLog","tags":["Statistiques"],"summary":"Le flux d'activité de l'espace, du plus récent au plus ancien","description":"Pagination par curseur : passez `nextBefore` comme `before`.","security":[{"apiKey":[]}],"parameters":[{"name":"before","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"1–200, 50 par défaut (hors de la plage : 400)."},{"name":"kinds","in":"query","required":false,"schema":{"type":"string"},"description":"Séparés par des virgules : `appointment`, `client`, `quote`, `invoice`, `automation`, `message`, `campaign`."},{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"string","format":"date-time"},"kind":{"type":"string"},"action":{"type":"string"},"label":{"type":"string"},"subject":{"type":"object","additionalProperties":true,"nullable":true},"actor":{"type":"object","additionalProperties":true},"channel":{"type":"string","nullable":true},"outcome":{"type":"string","nullable":true},"detail":{"type":"object","additionalProperties":true,"nullable":true}},"required":["id","at","kind","action","label","subject","actor","channel","outcome","detail"]}},"nextBefore":{"type":"string","format":"date-time","nullable":true}},"required":["items","nextBefore"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/appointments":{"get":{"operationId":"getAppointments","tags":["Rendez-vous"],"summary":"Lister les rendez-vous","description":"Les filtres se combinent. `from`/`to` bornent `startAt`. Sans `type`, seules les lignes `APPOINTMENT` (pas les créneaux bloqués) sont renvoyées. `clientId` correspond aussi aux rendez-vous où le contact est participant secondaire.","security":[{"apiKey":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["PENDING","AWAITING_CLIENT","CONFIRMED","IN_PROGRESS","COMPLETED","CANCELLED","NO_SHOW","RESCHEDULED","EXPIRED","REQUEST_REJECTED","OFFER_DECLINED"]}},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["APPOINTMENT","UNAVAILABILITY"]},"description":"`APPOINTMENT` (par défaut) ou `UNAVAILABILITY` (créneaux bloqués)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":200},"description":"200 par défaut ; une valeur supérieure est ramenée à 500."},{"name":"order","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"]},"description":"Par `startAt` ; `desc` par défaut."},{"name":"clientId","in":"query","required":false,"schema":{"type":"string"}},{"name":"siteId","in":"query","required":false,"schema":{"type":"string"},"description":"Tout ce qui s'est passé sur un site."},{"name":"companyId","in":"query","required":false,"schema":{"type":"string"},"description":"Une entreprise : les rendez-vous de ses personnes et de ses sites."},{"name":"projectId","in":"query","required":false,"schema":{"type":"string"},"description":"Les rendez-vous rattachés à un chantier."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"Texte libre sur le client, la prestation, le lieu et les notes."},{"name":"statuses","in":"query","required":false,"schema":{"type":"string"},"description":"Statuts séparés par des virgules."},{"name":"notStatuses","in":"query","required":false,"schema":{"type":"string"},"description":"Statuts à exclure, séparés par des virgules."},{"name":"serviceIds","in":"query","required":false,"schema":{"type":"string"},"description":"Identifiants de prestations séparés par des virgules."},{"name":"memberIds","in":"query","required":false,"schema":{"type":"string"},"description":"Identifiants d'adhésion séparés par des virgules : les interventions qui leur sont affectées."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 200 par défaut, 500 au maximum ; `limit` et `offset` sont renvoyés dans la réponse.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentListItem"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total","limit","offset"]},"example":{"items":[{"id":"a9s4d7f2g5h8j1k3l6z0x2c5","type":"APPOINTMENT","title":"Entretien chaudière","status":"CONFIRMED","source":"API","startAt":"2026-11-18T09:00:00.000Z","endAt":"2026-11-18T10:30:00.000Z","clientId":"k7m2x9q4w1e8r5t3y6u0i2o4","contactId":null,"serviceId":"s2d5f8g1h4j7k0l3z6x9c2v5","locationId":"l4k7j0h3g6f9d2s5a8q1w4e7","siteId":null,"internalNotes":"Prévoir le kit de joints.","metadata":{},"client":{"id":"k7m2x9q4w1e8r5t3y6u0i2o4","fullName":"Camille Durand","email":"camille.durand@example.com"},"service":{"id":"s2d5f8g1h4j7k0l3z6x9c2v5","name":"Entretien chaudière gaz","color":"#0E7C86","imageUrl":null},"location":{"id":"l4k7j0h3g6f9d2s5a8q1w4e7","name":"Atelier Nantes","city":"Nantes"},"participants":[]}],"total":1,"limit":200,"offset":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postAppointments","tags":["Rendez-vous"],"summary":"Créer ou modifier un rendez-vous","description":"Un seul point d'accès pour les deux : envoyez `id` pour modifier (un `id` inconnu renvoie une `404`, jamais une création), omettez-le pour créer. `startAt` et `endAt` sont toujours obligatoires, y compris pour une modification, avec `startAt` avant `endAt` (`400 invalid_range`). Une création exige `serviceId` pour un `APPOINTMENT` (`service_required`) et `title` pour une `UNAVAILABILITY` ; `clientId` est facultatif. Le statut vaut `PENDING` par défaut. Lors d'une modification, un champ omis conserve sa valeur stockée et un `null` explicite l'efface ; `memberIds`, `participants`, `metadata` et `customFields` remplacent la valeur stockée lorsqu'ils sont envoyés. Chaque identifiant référencé (client, prestation, site, lieu, chantier, membre) doit appartenir à l'espace (`400 <x>_not_found`). `source` est positionné à `API` sauf si vous en envoyez une. Émet `appointment.created`, `appointment.updated` ou `appointment.cancelled`. Pour ne changer que le statut, utilisez `PATCH /appointments/{id}/status`.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["APPOINTMENT","UNAVAILABILITY"]},"serviceId":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"startAt":{"type":"string","format":"date-time"},"endAt":{"type":"string","format":"date-time"},"clientId":{"type":"string","nullable":true},"contactId":{"type":"string","description":"Une personne du compte `clientId` (voir `/contacts`) ; la personne d'un autre compte renvoie une 400.","nullable":true},"participants":{"type":"array","items":{"type":"object","properties":{"clientId":{"type":"string","maxLength":64},"contactId":{"type":"string","maxLength":64,"nullable":true},"notify":{"type":"boolean","description":"Indique si ce participant reçoit les messages du rendez-vous ; `true` par défaut."},"isPrimary":{"type":"boolean","description":"Un seul participant principal au maximum ; si aucun n'est marqué, c'est le premier de la liste."}},"required":["clientId"],"additionalProperties":false},"maxItems":50,"description":"Tous les participants, le principal compris. Lorsqu'elle est envoyée, la liste fait foi : le participant principal devient `clientId` / `contactId` et les autres suivent la liste (`[]` retire le client). Omise : rien ne change."},"locationId":{"type":"string","nullable":true},"siteId":{"type":"string","description":"Le site enregistré où cela se déroule — renseigné en plus du texte de l'adresse, jamais à sa place.","nullable":true},"projectId":{"type":"string","description":"Le chantier auquel appartient ce rendez-vous ; `null` le détache.","nullable":true},"status":{"type":"string","enum":["PENDING","AWAITING_CLIENT","CONFIRMED","IN_PROGRESS","COMPLETED","CANCELLED","NO_SHOW","RESCHEDULED","EXPIRED","REQUEST_REJECTED","OFFER_DECLINED"]},"internalNotes":{"type":"string","nullable":true},"clientNotes":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":true},"memberIds":{"type":"array","items":{"type":"string"},"description":"L'équipe complète, en identifiants d'adhésion (voir `GET /team`) : remplace les membres affectés, `[]` les efface. Omise : l'équipe est conservée."},"customFields":{"type":"object","additionalProperties":true},"source":{"type":"string","enum":["DASHBOARD","WIDGET","MARKETPLACE","API","AI_ASSISTANT","WORKFLOW","IMPORT","PLUGIN"]}},"required":["startAt","endAt"],"additionalProperties":false},"example":{"title":"Entretien chaudière","startAt":"2026-11-18T09:00:00.000Z","endAt":"2026-11-18T10:30:00.000Z","serviceId":"s2d5f8g1h4j7k0l3z6x9c2v5","locationId":"l4k7j0h3g6f9d2s5a8q1w4e7","clientId":"k7m2x9q4w1e8r5t3y6u0i2o4","memberIds":["m1n4b7v0c3x6z9l2k5j8h1g4"],"status":"CONFIRMED","clientNotes":"Accès par le garage."}}}},"responses":{"201":{"description":"Créé ou modifié (201 dans les deux cas). Les colonnes propres du rendez-vous enregistré, identifiants compris (`clientId`, `serviceId`, `siteId`…) ; `members` uniquement si le corps contenait `memberIds`. Les objets joints `client`, `service`, `location`, `site`, `participants`, `report` et l'historique ne figurent que dans `GET /appointments/{id}`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"},"example":{"id":"a9s4d7f2g5h8j1k3l6z0x2c5","type":"APPOINTMENT","title":"Entretien chaudière","status":"CONFIRMED","source":"API","startAt":"2026-11-18T09:00:00.000Z","endAt":"2026-11-18T10:30:00.000Z","timezone":"Europe/Paris","clientId":"k7m2x9q4w1e8r5t3y6u0i2o4","contactId":null,"serviceId":"s2d5f8g1h4j7k0l3z6x9c2v5","locationId":"l4k7j0h3g6f9d2s5a8q1w4e7","siteId":null,"projectId":null,"clientNotes":"Accès par le garage.","internalNotes":"Prévoir le kit de joints.","metadata":{},"customFields":{},"client":{"id":"k7m2x9q4w1e8r5t3y6u0i2o4","fullName":"Camille Durand","email":"camille.durand@example.com","phone":"+33612345678"},"service":{"id":"s2d5f8g1h4j7k0l3z6x9c2v5","name":"Entretien chaudière gaz","color":"#0E7C86","imageUrl":null},"location":{"id":"l4k7j0h3g6f9d2s5a8q1w4e7","name":"Atelier Nantes","city":"Nantes"},"site":null,"members":[{"id":"m1n4b7v0c3x6z9l2k5j8h1g4","displayName":"Julien Martin","avatar":null,"color":"#F59E0B"}],"participants":[],"photos":[],"report":null,"activeContract":null,"totalAmountCents":12000,"depositAmountCents":null,"currency":"EUR","completedAt":null,"cancelledAt":null,"noShowAt":null,"createdAt":"2026-11-10T14:05:11.000Z","updatedAt":"2026-11-10T14:05:11.000Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/appointments/{id}":{"delete":{"operationId":"deleteAppointmentsId","tags":["Rendez-vous"],"summary":"Annuler et supprimer un rendez-vous","description":"Passe le statut à `CANCELLED` et supprime logiquement le rendez-vous (ainsi que les occurrences générées d'un rendez-vous récurrent) : ensuite `GET` répond `404` et les listes ne le renvoient plus. Émet `appointment.cancelled`. Pour garder visible un rendez-vous annulé, utilisez plutôt `PATCH …/status` avec `CANCELLED`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getAppointmentsId","tags":["Rendez-vous"],"summary":"Obtenir un rendez-vous, avec client, prestation, équipe, site et fiche","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"},"example":{"id":"a9s4d7f2g5h8j1k3l6z0x2c5","type":"APPOINTMENT","title":"Entretien chaudière","status":"CONFIRMED","source":"API","startAt":"2026-11-18T09:00:00.000Z","endAt":"2026-11-18T10:30:00.000Z","timezone":"Europe/Paris","clientId":"k7m2x9q4w1e8r5t3y6u0i2o4","contactId":null,"serviceId":"s2d5f8g1h4j7k0l3z6x9c2v5","locationId":"l4k7j0h3g6f9d2s5a8q1w4e7","siteId":null,"projectId":null,"clientNotes":"Accès par le garage.","internalNotes":"Prévoir le kit de joints.","metadata":{},"customFields":{},"client":{"id":"k7m2x9q4w1e8r5t3y6u0i2o4","fullName":"Camille Durand","email":"camille.durand@example.com","phone":"+33612345678"},"service":{"id":"s2d5f8g1h4j7k0l3z6x9c2v5","name":"Entretien chaudière gaz","color":"#0E7C86","imageUrl":null},"location":{"id":"l4k7j0h3g6f9d2s5a8q1w4e7","name":"Atelier Nantes","city":"Nantes"},"site":null,"members":[{"id":"m1n4b7v0c3x6z9l2k5j8h1g4","displayName":"Julien Martin","avatar":null,"color":"#F59E0B"}],"participants":[],"photos":[],"report":null,"activeContract":null,"totalAmountCents":12000,"depositAmountCents":null,"currency":"EUR","completedAt":null,"cancelledAt":null,"noShowAt":null,"createdAt":"2026-11-10T14:05:11.000Z","updatedAt":"2026-11-10T14:05:11.000Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/appointments/{id}/history":{"get":{"operationId":"getAppointmentsIdHistory","tags":["Rendez-vous"],"summary":"L'historique d'audit d'un rendez-vous, du plus récent au plus ancien","description":"Types d'événements : `modification` (changements de champs), `status`, `cancellation`, `notification` (messages envoyés), `creation`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["modification","status","cancellation","notification","creation"]},"at":{"type":"string","format":"date-time"}},"required":["kind","at"]}}},"required":["events"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/appointments/{id}/status":{"patch":{"operationId":"patchAppointmentsIdStatus","tags":["Rendez-vous"],"summary":"Modifier le statut d'un rendez-vous","description":"Seul le statut change (ainsi que son horodatage `cancelledAt` / `completedAt` / `noShowAt`). Émet `appointment.cancelled` pour `CANCELLED`, `appointment.updated` sinon ; rien lorsque le statut est inchangé.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["PENDING","AWAITING_CLIENT","CONFIRMED","IN_PROGRESS","COMPLETED","CANCELLED","NO_SHOW","RESCHEDULED","EXPIRED","REQUEST_REJECTED","OFFER_DECLINED"]}},"required":["status"],"additionalProperties":false},"example":{"status":"COMPLETED"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]},"example":{"ok":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/appointments/counts":{"get":{"operationId":"getAppointmentsCounts","tags":["Rendez-vous"],"summary":"Compteurs par onglet (tous, à venir, aujourd'hui, demandes, annulés, historique)","description":"Lignes `APPOINTMENT` uniquement. `today` utilise le fuseau horaire de l'espace ; `cancelled` compte CANCELLED, REQUEST_REJECTED et OFFER_DECLINED.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"all":{"type":"integer"},"upcoming":{"type":"integer"},"today":{"type":"integer"},"requests":{"type":"integer"},"requestsPending":{"type":"integer"},"cancelled":{"type":"integer"},"history":{"type":"integer"}},"required":["all","upcoming","today","requests","requestsPending","cancelled","history"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability":{"get":{"operationId":"getAvailability","tags":["Disponibilités"],"summary":"Jours de fermeture et jours fériés observés","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"orgCountry":{"type":"string","nullable":true},"closures":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"date":{"type":"string","format":"date"},"label":{"type":"string","nullable":true}},"required":["id","date","label"]}},"observed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"countryCode":{"type":"string"},"holidayId":{"type":"string"}},"required":["id","countryCode","holidayId"]}}},"required":["orgCountry","closures","observed"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/closures":{"post":{"operationId":"postAvailabilityClosures","tags":["Disponibilités"],"summary":"Ajouter un jour de fermeture","description":"Un seul par date : une fermeture existante à cette date est renvoyée telle quelle.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string"},"label":{"type":"string","minLength":1}},"required":["date","label"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"id":{"type":"string"}},"required":["ok","id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/closures/{id}":{"delete":{"operationId":"deleteAvailabilityClosuresId","tags":["Disponibilités"],"summary":"Supprimer un jour de fermeture","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/holiday-presets":{"get":{"operationId":"getAvailabilityHolidayPresets","tags":["Disponibilités"],"summary":"Jours fériés par pays, avec la prochaine date de chaque jour férié","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"presets":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["presets"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/observed-holidays":{"post":{"operationId":"postAvailabilityObservedHolidays","tags":["Disponibilités"],"summary":"Observer (ou ne plus observer) un jour férié","description":"Observer crée un créneau bloqué annuel sur la journée entière ; arrêter l'observation le supprime.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"countryCode":{"type":"string","minLength":2},"holidayId":{"type":"string","minLength":1},"observed":{"type":"boolean"}},"required":["countryCode","holidayId","observed"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/observed-holidays/bulk":{"post":{"operationId":"postAvailabilityObservedHolidaysBulk","tags":["Disponibilités"],"summary":"Ne plus observer tous les jours fériés d'un pays","description":"`observed: true` ne change rien.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"countryCode":{"type":"string","minLength":2},"observed":{"type":"boolean"}},"required":["countryCode","observed"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/resolve":{"get":{"operationId":"getAvailabilityResolve","tags":["Disponibilités"],"summary":"Plages de travail effectives par membre et par jour","description":"Plannings moins fermetures et jours fériés. Les rendez-vous existants ne sont PAS déduits : ce sont des horaires de travail, pas des créneaux libres. Une plage de plus de 62 jours (from et to inclus) renvoie `400 range_too_long` avec `details.maxDays` : découpez les périodes plus longues en plusieurs appels.","security":[{"apiKey":[]}],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"description":"`YYYY-MM-DD`."},{"name":"to","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"description":"`YYYY-MM-DD`, pas avant `from`."},{"name":"memberId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"memberId":{"type":"string"},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"type":"array","items":{"type":"object","additionalProperties":true}},"closed":{"type":"object","additionalProperties":true,"nullable":true}},"required":["date","windows","closed"]}}},"required":["memberId","days"]}},"closedDays":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["members","closedDays"]},"example":{"members":[{"memberId":"m1n4b7v0c3x6z9l2k5j8h1g4","days":[{"date":"2026-11-16","windows":[{"dow":1,"start":"08:00","end":"12:00"},{"dow":1,"start":"13:30","end":"17:30"}],"closed":null},{"date":"2026-11-17","windows":[],"closed":{"label":"Fermeture annuelle","kind":"closure"}}]}],"closedDays":[{"date":"2026-11-17","label":"Fermeture annuelle","kind":"closure"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/schedules":{"get":{"operationId":"getAvailabilitySchedules","tags":["Disponibilités"],"summary":"Lister les plannings (horaires par membre)","security":[{"apiKey":[]}],"parameters":[{"name":"memberId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postAvailabilitySchedules","tags":["Disponibilités"],"summary":"Créer ou modifier un planning","description":"Envoyez `id` pour modifier. Un planning qui chevauche un autre planning du même membre renvoie `409 schedule_conflict`, dont les `details` désignent le conflit.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"memberId":{"type":"string"},"name":{"type":"string","maxLength":200},"weeklyRules":{"type":"array","items":{"type":"object","properties":{"dow":{"type":"integer","minimum":0,"maximum":6},"start":{"type":"string","pattern":"^\\d{2}:\\d{2}$"},"end":{"type":"string","pattern":"^\\d{2}:\\d{2}$"},"gid":{"type":"string","maxLength":64},"zones":{"type":"array","items":{"type":"string"}},"services":{"type":"array","items":{"type":"string"}},"mode":{"type":"string","enum":["fixed","window"]},"step":{"type":"integer","minimum":5,"maximum":1440,"description":"Jusqu'à une journée complète : en mode plage d'arrivée, l'étape EST la plage, et une plage peut légitimement courir de 06:00 à 20:00 en une seule tranche."},"breakStart":{"type":"string","pattern":"^\\d{2}:\\d{2}$"},"breakEnd":{"type":"string","pattern":"^\\d{2}:\\d{2}$"},"limits":{"type":"object","properties":{"maxPerSlot":{"type":"integer","minimum":1,"maximum":24},"workloadMinutes":{"type":"integer","minimum":5,"maximum":720},"travelMinutes":{"type":"integer","minimum":0,"maximum":240},"distanceKm":{"type":"integer","minimum":0,"maximum":300}},"additionalProperties":false},"booking":{"type":"object","properties":{"leadMinutes":{"type":"integer","minimum":0,"maximum":4320},"horizonDays":{"type":"integer","minimum":1,"maximum":365},"cancelHours":{"type":"integer","minimum":0,"maximum":720},"breakAfterMinutes":{"type":"integer","minimum":0,"maximum":240},"breakBeyondHours":{"type":"boolean"},"bases":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","maxLength":300},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lng"],"additionalProperties":false}},"travelFromBase":{"type":"boolean"},"travelToBase":{"type":"boolean"},"anchorMinutes":{"type":"integer","minimum":5,"maximum":240},"travelOrigin":{"type":"string","enum":["member","office","none"]},"autoOrganise":{"type":"boolean"}},"additionalProperties":false}},"required":["dow","start","end"],"additionalProperties":false}},"startsOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"endsOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","nullable":true},"intervalWeeks":{"type":"integer","minimum":1,"maximum":4},"kind":{"type":"string","enum":["weekly","one_off","monthly_nth"]},"monthlyNth":{"type":"integer","minimum":1,"maximum":5}},"required":["memberId","name","weeklyRules","startsOn"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`schedule_conflict` : chevauche un autre planning du même membre ; `details` le désigne.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"schedule_conflict","message":"Schedule conflict","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/schedules/{id}":{"delete":{"operationId":"deleteAvailabilitySchedulesId","tags":["Disponibilités"],"summary":"Supprimer un planning","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/week-templates":{"get":{"operationId":"getAvailabilityWeekTemplates","tags":["Disponibilités"],"summary":"Lister les modèles de semaine","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"weeklyRules":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["id","name","weeklyRules"]}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postAvailabilityWeekTemplates","tags":["Disponibilités"],"summary":"Créer ou remplacer un modèle de semaine","description":"Création ou mise à jour par `id` ; les 50 plus récents sont conservés.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":120,"minLength":1},"weeklyRules":{"type":"array","items":{"type":"object","properties":{"dow":{"type":"integer","minimum":0,"maximum":6},"start":{"type":"string","pattern":"^\\d{2}:\\d{2}$"},"end":{"type":"string","pattern":"^\\d{2}:\\d{2}$"}},"required":["dow","start","end"],"additionalProperties":false}}},"required":["name","weeklyRules"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["id","items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/availability/week-templates/{id}":{"delete":{"operationId":"deleteAvailabilityWeekTemplatesId","tags":["Disponibilités"],"summary":"Supprimer un modèle de semaine","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["ok","items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/business-rules":{"get":{"operationId":"getBusinessRules","tags":["Configuration"],"summary":"Définitions des règles métier et leurs valeurs actuelles","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","additionalProperties":true}},"values":{"type":"object","additionalProperties":true}},"required":["rules","values"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postBusinessRules","tags":["Configuration"],"summary":"Définir une règle métier","description":"Corps `{ key, value }` ; une clé inconnue renvoie `400 unknown_rule`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"},"value":{}},"required":["key"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/business-rules/service-overrides":{"get":{"operationId":"getBusinessRulesServiceOverrides","tags":["Configuration"],"summary":"Surcharges de réservation par prestation et visibilité en ligne","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/business-rules/service-overrides/{serviceId}":{"patch":{"operationId":"patchBusinessRulesServiceOverridesServiceId","tags":["Configuration"],"summary":"Définir ou effacer une surcharge de réservation d'une prestation","security":[{"apiKey":[]}],"parameters":[{"name":"serviceId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","enum":["leadTimeMinutes","maxAdvanceDays","cancellationHours","breakAfterMinutes"]},"value":{"type":"integer","minimum":0,"nullable":true}},"required":["key"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/business-rules/service-visibility/{serviceId}":{"patch":{"operationId":"patchBusinessRulesServiceVisibilityServiceId","tags":["Configuration"],"summary":"Définir comment une prestation est proposée en ligne (réservable, sur demande, visible, masquée)","security":[{"apiKey":[]}],"parameters":[{"name":"serviceId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["bookable","request","visible","hidden"]},"message":{"type":"string","description":"Affiché aux clients lorsque le mode est « visible ».","nullable":true}},"required":["mode"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/calendar/bootstrap":{"get":{"operationId":"getCalendarBootstrap","tags":["Rendez-vous"],"summary":"Événements du calendrier et données de référence en un seul appel","description":"L'union de `/calendar/events` et `/calendar/refs`.","security":[{"apiKey":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarBootstrap"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/calendar/events":{"get":{"operationId":"getCalendarEvents","tags":["Rendez-vous"],"summary":"Fenêtre du calendrier : rendez-vous, créneaux bloqués, jours de fermeture et réservations en cours","description":"Tout ce qui chevauche `from`–`to` (−90 / +180 jours par défaut), lignes `UNAVAILABILITY` comprises. Le modèle de lecture derrière le calendrier du tableau de bord ; pour une synchronisation, préférez `GET /appointments`.","security":[{"apiKey":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"range":{"type":"object","additionalProperties":true},"closedDays":{"type":"array","items":{"type":"object","additionalProperties":true}},"holds":{"type":"array","items":{"type":"object","additionalProperties":true}},"appointments":{"type":"array","items":{"type":"object","additionalProperties":true}},"interventions":{"type":"object","additionalProperties":true},"clients":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/calendar/refs":{"get":{"operationId":"getCalendarRefs","tags":["Rendez-vous"],"summary":"Données de référence du calendrier : prestations, clients (500 plus récents), lieux, membres, statuts","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"object","additionalProperties":true}},"clients":{"type":"array","items":{"type":"object","additionalProperties":true}},"locations":{"type":"array","items":{"type":"object","additionalProperties":true}},"members":{"type":"array","items":{"type":"object","additionalProperties":true}},"customStatuses":{"type":"array","items":{"type":"object","additionalProperties":true}},"format":{"type":"object","additionalProperties":true}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/categories":{"get":{"operationId":"getCatalogCategories","tags":["Catalogue"],"summary":"Lister les catégories de prestations","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServiceCategory"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCatalogCategories","tags":["Catalogue"],"summary":"Créer ou modifier une catégorie","description":"Création ou mise à jour par `id` ; `name` est obligatoire.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string","nullable":true},"position":{"type":"integer"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceCategory"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/categories/{id}":{"delete":{"operationId":"deleteCatalogCategoriesId","tags":["Catalogue"],"summary":"Masquer une catégorie","description":"Ses prestations conservent leur `categoryId`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/contract-templates":{"get":{"operationId":"getCatalogContractTemplates","tags":["Catalogue"],"summary":"Lister les modèles de contrat","description":"Du plus récent au plus ancien ; les éléments supprimés sont exclus.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ContractTemplate"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCatalogContractTemplates","tags":["Catalogue"],"summary":"Créer ou modifier un modèle de contrat","description":"Création ou mise à jour par `id` ; les champs omis reviennent à leurs valeurs par défaut. `totalAnnualCents` est en centimes.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"cadenceMonths":{"type":"integer","minimum":1},"noticeDays":{"type":"integer","minimum":0},"autoRenew":{"type":"boolean"},"totalAnnualCents":{"type":"integer","minimum":0,"nullable":true},"currency":{"type":"string"},"vatRate":{"type":"number","nullable":true},"bodyMarkdown":{"type":"string","nullable":true},"active":{"type":"boolean"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractTemplate"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/contract-templates/{id}":{"delete":{"operationId":"deleteCatalogContractTemplatesId","tags":["Catalogue"],"summary":"Supprimer un modèle de contrat","description":"Les contrats existants sont conservés.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/library/services":{"get":{"operationId":"getCatalogLibraryServices","tags":["Catalogue"],"summary":"La bibliothèque de prestations partagée, depuis laquelle tout espace peut importer","description":"Les prix sont ici en centimes (`priceCents`).","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/library/services/{id}/import":{"post":{"operationId":"postCatalogLibraryServicesIdImport","tags":["Catalogue"],"summary":"Importer une prestation de la bibliothèque dans l'espace (créée inactive)","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/locations":{"get":{"operationId":"getCatalogLocations","tags":["Catalogue"],"summary":"Lister les lieux (adresses, zones, en ligne)","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Location"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCatalogLocations","tags":["Catalogue"],"summary":"Créer ou modifier un lieu","description":"Création ou mise à jour par `id` ; `name` est obligatoire. Lors d'une modification, les champs d'adresse omis sont effacés : envoyez le lieu complet.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["FIXED","ZONE","VIRTUAL","PHONE"]},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"postcode":{"type":"string","nullable":true},"region":{"type":"string","nullable":true},"country":{"type":"string"},"meetingUrl":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"active":{"type":"boolean"},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"postcodes":{"type":"array","items":{"type":"string"}},"cities":{"type":"array","items":{"type":"string"}},"polygon":{"type":"object","additionalProperties":true,"description":"FeatureCollection GeoJSON : polygones, cercles et anneaux.","nullable":true},"customFields":{"type":"object","additionalProperties":true,"nullable":true}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Location"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/locations/{id}":{"delete":{"operationId":"deleteCatalogLocationsId","tags":["Catalogue"],"summary":"Supprimer un lieu","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/promotions":{"get":{"operationId":"getCatalogPromotions","tags":["Catalogue"],"summary":"Lister les promotions et codes de réduction","description":"Du plus récent au plus ancien.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Promotion"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCatalogPromotions","tags":["Catalogue"],"summary":"Créer ou modifier une promotion","description":"Création ou mise à jour par `id`. `amountCents` est en centimes.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"code":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"type":{"type":"string","enum":["PERCENT","FIXED_AMOUNT","PACKAGE","LOYALTY"]},"percent":{"type":"integer","minimum":0,"nullable":true},"amountCents":{"type":"integer","minimum":0,"nullable":true},"active":{"type":"boolean"}},"required":["name","type"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Promotion"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/promotions/{id}":{"delete":{"operationId":"deleteCatalogPromotionsId","tags":["Catalogue"],"summary":"Supprimer une promotion","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/questionnaires":{"get":{"operationId":"getCatalogQuestionnaires","tags":["Catalogue"],"summary":"Lister les questionnaires avec leurs questions et prestations","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"kind":{"type":"string"},"active":{"type":"boolean"},"fieldCount":{"type":"integer"},"fields":{"type":"array","items":{"type":"object","additionalProperties":true}},"serviceIds":{"type":"array","items":{"type":"string"}}},"required":["id","name","description","kind","active","fieldCount","fields","serviceIds"]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCatalogQuestionnaires","tags":["Catalogue"],"summary":"Créer ou modifier un questionnaire","description":"Création ou mise à jour par `id`. Les `fields` omis conservent les questions ; les `serviceIds` omis conservent les liens, `[]` les supprime.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"kind":{"type":"string","enum":["INTAKE","SATISFACTION","TECHNICIAN_REPORT"]},"active":{"type":"boolean"},"fields":{"type":"array","items":{"type":"object","additionalProperties":true}},"serviceIds":{"type":"array","items":{"type":"string"}}},"required":["name","kind"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"La ligne de questionnaire telle qu'enregistrée. Ses liens vers les prestations ne sont pas renvoyés : lisez-les avec `GET /catalog/questionnaires`.","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"kind":{"type":"string","enum":["INTAKE","SATISFACTION","TECHNICIAN_REPORT"]},"schema":{"type":"object","additionalProperties":true,"description":"`{ fields: [...] }` : les questions."},"logic":{"description":"Les règles de branchement, telles que stockées (un tableau pour un questionnaire créé ici)."},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/questionnaires/{id}":{"delete":{"operationId":"deleteCatalogQuestionnairesId","tags":["Catalogue"],"summary":"Supprimer un questionnaire","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services":{"get":{"operationId":"getCatalogServices","tags":["Catalogue"],"summary":"Lister les prestations, avec catégorie, variantes et lieux","description":"Non paginé. Les prestations archivées sont exclues (voir `/catalog/services/archived`).","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCatalogServices","tags":["Catalogue"],"summary":"Créer ou modifier une prestation","description":"Création ou mise à jour par `id`. `name` et `durationMinutes` sont obligatoires ; `price` est en euros. Lors d'une modification, envoyez la prestation complète : `description`, `price`, `categoryId`, `color` omis sont effacés. `locationIds`, lorsqu'il est envoyé, remplace les liens.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"durationMinutes":{"type":"integer","minimum":1},"price":{"type":"number","description":"Euros, sous forme de nombre (`45.5`) ; `null` si pas de prix. Relu sous forme de chaîne décimale.","nullable":true},"currency":{"type":"string","nullable":true},"categoryId":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"imageUrl":{"type":"string","description":"Data URL ou URL stockée — la liste du catalogue l'affiche en miniature.","nullable":true},"pricing":{"type":"object","additionalProperties":true,"description":"Tarification avancée (base, TTC ou non, prix unitaire, majoration de distance), stockée telle qu'envoyée dans `customFields.pricing`.","nullable":true},"calendarCode":{"type":"string","maxLength":60,"description":"Un nom interne court affiché dans le calendrier à la place du nom de la prestation ; jamais montré aux clients. Omis : conservé ; `null` ou vide : effacé.","nullable":true},"clientInstructions":{"type":"string","maxLength":1000,"description":"Ce que le client doit apporter ou préparer, inclus dans les messages de réservation. Omis : conservé ; `null` ou vide : effacé.","nullable":true},"active":{"type":"boolean"},"atClientSite":{"type":"boolean"},"locationIds":{"type":"array","items":{"type":"string"},"description":"Les lieux où la prestation est proposée. Omis : les liens sont conservés ; `[]` les efface."}},"required":["name","durationMinutes"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services/{id}":{"delete":{"operationId":"deleteCatalogServicesId","tags":["Catalogue"],"summary":"Archiver une prestation","description":"Restaurable avec `POST /catalog/services/{id}/restore`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services/{id}/calendar-code":{"patch":{"operationId":"patchCatalogServicesIdCalendarCode","tags":["Catalogue"],"summary":"Définir ou effacer le nom court d'une prestation dans le calendrier (24 caractères)","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"calendarCode":{"type":"string","maxLength":60,"nullable":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"calendarCode":{"type":"string","nullable":true}},"required":["id","calendarCode"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services/{id}/duplicate":{"post":{"operationId":"postCatalogServicesIdDuplicate","tags":["Catalogue"],"summary":"Copier une prestation avec ses variantes, son personnel et ses lieux","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services/{id}/restore":{"post":{"operationId":"postCatalogServicesIdRestore","tags":["Catalogue"],"summary":"Restaurer une prestation archivée (elle revient inactive)","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services/{id}/variants":{"get":{"operationId":"getCatalogServicesIdVariants","tags":["Catalogue"],"summary":"Lister les variantes (formules) d'une prestation","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServiceVariant"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCatalogServicesIdVariants","tags":["Catalogue"],"summary":"Créer ou modifier une variante","description":"Création ou mise à jour par `id` ; `name` et `durationMinutes` sont obligatoires. Lors d'une modification, envoyez la variante complète.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"durationMinutes":{"type":"integer","minimum":1},"price":{"type":"number","description":"Euros, sous forme de nombre ; `null` : le prix de la prestation s'applique. Relu sous forme de chaîne décimale.","nullable":true},"position":{"type":"integer"},"pricing":{"type":"object","additionalProperties":true,"description":"Tarification avancée de cette variante — même structure que le `pricing` de la prestation.","nullable":true},"active":{"type":"boolean"}},"required":["name","durationMinutes"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceVariant"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services/archived":{"get":{"operationId":"getCatalogServicesArchived","tags":["Catalogue"],"summary":"Lister les prestations archivées","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/services/reorder":{"post":{"operationId":"postCatalogServicesReorder","tags":["Catalogue"],"summary":"Enregistrer l'ordre des prestations","description":"Corps `{ ids: [...] }` ; chaque index devient la position.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"description":"Identifiants de prestations dans leur nouvel ordre — l'index devient `position`."}},"required":["ids"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/specialties":{"get":{"operationId":"getCatalogSpecialties","tags":["Catalogue"],"summary":"Qui peut réaliser chaque prestation : membres, équipes, taille d'équipe, équipes nommées","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"serviceId":{"type":"string"},"serviceName":{"type":"string"},"memberIds":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"}},"crewSize":{"type":"integer"},"crews":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"memberIds":{"type":"array","items":{"type":"string"}}},"required":["id","name","memberIds"]}}},"required":["serviceId","serviceName","memberIds","teamIds","crewSize","crews"]}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/specialties/{serviceId}":{"post":{"operationId":"postCatalogSpecialtiesServiceId","tags":["Catalogue"],"summary":"Définir qui peut réaliser une prestation","description":"`memberIds` remplace la liste ; `teamIds` et `crewSize` ne changent que s'ils sont envoyés.","security":[{"apiKey":[]}],"parameters":[{"name":"serviceId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"memberIds":{"type":"array","items":{"type":"string"}},"teamIds":{"type":"array","items":{"type":"string"},"description":"Équipes de l'espace qualifiées pour la prestation."},"crewSize":{"type":"integer","minimum":1,"maximum":50,"description":"Nombre de personnes avec lesquelles l'intervention est réalisée."}},"required":["memberIds"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/specialties/{serviceId}/crews":{"post":{"operationId":"postCatalogSpecialtiesServiceIdCrews","tags":["Catalogue"],"summary":"Créer ou renommer une équipe nommée pour une prestation","security":[{"apiKey":[]}],"parameters":[{"name":"serviceId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Omis : crée ; présent : renomme/recompose."},"name":{"type":"string"},"memberIds":{"type":"array","items":{"type":"string"}}},"required":["name","memberIds"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/specialties/{serviceId}/crews/{crewId}":{"delete":{"operationId":"deleteCatalogSpecialtiesServiceIdCrewsCrewId","tags":["Catalogue"],"summary":"Supprimer une équipe","security":[{"apiKey":[]}],"parameters":[{"name":"serviceId","in":"path","required":true,"schema":{"type":"string"}},{"name":"crewId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/catalog/variants/{variantId}":{"delete":{"operationId":"deleteCatalogVariantsVariantId","tags":["Catalogue"],"summary":"Supprimer une variante","security":[{"apiKey":[]}],"parameters":[{"name":"variantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients":{"get":{"operationId":"getClients","tags":["Contacts"],"summary":"Lister les clients (personnes, entreprises, ou les deux)","description":"Sans `kind`, seules les personnes (`INDIVIDUAL`) sont renvoyées — envoyez `kind=ALL` pour les personnes et les entreprises, `kind=COMPANY` pour les entreprises. `search` porte sur le nom, l'e-mail et le téléphone, ainsi que sur les contacts du compte.","security":[{"apiKey":[]}],"parameters":[{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["INDIVIDUAL","COMPANY","ALL"]},"description":"`INDIVIDUAL` (par défaut), `COMPANY`, ou `ALL` pour les deux."},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["CLIENT","SUPPLIER","PARTNER"]},"description":"Entreprises ayant cette relation : `CLIENT`, `SUPPLIER` ou `PARTNER`."},{"name":"specialty","in":"query","required":false,"schema":{"type":"string","maxLength":60},"description":"Entreprises ayant ce code de spécialité (voir `GET /crm/specialties`)."},{"name":"companyId","in":"query","required":false,"schema":{"type":"string"},"description":"Les personnes rattachées à une entreprise."},{"name":"listId","in":"query","required":false,"schema":{"type":"string","maxLength":64},"description":"Les enregistrements apportés par une liste importée ou hébergée (voir `/companies/lists`)."},{"name":"view","in":"query","required":false,"schema":{"type":"string","enum":["list"]},"description":"`list` ne renvoie que les colonnes nécessaires à une ligne de tableau — une charge bien plus légère."},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["name","recent"]},"description":"`name` (par défaut) ou `recent` — du plus récent au plus ancien. Appliqué avant la pagination, sur tout l'espace."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"50 par défaut ; une valeur supérieure est ramenée à 100."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0},"description":"Lignes à ignorer."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 50 par défaut, 100 au maximum ; `limit` et `offset` sont renvoyés dans la réponse.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Client"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total","limit","offset"]},"example":{"items":[{"id":"k7m2x9q4w1e8r5t3y6u0i2o4","kind":"INDIVIDUAL","types":["CLIENT"],"firstName":"Camille","lastName":"Durand","fullName":"Camille Durand","email":"camille.durand@example.com","phone":"+33612345678","companyId":"c3v8b1n6m4q9w2e7r5t0y8u1","siret":null,"vatNumber":null,"trade":null,"website":null,"language":"fr","birthdate":null,"notes":"Portail côté jardin, sonner deux fois.","tags":["chaudière","contrat-entretien"],"specialties":[],"address":{"line1":"12 rue des Lilas","city":"Nantes","postalCode":"44000","country":"FR","lat":47.2184,"lng":-1.5536},"customFields":{"numero_client_erp":"C-10482"},"primaryContact":null,"mergedIntoId":null,"contactCount":0,"siteCount":1,"noShowCount":0,"cancellationCount":0,"ltvCents":48000,"createdAt":"2026-09-14T08:12:44.000Z","updatedAt":"2026-11-03T16:40:02.000Z"}],"total":1,"limit":50,"offset":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postClients","tags":["Contacts"],"summary":"Créer ou modifier un contact","description":"Création ou mise à jour par `id` IWANA uniquement : sans `id`, cela crée TOUJOURS — jamais de correspondance avec un client existant par e-mail, téléphone ou nom. Un `id` inconnu renvoie une `404`. Pour éviter les doublons, conservez votre correspondance id ↔ identifiant IWANA avec `PUT /external-refs` et consultez-la d'abord avec `GET /external-refs`. Une personne nécessite `firstName` ou `lastName` ; une entreprise (`kind=COMPANY`) nécessite `name`. Lors d'une modification, les champs omis sont conservés, sauf que `firstName` et `lastName` d'une personne sont reconstruits ensemble à partir du corps : envoyez les deux. `customFields`, lorsqu'il est envoyé, remplace l'objet stocké. `kind` ne compte qu'à la création (utilisez `/clients/{id}/convert`). Un SIRET d'entreprise déjà utilisé renvoie `409 siret_already_used`. Émet `client.created` ou `client.updated`.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["INDIVIDUAL","COMPANY"],"description":"`INDIVIDUAL` (par défaut) ou `COMPANY`. Lu uniquement à la création : changez-le ensuite avec `POST /clients/{id}/convert`."},"name":{"type":"string","maxLength":200,"description":"Raison sociale d'une entreprise (`kind=COMPANY`) ; obligatoire pour une entreprise.","nullable":true},"types":{"type":"array","items":{"type":"string","enum":["CLIENT","SUPPLIER","PARTNER"]}},"specialties":{"type":"array","items":{"type":"string","maxLength":60},"description":"Codes de spécialité d'une entreprise (voir `GET /crm/specialties`) ; un code inconnu est ignoré."},"website":{"type":"string","maxLength":300,"nullable":true},"trade":{"type":"string","maxLength":120,"nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"companyId":{"type":"string","description":"L'entreprise à laquelle appartient cette personne ; `null` si aucune.","nullable":true},"siret":{"type":"string","nullable":true},"vatNumber":{"type":"string","nullable":true},"address":{"type":"object","additionalProperties":true,"nullable":true},"language":{"type":"string","nullable":true},"birthdate":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"customFields":{"type":"object","additionalProperties":true}},"additionalProperties":false},"example":{"kind":"INDIVIDUAL","firstName":"Camille","lastName":"Durand","email":"camille.durand@example.com","phone":"+33612345678","address":{"line1":"12 rue des Lilas","city":"Nantes","postalCode":"44000","country":"FR"},"tags":["chaudière"],"customFields":{"numero_client_erp":"C-10482"}}}}},"responses":{"201":{"description":"Créé ou modifié (201 dans les deux cas).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"},"example":{"id":"k7m2x9q4w1e8r5t3y6u0i2o4","kind":"INDIVIDUAL","types":["CLIENT"],"firstName":"Camille","lastName":"Durand","fullName":"Camille Durand","email":"camille.durand@example.com","phone":"+33612345678","companyId":"c3v8b1n6m4q9w2e7r5t0y8u1","siret":null,"vatNumber":null,"trade":null,"website":null,"language":"fr","birthdate":null,"notes":"Portail côté jardin, sonner deux fois.","tags":["chaudière","contrat-entretien"],"specialties":[],"address":{"line1":"12 rue des Lilas","city":"Nantes","postalCode":"44000","country":"FR","lat":47.2184,"lng":-1.5536},"customFields":{"numero_client_erp":"C-10482"},"primaryContact":null,"mergedIntoId":null,"contactCount":0,"siteCount":1,"noShowCount":0,"cancellationCount":0,"ltvCents":48000,"createdAt":"2026-09-14T08:12:44.000Z","updatedAt":"2026-11-03T16:40:02.000Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`siret_already_used` : un autre enregistrement de l'espace a ce SIRET ; `details.clientId` le désigne.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"siret_already_used","message":"Siret already used","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/{id}":{"delete":{"operationId":"deleteClientsId","tags":["Contacts"],"summary":"Supprimer un contact","description":"Suppression logique : le contact disparaît des listes et `GET` répond 404.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getClientsId","tags":["Contacts"],"summary":"Obtenir un contact, avec ses compteurs de contacts et de sites","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientDetail"},"example":{"id":"k7m2x9q4w1e8r5t3y6u0i2o4","kind":"INDIVIDUAL","types":["CLIENT"],"firstName":"Camille","lastName":"Durand","fullName":"Camille Durand","email":"camille.durand@example.com","phone":"+33612345678","companyId":"c3v8b1n6m4q9w2e7r5t0y8u1","siret":null,"vatNumber":null,"trade":null,"website":null,"language":"fr","birthdate":null,"notes":"Portail côté jardin, sonner deux fois.","tags":["chaudière","contrat-entretien"],"specialties":[],"address":{"line1":"12 rue des Lilas","city":"Nantes","postalCode":"44000","country":"FR","lat":47.2184,"lng":-1.5536},"customFields":{"numero_client_erp":"C-10482"},"primaryContact":null,"mergedIntoId":null,"contactCount":0,"siteCount":1,"noShowCount":0,"cancellationCount":0,"ltvCents":48000,"createdAt":"2026-09-14T08:12:44.000Z","updatedAt":"2026-11-03T16:40:02.000Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/{id}/convert":{"post":{"operationId":"postClientsIdConvert","tags":["Contacts"],"summary":"Transformer une personne en entreprise ou l'inverse, en conservant l'identifiant et tous les enregistrements liés","description":"Une entreprise qui a encore des contacts ne peut pas devenir une personne (`409 company_has_contacts`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["INDIVIDUAL","COMPANY"]},"name":{"type":"string","maxLength":200},"types":{"type":"array","items":{"type":"string","enum":["CLIENT","SUPPLIER","PARTNER"]}}},"required":["kind"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`company_has_contacts` (une entreprise ayant des personnes ne peut pas devenir une personne ; `details.contacts`), ou `siret_already_used` (`details.clientId`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"company_has_contacts","message":"Company has contacts","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/{id}/documents":{"get":{"operationId":"getClientsIdDocuments","tags":["Contacts"],"summary":"Lister les documents d'un client, du plus récent au plus ancien","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"1–100 ; une valeur supérieure est ramenée à 100. Sans ce paramètre (ou avec une valeur qui n'est pas un entier positif), toute la liste est renvoyée."},{"name":"before","in":"query","required":false,"schema":{"type":"string"},"description":"Curseur : le `nextBefore` de la page précédente."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Document"}},"nextBefore":{"type":"string","description":"À passer comme `before` pour la page suivante ; absent lorsque `limit` n'a pas été envoyé.","nullable":true}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postClientsIdDocuments","tags":["Contacts"],"summary":"Téléverser un document sur un client","description":"Corps `{ filename, mimeType, dataUrl }` avec une data URL base64, 14 Mo au maximum.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string","maxLength":255},"mimeType":{"type":"string","maxLength":160},"dataUrl":{"type":"string","maxLength":20000000,"description":"`data:<mime>;base64,<payload>`. Plafonné sous la limite du corps de requête pour qu'un fichier trop volumineux échoue à la validation plutôt que d'être tronqué par l'analyseur."}},"required":["filename","mimeType","dataUrl"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"document":{"$ref":"#/components/schemas/Document"}},"required":["document"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/{id}/duplicates":{"get":{"operationId":"getClientsIdDuplicates","tags":["Contacts"],"summary":"Doublons possibles d'un client (même SIRET ou même nom normalisé)","description":"20 au maximum. Lecture seule : fusionnez avec `POST /clients/{id}/merge`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fullName":{"type":"string"},"kind":{"type":"string"},"siret":{"type":"string","nullable":true},"reasons":{"type":"array","items":{"type":"string","enum":["SIRET","NAME"]}}},"required":["id","fullName","kind","siret","reasons"]}},"suggestConvert":{"type":"object","additionalProperties":true,"nullable":true}},"required":["items","suggestConvert"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/{id}/merge":{"post":{"operationId":"postClientsIdMerge","tags":["Contacts"],"summary":"Fusionner ce doublon dans targetId","description":"Reporte toutes les références (rendez-vous, sites, devis…) vers la cible en une seule transaction, complète les champs vides de la cible et supprime logiquement ce contact avec `mergedIntoId`. Émet `client.updated` pour la cible.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetId":{"type":"string","maxLength":64}},"required":["targetId"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sourceId":{"type":"string"},"targetId":{"type":"string"},"moved":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["sourceId","targetId","moved"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/{id}/portal-link":{"delete":{"operationId":"deleteClientsIdPortalLink","tags":["Contacts"],"summary":"Révoquer le lien d'espace client du contact","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postClientsIdPortalLink","tags":["Contacts"],"summary":"Obtenir le lien d'espace client du contact (créé une fois, puis réutilisé)","description":"N'envoie rien : partagez vous-même l'URL.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string"}},"required":["token","url"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/{id}/summary":{"get":{"operationId":"getClientsIdSummary","tags":["Contacts"],"summary":"L'historique d'un client en un coup d'œil : compteurs et derniers enregistrements de chaque type","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"counts":{"type":"object","additionalProperties":true},"satisfaction":{"type":"object","additionalProperties":true,"nullable":true},"quotes":{"type":"array","items":{"type":"object","additionalProperties":true}},"invoices":{"type":"array","items":{"type":"object","additionalProperties":true}},"contracts":{"type":"array","items":{"type":"object","additionalProperties":true}},"projects":{"type":"array","items":{"type":"object","additionalProperties":true}},"equipments":{"type":"array","items":{"type":"object","additionalProperties":true}},"appointments":{"type":"object","properties":{"upcoming":{"type":"array","items":{"type":"object","additionalProperties":true}},"past":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["upcoming","past"]},"interventions":{"type":"array","items":{"type":"object","additionalProperties":true}},"communications":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/documents/{documentId}":{"delete":{"operationId":"deleteClientsDocumentsDocumentId","tags":["Contacts"],"summary":"Supprimer un document de contact et son fichier","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/documents/{documentId}/content":{"get":{"operationId":"getClientsDocumentsDocumentIdContent","tags":["Contacts"],"summary":"Télécharger un document de contact","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Les octets du fichier, avec son propre `Content-Type`.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/documents/{documentId}/refresh-url":{"post":{"operationId":"postClientsDocumentsDocumentIdRefreshUrl","tags":["Contacts"],"summary":"Obtenir une nouvelle URL de téléchargement pour un document","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"url":{"type":"string"}},"required":["ok","url"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/import":{"post":{"operationId":"postClientsImport","tags":["Contacts"],"summary":"Importer des clients en masse (jusqu'à 20 000 lignes)","description":"Sans `target` : mode historique — les lignes dont l'e-mail existe déjà sont ignorées. Avec `target` : l'import guidé, qui rapproche les lignes par e-mail, téléphone, SIRET ou nom et applique `duplicates` (`skip`, `update` ou `create`) en une seule transaction. Aucun webhook n'est émis pour les lignes importées.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"additionalEmails":{"type":"array","items":{"type":"string"}},"phone":{"type":"string","nullable":true},"address":{"type":"object","additionalProperties":true,"nullable":true},"birthdate":{"type":"string","nullable":true},"language":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"notes":{"type":"string","nullable":true},"customFields":{"type":"object","additionalProperties":true},"company":{"type":"string","nullable":true},"siret":{"type":"string","nullable":true},"civility":{"type":"string","maxLength":40,"nullable":true},"mobile":{"type":"string","maxLength":60,"nullable":true},"jobTitle":{"type":"string","maxLength":200,"nullable":true},"vatNumber":{"type":"string","maxLength":40,"nullable":true},"website":{"type":"string","maxLength":500,"nullable":true},"kind":{"type":"string","enum":["INDIVIDUAL","COMPANY"]}},"additionalProperties":false},"maxItems":20000},"target":{"type":"string","enum":["INDIVIDUAL","COMPANY","CONTACT"]},"duplicates":{"type":"string","enum":["skip","update","create"]},"createCompanies":{"type":"boolean"},"siteLabel":{"type":"string","maxLength":120},"mobileLabel":{"type":"string","maxLength":60},"geocode":{"type":"boolean"}},"required":["rows"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"inserted":{"type":"integer"},"created":{"type":"integer"},"updated":{"type":"integer"},"skipped":{"type":"integer"},"errors":{"type":"array","items":{"type":"object","additionalProperties":true}},"companiesCreated":{"type":"integer"},"contactsCreated":{"type":"integer"},"sitesCreated":{"type":"integer"},"outcomes":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/clients/import/check":{"post":{"operationId":"postClientsImportCheck","tags":["Contacts"],"summary":"Prévisualiser un import guidé : quelles lignes correspondent à un enregistrement existant","description":"Lecture seule, d'où la réponse `200`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"additionalEmails":{"type":"array","items":{"type":"string"}},"phone":{"type":"string","nullable":true},"address":{"type":"object","additionalProperties":true,"nullable":true},"birthdate":{"type":"string","nullable":true},"language":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"notes":{"type":"string","nullable":true},"customFields":{"type":"object","additionalProperties":true},"company":{"type":"string","nullable":true},"siret":{"type":"string","nullable":true},"civility":{"type":"string","maxLength":40,"nullable":true},"mobile":{"type":"string","maxLength":60,"nullable":true},"jobTitle":{"type":"string","maxLength":200,"nullable":true},"vatNumber":{"type":"string","maxLength":40,"nullable":true},"website":{"type":"string","maxLength":500,"nullable":true},"kind":{"type":"string","enum":["INDIVIDUAL","COMPANY"]}},"additionalProperties":false},"maxItems":20000},"target":{"type":"string","enum":["INDIVIDUAL","COMPANY","CONTACT"]}},"required":["rows","target"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"matches":{"type":"array","items":{"type":"object","additionalProperties":true}},"companies":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/bootstrap":{"get":{"operationId":"getCommunicationsBootstrap","tags":["Communications"],"summary":"Les 50 derniers envois, modèles, campagnes et identité de l'expéditeur","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunicationsBootstrap"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/history/{id}":{"get":{"operationId":"getCommunicationsHistoryId","tags":["Communications"],"summary":"Un message envoyé en intégralité, avec ses dates de remise","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"channel":{"type":"string","enum":["EMAIL","SMS","WHATSAPP","PUSH","IN_APP","VOICE"]},"type":{"type":"string","description":"Quelle lettre c'était, par son nom dans Notifications."},"subject":{"type":"string","nullable":true},"body":{"type":"string","description":"Ce qui est parti : du HTML pour un e-mail, du texte pour un SMS.","nullable":true},"bodyHtml":{"type":"string","description":"L'e-mail tel que le destinataire l'a vu ; `null` pour un SMS ou un corps vide.","nullable":true},"templateId":{"type":"string","nullable":true},"toEmail":{"type":"string","nullable":true},"toPhone":{"type":"string","nullable":true},"fromName":{"type":"string","nullable":true},"fromEmail":{"type":"string","nullable":true},"smsSender":{"type":"string","description":"Le nom d'expéditeur affiché par le téléphone ; `null` sauf pour un SMS.","nullable":true},"clientId":{"type":"string","nullable":true},"appointmentId":{"type":"string","nullable":true},"status":{"type":"string","enum":["QUEUED","SENT","DELIVERED","OPENED","CLICKED","FAILED","BOUNCED","COMPLAINED","SUPPRESSED"]},"providerId":{"type":"string","nullable":true},"providerMessageId":{"type":"string","nullable":true},"failureReason":{"type":"string","nullable":true},"sentAt":{"type":"string","format":"date-time","nullable":true},"deliveredAt":{"type":"string","format":"date-time","nullable":true},"openedAt":{"type":"string","format":"date-time","nullable":true},"clickedAt":{"type":"string","format":"date-time","nullable":true},"failedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","channel","type","subject","body","bodyHtml","templateId","toEmail","toPhone","fromName","fromEmail","smsSender","clientId","appointmentId","status","providerId","providerMessageId","failureReason","sentAt","deliveredAt","openedAt","clickedAt","failedAt","createdAt"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/media/attachment":{"post":{"operationId":"postCommunicationsMediaAttachment","tags":["Communications"],"summary":"Stocker une pièce jointe d'e-mail et obtenir sa référence","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dataUrl":{"type":"string","maxLength":14500000},"name":{"type":"string","maxLength":200}},"required":["dataUrl","name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"ref":{"type":"string"},"size":{"type":"integer"}},"required":["name","ref","size"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/media/image":{"post":{"operationId":"postCommunicationsMediaImage","tags":["Communications"],"summary":"Publier une image pour les e-mails et obtenir son URL publique","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dataUrl":{"type":"string","maxLength":7500000},"variantOf":{"type":"string","maxLength":500},"ratio":{"type":"string","enum":["16:9","4:3","3:2","1:1"]}},"required":["dataUrl"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications":{"get":{"operationId":"getCommunicationsNotifications","tags":["Communications"],"summary":"Les lettres de notification envoyées par l'espace, avec leur état activé/désactivé et leur texte","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationSettings"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/{id}":{"patch":{"operationId":"patchCommunicationsNotificationsId","tags":["Communications"],"summary":"Activer ou désactiver une lettre de notification","description":"Active ou désactive l'étape d'automatisation correspondante.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/{id}/letter":{"delete":{"operationId":"deleteCommunicationsNotificationsIdLetter","tags":["Communications"],"summary":"Revenir au texte de la plateforme pour une lettre","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"put":{"operationId":"putCommunicationsNotificationsIdLetter","tags":["Communications"],"summary":"Enregistrer le texte propre à l'espace pour une lettre","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","maxLength":300},"body":{"type":"string","maxLength":20000}},"required":["body"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/{id}/test":{"post":{"operationId":"postCommunicationsNotificationsIdTest","tags":["Communications"],"summary":"Envoyer un test d'une lettre, remplie avec des données d'exemple","description":"Un vrai message marqué « [Test] ». Envoyez `to` : une clé n'a pas d'e-mail de compte de repli, et un test SMS l'exige toujours (et est facturé).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","maxLength":300},"body":{"type":"string","maxLength":20000},"branding":{"type":"string","enum":["PLATFORM","CUSTOM"],"description":"L'enveloppe que montrait l'aperçu — le test utilise la même."},"proNote":{"type":"string","maxLength":4000},"to":{"type":"string","maxLength":200,"description":"Destinataire du test : une adresse e-mail, ou un numéro de mobile pour un SMS. E-mail vide = l'adresse du compte lui-même."}},"required":["body"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"to":{"type":"string"}},"required":["ok","to"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/brand":{"patch":{"operationId":"patchCommunicationsNotificationsBrand","tags":["Communications"],"summary":"Définir la marque des e-mails (mode, nom affiché, couleur)","security":[{"apiKey":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["custom","platform"]},"name":{"type":"string","maxLength":80,"nullable":true},"color":{"type":"string","nullable":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/custom":{"post":{"operationId":"postCommunicationsNotificationsCustom","tags":["Communications"],"summary":"Créer une lettre personnalisée","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"channel":{"type":"string","enum":["EMAIL","SMS"]}},"required":["name","channel"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/custom/{id}":{"delete":{"operationId":"deleteCommunicationsNotificationsCustomId","tags":["Communications"],"summary":"Supprimer une lettre personnalisée","description":"Les automatisations qui l'utilisent encore échoueront à l'envoi.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"patchCommunicationsNotificationsCustomId","tags":["Communications"],"summary":"Renommer une lettre personnalisée","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120}},"required":["name"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/letters":{"get":{"operationId":"getCommunicationsNotificationsLetters","tags":["Communications"],"summary":"Les lettres qu'une automatisation peut envoyer","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"letters":{"type":"array","items":{"type":"object","additionalProperties":true}},"others":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["letters","others"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/notes":{"put":{"operationId":"putCommunicationsNotificationsNotes","tags":["Communications"],"summary":"Remplacer les notes par prestation ajoutées aux lettres","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"array","items":{},"maxItems":20}},"required":["notes"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"notes":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["ok","notes"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/notifications/test":{"post":{"operationId":"postCommunicationsNotificationsTest","tags":["Communications"],"summary":"Envoyer un test de n'importe quel texte de message","description":"Comme le test de lettre, avec `channel` (`EMAIL` ou `SMS`). Envoyez `to`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","maxLength":300},"body":{"type":"string","maxLength":20000},"branding":{"type":"string","enum":["PLATFORM","CUSTOM"],"description":"L'enveloppe que montrait l'aperçu — le test utilise la même."},"proNote":{"type":"string","maxLength":4000},"to":{"type":"string","maxLength":200,"description":"Destinataire du test : une adresse e-mail, ou un numéro de mobile pour un SMS. E-mail vide = l'adresse du compte lui-même."},"channel":{"type":"string","enum":["EMAIL","SMS"]}},"required":["body","channel"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"to":{"type":"string"}},"required":["ok","to"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/presets":{"get":{"operationId":"getCommunicationsPresets","tags":["Communications"],"summary":"Les messages prédéfinis de la plateforme","description":"Lettres destinées aux clients uniquement.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"channel":{"type":"string","enum":["EMAIL","SMS","WHATSAPP"]},"contextType":{"type":"string"},"category":{"type":"string","nullable":true},"language":{"type":"string","nullable":true},"audience":{"type":"string","enum":["CLIENT"]},"subject":{"type":"string","nullable":true},"body":{"type":"string"},"position":{"type":"integer"}},"required":["slug","name","description","channel","contextType","category","language","audience","subject","body","position"]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/preview":{"post":{"operationId":"postCommunicationsPreview","tags":["Communications"],"summary":"Afficher un message dans la vraie mise en page e-mail (rien n'est envoyé)","description":"N'enregistre ni n'envoie rien, d'où la réponse `200`.","security":[{"apiKey":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","maxLength":300},"body":{"type":"string","maxLength":20000},"branding":{"type":"string","enum":["PLATFORM","CUSTOM"],"description":"À quel en-tête de lettre — détermine l'enveloppe, exactement comme à l'envoi."},"lang":{"type":"string","maxLength":8,"description":"Langue du mail — détermine le titre de la carte récapitulative et le format des valeurs d'exemple."},"sample":{"type":"boolean","description":"Remplacez les `{{variables}}` par des valeurs réalistes avant le rendu."},"platform":{"type":"boolean","description":"Une lettre de projet collaboratif : la marque d'IWANA, quel que soit le mode de l'espace."},"highlight":{"type":"boolean","description":"Avec `sample` : marque chaque valeur insérée (teinte turquoise) pour que le lecteur voie ce que l'expéditeur renseigne."},"sms":{"type":"string","maxLength":2000,"description":"Un SMS à remplir de la même façon — renvoyé dans `smsParts`."},"proNote":{"type":"string","maxLength":4000}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"html":{"type":"string","description":"Le document e-mail complet."},"subjectParts":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"variable":{"type":"boolean","description":"Indique si ce segment est une variable renseignée."}},"required":["text","variable"]}},"to":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"variable":{"type":"boolean","description":"Indique si ce segment est une variable renseignée."}},"required":["text","variable"]},"description":"Le destinataire d'exemple ; `null` sans `sample`.","nullable":true},"smsParts":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"variable":{"type":"boolean","description":"Indique si ce segment est une variable renseignée."}},"required":["text","variable"]},"description":"`null` sauf si `sms` a été envoyé.","nullable":true},"sampleVars":{"type":"object","additionalProperties":{"type":"string"},"description":"Variable → valeur d'exemple ; `null` sans `sample`.","nullable":true},"platform":{"type":"boolean","description":"Indique si l'espace envoie sous la marque IWANA."}},"required":["html","subjectParts","to","smsParts","sampleVars","platform"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/sender-identity":{"patch":{"operationId":"patchCommunicationsSenderIdentity","tags":["Communications"],"summary":"Définir l'e-mail expéditeur et/ou demander un nom d'expéditeur SMS","description":"Un nom d'expéditeur SMS est examiné par IWANA ; les messages partent sous le nom actuel tant qu'il n'est pas approuvé.","security":[{"apiKey":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"senderEmail":{"type":"string","nullable":true},"smsSenderId":{"type":"string","nullable":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/sms-sender":{"get":{"operationId":"getCommunicationsSmsSender","tags":["Communications"],"summary":"Les noms d'expéditeur SMS demandé et actif","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"requested":{"type":"string","nullable":true},"active":{"type":"string","nullable":true},"state":{"type":"string","enum":["none","pending","approved","rejected"]},"rejectReason":{"type":"string","nullable":true},"requestedAt":{"type":"string","format":"date-time","nullable":true}},"required":["requested","active","state","rejectReason","requestedAt"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/sms-sender/request":{"delete":{"operationId":"deleteCommunicationsSmsSenderRequest","tags":["Communications"],"summary":"Retirer la demande de nom d'expéditeur SMS en attente","description":"Le nom demandé revient au dernier nom approuvé (ou à celui par défaut). `404` lorsque rien n'est en attente. Renvoie le nouvel état, comme `GET /communications/sms-sender`.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"requested":{"type":"string","nullable":true},"active":{"type":"string","nullable":true},"state":{"type":"string","enum":["none","pending","approved","rejected"]},"rejectReason":{"type":"string","nullable":true},"requestedAt":{"type":"string","format":"date-time","nullable":true}},"required":["requested","active","state","rejectReason","requestedAt"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/communications/templates/{id}":{"get":{"operationId":"getCommunicationsTemplatesId","tags":["Communications"],"summary":"Un modèle de message avec son texte actuel","description":"Un modèle verrouillé (le texte propre à IWANA) affiche le texte actuel de la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"internalDescription":{"type":"string","nullable":true},"channel":{"type":"string","enum":["EMAIL","SMS","WHATSAPP","PUSH","IN_APP","VOICE"]},"contextType":{"type":"string"},"category":{"type":"string","nullable":true},"language":{"type":"string","nullable":true},"status":{"type":"string","enum":["DRAFT","ACTIVE","ARCHIVED"]},"branding":{"type":"string","enum":["PLATFORM","CUSTOM"]},"sourcePresetSlug":{"type":"string","nullable":true},"locked":{"type":"boolean","description":"Texte propre à IWANA : lecture seule."},"subject":{"type":"string","nullable":true},"body":{"type":"string"},"richBody":{"type":"string","nullable":true}},"required":["id","name","internalDescription","channel","contextType","category","language","status","branding","sourcePresetSlug","locked","subject","body","richBody"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies":{"get":{"operationId":"getCompanies","tags":["Contacts"],"summary":"Lister les entreprises","description":"Les clients `kind=COMPANY`, dans la présentation entreprise.","security":[{"apiKey":[]}],"parameters":[{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"listId","in":"query","required":false,"schema":{"type":"string"},"description":"Les entreprises apportées par une liste."},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["CLIENT","SUPPLIER","PARTNER"]},"description":"`CLIENT`, `SUPPLIER` ou `PARTNER`."},{"name":"specialty","in":"query","required":false,"schema":{"type":"string","maxLength":60},"description":"Un code de spécialité (voir `GET /crm/specialties`)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"50 par défaut ; une valeur supérieure est ramenée à 200."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 50 par défaut, 200 au maximum.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Company"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."}},"required":["items","total"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCompanies","tags":["Contacts"],"summary":"Créer ou modifier une entreprise","description":"Création ou mise à jour par `id` uniquement ; `name` est obligatoire. Lors d'une modification, le corps remplace `types`, `tags` et les champs de contact (un champ omis est effacé), `specialties` est conservé s'il est omis. Un SIRET déjà utilisé par un autre enregistrement renvoie `409 siret_already_used`, comme pour `POST /clients`.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":200,"minLength":1},"types":{"type":"array","items":{"type":"string","enum":["CLIENT","SUPPLIER","PARTNER"]}},"trade":{"type":"string","maxLength":120,"nullable":true},"specialties":{"type":"array","items":{"type":"string","maxLength":60},"description":"Codes de spécialité (voir `GET /crm/specialties`). Omis : conservés."},"siret":{"type":"string","maxLength":20,"nullable":true},"vatNumber":{"type":"string","maxLength":30,"nullable":true},"email":{"type":"string","maxLength":200,"nullable":true},"phone":{"type":"string","maxLength":40,"nullable":true},"website":{"type":"string","maxLength":300,"nullable":true},"address":{"type":"string","maxLength":300,"nullable":true},"postcode":{"type":"string","maxLength":20,"nullable":true},"city":{"type":"string","maxLength":120,"nullable":true},"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Degrés décimaux.","nullable":true},"lng":{"type":"number","minimum":-180,"maximum":180,"description":"Degrés décimaux.","nullable":true},"notes":{"type":"string","maxLength":4000,"nullable":true},"tags":{"type":"array","items":{"type":"string"}}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`siret_already_used` : un autre enregistrement de l'espace a ce SIRET ; `details.clientId` le désigne.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"siret_already_used","message":"Siret already used","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/{id}":{"delete":{"operationId":"deleteCompaniesId","tags":["Contacts"],"summary":"Supprimer une entreprise","description":"Suppression logique ; ses personnes restent, détachées.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getCompaniesId","tags":["Contacts"],"summary":"Obtenir une entreprise avec ses personnes","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Company"},{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"fullName":{"type":"string"},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true}},"required":["id","fullName","email","phone"]}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/{id}/contacts":{"post":{"operationId":"postCompaniesIdContacts","tags":["Contacts"],"summary":"Rattacher une personne existante (contact kind=INDIVIDUAL) à l'entreprise","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string"},"attached":{"type":"boolean"}},"required":["id","clientId","attached"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/{id}/contacts/{clientId}":{"delete":{"operationId":"deleteCompaniesIdContactsClientId","tags":["Contacts"],"summary":"Détacher une personne de l'entreprise","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"clientId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string"},"attached":{"type":"boolean"}},"required":["id","clientId","attached"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/lists":{"get":{"operationId":"getCompaniesLists","tags":["Contacts"],"summary":"Lister les listes d'entreprises et de clients (fichiers importés ou hébergés)","description":"Les identifiants ne sont jamais renvoyés.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CompanyList"}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postCompaniesLists","tags":["Contacts"],"summary":"Créer ou modifier une liste, éventuellement adossée à un fichier hébergé","description":"Création ou mise à jour par `id`. Une nouvelle liste `CONTACT` nécessite `companyId`. Une nouvelle liste hébergée est récupérée immédiatement.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":120,"minLength":1},"kind":{"type":"string","enum":["COMPANY","CONTACT"]},"companyId":{"type":"string","maxLength":64,"description":"Pour une liste `CONTACT` : l'entreprise dont chaque ligne devient une personne. Obligatoire à la création ; omis lors d'une modification : inchangé.","nullable":true},"source":{"type":"string","description":"L'intégration propriétaire de la liste ; lu uniquement à la création."},"exposedInBooking":{"type":"boolean","description":"Indique si la réservation en ligne peut rapprocher les clients de cette liste."},"protocol":{"type":"string","enum":["ftp","ftps","sftp","http","https"],"nullable":true},"url":{"type":"string","maxLength":1000,"nullable":true},"credentials":{"type":"object","properties":{"username":{"type":"string","maxLength":200},"password":{"type":"string","maxLength":400},"privateKey":{"type":"string","maxLength":8000},"port":{"type":"integer","minimum":1}},"additionalProperties":false},"columnMapping":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"minLength":1},"role":{"type":"string","maxLength":200},"email":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":200},"commune":{"type":"string","maxLength":200},"ref":{"type":"string","maxLength":200},"lat":{"type":"string","maxLength":200},"lng":{"type":"string","maxLength":200}},"required":["name"],"additionalProperties":false},"refreshInterval":{"type":"string","enum":["manual","6h","12h","24h","weekly"]},"active":{"type":"boolean"}},"required":["name","kind"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/lists/{id}":{"delete":{"operationId":"deleteCompaniesListsId","tags":["Contacts"],"summary":"Supprimer une liste (ses enregistrements restent)","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/lists/{id}/exposed":{"patch":{"operationId":"patchCompaniesListsIdExposed","tags":["Contacts"],"summary":"Afficher ou masquer une liste dans la réservation en ligne","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"exposedInBooking":{"type":"boolean"}},"required":["id","exposedInBooking"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/lists/{id}/run":{"post":{"operationId":"postCompaniesListsIdRun","tags":["Contacts"],"summary":"Actualiser maintenant une liste hébergée","description":"`400 list_not_hosted` pour une liste sans URL.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"queued":{"type":"boolean"}},"required":["id","queued"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/companies/lists/preview":{"post":{"operationId":"postCompaniesListsPreview","tags":["Contacts"],"summary":"Récupérer un fichier distant et renvoyer ses en-têtes et ses premières lignes","description":"N'enregistre rien, d'où la réponse `200`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"protocol":{"type":"string","enum":["ftp","ftps","sftp","http","https"]},"url":{"type":"string","maxLength":1000,"minLength":3},"credentials":{"type":"object","properties":{"username":{"type":"string","maxLength":200},"password":{"type":"string","maxLength":400},"privateKey":{"type":"string","maxLength":8000},"port":{"type":"integer","minimum":1}},"additionalProperties":false},"listId":{"type":"string","description":"Une liste enregistrée dont les identifiants stockés sont utilisés lorsque le corps de la requête n'en envoie pas."}},"required":["protocol","url"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"headers":{"type":"array","items":{"type":"string"}},"rows":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["headers","rows"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contacts":{"get":{"operationId":"getContacts","tags":["Contacts"],"summary":"Lister les personnes rattachées aux comptes","security":[{"apiKey":[]}],"parameters":[{"name":"clientId","in":"query","required":false,"schema":{"type":"string","maxLength":64},"description":"Les personnes d'un compte."},{"name":"search","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"Nom, e-mail ou téléphone."},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["INDIVIDUAL","COMPANY","ALL"]},"description":"`COMPANY` : personnes d'entreprises ; `INDIVIDUAL` : personnes de foyers."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"50 par défaut ; une valeur supérieure est ramenée à 100."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 50 par défaut, 100 au maximum ; `limit` et `offset` sont renvoyés dans la réponse.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total","limit","offset"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postContacts","tags":["Contacts"],"summary":"Créer ou modifier une personne rattachée à un compte","description":"Création ou mise à jour par `id` uniquement. Une création nécessite `clientId` (le compte) et `firstName` ou `lastName`. Déplacer un contact vers un autre compte se fait avec `POST /contacts/{id}/move` (`400 use_move` ici). Le premier contact d'un compte en devient le contact principal.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","maxLength":64},"clientId":{"type":"string","maxLength":64,"description":"Le compte (une personne ou une entreprise) ; obligatoire à la création."},"civility":{"type":"string","maxLength":20,"nullable":true},"firstName":{"type":"string","maxLength":120,"nullable":true},"lastName":{"type":"string","maxLength":120,"nullable":true},"role":{"type":"string","maxLength":120,"description":"Texte libre, par ex. « Gardien », « Comptabilité ».","nullable":true},"jobTitle":{"type":"string","maxLength":120,"nullable":true},"email":{"type":"string","maxLength":254,"nullable":true},"phone":{"type":"string","maxLength":40,"nullable":true},"mobile":{"type":"string","maxLength":40,"nullable":true},"isPrimary":{"type":"boolean","description":"Fait de cette personne le contact principal du compte — un seul à la fois, le précédent perd ce statut."},"notes":{"type":"string","maxLength":5000,"nullable":true},"position":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contacts/{id}":{"delete":{"operationId":"deleteContactsId","tags":["Contacts"],"summary":"Supprimer une personne rattachée","description":"Suppression logique.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getContactsId","tags":["Contacts"],"summary":"Obtenir une personne rattachée","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contacts/{id}/move":{"post":{"operationId":"postContactsIdMove","tags":["Contacts"],"summary":"Déplacer une personne rattachée vers un autre compte","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string","maxLength":64}},"required":["clientId"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts":{"get":{"operationId":"getContracts","tags":["Contrats"],"summary":"Lister les contrats de maintenance","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":300},"description":"300 par défaut, 500 au maximum (une valeur supérieure renvoie une 400)."},{"name":"clientId","in":"query","required":false,"schema":{"type":"string"}},{"name":"companyId","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}},{"name":"search","in":"query","required":false,"schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 300 par défaut, 500 au maximum.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Contract"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."}},"required":["items","total"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postContracts","tags":["Contrats"],"summary":"Créer un contrat","description":"Création uniquement ; `clientId` et `startsOn` sont obligatoires. Valeurs par défaut : référence `CTR-<year>-NNNN`, statut `DRAFT`, cadence de 12 mois, reconduction automatique, préavis de 60 jours.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string"},"contactId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true},"startsOn":{"type":"string","format":"date-time"},"reference":{"type":"string","nullable":true},"planId":{"type":"string","description":"Le ContractTemplate de l'espace sous lequel ce contrat est vendu.","nullable":true},"totalAnnualCents":{"type":"number","minimum":0},"currency":{"type":"string"},"vatRate":{"type":"number","nullable":true},"endsOn":{"type":"string","format":"date-time","nullable":true},"cadenceMonths":{"type":"number","minimum":1},"autoRenew":{"type":"boolean"},"noticeDays":{"type":"number","minimum":0},"billingDay":{"type":"number","minimum":1,"maximum":31,"nullable":true},"status":{"type":"string","enum":["DRAFT","PENDING_SIGNATURE","ACTIVE","SUSPENDED","EXPIRED","CANCELLED"]},"notes":{"type":"string","nullable":true}},"required":["clientId","startsOn"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts/{id}":{"delete":{"operationId":"deleteContractsId","tags":["Contrats"],"summary":"Supprimer un contrat en brouillon","description":"Suppression définitive, `DRAFT` uniquement (`400 delete_requires_draft`).\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getContractsId","tags":["Contrats"],"summary":"Obtenir un contrat avec son client, ses lignes et les équipements couverts","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"contract":{"$ref":"#/components/schemas/Contract"},"client":{"type":"object","additionalProperties":true,"nullable":true},"lines":{"type":"array","items":{"type":"object","additionalProperties":true}},"coverage":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["contract","client","lines","coverage"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"patchContractsId","tags":["Contrats"],"summary":"Modifier un champ d'un contrat","description":"Un champ par appel : `{ \"field\": \"<name>\", \"value\": <new value> }`.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string","enum":["status","autoRenew","notes","totalAnnualCents","billingDay","noticeDays","endsOn","cadenceMonths","clientId","contactId","siteId","currency","vatRate","startsOn","reference"]},"value":{}},"required":["field","value"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts/{id}/cancel":{"patch":{"operationId":"patchContractsIdCancel","tags":["Contrats"],"summary":"Résilier un contrat","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts/{id}/equipment":{"post":{"operationId":"postContractsIdEquipment","tags":["Contrats"],"summary":"Couvrir un équipement de plus","description":"Il doit appartenir au client du contrat.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"equipmentId":{"type":"string"}},"required":["equipmentId"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts/{id}/equipment/{equipmentId}":{"delete":{"operationId":"deleteContractsIdEquipmentEquipmentId","tags":["Contrats"],"summary":"Ne plus couvrir un équipement","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"equipmentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"patchContractsIdEquipmentEquipmentId","tags":["Contrats"],"summary":"Modifier la cadence ou le modèle d'intervention d'un équipement couvert","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"equipmentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"cadenceMonths":{"type":"integer","minimum":1,"nullable":true},"templateId":{"type":"string","nullable":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts/{id}/equipment/sync":{"post":{"operationId":"postContractsIdEquipmentSync","tags":["Contrats"],"summary":"Remplacer l'ensemble des équipements couverts","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"equipmentId":{"type":"string"},"cadenceMonths":{"type":"integer","minimum":1,"nullable":true},"templateId":{"type":"string","nullable":true}},"required":["equipmentId"],"additionalProperties":false}}},"required":["rows"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts/{id}/lines":{"post":{"operationId":"postContractsIdLines","tags":["Contrats"],"summary":"Ajouter ou modifier (par id) une ligne de contrat","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"serviceId":{"type":"string","nullable":true},"description":{"type":"string"},"quantity":{"type":"number","minimum":1},"unitPriceCents":{"type":"number"},"vatRate":{"type":"number","nullable":true}},"required":["description","quantity","unitPriceCents"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/contracts/{id}/lines/{lineId}":{"delete":{"operationId":"deleteContractsIdLinesLineId","tags":["Contrats"],"summary":"Supprimer une ligne de contrat","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"lineId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/crm/specialties":{"get":{"operationId":"getCrmSpecialties","tags":["Contacts"],"summary":"Les codes de spécialité qu'une entreprise peut porter","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"color":{"type":"string","nullable":true},"icon":{"type":"string","nullable":true},"audience":{"type":"string"}},"required":["code","label","color","icon","audience"]}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/dashboard/home":{"get":{"operationId":"getDashboardHome","tags":["Statistiques"],"summary":"Tuiles d'accueil : devis et factures en attente, chiffre d'affaires du mois","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"tiles":{"type":"object","additionalProperties":true},"recentQuotes":{"type":"array","items":{"type":"object","additionalProperties":true}},"recentInvoices":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["tiles","recentQuotes","recentInvoices"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/dashboard/stats":{"get":{"operationId":"getDashboardStats","tags":["Statistiques"],"summary":"Indicateurs, séries temporelles et ventilations sur une période","description":"`range` : `7d`, `30d` (par défaut), `90d`, `ytd`, `mtd`, ou `custom` avec `from`/`to` ; toute autre valeur renvoie `400 validation_failed`.","security":[{"apiKey":[]}],"parameters":[{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["7d","30d","90d","ytd","mtd","custom"]}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardStats"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/equipments":{"get":{"operationId":"getEquipments","tags":["Équipements"],"summary":"Lister les équipements","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":200},"description":"200 par défaut, 500 au maximum (une valeur supérieure renvoie une 400)."},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"clientId","in":"query","required":false,"schema":{"type":"string"}},{"name":"companyId","in":"query","required":false,"schema":{"type":"string"}},{"name":"siteId","in":"query","required":false,"schema":{"type":"string"},"description":"Tout ce qui est installé à une adresse."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 200 par défaut, 500 au maximum.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Equipment"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."}},"required":["items","total"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postEquipments","tags":["Équipements"],"summary":"Créer un équipement","description":"Création uniquement. `label` et `clientId` sont obligatoires ; un `siteId` doit appartenir à ce client (`400 site_not_in_account`).\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string"},"clientId":{"type":"string"},"siteId":{"type":"string"},"brand":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"serialNumber":{"type":"string","nullable":true}},"required":["label","clientId"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/equipments/{id}":{"delete":{"operationId":"deleteEquipmentsId","tags":["Équipements"],"summary":"Supprimer un équipement","description":"Suppression logique.\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getEquipmentsId","tags":["Équipements"],"summary":"Obtenir un équipement avec son prochain rendez-vous planifié","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"equipment":{"$ref":"#/components/schemas/Equipment"},"clientName":{"type":"string","nullable":true},"nextAppointment":{"type":"object","additionalProperties":true,"nullable":true}},"required":["equipment","clientName","nextAppointment"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"patchEquipmentsId","tags":["Équipements"],"summary":"Modifier un champ d'un équipement","description":"Un champ par appel : `{ \"field\": \"<name>\", \"value\": <new value> }`. Un nom qui n'est pas un champ natif est stocké dans `customFields` (`null` le supprime).\n\nNécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string"},"value":{}},"required":["field","value"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/equipments/{id}/documents":{"get":{"operationId":"getEquipmentsIdDocuments","tags":["Équipements"],"summary":"Lister les documents d'un équipement","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"1–100 ; une valeur supérieure est ramenée à 100. Sans ce paramètre (ou avec une valeur qui n'est pas un entier positif), toute la liste est renvoyée."},{"name":"before","in":"query","required":false,"schema":{"type":"string"},"description":"Curseur : le `nextBefore` de la page précédente."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Document"}},"nextBefore":{"type":"string","description":"À passer comme `before` pour la page suivante ; absent lorsque `limit` n'a pas été envoyé.","nullable":true}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/equipments/{id}/photos":{"post":{"operationId":"postEquipmentsIdPhotos","tags":["Équipements"],"summary":"Ajouter une photo (data URL base64 ; jpeg, png, webp, heic ; 12 Mo, 30 photos au maximum)","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string"},"mimeType":{"type":"string"},"dataUrl":{"type":"string","description":"`data:image/jpeg;base64,…` — la même enveloppe que les documents client."}},"required":["filename","mimeType","dataUrl"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"photos":{"type":"array","items":{"type":"string","description":"URL de la photo."}}},"required":["photos"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/equipments/{id}/photos/delete":{"post":{"operationId":"postEquipmentsIdPhotosDelete","tags":["Équipements"],"summary":"Supprimer une photo par URL","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"photos":{"type":"array","items":{"type":"string","description":"URL de la photo."}}},"required":["photos"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/equipments/bulk-delete":{"post":{"operationId":"postEquipmentsBulkDelete","tags":["Équipements"],"summary":"Supprimer plusieurs équipements (500 au maximum)","description":"Nécessite les modules ERP dans l'offre de l'espace — sinon `402 erp_required` ; `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer"}},"required":["deleted"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/external-refs":{"get":{"operationId":"getExternalRefs","tags":["IDs externes"],"summary":"Convertir votre identifiant en identifiant IWANA (ou l'inverse)","description":"Envoyez `provider`, `entityType` et soit `externalId` (votre identifiant), soit `localId` (l'identifiant IWANA). Une absence de résultat renvoie `200 { \"item\": null }`, jamais une 404 — la réponse attendue avant une création.","security":[{"apiKey":[]}],"parameters":[{"name":"provider","in":"query","required":true,"schema":{"type":"string"},"description":"Votre système, par ex. `hubspot`. Texte libre — restez cohérent."},{"name":"entityType","in":"query","required":true,"schema":{"type":"string"},"description":"Texte libre, par ex. `client`, `appointment`."},{"name":"externalId","in":"query","required":false,"schema":{"type":"string"},"description":"Votre identifiant. Prioritaire sur `localId`."},{"name":"localId","in":"query","required":false,"schema":{"type":"string"},"description":"L'identifiant IWANA."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"oneOf":[{"$ref":"#/components/schemas/ExternalRef"},{"type":"null"}]}},"required":["item"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"put":{"operationId":"putExternalRefs","tags":["IDs externes"],"summary":"Enregistrer la correspondance entre votre identifiant et l'identifiant IWANA","description":"Création ou mise à jour : correspond sur (provider, entityType, externalId), puis sur (provider, entityType, localId), sinon crée. Chaque côté est unique par fournisseur et type d'entité.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string"},"entityType":{"type":"string"},"localId":{"type":"string"},"externalId":{"type":"string"},"shopId":{"type":"string","nullable":true},"payloadHash":{"type":"string","nullable":true}},"required":["provider","entityType","localId","externalId"],"additionalProperties":false},"example":{"provider":"mon-erp","entityType":"client","localId":"k7m2x9q4w1e8r5t3y6u0i2o4","externalId":"C-10482"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"$ref":"#/components/schemas/ExternalRef"}},"required":["item"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/fields":{"get":{"operationId":"getFields","tags":["Configuration"],"summary":"Lister les champs personnalisés d'un type d'enregistrement","description":"Les valeurs sont dans le `customFields` de chaque enregistrement, indexées par la `key` du champ.","security":[{"apiKey":[]}],"parameters":[{"name":"scope","in":"query","required":true,"schema":{"type":"string"},"description":"Type d'enregistrement : `client`, `appointment`, `equipment`, `project`, `quote`, `invoice`, `contract`, `job_sheet`…"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CustomField"}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postFields","tags":["Configuration"],"summary":"Créer ou modifier (par id) un champ personnalisé","description":"Une `key` déjà utilisée renvoie `400 key_already_exists`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"scope":{"type":"string"},"key":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"string","minLength":1},"type":{"type":"string","enum":["TEXT","LONGTEXT","EMAIL","PHONE","URL","NUMBER","DATE","CHECKBOX","SELECT","MULTI_SELECT","TAG","ADDRESS"]},"config":{"type":"object","additionalProperties":true},"group":{"type":"string","nullable":true},"position":{"type":"integer"},"required":{"type":"boolean"},"helpText":{"type":"string","nullable":true},"active":{"type":"boolean"}},"required":["scope","key","label","type"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/fields/{id}":{"delete":{"operationId":"deleteFieldsId","tags":["Configuration"],"summary":"Archiver un champ personnalisé","description":"Les champs natifs ne peuvent pas être supprimés (403).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/fields/{id}/active":{"patch":{"operationId":"patchFieldsIdActive","tags":["Configuration"],"summary":"Afficher ou masquer un champ personnalisé","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"}},"required":["active"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/fields/reorder":{"post":{"operationId":"postFieldsReorder","tags":["Configuration"],"summary":"Enregistrer l'ordre des champs d'un type d'enregistrement","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string"},"orderedIds":{"type":"array","items":{"type":"string"}}},"required":["scope","orderedIds"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/geo/regions":{"get":{"operationId":"getGeoRegions","tags":["Géo"],"summary":"Régions françaises et leurs départements","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"insee":{"type":"string"},"label":{"type":"string"},"departments":{"type":"array","items":{"type":"string"}}},"required":["code","insee","label","departments"]}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/geo/resolve":{"get":{"operationId":"getGeoResolve","tags":["Géo"],"summary":"Géocoder une adresse (placeId issu de suggest, ou texte libre q)","description":"Une absence de résultat renvoie `{ \"result\": null }`.","security":[{"apiKey":[]}],"parameters":[{"name":"placeId","in":"query","required":false,"schema":{"type":"string"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"result":{"oneOf":[{"type":"object","properties":{"label":{"type":"string"},"line1":{"type":"string"},"postcode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"countryCode":{"type":"string","nullable":true},"lat":{"type":"number"},"lng":{"type":"number"},"placeId":{"type":"string","nullable":true}},"required":["label","line1","postcode","city","countryCode","lat","lng","placeId"]},{"type":"null"}]}},"required":["result"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/geo/suggest":{"get":{"operationId":"getGeoSuggest","tags":["Géo"],"summary":"Autocomplétion d'adresse (6 suggestions)","description":"Un `q` de moins de 3 caractères ne renvoie rien.","security":[{"apiKey":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"sub":{"type":"string","nullable":true}},"required":["id","label","sub"]}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/integrations/me":{"get":{"operationId":"getIntegrationsMe","tags":["Intégration"],"summary":"Qui suis-je : l'espace et les permissions de cette clé","description":"Un contrôle de santé pour une intégration. `actor.kind` vaut `api_key` pour une clé, `oauth` pour un jeton.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"orgId":{"type":"string"},"actor":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"mode":{"type":"string","enum":["live","test"]}}},"permissions":{"type":"array","items":{"type":"string"}},"locale":{"type":"string"},"timezone":{"type":"string"}},"required":["orgId","actor","permissions","locale","timezone"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/map/bootstrap":{"get":{"operationId":"getMapBootstrap","tags":["Rendez-vous"],"summary":"Données de la carte : rendez-vous d'une fenêtre avec leurs clients, prestations, lieux et membres","description":"`from`/`to` valent par défaut −30 / +60 jours ; une fenêtre de plus de 100 jours revient à la valeur par défaut.","security":[{"apiKey":[]}],"parameters":[{"name":"clients","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"appointments","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"appointments":{"type":"array","items":{"type":"object","additionalProperties":true}},"services":{"type":"array","items":{"type":"object","additionalProperties":true}},"clients":{"type":"array","items":{"type":"object","additionalProperties":true}},"locations":{"type":"array","items":{"type":"object","additionalProperties":true}},"members":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/map/route":{"post":{"operationId":"postMapRoute","tags":["Rendez-vous"],"summary":"Itinéraire routier (polyligne) passant par 2 à 27 arrêts ordonnés","description":"Ne calcule ni ne stocke rien, d'où la réponse `200`. Facturé sur les crédits mesurés de l'espace. Lorsque le calcul d'itinéraire n'est pas disponible, la réponse est `{ \"status\": \"unavailable\" }`, et non une erreur.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stops":{"type":"array","items":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lng"],"additionalProperties":false},"maxItems":27,"minItems":2}},"required":["stops"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","unavailable"]},"polyline":{"type":"string"}},"required":["status"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects":{"get":{"operationId":"getProjects","tags":["Chantiers"],"summary":"Lister les chantiers, du plus récent au plus ancien","description":"Envoyez `counts=1` pour obtenir aussi les compteurs par statut. Les chantiers archivés sont exclus ; `inArchive=true` ne liste qu'eux. Hors `collaborative=true`, cela nécessite les modules ERP (`402 erp_required`).\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":200}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"clientId","in":"query","required":false,"schema":{"type":"string"}},{"name":"collaborative","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"inArchive","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"companyId","in":"query","required":false,"schema":{"type":"string"}},{"name":"accountId","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}},{"name":"status","in":"query","required":false,"schema":{"type":"string","maxLength":32},"description":"Un statut (`IN_PROGRESS`…), comparé en majuscules — une pastille de la liste."},{"name":"archived","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"counts","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 200 par défaut, 500 au maximum.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProjectListItem"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."}},"required":["items","total"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postProjects","tags":["Chantiers"],"summary":"Créer un chantier (brouillon, référence CH-…)","description":"Tous les champs sont facultatifs.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"collaborative":{"type":"boolean"},"kind":{"type":"string","enum":["CHANTIER","INTERVENTION","AUTRE"]},"partyRoles":{"type":"array","items":{"type":"string"}},"clientId":{"type":"string","nullable":true},"name":{"type":"string","nullable":true}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}":{"delete":{"operationId":"deleteProjectsId","tags":["Chantiers"],"summary":"Supprimer définitivement un chantier","description":"Généralement fait depuis l'archive ; `POST …/archive` est la suppression réversible. Émet `project.deleted`.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getProjectsId","tags":["Chantiers"],"summary":"Obtenir un chantier","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"clientName":{"type":"string","nullable":true},"siteName":{"type":"string","nullable":true},"leadMemberName":{"type":"string","nullable":true},"memberNames":{"type":"array","items":{"type":"string"}}},"required":["project","clientName","siteName","leadMemberName","memberNames"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"patchProjectsId","tags":["Chantiers"],"summary":"Modifier un champ d'un chantier","description":"Un champ par appel : `{ \"field\": \"<name>\", \"value\": <new value> }`. `customFields` est fusionné.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string"},"value":{}},"required":["field","value"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/archive":{"post":{"operationId":"postProjectsIdArchive","tags":["Chantiers"],"summary":"Archiver un chantier","description":"La suppression réversible : sortie de la liste, lecture seule (les écritures répondent `409 project_archived`), restaurable. Émet `project.updated`.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"archived":{"type":"integer"}},"required":["archived"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/documents":{"post":{"operationId":"postProjectsIdDocuments","tags":["Chantiers"],"summary":"Téléverser un document sur un chantier (data URL base64, 14 Mo)","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string"},"mimeType":{"type":"string"},"dataUrl":{"type":"string"},"visibility":{"type":"string","enum":["internal","shared"],"description":"'internal' (par défaut) ou 'shared' avec les entreprises partenaires."},"audience":{"type":"array","items":{"type":"string","maxLength":80}},"approvers":{"type":"array","items":{"type":"string","maxLength":80}}},"required":["filename","mimeType","dataUrl"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"document":{"$ref":"#/components/schemas/Document"}},"required":["document"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/duplicate":{"post":{"operationId":"postProjectsIdDuplicate","tags":["Chantiers"],"summary":"Copier un chantier dans un nouveau brouillon (le chantier seul, sans ses phases ni ses tâches)","description":"Envoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/measurements":{"post":{"operationId":"postProjectsIdMeasurements","tags":["Chantiers"],"summary":"Ajouter ou modifier (par id) un métré","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string","maxLength":300},"roomId":{"type":"string","nullable":true},"valueMm":{"type":"integer","minimum":0,"nullable":true},"unit":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"sortOrder":{"type":"integer"}},"required":["label"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/measurements/{measurementId}":{"delete":{"operationId":"deleteProjectsIdMeasurementsMeasurementId","tags":["Chantiers"],"summary":"Supprimer un métré","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"measurementId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/participants":{"get":{"operationId":"getProjectsIdParticipants","tags":["Chantiers"],"summary":"Toutes les personnes du chantier, avec le statut d'invitation","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postProjectsIdParticipants","tags":["Chantiers"],"summary":"Inviter une entreprise ou une personne sur le chantier","description":"Envoie l'invitation par e-mail et SMS. Une personne a besoin d'un e-mail et d'un téléphone, une entreprise extérieure d'un e-mail.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["company","person"]},"name":{"type":"string","maxLength":200},"email":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":40},"role":{"type":"string","maxLength":80},"orgId":{"type":"string","maxLength":64},"contactName":{"type":"string","maxLength":120},"clientId":{"type":"string","maxLength":64},"contactId":{"type":"string","maxLength":64}},"required":["kind"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"ok":{"type":"boolean"}},"required":["id","ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/parties":{"get":{"operationId":"getProjectsIdParties","tags":["Chantiers"],"summary":"Les clients et entreprises liés à un chantier, avec leurs rôles","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postProjectsIdParties","tags":["Chantiers"],"summary":"Rattacher un client ou une entreprise à un chantier","description":"Exactement un de `clientId` / `companyId`. Le rattachement n'envoie rien.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string"},"companyId":{"type":"string"},"contactId":{"type":"string","nullable":true},"role":{"type":"string","maxLength":80}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/parties/{partyId}":{"delete":{"operationId":"deleteProjectsIdPartiesPartyId","tags":["Chantiers"],"summary":"Délier un intervenant","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"partyId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/partners":{"post":{"operationId":"postProjectsIdPartners","tags":["Chantiers"],"summary":"Ajouter ou modifier (par id) une entreprise partenaire sur le chantier","description":"N'envoie rien ; invitez avec `…/partners/{partnerId}/invite`.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":300},"trade":{"type":"string","nullable":true},"contactName":{"type":"string","nullable":true},"contactEmail":{"type":"string","nullable":true},"contactPhone":{"type":"string","nullable":true},"clientId":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"status":{"type":"string","nullable":true},"sortOrder":{"type":"integer"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/partners/{partnerId}":{"delete":{"operationId":"deleteProjectsIdPartnersPartnerId","tags":["Chantiers"],"summary":"Retirer un partenaire (et son accès)","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"partnerId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/partners/{partnerId}/invite":{"delete":{"operationId":"deleteProjectsIdPartnersPartnerIdInvite","tags":["Chantiers"],"summary":"Retirer une invitation de partenaire","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"partnerId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postProjectsIdPartnersPartnerIdInvite","tags":["Chantiers"],"summary":"Envoyer par e-mail (et SMS) son lien d'invitation à un partenaire","description":"Nécessite le `contactEmail` du partenaire.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"partnerId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"invitedAt":{"type":"string","format":"date-time"}},"required":["ok","invitedAt"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/phases":{"post":{"operationId":"postProjectsIdPhases","tags":["Chantiers"],"summary":"Ajouter ou modifier (par id) une phase du planning","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":300},"color":{"type":"string","nullable":true},"status":{"type":"string","nullable":true},"startsOn":{"type":"string","nullable":true},"endsOn":{"type":"string","nullable":true},"assignee":{"type":"string","nullable":true},"sortOrder":{"type":"integer"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/phases/{phaseId}":{"delete":{"operationId":"deleteProjectsIdPhasesPhaseId","tags":["Chantiers"],"summary":"Supprimer une phase","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"phaseId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/planning/share":{"post":{"operationId":"postProjectsIdPlanningShare","tags":["Chantiers"],"summary":"Envoyer le planning par e-mail à tous les participants qui ont une adresse e-mail","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"string","maxLength":80}},"message":{"type":"string","maxLength":2000}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"array","items":{"type":"string"}},"skipped":{"type":"array","items":{"type":"string"}}},"required":["sent","skipped"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/reserves":{"post":{"operationId":"postProjectsIdReserves","tags":["Chantiers"],"summary":"Ajouter ou modifier (par id) une réserve","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string","maxLength":500},"status":{"type":"string","nullable":true},"resolvedAt":{"type":"string","nullable":true}},"required":["label"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/reserves/{reserveId}":{"delete":{"operationId":"deleteProjectsIdReservesReserveId","tags":["Chantiers"],"summary":"Supprimer une réserve","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"reserveId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/restore":{"post":{"operationId":"postProjectsIdRestore","tags":["Chantiers"],"summary":"Restaurer un chantier archivé","description":"Émet `project.updated`.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"restored":{"type":"integer"}},"required":["restored"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/rooms":{"post":{"operationId":"postProjectsIdRooms","tags":["Chantiers"],"summary":"Ajouter ou modifier (par id) une pièce (local)","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":300},"areaSqm":{},"floor":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"sortOrder":{"type":"integer"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/rooms/{roomId}":{"delete":{"operationId":"deleteProjectsIdRoomsRoomId","tags":["Chantiers"],"summary":"Supprimer une pièce (local)","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"roomId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/structure":{"get":{"operationId":"getProjectsIdStructure","tags":["Chantiers"],"summary":"Tout ce que contient un chantier : phases, tâches, pièces, métrés, partenaires, réserves, aspects financiers, enregistrements liés, documents","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"phases":{"type":"array","items":{"type":"object","additionalProperties":true}},"tasks":{"type":"array","items":{"type":"object","additionalProperties":true}},"rooms":{"type":"array","items":{"type":"object","additionalProperties":true}},"measurements":{"type":"array","items":{"type":"object","additionalProperties":true}},"partners":{"type":"array","items":{"type":"object","additionalProperties":true}},"reserves":{"type":"array","items":{"type":"object","additionalProperties":true}},"financials":{"type":"object","properties":{"quotedAcceptedCents":{"type":"integer"},"invoicedCents":{"type":"integer"},"paidCents":{"type":"integer"}},"required":["quotedAcceptedCents","invoicedCents","paidCents"]},"progressPct":{"type":"number"},"quotes":{"type":"array","items":{"type":"object","additionalProperties":true}},"invoices":{"type":"array","items":{"type":"object","additionalProperties":true}},"interventions":{"type":"array","items":{"type":"object","additionalProperties":true}},"appointments":{"type":"array","items":{"type":"object","additionalProperties":true}},"documents":{"type":"array","items":{"type":"object","additionalProperties":true}},"folders":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/tasks":{"post":{"operationId":"postProjectsIdTasks","tags":["Chantiers"],"summary":"Ajouter ou modifier (par id) un poste de travaux du chantier","description":"Ce ne sont pas les tâches de l'espace de `/tasks`. Lors d'une modification, seules les clés envoyées changent.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":500},"lot":{"type":"string","nullable":true},"status":{"type":"string","nullable":true},"dueDate":{"type":"string","nullable":true},"startDate":{"type":"string","nullable":true},"endDate":{"type":"string","nullable":true},"assignee":{"type":"string","nullable":true},"durationMinutes":{"type":"integer","minimum":0,"description":"Charge estimée en minutes ; null efface l'estimation.","nullable":true},"sortOrder":{"type":"integer"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/{id}/tasks/{taskId}":{"delete":{"operationId":"deleteProjectsIdTasksTaskId","tags":["Chantiers"],"summary":"Supprimer un poste de travaux du chantier","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/bulk-archive":{"post":{"operationId":"postProjectsBulkArchive","tags":["Chantiers"],"summary":"Archiver plusieurs chantiers (500 au maximum)","description":"Les identifiants hors de portée ou déjà archivés sont ignorés ; `archived` compte les autres.\n\nUn chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"archived":{"type":"integer"}},"required":["archived"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}":{"delete":{"operationId":"deleteProjectsDocumentsDocumentId","tags":["Chantiers"],"summary":"Supprimer un document de chantier et son fichier","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}/approve":{"post":{"operationId":"postProjectsDocumentsDocumentIdApprove","tags":["Chantiers"],"summary":"Approuver une fiche de chantier partagée au nom de l'espace","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"approval":{"type":"object","additionalProperties":true}},"required":["approval"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}/audience":{"post":{"operationId":"postProjectsDocumentsDocumentIdAudience","tags":["Chantiers"],"summary":"Définir qui peut ouvrir un document sur le chantier","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"audience":{"type":"array","items":{"type":"string","maxLength":80}}},"required":["audience"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"audience":{"type":"array","items":{"type":"string"}}},"required":["ok","audience"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}/content":{"get":{"operationId":"getProjectsDocumentsDocumentIdContent","tags":["Chantiers"],"summary":"Télécharger un document de chantier (thumb=1 pour sa miniature)","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"download","in":"query","required":false,"schema":{"type":"string"}},{"name":"thumb","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Les octets du fichier, avec son propre `Content-Type`.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}/pdf":{"get":{"operationId":"getProjectsDocumentsDocumentIdPdf","tags":["Chantiers"],"summary":"Une fiche de chantier rendue en PDF","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"download","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Les octets du fichier, avec son propre `Content-Type`.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`report_draft` : la fiche est encore un brouillon.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"report_draft","message":"Report draft","requestId":"5f0c2a9e-3b7d-4e61-9a24-8c1f0e6d7b52"}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}/preview":{"get":{"operationId":"getProjectsDocumentsDocumentIdPreview","tags":["Chantiers"],"summary":"Un document .docx sous forme de blocs de texte","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"blocks":{"type":"array","items":{"type":"object","additionalProperties":true}},"truncated":{"type":"boolean"}},"required":["blocks","truncated"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}/read":{"post":{"operationId":"postProjectsDocumentsDocumentIdRead","tags":["Chantiers"],"summary":"Marquer une fiche de chantier partagée comme lue","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/documents/{documentId}/sharing":{"post":{"operationId":"postProjectsDocumentsDocumentIdSharing","tags":["Chantiers"],"summary":"Partager un document avec les entreprises partenaires et le client (ancien modèle)","description":"Un chantier non collaboratif (sans indicateur `collaborative`, sans entreprise partenaire, sans client particulier invité) relève des modules ERP : sans eux dans l'offre de l'espace, la réponse est `402 erp_required`, et `403 offer_disabled` lorsque l'offre ERP est désactivée pour toute la plateforme. Un projet collaboratif reste ouvert sur toutes les offres — sauf si l'offre de projets collaboratifs est désactivée pour toute la plateforme et que l'espace n'a pas l'ERP (`403 offer_disabled`).","security":[{"apiKey":[]}],"parameters":[{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"partnerIds":{"type":"array","items":{"type":"string"}},"client":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/ErpRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/invitations":{"get":{"operationId":"getProjectsInvitations","tags":["Chantiers"],"summary":"Invitations en attente envoyées à cet espace par d'autres espaces (50 au maximum)","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/partners/directory":{"get":{"operationId":"getProjectsPartnersDirectory","tags":["Chantiers"],"summary":"Entreprises avec lesquelles cet espace a déjà travaillé","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/partners/iwana":{"get":{"operationId":"getProjectsPartnersIwana","tags":["Chantiers"],"summary":"Rechercher des entreprises IWANA ayant un profil public (8 résultats)","security":[{"apiKey":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"orgId":{"type":"string"},"name":{"type":"string"},"logoUrl":{"type":"string","nullable":true},"city":{"type":"string","nullable":true}},"required":["orgId","name","logoUrl","city"]}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/projects/partners/trades":{"get":{"operationId":"getProjectsPartnersTrades","tags":["Chantiers"],"summary":"Les corps de métier dont un partenaire peut être étiqueté","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"color":{"type":"string","nullable":true},"icon":{"type":"string","nullable":true}},"required":["code","label","color","icon"]}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/sites":{"get":{"operationId":"getSites","tags":["Sites"],"summary":"Lister les sites, avec leurs personnes sur place","security":[{"apiKey":[]}],"parameters":[{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"clientId","in":"query","required":false,"schema":{"type":"string"},"description":"Les sites d'un client."},{"name":"clientIds","in":"query","required":false,"schema":{"type":"string","maxLength":2000},"description":"Les sites de plusieurs comptes : identifiants séparés par des virgules, 50 au maximum."},{"name":"companyId","in":"query","required":false,"schema":{"type":"string"},"description":"Les sites d'une entreprise : les siens et ceux de ses personnes."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":200},"description":"200 par défaut ; une valeur supérieure est ramenée à 500."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","description":"Taille de page : 200 par défaut, 500 au maximum ; `limit` et `offset` sont renvoyés dans la réponse.","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Site"}},"total":{"type":"integer","description":"Lignes correspondant au filtre, sans tenir compte de la pagination."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total","limit","offset"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postSites","tags":["Sites"],"summary":"Créer un site","description":"Création uniquement (modification avec `PATCH /sites/{id}`). Le propriétaire est obligatoire : `clientId` ou `companyId` (si les deux sont envoyés, `companyId` l'emporte) — sans propriétaire, la réponse est `400 owner_required` ; un identifiant de propriétaire absent de l'espace renvoie une `404`. `line1` est facultatif. Envoyez `lat`/`lng` si vous les avez déjà.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string"},"companyId":{"type":"string","description":"L'entreprise propriétaire, lorsque le site n'appartient à aucune personne en particulier."},"label":{"type":"string","maxLength":200,"nullable":true},"line1":{"type":"string","maxLength":300},"line2":{"type":"string","maxLength":300,"nullable":true},"postcode":{"type":"string","maxLength":20,"nullable":true},"city":{"type":"string","maxLength":160,"nullable":true},"country":{"type":"string","maxLength":80,"nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"accessNotes":{"type":"string","maxLength":2000,"nullable":true},"contactName":{"type":"string","maxLength":160,"nullable":true},"contactPhone":{"type":"string","maxLength":40,"nullable":true},"active":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/sites/{id}":{"delete":{"operationId":"deleteSitesId","tags":["Sites"],"summary":"Supprimer un site","description":"Suppression logique.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getSitesId","tags":["Sites"],"summary":"Obtenir un site, avec ses personnes et son propriétaire","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"patchSitesId","tags":["Sites"],"summary":"Modifier un site","description":"Les champs omis sont conservés, `null` efface. `companyId` est ignoré ici.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string"},"companyId":{"type":"string","description":"Ignoré lors d'une modification : le propriétaire est défini à la création."},"label":{"type":"string","maxLength":200,"nullable":true},"line1":{"type":"string","maxLength":300},"line2":{"type":"string","maxLength":300,"nullable":true},"postcode":{"type":"string","maxLength":20,"nullable":true},"city":{"type":"string","maxLength":160,"nullable":true},"country":{"type":"string","maxLength":80,"nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"accessNotes":{"type":"string","maxLength":2000,"nullable":true},"contactName":{"type":"string","maxLength":160,"nullable":true},"contactPhone":{"type":"string","maxLength":40,"nullable":true},"active":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/sites/{id}/contacts":{"post":{"operationId":"postSitesIdContacts","tags":["Sites"],"summary":"Remplacer les personnes d'un site (propriétaire, agence, locataire…)","description":"La liste envoyée est la liste complète.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string","description":"Une vraie fiche contact, lorsque la personne fait partie de l'espace.","nullable":true},"contactId":{"type":"string","nullable":true},"role":{"type":"string","maxLength":40},"name":{"type":"string","maxLength":160,"nullable":true},"email":{"type":"string","maxLength":200,"nullable":true},"phone":{"type":"string","maxLength":40,"nullable":true},"isPrimary":{"type":"boolean"}},"additionalProperties":false},"description":"La liste COMPLÈTE — tout ce qui est absent est supprimé."}},"required":["contacts"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/sites/{id}/documents":{"get":{"operationId":"getSitesIdDocuments","tags":["Sites"],"summary":"Documents d'un site, ses rendez-vous et ses équipements","description":"200 au maximum.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Document"}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/sites/bulk-delete":{"post":{"operationId":"postSitesBulkDelete","tags":["Sites"],"summary":"Supprimer plusieurs sites","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"integer"}},"required":["ok","deleted"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/tasks":{"get":{"operationId":"getTasks","tags":["Tâches"],"summary":"Lister les tâches à faire par date d'échéance","description":"Non paginé : 500 au maximum (`cap`).","security":[{"apiKey":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["TODO","IN_PROGRESS","DONE","CANCELLED"]}},{"name":"assignee","in":"query","required":false,"schema":{"type":"string"}},{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["voice_call"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"total":{"type":"integer"},"cap":{"type":"integer"}},"required":["items","total","cap"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"postTasks","tags":["Tâches"],"summary":"Créer ou modifier une tâche à faire","description":"Création ou mise à jour par `id` (inconnu : 404) ; `title` est obligatoire. Lors d'une modification, envoyez la tâche complète : `status`, `dueAt`, `assigneeId`, `tags` omis sont réinitialisés.\n\nEnvoyez `Idempotency-Key` pour rendre un nouvel essai sûr : la même clé dans les 48 h rejoue la première réponse.","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string","maxLength":500,"minLength":1},"description":{"type":"string","maxLength":20000,"nullable":true},"status":{"type":"string","enum":["TODO","IN_PROGRESS","DONE","CANCELLED"]},"priority":{"type":"string","enum":["LOW","NORMAL","HIGH","URGENT"]},"dueAt":{"type":"string","nullable":true},"assigneeId":{"type":"string","nullable":true},"targetKind":{"type":"string","nullable":true},"targetId":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"references":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["client","contact","site","appointment","project","quote","invoice","intervention","contract","equipment","service","call"]},"id":{"type":"string","maxLength":100,"minLength":1},"label":{"type":"string","maxLength":200,"nullable":true}},"required":["type","id"],"additionalProperties":false},"maxItems":30,"description":"Absent = les mentions de la tâche sont conservées ; un tableau les remplace."}},"required":["title"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/tasks/{id}":{"delete":{"operationId":"deleteTasksId","tags":["Tâches"],"summary":"Supprimer une tâche à faire","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/tasks/{id}/status":{"patch":{"operationId":"patchTasksIdStatus","tags":["Tâches"],"summary":"Définir le statut d'une tâche à faire","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["TODO","IN_PROGRESS","DONE","CANCELLED"]}},"required":["status"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team":{"get":{"operationId":"getTeam","tags":["Équipe"],"summary":"Lister les membres, les équipes et la composition de chaque équipe","description":"Les membres retirés et les équipes archivées sont inclus, avec `deletedAt` renseigné.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/Member"}},"teams":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"icon":{"type":"string","nullable":true},"color":{"type":"string","nullable":true},"active":{"type":"boolean"},"deletedAt":{"type":"string","format":"date-time","nullable":true}},"required":["id","name","description","icon","color","active","deletedAt"]}},"teamMembers":{"type":"array","items":{"type":"object","properties":{"teamId":{"type":"string"},"membershipId":{"type":"string"}},"required":["teamId","membershipId"]}}},"required":["members","teams","teamMembers"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}":{"delete":{"operationId":"deleteTeamMembersId","tags":["Équipe"],"summary":"Retirer un membre","description":"Suspend l'adhésion ; un propriétaire ne peut pas être retiré.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}/base-address":{"patch":{"operationId":"patchTeamMembersIdBaseAddress","tags":["Équipe"],"summary":"Définir ou effacer l'adresse de départ d'un membre pour les itinéraires","description":"Doit contenir `lat`/`lng` (`400 address_not_geocoded`) ; géocodez avec `GET /geo/resolve`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"baseAddress":{"type":"object","properties":{"label":{"type":"string","maxLength":300,"nullable":true},"line1":{"type":"string","maxLength":200,"nullable":true},"postcode":{"type":"string","maxLength":20,"nullable":true},"city":{"type":"string","maxLength":120,"nullable":true},"countryCode":{"type":"string","maxLength":4,"nullable":true},"departmentCode":{"type":"string","maxLength":10,"nullable":true},"regionCode":{"type":"string","maxLength":10,"nullable":true},"regionLabel":{"type":"string","maxLength":120,"nullable":true},"placeId":{"type":"string","maxLength":200,"nullable":true},"lat":{"type":"number","minimum":-90,"maximum":90,"nullable":true},"lng":{"type":"number","minimum":-180,"maximum":180,"nullable":true}},"additionalProperties":false,"nullable":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}/color":{"patch":{"operationId":"patchTeamMembersIdColor","tags":["Équipe"],"summary":"Définir ou effacer la couleur de calendrier d'un membre (#RRGGBB)","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"color":{"type":"string","nullable":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}/invite-link":{"get":{"operationId":"getTeamMembersIdInviteLink","tags":["Équipe"],"summary":"Le lien d'acceptation d'une invitation en attente","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"link":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["link","expiresAt"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}/invite/resend":{"post":{"operationId":"postTeamMembersIdInviteResend","tags":["Équipe"],"summary":"Renvoyer une invitation en attente (nouveau lien, 14 jours)","description":"Dans les 60 s suivant la précédente : `429 resend_too_soon`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time"}},"required":["ok","expiresAt"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}/permissions":{"patch":{"operationId":"patchTeamMembersIdPermissions","tags":["Équipe"],"summary":"Remplacer le niveau de permission d'un membre par catégorie","description":"`null` remet une catégorie au niveau par défaut du rôle. Les propriétaires ne peuvent pas être modifiés.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"overrides":{"type":"object","additionalProperties":true}},"required":["overrides"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"overrides":{"type":"object","additionalProperties":true},"levels":{"type":"object","additionalProperties":true}},"required":["ok","overrides","levels"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}/restore":{"post":{"operationId":"postTeamMembersIdRestore","tags":["Équipe"],"summary":"Restaurer un membre retiré","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/{id}/role":{"patch":{"operationId":"patchTeamMembersIdRole","tags":["Équipe"],"summary":"Modifier le rôle d'un membre","description":"Seul un propriétaire peut accorder ou retirer `OWNER`, et seul un propriétaire ou un administrateur peut toucher à `ADMIN` — une clé n'est ni l'un ni l'autre, ces modifications sont donc refusées (`403 forbidden_admin`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["OWNER","ADMIN","MANAGER","COLLABORATOR","VIEWER","ARTISAN"]}},"required":["role"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/members/invite":{"post":{"operationId":"postTeamMembersInvite","tags":["Équipe"],"summary":"Inviter un membre de l'équipe (un collègue, PAS un client)","description":"Ajoute quelqu'un à l'ESPACE — un technicien, un responsable administratif — et lui envoie par e-mail une invitation valable 14 jours. C'est le point d'accès pour « ajouter un membre / un collaborateur / quelqu'un à l'équipe » ; pour un client ou un prospect, utilisez `POST /clients`. `OWNER` ne peut pas être invité (`400 invalid_role`) ; un membre actif renvoie `400 member_exists`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["OWNER","ADMIN","MANAGER","COLLABORATOR","VIEWER","ARTISAN"]},"displayName":{"type":"string","maxLength":120},"linkedWorkspaceId":{"type":"string","maxLength":60,"description":"Invitations ARTISAN : l'identifiant de l'espace PRO propre à l'entreprise invitée."}},"required":["email","role"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"membershipId":{"type":"string"},"pending":{"type":"boolean"}},"required":["ok","membershipId","pending"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/permissions":{"get":{"operationId":"getTeamPermissions","tags":["Équipe"],"summary":"La matrice des permissions : catégories, valeurs par défaut des rôles et niveaux de chaque membre","description":"Les entreprises partenaires (`ARTISAN`) et les membres supprimés ne figurent pas dans la matrice.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"offers":{"type":"array","items":{"type":"object","additionalProperties":true}},"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"offer":{"type":"string","nullable":true},"levels":{"type":"array","items":{"type":"string"}}},"required":["id","offer","levels"]}},"roleDefaults":{"type":"object","description":"Rôle → identifiant de catégorie → niveau.","additionalProperties":{"type":"object","additionalProperties":{"type":"string"}}},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifiant d'adhésion."},"role":{"type":"string"},"locked":{"type":"boolean","description":"`true` pour le propriétaire, dont les niveaux ne peuvent pas changer."},"overrides":{"type":"object","additionalProperties":{"type":"string"}},"levels":{"type":"object","additionalProperties":{"type":"string"}}},"required":["id","role","locked","overrides","levels"]}},"canEdit":{"type":"boolean","description":"Indique si cet appelant peut modifier les niveaux (`PATCH /team/members/{id}/permissions`)."}},"required":["offers","categories","roleDefaults","members","canEdit"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/teams":{"post":{"operationId":"postTeamTeams","tags":["Équipe"],"summary":"Créer ou modifier une équipe (un groupe de membres)","description":"Création ou mise à jour par `id`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","maxLength":120,"minLength":1},"description":{"type":"string","maxLength":500,"nullable":true},"icon":{"type":"string","maxLength":60,"nullable":true},"color":{"type":"string","maxLength":30,"nullable":true}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"id":{"type":"string"}},"required":["ok","id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/teams/{id}":{"delete":{"operationId":"deleteTeamTeamsId","tags":["Équipe"],"summary":"Archiver une équipe","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/teams/{id}/members":{"post":{"operationId":"postTeamTeamsIdMembers","tags":["Équipe"],"summary":"Ajouter un membre existant à une équipe","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"membershipId":{"type":"string"}},"required":["membershipId"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/teams/{id}/members/{membershipId}":{"delete":{"operationId":"deleteTeamTeamsIdMembersMembershipId","tags":["Équipe"],"summary":"Retirer un membre d'une équipe","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"membershipId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/team/teams/{id}/restore":{"post":{"operationId":"postTeamTeamsIdRestore","tags":["Équipe"],"summary":"Restaurer une équipe archivée","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows":{"post":{"operationId":"postWorkflows","tags":["Workflows"],"summary":"Créer une automatisation (brouillon)","description":"`name`, plus un `templateId`, une `spec` ou un `object`. Ne l'active jamais. Une clé ou un jeton ne peut pas enregistrer une étape Léo (`ask_leo`), y compris celle d'un modèle : `403 scope_required` avec `details.reason: ai_step_session_only`.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"templateId":{"type":"string","description":"Partir d'un modèle fourni…"},"object":{"type":"string","description":"…ou vide sur un objet choisi."},"spec":{"type":"object","additionalProperties":true},"description":{"type":"string","maxLength":500,"description":"Transmis avec une spécification toute prête — le résumé en une ligne de l'IA."}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/{id}":{"delete":{"operationId":"deleteWorkflowsId","tags":["Workflows"],"summary":"Archiver une automatisation et annuler ses exécutions en attente","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"getWorkflowsId","tags":["Workflows"],"summary":"Obtenir une automatisation avec sa spécification et ses compteurs d'exécutions","description":"Une ancienne automatisation qui n'est pas une automatisation renvoie `400 not_an_automation`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["DRAFT","ACTIVE","PAUSED","ARCHIVED"]},"version":{"type":"integer"},"objectScope":{"type":"string","description":"Le type d'enregistrement sur lequel elle s'exécute."},"spec":{"type":"object","additionalProperties":true,"description":"L'automatisation elle-même : `{ format: \"automation/v1\", object, trigger, steps, … }`."},"canEdit":{"type":"boolean","description":"Indique si cet appelant peut le modifier."},"runCountTotal":{"type":"integer"},"runCountSucceeded":{"type":"integer"},"runCountFailed":{"type":"integer"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","description","status","version","objectScope","spec","canEdit","runCountTotal","runCountSucceeded","runCountFailed","updatedAt"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"patchWorkflowsId","tags":["Workflows"],"summary":"Modifier le nom, la description ou la spécification d'une automatisation","description":"Une clé ou un jeton peut conserver les étapes Léo (`ask_leo`) écrites par une personne, mais pas en ajouter ni en modifier (`403 scope_required`, `details.reason: ai_step_session_only`).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"description":{"type":"string","maxLength":500},"spec":{"type":"object","additionalProperties":true}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/{id}/duplicate":{"post":{"operationId":"postWorkflowsIdDuplicate","tags":["Workflows"],"summary":"Copier une automatisation en brouillon","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/{id}/runs":{"get":{"operationId":"getWorkflowsIdRuns","tags":["Workflows"],"summary":"Les dernières exécutions d'une automatisation (200)","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"includeSkipped","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/{id}/status":{"patch":{"operationId":"patchWorkflowsIdStatus","tags":["Workflows"],"summary":"Activer, mettre en pause ou archiver une automatisation","description":"Activer une automatisation sans étape renvoie `400 empty_workflow`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ACTIVE","PAUSED","ARCHIVED"]}},"required":["status"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/{id}/test":{"post":{"operationId":"postWorkflowsIdTest","tags":["Workflows"],"summary":"Tester une automatisation à blanc sur un enregistrement réel","description":"Montre ce que ferait chaque étape ; rien n'est envoyé ni écrit, d'où la réponse `200`.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"subjectId":{"type":"string","description":"L'enregistrement sur lequel faire le test à blanc. Absent pour une séquence `workspace`, qui n'a pas d'enregistrement — elle s'exécute sur l'espace et la période qui vient de s'achever."}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"outcome":{"type":"string"},"log":{"type":"array","items":{"type":"object","additionalProperties":true}},"actions":{"type":"array","items":{"type":"object","additionalProperties":true}},"reason":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/bootstrap":{"get":{"operationId":"getWorkflowsBootstrap","tags":["Workflows"],"summary":"Lister les automatisations et les modèles publiés","description":"La route de liste ; les automatisations archivées sont exclues.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"vertical":{"type":"string","nullable":true},"canEdit":{"type":"boolean"},"workflows":{"type":"array","items":{"type":"object","additionalProperties":true}},"automationTemplates":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["vertical","canEdit","workflows","automationTemplates"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/runs":{"get":{"operationId":"getWorkflowsRuns","tags":["Workflows"],"summary":"Les dernières exécutions de toutes les automatisations (200)","security":[{"apiKey":[]}],"parameters":[{"name":"includeSkipped","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["items"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/runs/{runId}":{"get":{"operationId":"getWorkflowsRunsRunId","tags":["Workflows"],"summary":"Une exécution avec son journal d'étapes","security":[{"apiKey":[]}],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"workflowId":{"type":"string"},"workflowName":{"type":"string"},"triggerEvent":{"type":"string"},"status":{"type":"string","enum":["QUEUED","RUNNING","WAITING","COMPLETED","SKIPPED","FAILED","CANCELLED","AWAITING_APPROVAL"]},"subjectKind":{"type":"string","nullable":true},"subjectId":{"type":"string","description":"L'enregistrement concerné par l'exécution.","nullable":true},"errorMessage":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"kind":{"type":"string"},"status":{"type":"string","enum":["ok","stopped","waiting","failed","info"]},"detail":{"type":"string"},"at":{"type":"string","format":"date-time"}}},"description":"Le journal des étapes, dans l'ordre."},"wakeAt":{"type":"string","format":"date-time","description":"Quand une exécution en attente reprend.","nullable":true},"startedAt":{"type":"string","format":"date-time","nullable":true},"finishedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"durationMs":{"type":"integer","nullable":true}},"required":["id","workflowId","workflowName","triggerEvent","status","subjectKind","subjectId","errorMessage","steps","wakeAt","startedAt","finishedAt","createdAt","durationMs"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/runs/{runId}/approve":{"post":{"operationId":"postWorkflowsRunsRunIdApprove","tags":["Workflows"],"summary":"Approuver une exécution en attente d'approbation","description":"L'exécution reprend et ses actions restantes — envois compris — s'exécutent.","security":[{"apiKey":[]}],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/runs/{runId}/reject":{"post":{"operationId":"postWorkflowsRunsRunIdReject","tags":["Workflows"],"summary":"Rejeter une exécution en attente d'approbation","security":[{"apiKey":[]}],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/schema":{"get":{"operationId":"getWorkflowsSchema","tags":["Workflows"],"summary":"Champs et relations disponibles pour les automatisations sur un type d'enregistrement","security":[{"apiKey":[]}],"parameters":[{"name":"scope","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string"},"fields":{"type":"array","items":{"type":"object","additionalProperties":true}},"relations":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["scope","fields","relations"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pro/workflows/templates/{templateId}":{"get":{"operationId":"getWorkflowsTemplatesTemplateId","tags":["Workflows"],"summary":"Un modèle d'automatisation publié avec sa spécification","description":"Les lettres de la spécification sont désignées par le slug du modèle prédéfini, sans être résolues pour l'espace.","security":[{"apiKey":[]}],"parameters":[{"name":"templateId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"category":{"type":"string","enum":["appointment","quote","invoice","equipment","contract","client","project","workspace"]},"group":{"type":"string","enum":["appointment","aftercare","collab","reporting","billing","assets"]},"channel":{"type":"string","enum":["email","sms","both","none"],"description":"Ce que ses étapes envoient."},"spec":{"type":"object","additionalProperties":true,"description":"L'automatisation elle-même : `{ format: \"automation/v1\", object, trigger, steps, … }`."}},"required":["id","name","description","category","group","channel","spec"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"x-iwana-webhook-events":["appointment.created","appointment.updated","appointment.cancelled","client.created","client.updated","client.deleted","quote.created","quote.updated","quote.deleted","invoice.created","invoice.updated","invoice.deleted","payment.created","intervention.created","intervention.updated","intervention.deleted","project.created","project.updated","project.deleted"],"x-webhooks":{"event":{"post":{"summary":"Un événement de l'espace","description":"Envoyé une seule fois à chaque abonnement actif qui liste l'événement — il n'y a pas encore de nouvel essai automatique. En-têtes : `Iwana-Signature: sha256=<hex HMAC-SHA256 of the raw body with the subscription secret>`, `Iwana-Event: <type>`, `Iwana-Delivery: <uuid>`. Répondez `2xx` dans les 15 secondes.","parameters":[{"name":"Iwana-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"Iwana-Event","in":"header","required":true,"schema":{"type":"string","enum":["appointment.created","appointment.updated","appointment.cancelled","client.created","client.updated","client.deleted","quote.created","quote.updated","quote.deleted","invoice.created","invoice.updated","invoice.deleted","payment.created","intervention.created","intervention.updated","intervention.deleted","project.created","project.updated","project.deleted"]}},{"name":"Iwana-Delivery","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"event":"appointment.created","orgId":"of96fzhku1ajprqf37dithpq","occurredAt":"2026-11-10T14:05:11.482Z","data":{"id":"a9s4d7f2g5h8j1k3l6z0x2c5","startAt":"2026-11-18T09:00:00.000Z","endAt":"2026-11-18T10:30:00.000Z","status":"CONFIRMED","clientId":"k7m2x9q4w1e8r5t3y6u0i2o4","serviceId":"s2d5f8g1h4j7k0l3z6x9c2v5","locationId":"l4k7j0h3g6f9d2s5a8q1w4e7","source":"API"}}}}},"responses":{"200":{"description":"Tout code 2xx accuse réception de la livraison."}}}}}}