Référence API
Cette fiche donne une vue d'ensemble de l'API REST de TimePick, organisée par familles d'endpoints. Elle sert de point d'entrée pour un développeur ou un intégrateur qui doit appeler l'API depuis un script ou un outil externe — elle ne documente pas chaque endpoint en détail. Pour la liste exhaustive, chaque route est déclarée dans un fichier de server/src/routes/ sur GitHub.
API interne, sans garantie de stabilité
Cette API a été conçue pour l'interface web de TimePick elle-même, pas comme une API publique versionnée. Tant que le projet est en pré-V1 (état au 2026-07-13), ses chemins, ses formats de payload et ses codes d'erreur peuvent changer sans préavis ni période de dépréciation. Un usage en intégration externe reste possible mais doit être revalidé à chaque mise à jour de l'instance.
Base et authentification
URL de base : https://<votre-domaine.tld>/api (ou http://localhost:3000/api en développement).
La quasi-totalité des endpoints hors /setup, /public (lecture) et /health requièrent l'en-tête :
Authorization: Bearer <token>Le <token> est un JWT de session, obtenu via le flux magic link :
POST /api/auth/loginavec{ "email": "alice@example.org" }— envoie un email contenant un lien de connexion. La réponse est volontairement générique (anti-énumération), qu'un compte corresponde ou non à l'email :json{ "data": { "message": "Si cet email est enregistré, vous recevrez un lien de connexion." } }Le lien reçu par email contient un
tokenen paramètre. Le client appelle ensuitePOST /api/auth/verifyavec{ "token": "<token>" }, qui ouvre la session et renvoie le JWT à utiliser dans l'en-têteAuthorizationpour tous les appels suivants.Pour terminer une session côté serveur,
POST /api/auth/logout(authentifié, en-têteAuthorizationrequis) révoque immédiatement toutes les sessions actives de l'utilisateur, pas seulement celle de l'appel : tout jeton émis avant cet appel, même non expiré, est ensuite rejeté par les routes protégées avec401et le codeSESSION_INVALID.
Deux rôles distincts sont portés par le JWT : admin (accès à /api/admin/*) et user (accès à /api/me/* et aux réservations, si hasMemberAccess est vrai — c'est-à-dire si l'utilisateur est invité sur au moins un événement).
Détail des durées de vie de session/token, du rate limiting et des autres protections : voir Sécurité.
Enveloppe de réponse
Toutes les réponses de succès sont enveloppées dans un objet data. Exemple réel, réponse de POST /api/auth/verify :
{
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"sessionTTL": 7200,
"user": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "alice@example.org",
"firstName": "Alice",
"lastName": "Dupont",
"role": "user",
"hasMemberAccess": true
}
}
}Les réponses d'erreur suivent le même principe avec la clé error :
{
"error": {
"code": "INVALID_TOKEN",
"message": "Lien de connexion invalide."
}
}Conversion snake_case → camelCase
La base de données stocke ses colonnes en snake_case (first_name, is_published…). Un middleware global (snakeToCamelMiddleware, monté avant toutes les routes dans server/src/app.ts) convertit automatiquement chaque réponse JSON de succès en camelCase (firstName, isPublished…) avant l'envoi au client. Les corps de requête envoyés par le client, eux, restent en camelCase — la conversion inverse est gérée côté requêtes SQL, pas au niveau de l'API.
Familles d'endpoints
| Préfixe | Rôle | Authentification |
|---|---|---|
/api/auth | Connexion par magic link (demande, vérification), rafraîchissement de session, déconnexion (révocation de toutes les sessions du compte), connexion de secours, renvoi d'invitation | Public pour la demande/vérification/secours ; user/admin pour le rafraîchissement et la déconnexion |
/api/setup | Configuration initiale (création du premier admin avec prénom/nom, identité de l'organisation, test SMTP de démarrage — sortie de secours DELETE /smtp si la config enregistrée devient injoignable) | Public — se ferme dès qu'un administrateur existe |
/api/public | Consultation d'un événement publié par UUID et de ses créneaux, gestion de ses propres réservations, lecture de l'identité publique de l'organisation (façade d'accueil) | Auth optionnelle en lecture (mode consultation sans session) ; user requis pour réserver/annuler |
/api/me | Profil et données de l'utilisateur connecté (événements, créneaux réservés, créneaux disponibles) | user (accès à un membre admin également) |
/api/admin | Administration : dashboard/analytics, utilisateurs, événements, créneaux, invitations, paramètres SMTP/emails/identité visuelle, identité de l'organisation (nom/logo/description/façade), configuration (polling, TTL), enveloppes d'email (shell-parts), codes de secours, notifications d'annulation | admin |
/api/admin/uploads | Upload d'image pour l'éditeur d'emails (max 5 Mo, retourne une URL absolue) | admin |
/health | Statut de santé public, binaire (ok/degraded), sans détail interne | Public |
Deux préfixes publics annexes
Le code expose aussi /api/events (liste et détail des événements publiés, sans authentification, filtrage minimal) et /api/slots (regroupe des routes de gestion admin des créneaux et de réservation/annulation par un membre authentifié, sous un préfixe distinct de /api/admin et /api/public). Ces deux préfixes se recoupent fonctionnellement avec /api/public et /api/admin/events/:id/slots — voir le code pour arbitrer lequel utiliser dans une intégration (server/src/routes/public.events.routes.ts, server/src/routes/slots.routes.ts).
Détail — /api/admin, sous-familles
| Sous-préfixe / groupe | Exemples de routes | Description |
|---|---|---|
| Dashboard / analytics | GET /admin/dashboard, GET /admin/stats, GET /admin/analytics/… | KPI, réservations brutes, engagement, activité par événement |
| Utilisateurs | GET/POST /admin/users, GET/PUT/DELETE /admin/users/:id, POST /admin/users/bulk-delete, POST /admin/users/import, GET /admin/users/export | CRUD, import/export CSV, suppression groupée |
| Événements | GET/POST /admin/events, PUT/DELETE /admin/events/:id, POST /admin/events/:id/duplicate, PUT /admin/events/:id/publish | /unpublish, PUT /admin/events/:id/opening-date | CRUD, publication, duplication, date d'ouverture |
| Créneaux | GET/POST /admin/events/:eventId/slots, GET/PUT/DELETE /admin/slots/:id | Gestion des créneaux d'un événement |
| Invitations | POST /admin/events/:id/invitations/send, GET /admin/events/:id/invitations, POST /admin/events/:id/invitations/:userId/resend, POST /admin/events/:id/invitations/resend-unanswered | Envoi, statut, relances |
Paramètres (/admin/settings) | GET/PUT/DELETE /settings/smtp, GET/PATCH /settings/email-brand, GET/PATCH /settings/email-templates/:templateKey, GET/PUT /settings/organization, POST/DELETE /settings/organization/logo | SMTP, identité visuelle, modèles d'email, identité de l'organisation |
| Configuration | GET/PUT /admin/config/polling-interval, GET/PUT /admin/config/magic-link | Intervalle de polling, TTL magic link |
Enveloppes d'email (/admin/shell-parts) | PUT/DELETE /shell-parts/:ownerKind/:ownerId/:partKind | Parties MJML d'en-tête/pied de page (cascade marque → modèle → événement) |
| Codes de secours | GET /admin/recovery-codes/status, POST /admin/recovery-codes/generate, PATCH /admin/recovery-codes/dismiss | Génération et statut des codes de secours admin |
| Notifications d'annulation | GET /admin/cancellation-notifications, POST /admin/cancellation-notifications/resend | Notifications en attente et relance groupée |
Détail — identité de l'organisation
Nom, description et logo affichés sur la façade publique (OrganizationHome) et repris dans l'administration sont servis par les mêmes handlers, montés à trois endroits :
| Méthode | Chemin | Auth |
|---|---|---|
GET | /api/admin/settings/organization | admin |
PUT | /api/admin/settings/organization | admin |
POST | /api/admin/settings/organization/logo | admin |
DELETE | /api/admin/settings/organization/logo | admin |
GET | /api/setup/organization | Public — se ferme dès qu'un administrateur existe |
PUT | /api/setup/organization | Public — se ferme dès qu'un administrateur existe |
POST | /api/setup/organization/logo | Public — se ferme dès qu'un administrateur existe |
DELETE | /api/setup/organization/logo | Public — se ferme dès qu'un administrateur existe |
GET | /api/public/organization | Public |
GET renvoie { "data": { "name", "logo", "description", "homepageFacade" } } — les 4 mêmes champs sur les trois variantes, y compris la façade publique. PUT accepte :
{
"name": "Mon association",
"description": "<p>Texte riche…</p>",
"homepageFacade": true
}name est la seule clé requise (chaîne vide acceptée — la façade publique retombe alors sur /login) ; description (HTML riche, assaini côté serveur, balises autorisées limitées à p/br/strong/em/a) et homepageFacade sont optionnels. La réponse renvoie l'objet complet mis à jour, dans la même forme que GET.
POST .../organization/logo attend un multipart/form-data avec un unique champ fichier logo (image, 5 Mo max, normalisée en WebP 512×512) et renvoie { "data": { "logo": "<url>" } }. DELETE .../organization/logo supprime le fichier et renvoie 204 sans corps.
Périmètre volontairement restreint de la façade publique
GET /api/public/organization ne requiert aucune authentification et ne renvoie que ces 4 champs — jamais un autre réglage de l'instance, même si l'administration en gère davantage.
Exemple de payload d'erreur de validation
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Format d'email invalide"
}
}La syntaxe du corps d'erreur varie selon l'endpoint et son ancienneté : certaines routes renvoient la forme étiquetée { "error": { "code", "message" } } ci-dessus, d'autres une forme plate à code frère { "error": "message", "code": "…" }. Ce qui ne varie pas : toute réponse 4xx porte un code, dans l'une ou l'autre forme. Deux exceptions, délibérées — la connexion de secours répond un code seul sans message ({ "code": "INVALID_CREDENTIALS" }, pour ne pas distinguer un compte inconnu d'un code erroné), et les replis techniques en 5xx n'ont pas de code (leur message est une phrase générique qui n'apprend rien de plus).
Lisez le code, pas le message : le message est destiné à l'utilisateur final, peut être reformulé sans préavis, et n'est pas un identifiant. La liste des codes vit dans shared/src/errorCodes.ts.
Endpoint de santé
GET /health (public, hors préfixe /api) renvoie un statut binaire sans authentification, utilisable par une sonde de supervision externe :
{
"status": "ok",
"timestamp": "2026-07-13T08:00:00.000Z",
"services": {
"smtp": "ok"
}
}status et services.smtp valent ok ou degraded (chaînes) ; le code HTTP reste 200 dans les deux cas.
Un statut détaillé réservé aux administrateurs (base de données incluse) existe à GET /api/admin/health. Voir Dépannage pour l'usage de ces deux endpoints en diagnostic.
Rate limiting
Plusieurs endpoints sensibles (/api/auth/login, /api/auth/emergency-login, envois de test SMTP/email, upload d'image) sont limités en nombre de requêtes par fenêtre de temps, indépendamment de toute configuration. Le détail des seuils est documenté dans Sécurité — Limitation de débit ; un dépassement renvoie un code HTTP 429 avec { "error": { "code": "RATE_LIMITED", … } }.
Pour aller plus loin
- Détail des tables et colonnes manipulées par ces endpoints : Modèle de données.
- Liste exhaustive et à jour des routes :
server/src/routes/sur GitHub (un fichier par famille, montage des préfixes dansserver/src/app.ts).