Comment constituer, pour une copropriété, la liste des contacts destinataires d'un courrier (postal ou courriel), consulter l'historique des envois déjà réalisés pour éviter les doublons, puis journaliser un envoi une fois qu'il a réellement été transmis.
Une diffusion de courrier ne s'adresse jamais à un « copropriétaire » directement : elle s'adresse à des contacts, rattachés à un copropriétaire via les lots qu'il détient. Un même copropriétaire peut avoir plusieurs contacts (un mandataire, un usufruitier, un locataire...), et un même contact peut être rattaché à plusieurs lots. C'est cette structure imbriquée — copropriétaire → lots → contacts par lot — qui porte l'essentiel des règles de ce guide.
Point d'entrée de la liste ; porte l'identité affichée sur un courrier groupé (ex. « Appartement 3B — M. Dupont »).
Le rattachement d'un contact à un lot précis, avec sa qualité et sa fenêtre de validité — c'est ici que se détermine le rôle réel du contact.
Le rôle n'est pas un champ unique à lire tel quel : c'est une combinaison à interpréter. isDroitReceptionCourrier = mandataire (habilité à recevoir le courrier pour ce lot, que ce soit le copropriétaire lui-même, un gérant ou une agence) ; type égal au type « titulaire » et estUsufruitier faux = propriétaire plein ; estUsufruitier vrai = usufruitier (prioritaire sur le nu-propriétaire) ; estLocataire = locataire ; estUsager = occupant du lot sans bail identifié (à distinguer de estLocataire, qui suppose un bail) — combiné à estLocataire par un OU logique, c'est ce qui détermine si le contact réside effectivement sur place. Un même contact peut cumuler plusieurs de ces qualités sur le même lot.
L'identité, les coordonnées et les préférences de canal du destinataire.
Chaque contact déclare individuellement ses préférences de canal (receptionCourrier/receptionEmail/receptionLRE) indépendamment de la présence ou non d'une adresse ou d'un email — une adresse renseignée ne signifie pas que le contact accepte d'être contacté par ce canal. Ne pas confondre ces préférences, propres au contact, avec l'objet preference décrit ci-après, qui est partagé.
Un objet rattaché au lot-contact plutôt qu'au contact lui-même : plusieurs lotContacts relevant d'un même mandat peuvent référencer la même fiche préférence. Porte notamment des tags libres (ex. un tag « remise en main propre » qui écarte l'envoi postal classique) — sans lien avec les préférences de canal du contact.
La fenêtre dateDebut/dateFin d'un lot-contact est déterminante : sans précision de date, seuls les rattachements valides aujourd'hui sont retournés (voir Parcours A). Un contact peut aussi être historisé : une fiche contact obsolète (fusionnée avec une plus récente) n'a alors plus de rattachement de lot valide et disparaît donc des lotContacts actuels de coproprietaires — mais elle reste présente, avec une référence vers le contact qui l'a remplacée, dans la liste à plat des contacts de la copropriété (voir Limites & pièges).
Chaque appel nécessite un jeton d'accès OAuth2, et le client doit être explicitement autorisé sur la copropriété visée par idCopro — sinon la réponse est un 403, sans détail sur l'autorisation manquante. Sur les appels d'écriture (journalisation d'un envoi), référencer un contact, un document ou un regroupement d'envoi qui n'appartient pas à cette même copropriété renvoie également un 403.
/v2 et servies depuis https://syndic.datacopro.org en production, ou https://preprod-syndic.datacopro.org sur la plateforme de test.
Ces quatre parcours couvrent la diffusion de bout en bout : constituer l'audience, éventuellement la restreindre, vérifier l'historique avant d'envoyer, puis tracer l'envoi une fois qu'il a réellement eu lieu.
Cas d'usage : pour une copropriété donnée, obtenir chaque contact à qui adresser un courrier, avec son rôle et ses préférences de canal.
La réponse imbrique coproprietaire → lots[] → lotContacts[] → contact. Un même contact peut apparaître sous plusieurs lots d'un même copropriétaire : c'est en regroupant les lotContacts par contact.id qu'on obtient une ligne par destinataire réel plutôt qu'une ligne par rattachement. Il n'existe pas de route séparée « liste des contacts d'une copropriété avec leur rôle » : cette structure imbriquée est le contrat.
lotContacts dont la fenêtre dateDebut/dateFin couvre la date du jour sont inclus. C'est une garantie utile pour une diffusion « au fil de l'eau » — elle exclut automatiquement un ancien mandataire dont le mandat vient de se terminer — mais elle rend la liste instable dans le temps : la relancer demain peut renvoyer un ensemble différent, même sans aucune modification volontaire des contacts. Cette fenêtre peut être explicitement déplacée avec les mêmes paramètres date/dateDebut/dateFin que sur cette route (voir le guide dédié aux copropriétaires pour leur syntaxe complète), ce qui permet de reconstituer l'audience valable à une date passée précise.
idsContacts retenus au moment de l'envoi (Parcours D) plutôt que de recalculer l'audience après coup.
Cas d'usage : un courrier ne concerne pas tous les lots (ex. une charge de travaux qui ne touche qu'un bâtiment) — restreindre l'audience aux seuls lots couverts par une clé de répartition donnée.
Chaque clé expose tantiemesParLot[].lot.id : l'ensemble des lots qu'elle couvre. En croisant ces identifiants de lot avec ceux portés par lotContacts côté Parcours A, on ne retient que les contacts rattachés à un lot couvert par la ou les clés choisies.
Cas d'usage : avant de (re)lancer une diffusion, vérifier qui a déjà reçu ce courrier précis afin de ne pas le renvoyer aux mêmes contacts.
Chaque envoi journalisé porte les contacts et les documents effectivement associés à cet envoi (voir Parcours D). Filtrer sur documents.id (ou documents.url) retrouve les envois liés à un document précis déjà connu.
documents.id, documents.url, ainsi qu'un filtre sur dateEnvoi exprimé avec un opérateur en préfixe (filters[dateEnvoi]=<2024-06-01%2000:00:00), sont les seuls filtres de cette route traduits directement dans la requête qui va chercher les données — donc réellement économes. Tout autre filtre imbriqué, notamment documents.tags ou documents.libelle, s'applique après coup, sur l'ensemble de l'historique déjà récupéré pour la fenêtre de dates demandée, ce qui le rend nettement plus coûteux. Sans borne dateDebut, cette fenêtre couvre tout l'historique de la copropriété : borner systématiquement la période interrogée, et préférer documents.id/documents.url à documents.tags/documents.libelle quand l'identifiant du document est déjà connu.
Cas d'usage : le courrier (email ou postal) vient d'être effectivement transmis aux contacts retenus — enregistrer cet envoi dans Datacopro pour qu'il apparaisse dans l'historique de la copropriété et soit consultable plus tard.
Optionnel : crée un identifiant de regroupement (idGroupe) vide, à référencer depuis plusieurs appels individuels ci-dessous, pour indiquer qu'ils appartiennent à une même action d'envoi (ex. un courrier envoyé en une fois à 40 contacts, mais journalisé contact par contact).
{
"libelle": "Diffusion #128 - Travaux toiture - Bât. A",
"mode": "courrier",
"urlContenu": "https://mon-domaine.example/archives/diffusion-128.html",
"idsContacts": [55231, 55240],
"idsDocuments": [4821],
"idGroupe": 9017
}
mode n'accepte que deux valeurs littérales : mail ou courrier. Le contenu du courrier se transmet par urlContenu (une URL déjà accessible publiquement, hébergée par l'appelant) — ou, à défaut, par contenu contenant le HTML encodé en base64 ; Datacopro l'héberge alors lui-même et renvoie l'URL résultante. idsContacts doit contenir au moins un identifiant, sous peine de 400.
mode différente de mail/courrier renvoie une erreur serveur générique plutôt qu'un message de validation explicite — à valider côté appelant avant l'envoi. Un idGroupe qui ne correspond à aucun regroupement existant échoue également de façon abrupte plutôt que par un message clair : vérifier qu'il a bien été créé par l'appel envoi-groupe précédent avant de le réutiliser.
Le typeEvenement de l'URL (ici envoi) et le champ type renvoyé en lecture par le Parcours C n'utilisent pas le même vocabulaire — voir la table de correspondance ci-dessous. Pour un courrier libre non lié à un objet métier précis (assemblée, mutation, intervention...), envoi est la valeur à utiliser en écriture ; l'envoi correspondant revient alors en lecture avec un type vide.
| typeEvenement (écriture, dans l'URL) | type (lecture, dans la réponse) | Usage |
|---|---|---|
envoi | (vide) | Courrier libre, sans objet métier lié — le cas de ce guide |
ag | Assemblee | Lié à une assemblée générale |
mutation | Mutation | Lié à une mutation de copropriétaire |
opar | OpAR | Lié à un accusé de réception postal |
intervention | Intervention | Lié à une intervention |
echeance-ensemble | EcheanceEnsemble | Lié à un ensemble d'échéances |
demande-devis-fournisseur | DemandeDevisFournisseur | Lié à une demande de devis fournisseur |
| Méthode | Route | Rôle |
|---|---|---|
| GET | /v2/copro/{idCopro}/coproprietaires.json | Copropriétaires avec leurs lots et, par lot, les contacts rattachés (rôle, canal, fenêtre de validité) — base de la liste de destinataires |
| GET | /v2/copro/{idCopro}/cles.json | Clés de répartition et lots couverts, pour restreindre l'audience à un sous-ensemble de lots |
| GET | /v2/copro/{idCopro}/contacts.json | Liste à plat de tous les contacts de la copropriété, y compris ceux devenus obsolètes suite à une fusion |
| GET | /v2/copro/{idCopro}/envois.json | Historique des envois déjà journalisés, filtrable par document ou par date |
| GET | /v2/copro/{idCopro}/envoi/{idEnvoi}.json | Détail d'un envoi journalisé |
| POST | /v2/copro/{idCopro}/envoi-groupe.json | Crée un identifiant de regroupement vide, à référencer depuis plusieurs envois individuels |
| POST | /v2/copro/{idCopro}/envoi/{typeEvenement}.json | Journalise un envoi déjà transmis (contacts et documents concernés) — n'envoie rien lui-même |
| GET | /v2/copro/{idCopro}/document/{idDocument}.json | Détail d'un document, à référencer dans idsDocuments d'un envoi |
La route attend une extension de format explicite en fin de chemin (par exemple .json).
Ces familles de paramètres sont communes à l'ensemble des routes de liste de ce guide (coproprietaires, cles, contacts, envois) :
filters[champ]=valeur — sur un champ texte, en l'absence d'opérateur explicite le filtre teste une sous-chaîne (insensible à la casse), pas une égalité stricte ; pour une égalité stricte utiliser filters[champ__eq]=valeur (ou préfixer la valeur d'un =, comme dans filters[id]==12 au Parcours B). Sur un champ numérique ou une date, un opérateur est obligatoire — soit en suffixe du nom (filters[champ__gt]=...), soit en préfixe de la valeur (<, >, <=, >=, !=) — son absence renvoie une erreur explicite plutôt qu'un résultat vide. Un chemin imbriqué se filtre par point, comme documents.tags.displays[] — un nom de champ l'inclut explicitement, le même préfixé de ! l'exclut. Sur la plupart des routes il ne fait que réduire la taille de la réponse, une fois celle-ci déjà entièrement construite côté serveur ; sur cles, en revanche, il conditionne si tantiemesParLot est effectivement récupéré (Parcours B) — dans ce cas précis il change aussi le travail réellement effectué, pas seulement la forme de la réponse.sorts[champ]=ASC|DESC — tri appliqué après coup sur le résultat déjà chargé ; sans effet sur le coût de l'appel.paginates[page]=N&paginates[nb]=N — découpage en pages (1-indexé), lui aussi appliqué après coup : il réduit la taille de la réponse transmise, jamais le travail effectué pour la construire.Sur envois spécifiquement, voir la nuance de performance détaillée au Parcours C : deux filtres seulement (documents.id, documents.url) et les bornes de date sont réellement économes, tous les autres filtres imbriqués s'appliquent après chargement complet de la période demandée.
POST /envoi/{typeEvenement} ne transmet ni email ni courrier — c'est une journalisation pure. Croire qu'un appel réussi (201) signifie qu'un message a été délivré au contact est l'erreur la plus coûteuse possible avec cette API (Parcours D).envoi-groupe (simple regroupement vide, sans contrainte), ni sur envoi : rien n'empêche de journaliser deux fois le même envoi au même contact. C'est à l'appelant de vérifier l'historique avant d'envoyer (Parcours C).coproprietaires ne renvoie que les rattachements contact/lot valides aujourd'hui : un contact dont le mandat vient de se terminer disparaît silencieusement de la liste, sans erreur ni avertissement (Parcours A).typeEvenement envoyé en écriture (envoi, ag, mutation...) diffère de celui du champ type renvoyé en lecture (vide, Assemblee, Mutation...) — se référer à la table de correspondance du Parcours D plutôt que de supposer une symétrie.lotContacts actuels de coproprietaires : les fiches contact peuvent être fusionnées au fil du temps. Pour le résoudre, interroger la liste à plat GET /v2/copro/{idCopro}/contacts.json?filters[id]==5502, qui conserve l'historique complet et indique, pour une fiche obsolète, une référence vers le contact qui l'a remplacée (Modèle & périmètre).contenu (alternative à urlContenu), le HTML doit être encodé en base64 : envoyer du HTML brut ne provoque pas d'erreur immédiate mais produit un contenu corrompu côté serveur (Parcours D).POST /envoi/{typeEvenement} avec les bons contacts et documents, est-ce que Datacopro envoie effectivement l'email ou le courrier ?GET /envois. Parcours Denvoi-groupe puis que j'y rattache plusieurs appels envoi, est-ce que je peux réutiliser le même groupe pour un renvoi ultérieur, ou dois-je en recréer un à chaque fois ?envoi-groupe est un simple identifiant de regroupement, sans aucune contrainte d'unicité ou de cohérence côté serveur, et toujours rattaché à une seule copropriété (le réutiliser depuis une autre copropriété échoue en 403). Rien ne fusionne, rien ne se déduplique automatiquement — c'est une convention purement applicative. Pour un renvoi distinct, créer un nouveau groupe évite de mélanger deux actions d'envoi différentes dans le même historique. Parcours D, Contrôle d'accès, Limites & pièges connusGET /coproprietaires reste-t-elle valable si je diffère l'envoi de quelques jours ?date/dateDebut/dateFin, Parcours A), ou, plus sûr, conserver explicitement les idsContacts retenus au moment de la préparation plutôt que de recalculer la liste juste avant l'envoi. Parcours A, Limites & pièges connus