Documentation d'intégration · API Datacopro

Diffusion de courrier

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.

À propos de ce guide. Ce document décrit uniquement le contrat de l'API Datacopro : les appels HTTP disponibles, leurs paramètres et les réponses qu'ils renvoient. Aucune connaissance d'un logiciel de gestion de copropriété en particulier n'est nécessaire pour le suivre — les cas d'usage cités servent uniquement à motiver la manière dont les paramètres sont choisis.
01

Modèle & périmètre

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.

Copropriétaire racine

Point d'entrée de la liste ; porte l'identité affichée sur un courrier groupé (ex. « Appartement 3B — M. Dupont »).

idlibellelots[]

Lot-contact lots[].lotContacts[]

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.

idtypeisDroitReceptionCourrier estUsufruitierestLocataireestUsager dateDebutdateFin contactpreference

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.

Contact lotContacts[].contact

L'identité, les coordonnées et les préférences de canal du destinataire.

idprenomnom emailadressecodePostal villepayslangue receptionCourrierreceptionEmailreceptionLRE

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é.

Préférence lotContacts[].preference

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).

02

Contrôle d'accès

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.

Base URL Toutes les routes de ce guide sont préfixées /v2 et servies depuis https://syndic.datacopro.org en production, ou https://preprod-syndic.datacopro.org sur la plateforme de test.
03

Parcours fonctionnels

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.

A

Constituer la liste des destinataires actuels

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.

GET/v2/copro/{idCopro}/coproprietaires.json

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.

Pourquoi Par défaut, la liste est scopée sur aujourd'hui : seuls les 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.
Limite Reconstituer l'audience à une date passée suppose de connaître cette date à l'avance. Si la diffusion doit rester reconstituable à l'identique sans dépendre d'une date mémorisée par ailleurs (par exemple pour prouver qui était destinataire au moment précis de l'envoi), il reste préférable de figer la liste des idsContacts retenus au moment de l'envoi (Parcours D) plutôt que de recalculer l'audience après coup.
B

Restreindre l'audience à une clé de répartition

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.

GET/v2/copro/{idCopro}/cles.json?displays[]=tantiemesParLot&filters[id]==12

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.

Illustration C'est ainsi qu'un logiciel existant permet de sélectionner une ou plusieurs clés au moment de préparer une diffusion, en indiquant que les clés sont additives : sélectionner deux clés élargit l'audience à l'union des lots couverts, ce n'est jamais une intersection.
C

Consulter l'historique pour éviter d'envoyer deux fois le même courrier

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.

GET/v2/copro/{idCopro}/envois.json?dateDebut=2024-01-01%2000:00:00&filters[documents.id]==4821

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.

Pourquoi 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.
Limite L'API ne fait elle-même aucun rapprochement automatique pour empêcher un doublon : rien n'y interdit de journaliser deux fois le même envoi au même contact. La vérification de l'historique décrite ici est donc entièrement à la charge de l'appelant, avant d'envoyer — l'API n'est qu'un registre passif (voir aussi Parcours D).
D

Journaliser un envoi une fois qu'il a réellement été transmis

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.

POST/v2/copro/{idCopro}/envoi-groupe.json

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).

POST/v2/copro/{idCopro}/envoi/envoi.json
{
  "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.

Pourquoi — le point le plus important de ce guide Cet appel n'envoie rien. Il ne déclenche ni email, ni impression, ni dépôt postal : il enregistre a posteriori qu'un envoi a eu lieu, avec son libellé, ses destinataires et les documents associés. L'email ou le courrier doit avoir été réellement transmis par vos propres moyens avant cet appel — Datacopro n'est que le registre de traçabilité consulté au Parcours C, jamais l'expéditeur.
Limite Une valeur de 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
agAssembleeLié à une assemblée générale
mutationMutationLié à une mutation de copropriétaire
oparOpARLié à un accusé de réception postal
interventionInterventionLié à une intervention
echeance-ensembleEcheanceEnsembleLié à un ensemble d'échéances
demande-devis-fournisseurDemandeDevisFournisseurLié à une demande de devis fournisseur
04

Référence des endpoints

MéthodeRouteRôle
GET/v2/copro/{idCopro}/coproprietaires.jsonCoproprié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.jsonClés de répartition et lots couverts, pour restreindre l'audience à un sous-ensemble de lots
GET/v2/copro/{idCopro}/contacts.jsonListe à plat de tous les contacts de la copropriété, y compris ceux devenus obsolètes suite à une fusion
GET/v2/copro/{idCopro}/envois.jsonHistorique des envois déjà journalisés, filtrable par document ou par date
GET/v2/copro/{idCopro}/envoi/{idEnvoi}.jsonDétail d'un envoi journalisé
POST/v2/copro/{idCopro}/envoi-groupe.jsonCrée un identifiant de regroupement vide, à référencer depuis plusieurs envois individuels
POST/v2/copro/{idCopro}/envoi/{typeEvenement}.jsonJournalise un envoi déjà transmis (contacts et documents concernés) — n'envoie rien lui-même
GET/v2/copro/{idCopro}/document/{idDocument}.jsonDé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).

05

Paramètres transverses

Ces familles de paramètres sont communes à l'ensemble des routes de liste de ce guide (coproprietaires, cles, contacts, envois) :

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.

06

Limites & pièges connus

✕
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).
✕
Aucune déduplication n'est assurée côté API, ni sur 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).
✕
Sans paramètre de date, 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).
✕
Le vocabulaire du 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.
✕
Un identifiant de contact retrouvé dans un envoi ancien peut ne plus figurer parmi les 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).
✕
Pour 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).
07

Questions de validation

Q1. Si j'appelle POST /envoi/{typeEvenement} avec les bons contacts et documents, est-ce que Datacopro envoie effectivement l'email ou le courrier ?
Non, jamais. Cet appel journalise uniquement qu'un envoi a eu lieu (libellé, contacts, documents, date) — il ne déclenche aucune transmission. L'email ou le courrier doit avoir été réellement envoyé par vos propres moyens avant cet appel ; Datacopro n'en garde que la trace, consultable ensuite via GET /envois. Parcours D
Q2. Si je crée un envoi-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 ?
L'API ne l'impose ni dans un sens ni dans l'autre : un 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 connus
Q3. La liste de contacts que je récupère aujourd'hui via GET /coproprietaires reste-t-elle valable si je diffère l'envoi de quelques jours ?
Pas de façon garantie, mais reconstituable. Par défaut cette liste est scopée sur la date du jour de l'appel : un contact dont le rattachement à un lot se termine entre-temps disparaîtra d'un nouvel appel, sans que rien ne le signale. Deux parades : repasser l'appel avec une date explicite si elle est connue (paramètres 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