API REST — CovoitGroupe
Documentation pour les intégrations tierces
Introduction
CovoitGroupe expose une API REST JSON qui permet à des outils tiers de créer et gérer des covoiturages sans passer par l'interface web. Cas d'usage typiques :
- Un outil de gestion d'asso ou de club crée automatiquement un covoiturage lors d'un événement.
- Une application mobile pousse la liste des membres et des voitures disponibles.
- Un script récupère l'état des présences et des assignations pour les afficher ailleurs.
Une fois créé via l'API, l'événement est accessible via l'interface web habituelle — les participants peuvent répondre, les conducteurs peuvent être modifiés, etc.
Authentification
CovoitGroupe utilise un système de double slug à la place d'une authentification par token ou session :
🔒 Slug admin
Accès complet en lecture et écriture. À garder secret — c'est la clé de l'événement. Toutes les routes d'écriture utilisent ce slug.
👥 Slug visiteur
Accès collaboratif : lecture, ajout de participants et de conducteurs, assignations. C'est ce lien que vous partagez avec les participants.
Les deux slugs sont des chaînes de 10 caractères alphanumériques générées aléatoirement (environ 3,6×10¹⁵ combinaisons), retournés à la création de l'événement. Aucun header d'authentification n'est nécessaire : posséder le slug admin suffit. La suppression de l'événement lui-même requiert le slug admin.
Format & limites
Requêtes
- • Corps JSON, header
Content-Type: application/json - • Base URL :
https://groupe.covoitequipe.fr - • CORS ouvert pour tous les domaines sur
/api/*
Rate limiting (par IP)
- • Création événement : 10 / heure
- • Ajout participant : 60 / heure
- • Ajout conducteur : 30 / heure
- • Global : 3 000 / jour, 600 / heure
Réponses d'erreur
{ "error": "Event not found" } → 404
{ "error": "Discussion disabled" } → 403
{ "error": "Message limit reached" } → 429
{ "error": "Internal server error" } → 500
Couverture UI ↔ API
L'API permet de réaliser l'intégralité des actions disponibles dans l'interface web.
| Action dans l'UI | Endpoint API | Statut |
|---|---|---|
| Créer un événement | POST /api/events | ✓ |
| Créer event + participants + voitures d'un coup | POST /api/events/bulk | ✓ |
| Modifier les infos de l'événement | PUT /api/events/<admin_slug> | ✓ |
| Dupliquer un événement | POST /api/events/<admin_slug>/duplicate | ✓ |
| Lire tout l'état (présences, assignations…) | GET /api/events/<slug> | ✓ |
| Ajouter un participant | POST /api/events/<slug>/participants | ✓ |
| Importer une liste de participants | POST /api/events/<slug>/participants/import | ✓ |
| Marquer présence / absence | PUT /api/participants/<id> | ✓ |
| Définir le besoin de covoiturage | PUT /api/participants/<id> | ✓ |
| Assigner un participant à un conducteur | PUT /api/participants/<id> | ✓ |
| Assigner un point de ramassage | PUT /api/participants/<id> | ✓ |
| Supprimer un participant | DELETE /api/participants/<id> | ✓ |
| Ajouter un conducteur | POST /api/events/<slug>/drivers | ✓ |
| Importer une liste de conducteurs | POST /api/events/<slug>/drivers/import | ✓ |
| Modifier places / aller-retour / notes d'un conducteur | PUT /api/drivers/<id> | ✓ |
| Supprimer un conducteur | DELETE /api/drivers/<id> | ✓ |
| Ajouter / modifier / supprimer un point de ramassage | POST, PUT, DELETE /api/…/pickup_points | ✓ |
| Ajouter / modifier / supprimer un accompagnant | POST, PUT, DELETE /api/…/accompagnants | ✓ |
| Lire / poster / supprimer un message | GET, POST, DELETE /api/events/<slug>/messages | ✓ |
Événements
/api/events/bulk
Recommandé pour les intégrations
Crée un événement complet en une seule requête atomique : infos de l'événement, liste de participants, conducteurs et points de ramassage. Idéal pour les imports depuis un outil tiers.
Corps de la requête
{
"event": {
"title": "Répétition chorale", // requis
"date": "2026-06-20", // format YYYY-MM-DD
"location": "Salle Molière, Lyon",
"address": "12 rue Molière, Lyon",
"startTime": "20:00", // heure de début (HH:MM)
"departureTime": "19:30", // heure de départ
"note": "Répétition de printemps",
"official_link": "https://…",
"contextType": "music", // generic | sport | music | corporate | scout
"returnEnabled": 1, // 0 = aller seul, 1 = aller + retour
"mode": "classic" // "classic" (défaut) | "light" (lien unique sans covoiturage)
},
"participants": [
{ "name": "Alice" },
{ "name": "Bob", "is_coming": 1 }, // 1 = présent, 0 = absent, null = pas répondu
{ "name": "Claire" }
],
"drivers": [
{ "name": "Marie", "seats": 4, "drives_outbound": 1, "drives_return": 0 },
{ "name": "Paul", "seats": 3, "drives_outbound": 1, "drives_return": 1 }
],
"pickup_points": [
{ "name": "Gare Part-Dieu", "address": "Place Charles Béraudier", "time": "19:30" },
{ "name": "Mairie du 8e", "address": "Place Ambroise Courtois", "time": "19:45" }
]
}
Réponse 201 Created
{
"admin_slug": "a1b2c3d4e5",
"view_slug": "f6g7h8i9j0",
"admin_url": "https://groupe.covoitequipe.fr/a1b2c3d4e5", // lien organisateur (privé)
"view_url": "https://groupe.covoitequipe.fr/f6g7h8i9j0" // lien participants (à partager)
}
admin_slug — c'est la seule façon de modifier l'événement ensuite.
Limites : 200 participants, 100 conducteurs, 20 points de ramassage par requête.
/api/events
Crée un événement vide. Utilisez ensuite les endpoints participants/conducteurs
pour le peupler, ou préférez POST /api/events/bulk.
Corps de la requête
// Mêmes champs que dans "event" de /api/events/bulk
{ "title": "Sortie scouts", "date": "2026-07-10", "contextType": "scout", "mode": "classic" }
Réponse 201 Created
{ "slug": "a1b2c3d4e5", "id": 42, "mode": "classic",
"view_slug": "f6g7h8i9j0",
"admin_url": "…/a1b2c3d4e5", "view_url": "…/f6g7h8i9j0" }
/api/events/<slug>
Retourne l'état complet de l'événement : infos, participants (avec présences
et assignations), conducteurs, points de ramassage, accompagnants.
Fonctionne avec le slug admin et le slug visiteur
(le slug visiteur ne retourne pas view_slug dans la réponse).
Réponse 200 OK
{
"event": {
// ni slug, ni view_slug, ni identifiants internes : la réponse est
// lisible avec le lien participant, elle ne doit rien révéler de plus
"title": "Répétition chorale", "date": "2026-06-20",
"location": "Salle Molière", "address": "12 rue Molière",
"startTime": "20:00", "departureTime": "19:30",
"note": "…", "official_link": "…",
"contextType": "music", "returnEnabled": 1,
"mode": "classic", // "classic" | "light"
"pickupEnabled": 0, // ramassage activé
"cargoEnabled": 0, // coffres activés
"discussionEnabled": 1, // espace de discussion activé
"accompagnantsEnabled": 0, // accompagnants activés
"created_at": "2026-06-10T14:32:00"
},
"participants": [
{ "id": 1, "name": "Alice", "is_coming": null,
"needs_carpool": 0, "needs_carpool_return": 0,
"driver_id": null, "return_driver_id": null,
"pickup_point_id": null, "acc_driver_id": null },
{ "id": 2, "name": "Bob", "is_coming": 1,
"needs_carpool": 1, "driver_id": 5, "pickup_point_id": 3,
"acc_driver_id": null }
],
"drivers": [
{ "id": 5, "name": "Marie", "seats": 4,
"drives_outbound": 1, "drives_return": 0,
"pickup_point_ids": [3],
"cargo_size": null, // null | "small" | "medium" | "large"
"notes": "Voiture rouge, plaque AB-123" }
],
"pickup_points": [
{ "id": 3, "name": "Gare Part-Dieu", "address": "…", "time": "19:30" }
],
"accompagnants": [],
"messages": [ // présent si discussionEnabled = 1
{ "id": 1, "author": "Marie", "body": "Qui conduit ?", "created_at": "2026-06-18T19:42:00Z" }
],
"view_slug": "f6g7h8i9j0", // présent uniquement avec le slug admin
"is_view_mode": false
}
is_coming vaut
null (pas répondu), 1 (présent)
ou 0 (absent).
/api/events/<admin_slug>
Met à jour les informations de l'événement. Nécessite le slug admin.
{
"title": "Nouveau titre",
"date": "2026-06-21",
"location": "…", "address": "…",
"startTime": "20:30", "departureTime": "19:45",
"note": "…", "official_link": "…",
"contextType": "generic",
"returnEnabled": 0,
"pickupEnabled": 1, // activer/désactiver le ramassage
"cargoEnabled": 0,
"discussionEnabled": 1, // activer/désactiver l'espace de discussion
"accompagnantsEnabled": 0
}
Le champ mode (classic/light)
est fixé à la création et ne peut pas être modifié via cet endpoint.
Réponse
{ "success": true }
/api/events/<admin_slug>/duplicate
Duplique l'événement pour une nouvelle date. Copie les conducteurs, les points de ramassage
et tous les réglages (mode, pickupEnabled,
discussionEnabled, etc.).
Les participants ne sont pas copiés (à remplir ensuite).
{ "date": "2026-07-18" }
Réponse 201 Created
{ "slug": "z9y8x7w6v5" } // admin slug du nouvel événement
Participants
/api/events/<slug>/participants
Ajoute un participant à l'événement. Fonctionne avec le slug admin et le slug visiteur (modèle collaboratif : chacun peut s'ajouter depuis le lien partagé).
{ "name": "Alice" }
Réponse 201 Created
{ "id": 12, "name": "Alice", "is_coming": null,
"needs_carpool": 0, "needs_carpool_return": 0,
"driver_id": null, "return_driver_id": null,
"pickup_point_id": null }
/api/events/<slug>/participants/import
Importe une liste de participants en une seule requête. Maximum 200 par appel.
{ "items": [{ "name": "Alice" }, { "name": "Bob" }, { "name": "Claire" }] }
Réponse 201 Created
{ "created": [ { "id": 12, "name": "Alice", … }, … ] }
/api/participants/<id>
Met à jour un participant : nom, présence, besoin covoiturage, conducteur assigné, point de ramassage. Tous les champs sont requis (envoyez l'objet participant complet).
{
"name": "Alice Dupont",
"is_coming": 1, // 1 = présent, 0 = absent, null = pas répondu
"needs_carpool": 1, // 1 = besoin d'une place à l'aller
"needs_carpool_return": 0, // 1 = besoin d'une place au retour
"driver_id": 5, // null pour désassigner (aller)
"return_driver_id": null, // null pour désassigner (retour)
"pickup_point_id": 3, // null pour désassigner
"acc_driver_id": null // ID d'un accompagnant avec voiture (null sinon)
}
Réponse
{ "success": true }
/api/participants/<id>
Supprime un participant.
{ "success": true }
Conducteurs
/api/events/<slug>/drivers
Ajoute un conducteur. Fonctionne avec le slug admin et le slug visiteur (modèle collaboratif).
{
"name": "Marie",
"seats": 4, // entre 1 et 9, défaut 2
"notes": "Voiture rouge" // note visible dans le mode répartition (optionnel)
}
Réponse 201 Created
{ "id": 5, "name": "Marie", "seats": 4,
"drives_outbound": 1, "drives_return": 0,
"pickup_point_ids": [],
"notes": "Voiture rouge" }
/api/events/<slug>/drivers/import
Importe plusieurs conducteurs. Maximum 100 par appel.
{ "items": [{ "name": "Marie", "seats": 4 }, { "name": "Paul", "seats": 3 }] }
Réponse 201 Created
{ "created": [ { "id": 5, "name": "Marie", … }, … ] }
/api/drivers/<id>
Met à jour un conducteur.
{
"name": "Marie Martin",
"seats": 5,
"drives_outbound": 1, // conduit à l'aller
"drives_return": 1, // conduit au retour
"pickup_point_ids": [3, 7], // IDs des points de ramassage desservis
"cargo_size": "medium", // null | "small" | "medium" | "large"
"notes": "Voiture rouge" // note affichée dans le mode répartition (null pour effacer)
}
Réponse
{ "success": true }
/api/drivers/<id>
Supprime un conducteur. Les participants qui lui étaient assignés sont automatiquement
désassignés (driver_id → null).
{ "success": true }
Points de ramassage
/api/events/<slug>/pickup_points
{ "name": "Gare Part-Dieu", "address": "Place Charles Béraudier", "time": "19:30" }
Réponse 201 Created
{ "id": 3, "name": "Gare Part-Dieu", "address": "…", "time": "19:30" }
/api/pickup_points/<id>
{ "name": "Gare Part-Dieu", "address": "Place Charles Béraudier, 69003", "time": "19:45" }
Réponse
{ "success": true }
/api/pickup_points/<id>
Supprime un point de ramassage. Les participants qui lui étaient assignés sont automatiquement désassignés.
{ "success": true }
Accompagnants
/api/events/<slug>/accompagnants
Ajoute un accompagnant (groupe sans covoiturage, optionnellement avec un véhicule).
Nécessite d'activer accompagnantsEnabled sur l'événement.
{
"name": "Famille Dupont",
"people_count": 3, // nombre de personnes dans le groupe
"has_car": 1, // 1 = possède un véhicule
"seats": 4 // places disponibles dans ce véhicule (si has_car = 1)
}
Réponse 201 Created
{ "id": 8, "name": "Famille Dupont", "people_count": 3, "has_car": 1, "seats": 4 }
/api/accompagnants/<id>
{
"name": "Famille Dupont",
"people_count": 2,
"has_car": 0,
"seats": 0,
"driver_id": null, // ID d'un conducteur principal (si l'accompagnant voyage avec lui)
"acc_car_id": null // ID d'un autre accompagnant avec voiture
}
Réponse
{ "success": true }
/api/accompagnants/<id>
{ "success": true }
Discussion
Espace de messages rattaché à l'événement. Disponible uniquement si
discussionEnabled = 1 (à activer via PUT /api/events/<admin_slug>).
Maximum 200 messages par événement.
/api/events/<slug>/messages
Retourne les messages dans l'ordre chronologique. Fonctionne avec le slug admin et le slug visiteur.
Réponse 200 OK
[
{ "id": 1, "author": "Marie", "body": "Qui conduit vendredi ?", "created_at": "2026-06-18T19:42:00Z" },
{ "id": 2, "author": "Paul", "body": "Je peux prendre 3 personnes", "created_at": "2026-06-18T20:01:00Z" }
]
/api/events/<slug>/messages
Poste un message. Fonctionne avec le slug admin et le slug visiteur. Retourne 403 si la discussion est désactivée sur l'événement.
{ "author": "Marie", "body": "Qui conduit vendredi ?" }
Réponse 201 Created
{ "id": 1, "author": "Marie", "body": "Qui conduit vendredi ?", "created_at": "2026-06-18T19:42:00Z" }
/api/events/<admin_slug>/messages/<id>
Supprime un message. Nécessite le slug admin (non disponible depuis le lien visiteur).
{ "success": true }
Exemple complet — Python
Créer un covoiturage depuis un script Python, puis récupérer l'état des présences après que les participants ont répondu.
import requests BASE = "https://groupe.covoitequipe.fr" # 1. Créer l'événement avec tout le contenu resp = requests.post(f"{BASE}/api/events/bulk", json={ "event": { "title": "Répétition chorale du 20 juin", "date": "2026-06-20", "location": "Salle Molière, Lyon", "startTime": "20:00", "contextType": "music", "discussionEnabled": 1 }, "participants": [{"name": n} for n in ["Alice", "Bob", "Claire", "David"]], "drivers": [ {"name": "Marie", "seats": 4, "drives_outbound": 1, "drives_return": 1}, {"name": "Paul", "seats": 3, "drives_outbound": 1, "drives_return": 0}, ], "pickup_points": [ {"name": "Gare Part-Dieu", "address": "Place Charles Béraudier", "time": "19:30"} ] }) slugs = resp.json() admin_slug = slugs["admin_slug"] view_url = slugs["view_url"] print(f"Lien participants : {view_url}") print(f"Lien organisateur : {slugs['admin_url']}") # 2. Plus tard : récupérer l'état après que les gens ont répondu state = requests.get(f"{BASE}/api/events/{admin_slug}").json() for p in state["participants"]: statut = {1: "✓ Présent", 0: "✗ Absent", None: "? Pas répondu"}[p["is_coming"]] driver = next((d for d in state["drivers"] if d["id"] == p["driver_id"]), None) voiture = f" → {driver['name']}" if driver else "" print(f" {p['name']} — {statut}{voiture}")