ELRM developer Bac à sable

Suivre un dossier#

Ce qui se passe après votre validation, documents, signatures, session, facture. Et comment le savoir sans interroger l'API en boucle.

Portées : clients:dossiers:read, clients:apprenants:read, clients:documents:read.

Le journal des événements#

GET /dossiers/{dossier_id}/evenements renvoie, dans l'ordre chronologique, tout ce qui est arrivé au dossier, y compris ce que fait l'organisme après vous.

{
  "items": [
    { "id": "…", "type": "dossier.created", "occurred_at": "2026-09-01T10:02:11+02:00", "details": {}, "by_partner": true },
    { "id": "…", "type": "dossier.statut_changed", "occurred_at": "2026-09-02T09:15:40+02:00", "details": { "from": "brouillon", "to": "envoye" }, "by_partner": false },
    { "id": "…", "type": "inscriptions.validated", "occurred_at": "2026-09-03T11:00:00+02:00", "details": {}, "by_partner": true },
    { "id": "…", "type": "documents.sent", "occurred_at": "2026-09-03T11:05:12+02:00", "details": {}, "by_partner": false }
  ],
  "next_since": "2026-09-03T11:05:12+02:00",
  "has_more": false
}

Le mécanisme est un curseur : conservez next_since et repassez-le en since au prochain appel pour ne recevoir que la suite. null signifie qu'il n'y a rien de nouveau. Si has_more est vrai, la page était pleine (limit, 100 par défaut) : rappelez tout de suite avec since=next_since.

since = load_cursor(dossier_id)          # None au premier appel
r = api.get(f"/dossiers/{dossier_id}/evenements",
            params={"since": since} if since else None).json()
for ev in r["items"]:
    handle(ev)                            # ignorer les types inconnus
if r["next_since"]:
    save_cursor(dossier_id, r["next_since"])
# si r["has_more"] : rappeler immédiatement avec since=r["next_since"]

Types d'événements#

type Signification details
dossier.created Dossier créé aucun
analyse_besoin.attached Analyse du besoin rattachée aucun
dossier.statut_changed Changement de statut CRM (brouillon, envoye, accepte, en_cours, facture…) from, to
dossier.confirmed Bon de commande enregistré par l'organisme aucun
inscriptions.validated Inscriptions validées, dossier verrouillé aucun
inscriptions.reopened Inscriptions rouvertes par l'organisme aucun
documents.sent Documents envoyés à la signature aucun
documents.signed Documents signés aucun
documents.paper_signature_issued Signature papier sur place émise aucun
apprenant.moved Un apprenant a changé de session (par l'organisme) session_numero
apprenant.removed Un apprenant a été retiré (par l'organisme) aucun

by_partner vaut true quand l'événement vient de votre intégration : pratique pour ne réagir qu'à ce que fait l'organisme. L'énumération est additive : ignorez les types inconnus.

Cadence#

Un dossier évolue à l'échelle de la journée. Une lecture toutes les 15 à 60 minutes par dossier actif est largement suffisante ; n'interrogez pas les dossiers terminés (stage final) plus d'une fois par jour.

L'état à l'instant t#

GET …/dossiers/{dossier_id}/suivi renvoie le dossier avec stage, allowed_actions, montants et lignes : le même objet que la création. Utile pour un affichage, moins pour détecter un changement.

Les documents#

GET …/dossiers/{dossier_id}/documents liste les documents du dossier qu'ELRM a rendus disponibles pour l'API, document par document, depuis son CRM. Le type n'y change rien : c'est un choix par pièce. Selon ce qui a été autorisé, vous pouvez y trouver :

doc_type Libellé
devis_signe Devis signé
convention Convention
convocations Convocation
inscription Feuille d'inscription
emargement Émargement
evaluation Évaluation
satisfaction Satisfaction
cartes_caces Carte CACES
attestations Attestation de fin de formation
factures Facture
autre Document
{
  "items": [
    { "id": "…", "file_name": "Convocation_Martin_Lucas.pdf", "doc_type": "convocations", "doc_type_label": "Convocation", "mime_type": "application/pdf", "size_bytes": 61240 },
    { "id": "…", "file_name": "Attestation_Martin_Lucas.pdf", "doc_type": "attestations", "doc_type_label": "Attestation de fin de formation", "mime_type": "application/pdf", "size_bytes": 92110 }
  ],
  "total": 2
}

GET …/documents/{document_id} renvoie le fichier lui-même (Content-Type du document, Content-Disposition: attachment).

docs = api.get(f"/dossiers/{dossier_id}/documents").json()["items"]
for d in docs:
    content = api.get(f"/dossiers/{dossier_id}/documents/{d['id']}").content
    Path(d["file_name"]).write_bytes(content)

Un événement documents.sent / documents.signed est le bon déclencheur pour relire la liste.

Les apprenants#

GET /apprenants (filtrable par dossier_id) renvoie les apprenants inscrits avec leur session : une entrée par couple apprenant × dossier.

{
  "items": [
    { "id": "…", "nom": "Martin", "prenom": "Lucas", "email": "l.martin@exemple.fr", "telephone": "06 12 34 56 78", "date_naissance": "1991-04-12",
      "dossier_id": "…", "dossier_numero": "DEV-2026-1400", "session_numero": "S-2026-0412" }
  ],
  "total": 4, "limit": 100, "offset": 0
}

Tous vos dossiers#

GET /dossiers liste vos dossiers, y compris ceux créés hors API (par téléphone, par l'espace client). Chaque entrée porte stage, updated_at, les dates extrêmes, les formations et le nombre de places. Deux filtres évitent de lire …/suivi dossier par dossier :

  • ?stage=inscriptions_ouvertes : les dossiers où vous avez quelque chose à faire ;
  • ?updated_since=2026-09-01T00:00:00Z : ce qui a bougé depuis votre dernière synchronisation.