Aller au contenu

Pro Account And Structure For Front

Ce document décrit comment le front doit consommer les APIs backend pour le début du parcours pro.

Périmètre de cette première partie :

  • choix du type de compte ;
  • création d'un compte pro ;
  • inscription en waitlist pour un futur compte pro ;
  • état initial d'un compte pro sans structure ;
  • création d'une structure ;
  • revendication d'une structure catalogue existante ;
  • rattachement à une structure déjà gérée ;
  • lecture du contexte pro courant ;
  • lecture des capabilities et quotas de structure ;
  • création et gestion d'un event pro.

Les exemples ci-dessous utilisent les préfixes :

  • public : /v1/public
  • privé web : /v1/web/api

Les mêmes routes privées existent aussi sous /v1/mobile/api et /v1/dev/api.

Principes importants pour le front

Avant d'implémenter les écrans, il faut garder en tête ces contraintes du backend actuel :

  • le choix individual vs pro se fait via accountType, au signup direct ou à l'inscription waitlist ;
  • un compte pro peut exister sans structure ;
  • créer une structure et revendiquer une structure existante sont deux flows différents ;
  • ces deux flows sont asynchrones :
  • ils passent par une modération / approbation ;
  • ils ne donnent pas immédiatement un ownership actif sur une structure valid ;
  • GET /me/subscription décrit la souscription de la Person, pas les entitlements de structure ;
  • le backend expose désormais des endpoints self dédiés pour :
  • lister "mes structures gérées" ;
  • lire les capabilities et quotas effectifs d'une structure.

Conséquence front :

  • le front peut implémenter correctement les étapes 1 à 5 avec les APIs existantes ;
  • l'étape 6 reste partiellement couverte car il n'existe toujours pas de flow self-service pour rejoindre une structure déjà gérée ;
  • les étapes 7 et 8 disposent maintenant d'endpoints canoniques.

Étape 1 - Choisir le type de compte à la création

Objectif

Faire diverger le parcours individual et pro avant le signup.

Ce que le front doit faire

  • afficher un choix explicite entre individual et pro ;
  • mémoriser ce choix jusqu'à l'appel de signup ;
  • ne pas essayer de convertir un compte après coup : ce n'est pas supporté en V1.

API à appeler

Aucune.

Le choix est porté dans le payload de signup.

Sortie attendue

Le front sait s'il doit appeler POST /v1/public/accounts avec :

  • accountType=individual
  • ou accountType=pro

Étape 2 - Créer un compte pro

Objectif

Créer un compte authentifiable de type pro.

Endpoint

  • POST /v1/public/accounts

Payload

{
  "pseudo": "club-alpha",
  "email": "contact@club-alpha.test",
  "accountType": "pro",
  "challengeToken": "token"
}

Réponse de succès

201 Created

{
  "id": 123,
  "pseudo": "club-alpha",
  "accountType": "pro"
}

Réponses à gérer

  • 400 si payload invalide, pseudo invalide, email invalide ou conflit pseudo / email ;
  • 403 avec code = "invalid_challenge" si le challenge antibot est invalide, manquant ou expiré ;
  • 503 avec code = "challenge_unavailable" si la vérification antibot est indisponible ;
  • 500 si le backend ne peut pas finaliser le signup pour une autre raison.

Comportement UI attendu

  • en cas de succès, considérer le compte comme créé mais pas encore activé ;
  • afficher la suite du flow d'activation générique du produit ;
  • ne pas attendre qu'une structure soit créée automatiquement ;
  • ne pas déduire de contexte structure à ce stade.

Point produit important

Le signup pro :

  • crée bien une Person de type pro ;
  • attribue aussi le rôle ROLE_PRO ;
  • attribue une souscription par défaut au niveau Person.

Cette souscription Person ne doit pas être utilisée pour déduire les droits pro d'une structure future.

Étape 2 bis - Inscrire un futur compte pro en waitlist

Objectif

Permettre à un utilisateur qui vise un compte pro d'entrer dans la waitlist globale sans perdre cette intention.

Endpoint

  • POST /v1/public/waitlist-entries

Payload

{
  "email": "contact@club-alpha.test",
  "accountType": "pro",
  "cityId": 12,
  "sportIds": [1, 2],
  "challengeToken": "token"
}

Règle contractuelle

accountType est obligatoire et doit valoir individual ou pro.

Le backend ne remplace pas un champ absent par individual. Un payload waitlist sans accountType explicite est invalide.

Effet au signup depuis invitation waitlist

Quand l'utilisateur finalise son inscription via une invitation waitlist, le backend crée le compte avec le accountType porté par l'entrée waitlist.

Le front ne doit donc pas demander ni envoyer accountType dans le payload de signup depuis invitation waitlist; il doit seulement envoyer le token, le pseudo et le challenge.

Étape 3 - Comprendre l'état initial d'un compte pro sans structure

Objectif

Savoir quoi afficher juste après création / activation d'un compte pro.

Endpoints utiles

  • GET /v1/web/api/me
  • GET /v1/web/api/me/subscription

Ce que renvoie GET /me

Le payload expose notamment :

  • userId
  • personId
  • accountType
  • profileStatus
  • pseudo
  • roles

Pour un compte pro, le front doit s'attendre à :

  • accountType = "pro"
  • roles contenant ROLE_USER et ROLE_PRO

Ce que renvoie GET /me/subscription

Le payload décrit :

  • le plan courant de la Person ;
  • les rights ;
  • les organizerCapabilities ;
  • les capabilities ;
  • les quotas ;
  • les quotaUsage.

Ce que le front doit en conclure

Après signup pro, l'utilisateur peut être :

  • pro ;
  • authentifié ;
  • sans aucune structure gérée.

Le front doit donc prévoir un écran ou état vide de type :

  • "Créer une structure"
  • "Revendiquer une structure existante"

Important

GET /me/subscription ne décrit pas les capabilities / quotas d'une structure.

Le front ne doit pas utiliser cette réponse pour décider si l'utilisateur peut :

  • créer un event pro ;
  • créer une proposal pro ;
  • inviter des adhérents ;
  • configurer la solidarité d'un event pro ;
  • utiliser les scopes d'audience avancés.

Ces décisions dépendent du plan effectif de la structure, pas du compte seul.

Étape 4 - Créer une nouvelle structure

Objectif

Permettre à un compte pro de créer une structure qui entrera ensuite dans un cycle de modération / approbation.

Endpoint

  • POST /v1/web/api/structures

Préconditions

  • utilisateur authentifié ;
  • rôle ROLE_PRO.

Payload

{
  "active": true,
  "type": "associative",
  "name": "Club Alpha",
  "description": "Club de test",
  "address": "10 rue du Test",
  "email": "contact@club-alpha.test",
  "phoneNumber": "0102030405",
  "cityId": 12,
  "avatarId": 44,
  "sportsIds": [3, 7]
}

Réponse de succès

201 Created

Le backend renvoie uniquement l'identifiant créé :

{
  "id": 123
}

Sémantique métier importante

Le backend crée :

  • la structure ;
  • un plan par défaut au niveau structure ;
  • une StructureClaimRequest de type create en statut pending.

En revanche, il ne crée pas immédiatement un ownership actif.

La structure créée sort donc en pratique dans un état de type :

  • reviewStatus = "pending_moderation"

Comportement UI attendu

Après succès :

  • stocker structureId ;
  • si l'écran courant a besoin de la fiche complète, relire GET /structures/{idStructure} ;
  • si l'écran courant est une liste paginée / filtrée / triée côté serveur, relancer la requête canonique de liste plutôt que d'injecter localement un item ;
  • afficher clairement que la structure est en attente de validation ;
  • ne pas présenter la structure comme déjà "validée et pleinement gérée".

Si la vue courante est le feed public GET /structures, la structure créée n'a en pratique pas vocation à y apparaître tant qu'elle reste en pending_moderation.

Point subtil mais utile au front

Même en attente de modération, le créateur peut relire et éditer sa structure via les APIs de structure tant que le backend reconnaît sa demande de création en cours.

Le front peut donc enchaîner sur :

  • GET /structures/{idStructure}
  • PATCH /structures/{idStructure}

mais doit garder l'état "pending moderation" visible.

Étape 5 - Revendiquer une structure catalogue existante

Objectif

Permettre à un compte pro de demander la gestion d'une structure déjà présente dans le catalogue.

Séquence front recommandée

  1. rechercher une structure existante ;
  2. ouvrir sa fiche ;
  3. si l'utilisateur veut la gérer, créer une demande de revendication.

Endpoints utiles

  • GET /v1/web/api/structures
  • GET /v1/web/api/structures/{idStructure}
  • POST /v1/web/api/structures/{idStructure}/claim

Recherche de structure

GET /structures renvoie un StructureFeedResponse :

  • items
  • nextCursor

Chaque item expose notamment :

  • id
  • type
  • name
  • description
  • primarySport
  • totalSports
  • activeAdherentsCount
  • city
  • avatar
  • hasFacilities
  • viewerRelation si l'utilisateur connecté a une relation avec la structure

Valeurs possibles pour viewerRelation :

  • owner
  • manager
  • adherent

Filtres utiles :

  • q
  • sports[]
  • types[]
  • city
  • distance
  • itemsPerPage
  • cursor

Création de la revendication

POST /structures/{idStructure}/claim ne prend pas de body.

Réponse de succès

201 Created

Le backend renvoie un StructureClaimRequestDTO avec notamment :

  • id
  • structureId
  • structureName
  • requestedByPersonId
  • requestType
  • status
  • createdAt

Le front doit s'attendre à :

  • requestType = "claim_existing"
  • status = "pending"

Réponses métier à gérer

  • 404 si la structure n'existe pas ou n'est pas revendicable ;
  • 400 si une revendication est déjà en cours ;
  • 400 si la structure est déjà gérée ;
  • 403 si l'acteur n'est pas un compte pro.

Comportement UI attendu

Après succès :

  • afficher un état "demande envoyée" ;
  • bloquer le bouton de revendication immédiat ;
  • ne pas considérer l'utilisateur comme déjà gestionnaire ;
  • ne pas afficher les écrans de gestion structure comme disponibles.

Étape 6 - Rejoindre une structure déjà gérée

Objectif

Couvrir le cas où un compte pro est rattaché à une structure déjà gérée par un autre owner / gestionnaire.

État actuel du backend

Le modèle backend supporte bien :

  • StructureMembership
  • un owner ;
  • des permissions déléguées

Mais il n'existe pas aujourd'hui d'endpoint front self dédié pour :

  • inviter un compte pro dans une structure ;
  • accepter cette invitation ;
  • lister les memberships du compte connecté.

Conséquence front

Cette étape ne peut pas être implémentée proprement en self-service avec les APIs actuelles.

Le front doit donc la considérer comme :

  • un état métier existant ;
  • mais sans flow utilisateur canonique exposé aujourd'hui.

Ce qu'il faut documenter côté front

Si ce cas apparaît plus tard dans le produit, il faudra ajouter au minimum des endpoints pour :

  • lister les structures gérées par l'utilisateur ;
  • lister les permissions portées par chaque membership ;
  • inviter / rattacher un compte pro à une structure ;
  • accepter ou refuser ce rattachement.

Étape 7 - Lire le contexte pro courant

Objectif

Savoir dans quelle structure l'utilisateur agit et quelles fiches structure il peut ouvrir pour travailler.

Endpoints à utiliser

  • GET /v1/web/api/me
  • GET /v1/web/api/me/structures
  • GET /v1/web/api/structures/{idStructure}
  • GET /v1/web/api/structures/{idStructure}/entitlements

Ce que ces endpoints permettent réellement

GET /me permet de savoir :

  • qui est connecté ;
  • que le compte est de type pro ;
  • quels rôles globaux il porte.

GET /me/structures permet de lister le contexte structure exploitable du compte connecté.

Chaque item expose notamment :

  • structureId
  • name
  • reviewStatus
  • type
  • active
  • city
  • avatar
  • primarySport
  • activeAdherentsCount
  • access

Le bloc access expose notamment :

  • kind
  • isOwner
  • membershipStatus
  • permissions
  • claimRequestId
  • claimRequestType
  • claimRequestStatus
  • reviewNote

Les valeurs utiles de access.kind sont :

  • owner_membership
  • manager_membership
  • pending_create_claim
  • rejected_create_claim
  • pending_claim_existing
  • rejected_claim_existing

GET /structures/{idStructure} permet de relire :

  • une structure publique valid ;
  • ou une structure non publique si l'acteur y a déjà accès.

Quand le viewer connecté a déjà un contexte sur la structure, la réponse peut aussi exposer :

  • access
  • actions

Le bloc access reste aligné avec GET /me/structures.

Le bloc actions reste aligné avec GET /structures/{idStructure}/entitlements mais sans les détails de plan / quotas / usages.

GET /structures/{idStructure}/entitlements permet de lire, pour une structure donnée :

  • le type d'accès courant ;
  • le plan effectif ;
  • les capabilities ;
  • les quotas ;
  • le quota usage ;
  • les actions front-friendly.

Séquence front recommandée

  1. appeler GET /me pour identifier le compte connecté ;
  2. appeler GET /me/structures pour construire le sélecteur ou la landing pro ;
  3. choisir une structure à partir de la liste ;
  4. relire si nécessaire la fiche complète avec GET /structures/{idStructure} ;
  5. appeler GET /structures/{idStructure}/entitlements pour piloter les écrans d'action.

Ce que le front doit en conclure

  • le cold start pro est désormais reconstructible proprement ;
  • le front ne doit plus dépendre uniquement d'un structureId conservé dans son state local ;
  • le bloc access de GET /me/structures est la source de vérité pour comprendre si l'utilisateur est owner, manager, créateur en attente de modération, ou simple revendication en attente ;
  • GET /structures/{idStructure} reste utile pour la fiche structure et peut maintenant porter le contexte viewer courant (access, actions) ;
  • GET /structures/{idStructure}/entitlements reste la source de vérité pour piloter les actions métier.

Étape 8 - Lire les capabilities et quotas de la structure

Objectif

Décider côté UI si une action pro doit être :

  • visible ;
  • masquée ;
  • désactivée ;
  • ou laissée tentable puis gérée via erreur métier backend.

Endpoint à utiliser

  • GET /v1/web/api/structures/{idStructure}/entitlements

Ce que renvoie cet endpoint

Le payload expose notamment :

  • structureId
  • structureName
  • reviewStatus
  • access
  • plan
  • capabilities
  • quotas
  • quotaUsage
  • actions

Le bloc plan expose :

  • code
  • name
  • active
  • isDefault
  • periodStart
  • periodEnd

Le bloc actions expose notamment :

  • canManageProfile
  • canManageMedia
  • canManageActivities
  • canManageFacilities
  • canCreateEvents
  • canManageAdherents
  • canInviteAdherentsWithoutWaitlist
  • canCreateEventProposals
  • canUseAdvancedAudience
  • canConfigureEventSolidarity
  • canAllowExternalHelpers

Règles d'interprétation importantes

  • capabilities et quotas décrivent le plan effectif de la structure ;
  • quotaUsage décrit l'usage courant de la période active ;
  • actions est la projection backend canonique pour piloter l'UI ;
  • GET /me/subscription reste limité aux entitlements de la Person.

Règle utile :

  • canInviteAdherentsWithoutWaitlist implique aussi la gestion adhérents ;
  • autrement dit, une structure ne doit pouvoir bypass la waitlist que si elle peut déjà gérer son roster d'adhérents.

Cas subtil important

Une structure créée par le compte mais encore en attente de modération peut ressortir dans GET /structures/{idStructure}/entitlements avec :

  • des droits d'édition de structure ;
  • mais sans droit de création d'event, de proposal, ni de gestion adhérents.

Le front doit donc utiliser actions, et non déduire seul les possibilités à partir du simple fait que l'utilisateur "voit" la structure.

Stratégie front recommandée

  • utiliser GET /me/structures pour choisir la structure courante ;
  • utiliser GET /structures/{idStructure}/entitlements pour afficher ou masquer les actions ;
  • conserver les erreurs métier des endpoints d'action comme filet de sécurité ;
  • ne pas réutiliser GET /me/subscription pour piloter une UI structure.

Étape 9 - Créer un event pro

Objectif

Créer un event rattaché à une structure gérée par le compte pro.

Endpoints à utiliser

  • GET /v1/web/api/me/structures
  • GET /v1/web/api/structures/{idStructure}/entitlements
  • POST /v1/web/api/events

Préconditions

  • utilisateur authentifié ;
  • profil valid ;
  • compte de type pro ;
  • structure courante choisie ;
  • actions.canCreateEvents = true sur GET /structures/{idStructure}/entitlements.

Payload

{
  "title": "Tournoi Alpha",
  "description": "Session de test",
  "address": "10 rue du Test",
  "organizerStructureId": 12,
  "venueStructureId": 34,
  "creatorParticipates": true,
  "audienceScope": "everyone",
  "level": 3,
  "startAt": "2026-04-20T18:00:00Z",
  "endAt": "2026-04-20T20:00:00Z",
  "minSeats": 8,
  "maxSeats": 16,
  "waitlistCapacity": 4,
  "sportId": 7,
  "cityId": 44,
  "avatarId": 91,
  "solidarity": {
    "enabled": false
  }
}

Règles métier importantes

  • pour un compte pro, organizerStructureId est obligatoire ;
  • venueStructureId est optionnel ;
  • audienceScope vaut everyone par défaut ;
  • un audienceScope différent de everyone n'est autorisé que si la structure dispose de la capability d'audience avancée ;
  • creatorParticipates vaut true par défaut ;
  • si creatorParticipates=false, le créateur n'est pas inscrit comme participant et devient EventStaffMember actif avec le titre Organisateur ;
  • solidarity est optionnel ; son paramétrage détaillé sera documenté dans un bloc séparé.

Réponse de succès

201 Created

Le backend renvoie un Event complet, avec notamment :

  • id
  • title
  • description
  • address
  • startAt
  • endAt
  • minSeats
  • maxSeats
  • confirmedCount
  • waitlistCapacity
  • waitlistCount
  • recruitmentMode
  • audienceScope
  • organizerStructureId
  • venueStructureId
  • avatar
  • sport
  • city
  • creator
  • participants

Réponses métier à gérer

  • 400 :
  • payload invalide ;
  • date passée ;
  • endAt <= startAt ;
  • incohérence minSeats/maxSeats ;
  • waitlistCapacity trop élevée ;
  • audienceScope invalide ;
  • level invalide ;
  • 403 :
  • compte non pro ;
  • profil non validé ;
  • structure non exploitable ;
  • permission structure absente ;
  • capability plan absente ;
  • quota mensuel dépassé ;
  • 404 :
  • structure, ville, sport, avatar ou venueStructureId introuvable.

Comportement UI attendu

  • avant l'appel de création, choisir explicitement la structure courante ;
  • idéalement, bloquer le CTA si actions.canCreateEvents=false ;
  • sur succès, naviguer vers la fiche détail de l'event ;
  • conserver les erreurs backend comme source de vérité finale.

Point important pour le front

Le backend calcule recruitmentMode automatiquement :

  • public si audienceScope=everyone
  • restricted sinon

Le front ne doit pas essayer de l'envoyer lui-même.

Étape 10 - Lire et gérer un event pro existant

Objectif

Permettre au front de :

  • afficher une fiche détail d'event pro ;
  • éditer les champs autorisés ;
  • changer l'avatar ;
  • supprimer l'event.

Endpoints à utiliser

  • GET /v1/web/api/events/{idEvent}
  • PATCH /v1/web/api/events/{idEvent}
  • PATCH /v1/web/api/events/{idEvent}/avatar
  • DELETE /v1/web/api/events/{idEvent}

Lecture détail

GET /events/{idEvent} renvoie un EventDetailDTO avec notamment :

  • id
  • title
  • description
  • address
  • organizerStructureId
  • venueStructureId
  • level
  • startAt
  • endAt
  • minSeats
  • maxSeats
  • confirmedCount
  • waitlistCapacity
  • waitlistCount
  • recruitmentMode
  • audienceScope
  • avatar
  • sport
  • city
  • creator
  • isRegistered
  • isCreator
  • participants
  • solidarity

Règle importante :

  • si l'acteur n'a pas accès à l'event, le backend renvoie 404, pas un détail partiel.

Qui peut gérer un event pro

Pour un event structure-backed, un acteur peut gérer l'event s'il a sur la structure :

  • un membership actif owner ;
  • ou un membership actif avec la permission create_structure_events.

Le front doit s'appuyer en pratique sur :

  • GET /structures/{idStructure}/entitlements
  • puis les réponses métier des endpoints d'action.

Mise à jour de l'event

PATCH /events/{idEvent} accepte uniquement les champs suivants :

  • title
  • description
  • address
  • audienceScope
  • level
  • startAt
  • endAt
  • minSeats
  • maxSeats
  • waitlistCapacity
  • sportId
  • cityId

Champs non patchables via cet endpoint

Le front ne doit pas essayer de modifier ici :

  • organizerStructureId
  • venueStructureId
  • creatorParticipates
  • avatarId

Réponse de succès du patch

200 OK

Le backend renvoie un UpdateEventResponse :

  • event
  • participantsDelta

participantsDelta permet au front de comprendre l'effet d'un changement de maxSeats :

  • promotedToMain
  • movedToWaiting

Règles métier importantes du patch

  • audienceScope n'est patchable que pour un event pro ;
  • un audienceScope avancé exige toujours la capability structure dédiée ;
  • un changement de maxSeats peut promouvoir ou rétrograder des participants ;
  • les validations de dates, places et waitlistCapacity restent actives à l'édition.

Mise à jour de l'avatar

PATCH /events/{idEvent}/avatar

Payload :

{
  "avatarId": 92
}

Réponse de succès :

200 OK

{
  "eventId": 123,
  "avatarId": 92
}

Suppression

DELETE /events/{idEvent}

Réponse de succès :

204 No Content

Règle métier importante de suppression

Le backend désinscrit les participants actifs avant de supprimer l'event.

Le front peut donc considérer l'event comme définitivement indisponible après succès du 204.

Ce qui n'est pas couvert dans ce bloc

La doc de cette étape ne détaille pas encore :

  • les audienceScope un par un ;
  • les invitations d'accès à un event ;
  • le staff d'event ;
  • le paramétrage détaillé de la solidarité.

Étape 11 - Comprendre les audienceScope d'un event pro

Objectif

Permettre au front de :

  • choisir le bon audienceScope à la création ou à l'édition ;
  • comprendre les règles de visibilité dans le feed ;
  • comprendre les règles d'accès au détail et à l'inscription.

Valeurs supportées

  • everyone
  • invite_only
  • members_only
  • members_and_invitees

Règles de création / édition

  • pour un event pro, audienceScope vaut everyone par défaut ;
  • un audienceScope différent de everyone exige la capability d'audience avancée côté structure ;
  • le front doit donc idéalement n'afficher ces options que si actions.canUseAdvancedAudience=true sur GET /structures/{idStructure}/entitlements.

Sémantique d'accès

Pour un event pro, le backend calcule l'accès complet à partir de :

  • la capacité à gérer l'event ;
  • l'état registered ;
  • le statut EventStaffMember actif ;
  • l'adhésion active à la structure ;
  • une invitation d'event active d'effet access.

Règles par scope

  • everyone
  • accès complet pour tout utilisateur autorisé à consulter le feed ;
  • inscription autorisée sans invitation d'accès ;
  • invite_only
  • accès complet si invitation active d'effet access ;
  • sinon, feed en mode signal et détail indisponible ;
  • members_only
  • accès complet si adhésion active à la structure organisatrice ;
  • sinon, feed en mode signal et détail indisponible ;
  • members_and_invitees
  • accès complet si adhésion active ou invitation active d'effet access ;
  • sinon, feed en mode signal et détail indisponible.

Exceptions importantes

Quel que soit le audienceScope, le backend conserve un accès complet pour :

  • les gestionnaires autorisés de l'event ;
  • les participants inscrits ;
  • les EventStaffMember actifs.

Conséquence feed / détail

Dans GET /events/feed :

  • carte event complète si l'acteur a l'accès complet ;
  • carte signal sinon.

Dans GET /events/{idEvent} :

  • 200 si l'acteur a l'accès complet ;
  • 404 sinon.

Le front ne doit donc pas supposer qu'un item vu dans le feed donne toujours accès au détail complet.

Étape 12 - Gérer les invitations d'event

Objectif

Permettre à un gestionnaire d'event pro de :

  • cibler des individual ;
  • leur ouvrir l'accès à un event restreint ;
  • ou simplement les notifier.

Endpoints à utiliser

  • GET /v1/web/api/events/{idEvent}/invitations
  • POST /v1/web/api/events/{idEvent}/invitations
  • DELETE /v1/web/api/events/{idEvent}/invitations/{idPerson}?effect={effect}
  • POST /v1/web/api/events/{idEvent}/invitations/decline

Préconditions côté gestionnaire

  • l'event doit être un event pro ;
  • l'acteur doit pouvoir gérer l'event.

Les invitations d'event ne sont pas supportées pour les events perso.

Effets supportés

  • access
  • notify

Payload d'upsert

{
  "personId": 456,
  "effect": "access"
}

Réponse de succès

GET, POST et DELETE renvoient une liste de EventInvitationDTO :

  • id
  • effect
  • status
  • person

Cible supportée

Le backend n'accepte que des comptes individual.

Une invitation d'event vers un compte pro est refusée.

Règles métier par audienceScope

  • everyone
  • seulement des invitations notify ;
  • invite_only
  • seulement des invitations access ;
  • members_only
  • seulement des invitations notify ;
  • et uniquement vers des adhérents actifs de la structure ;
  • members_and_invitees
  • adhérent actif => seulement notify ;
  • non-adhérent => seulement access.

Sémantique d'upsert

L'upsert ne crée pas un doublon logique par event + person + effect.

Si une ligne existe déjà :

  • si elle est registered, elle reste telle quelle ;
  • sinon, le backend la remet en sent.

Révocation

La révocation se fait par :

  • DELETE /events/{idEvent}/invitations/{idPerson}?effect=access
  • ou ...effect=notify

Point important :

  • une invitation registered ne peut plus être révoquée.

Decline côté invité

L'invité peut décliner ses invitations actives via :

  • POST /events/{idEvent}/invitations/decline

Cet endpoint agit sur les invitations de la personne connectée pour cet event.

Statuts utiles pour le front

Le backend peut renvoyer :

  • sent
  • seen
  • declined
  • revoked
  • registered

En V1, le front doit surtout piloter les parcours autour de :

  • sent
  • declined
  • revoked
  • registered

Le statut seen peut apparaître côté API, mais il n'y a pas dans ce bloc de flow front dédié pour le piloter explicitement.

Point produit important

Une invitation access n'a pas de flow d'acceptation explicite séparé.

Son effet est immédiat sur les règles de visibilité / inscription tant qu'elle est active.

Étape 13 - Gérer le EventStaffMember

Objectif

Permettre à un gestionnaire d'event pro d'ajouter une équipe opérationnelle autour de l'event.

Terminologie importante

Le nom métier / persistance à jour est :

  • EventStaffMember

Les endpoints exposés au front restent cependant :

  • /events/{idEvent}/staff

et les DTO de réponse restent :

  • EventStaffDTO

Endpoints à utiliser

  • GET /v1/web/api/events/{idEvent}/staff
  • POST /v1/web/api/events/{idEvent}/staff
  • PATCH /v1/web/api/events/{idEvent}/staff/{idPerson}
  • DELETE /v1/web/api/events/{idEvent}/staff/{idPerson}

Qui peut lire le staff

Le staff d'un event pro est lisible par un acteur qui a déjà le droit de voir l'event.

Donc :

  • gestionnaire ;
  • participant ;
  • EventStaffMember actif ;
  • ou utilisateur ayant l'accès normal via le audienceScope.

Qui peut gérer le staff

Seul un acteur qui peut gérer l'event pro.

Cibles supportées

Le backend accepte :

  • individual
  • pro

à condition que le profil soit valid.

Payload de création / upsert

{
  "personId": 456,
  "title": "Accueil"
}

Payload de patch

{
  "title": "Logistique",
  "status": 1
}

status supporte :

  • 1 = actif
  • 2 = inactif

Réponse de succès

Les endpoints GET, POST, PATCH et DELETE renvoient une liste de EventStaffDTO actifs :

  • id
  • title
  • status
  • person

Sémantique métier importante

  • un EventStaffMember n'est pas un participant par défaut ;
  • un EventStaffMember n'obtient pas de droit de gestion ;
  • un EventStaffMember obtient un accès opérationnel de lecture ;
  • un EventStaffMember actif peut voir le détail complet de l'event ;
  • un EventStaffMember actif peut voir la liste des participants ;
  • un EventStaffMember actif accède à la conversation générale de l'event.

Cas particulier à connaître

Si un créateur pro crée un event avec creatorParticipates=false, le backend le crée automatiquement en EventStaffMember actif avec le titre Organisateur.

Le front ne doit pas essayer de recréer cette relation immédiatement après la création.

Étape 14 - Gérer les event proposals pro

Objectif

Permettre à une structure de proposer plusieurs créneaux à un groupe de personnes, puis de convertir automatiquement la proposal en event quand un créneau atteint le seuil requis.

Endpoints à utiliser

  • GET /v1/web/api/event-proposals/feed
  • GET /v1/web/api/event-proposals/history
  • GET /v1/web/api/event-proposals/{idEventProposal}
  • POST /v1/web/api/event-proposals
  • PATCH /v1/web/api/event-proposals/{idEventProposal}
  • PUT /v1/web/api/event-proposals/{idEventProposal}/slots/{idSlot}/response

Préconditions de création côté pro

  • utilisateur authentifié ;
  • profil valid ;
  • compte pro ;
  • structure courante choisie ;
  • actions.canCreateEventProposals = true sur GET /structures/{idStructure}/entitlements.

Payload de création

{
  "title": "Créneau entraînement Alpha",
  "description": "On cherche le meilleur créneau",
  "address": "10 rue du Test",
  "organizerStructureId": 12,
  "creatorParticipatesOnConversion": true,
  "sportId": 7,
  "cityId": 44,
  "level": 3,
  "minSeats": 6,
  "maxSeats": 12,
  "avatarId": 91,
  "slots": [
    {
      "startAt": "2026-04-20T18:00:00Z",
      "endAt": "2026-04-20T20:00:00Z"
    },
    {
      "startAt": "2026-04-21T18:00:00Z",
      "endAt": "2026-04-21T20:00:00Z"
    }
  ],
  "inviteePersonIds": [101, 102, 103]
}

Règles métier importantes de création

  • pour un compte pro, organizerStructureId est obligatoire ;
  • avatarId est obligatoire ;
  • slots doit contenir entre 1 et 3 créneaux ;
  • tous les slots doivent être futurs, avec endAt > startAt ;
  • les invités doivent être :
  • des comptes individual ;
  • avec un profil valid ;
  • différents du créateur ;
  • creatorParticipatesOnConversion vaut true par défaut ;
  • si creatorParticipatesOnConversion=false, le créateur deviendra EventStaffMember sur l'event converti au lieu d'être inscrit comme participant.

Réponse de succès à la création

201 Created

Le backend renvoie un EventProposal brut avec notamment :

  • id
  • createdById
  • organizerStructureId
  • creatorParticipatesOnConversion
  • expiresAt
  • title
  • description
  • address
  • sportId
  • cityId
  • minSeats
  • maxSeats
  • avatarId
  • status
  • slots
  • invitees

Statuts de proposal

Le champ status est numérique.

Mapping à utiliser côté front :

  • 1 = open
  • 2 = converted
  • 3 = cancelled
  • 4 = expired

Lecture feed / history

GET /event-proposals/feed et GET /event-proposals/history renvoient un EventProposalFeedResponse :

  • items
  • nextCursor

Chaque item expose notamment :

  • proposalId
  • createdAt
  • expiresAt
  • status
  • title
  • description
  • address
  • sport
  • city
  • avatar
  • minSeats
  • maxSeats
  • creatorId
  • creatorPseudo
  • slots
  • inviteesCount
  • declinedCount
  • respondedCount
  • responsesYes
  • responsesNo
  • isCreator
  • isInvitee
  • hasDeclined

Lecture détail

GET /event-proposals/{idEventProposal} renvoie un EventProposalDetailResponse :

  • proposal
  • creator
  • invitees
  • slots
  • viewer

Le bloc proposal expose notamment :

  • proposalId
  • status
  • createdAt
  • expiresAt
  • organizerStructureId
  • creatorParticipatesOnConversion
  • title
  • description
  • address
  • sport
  • level
  • city
  • minSeats
  • maxSeats
  • waitlistCapacity
  • eventId
  • chosenSlotId
  • firstRespondedAt
  • avatar

Le bloc viewer expose notamment :

  • isCreator
  • isInvitee
  • hasDeclined
  • canManageProposal
  • mySlotResponses
  • myEventStatus
  • canDeclineProposal
  • canRespondToSlots
  • canCounterPropose
  • canChooseEventParticipation
  • isTwoPeopleProposal

Point important sur le détail

Quand une proposal est convertie :

  • proposal.status passe à 2
  • proposal.eventId est renseigné
  • proposal.chosenSlotId est renseigné

Le front peut donc naviguer directement vers l'event créé.

Réponse d’un invité sur un slot

PUT /event-proposals/{idEventProposal}/slots/{idSlot}/response

Payload :

{
  "response": 1
}

Mapping à utiliser :

  • 1 = YES
  • 2 = NO

Réponse de succès :

200 OK

{
  "proposalId": 77,
  "slotId": 3,
  "response": 1,
  "respondedAt": "2026-04-15T10:00:00Z"
}

Règles métier importantes de réponse

  • seul un invité lié à la proposal peut répondre ;
  • un invité qui a déjà décliné la proposal ne peut plus répondre ;
  • une proposal doit être open ;
  • un slot fermé ne peut plus recevoir de réponse ;
  • si l'acteur n'est pas lié à la proposal, le backend renvoie 404 pour ne pas révéler son existence.

Conflits à gérer côté front

Le backend peut renvoyer 409 Conflict si :

  • la proposal est expirée ;
  • la proposal n'est plus open ;
  • le slot est fermé.

Auto-conversion en event

Quand un slot atteint minSeats, la proposal peut être convertie automatiquement dans la même transaction.

Pour une proposal pro convertie :

  • l'event créé est un event structure-backed ;
  • l'audienceScope initial de l'event converti est invite_only ;
  • les invités de la proposal sont propagés en invitations d'event ;
  • si creatorParticipatesOnConversion=false, le créateur devient EventStaffMember sur l'event converti.

Notifications liées aux proposals

La création d'une proposal envoie une notification système aux invités initiaux :

  • type centre : EventProposalSentNotificationType (16) ;
  • payload.kind = "eventProposalSent" ;
  • push data.type = "eventProposalSent" ;
  • destination front : détail de la proposal via proposalId.

Le patch avec inviteePersonIdsToAdd envoie la même notification uniquement aux nouveaux invités ajoutés.

La conversion automatique en event envoie une notification système :

  • au créateur de la proposal ;
  • aux invités inscrits en liste principale sur l'event ;
  • aux invités inscrits en liste d'attente sur l'event ;
  • aux invités qui n'avaient pas répondu sur le créneau choisi mais peuvent encore rejoindre l'event.

Contrat :

  • type centre : EventProposalConvertedNotificationType (15) ;
  • payload.kind = "eventProposalConverted" ;
  • push data.type = "eventProposalConverted" ;
  • destination front : event créé via eventId.

Statuts de conversion :

  • creator : créateur de la proposal ;
  • registered : invité inscrit en liste principale ;
  • waitlisted : invité inscrit en liste d'attente ;
  • joinable : invité sans réponse pouvant encore rejoindre l'event.

Référence exhaustive : - Notification catalog

Patch de proposal

PATCH /event-proposals/{idEventProposal} permet de modifier :

  • title
  • description
  • address
  • level
  • sportId
  • cityId
  • minSeats
  • maxSeats
  • avatarId
  • slots
  • inviteePersonIdsToAdd

Règles importantes du patch

  • le patch n'est possible que tant que la proposal est open ;
  • dès qu'il existe une première réponse, les modifications sont fortement verrouillées par le backend ;
  • inviteePersonIdsToAdd est add-only ;
  • il n'y a pas dans ce bloc de suppression d'invité ;
  • il n'y a pas de flow documenté ici pour créer une contre-proposition, même si certains flags de lecture existent déjà côté API.

Étape 15 - Inviter des adhérents sans waitlist

Objectif

Permettre à une structure d'inviter une adresse email à devenir adhérent sans passer par la waitlist globale.

Endpoints structure / admin

  • GET /v1/web/api/structures/{idStructure}/adherent-invitations
  • POST /v1/web/api/structures/{idStructure}/adherent-invitations
  • GET /v1/web/api/admin/structure-adherent-invitations

Endpoints invitation côté invité

  • POST /v1/public/structure-adherent-invitations/resolve
  • POST /v1/public/structure-adherent-invitations/signup
  • POST /v1/public/structure-adherent-invitations/decline
  • POST /v1/web/api/structure-adherent-invitations/accept

Préconditions côté structure

  • utilisateur authentifié ;
  • compte pro ;
  • structure courante choisie ;
  • actions.canManageAdherents = true ;
  • actions.canInviteAdherentsWithoutWaitlist = true sur GET /structures/{idStructure}/entitlements.

Envoi batch

Payload :

{
  "emails": [
    "alice@example.com",
    "bob@example.com"
  ]
}

Réponse :

  • results
  • quotaLimit
  • quotaUsed
  • quotaRemaining
  • periodStart
  • periodEnd

Chaque item de results expose :

  • email
  • normalizedEmail
  • status
  • invitationId

Statuts métier du batch à gérer

  • sent
  • invalid_email
  • already_active_invitation
  • already_adherent
  • already_declined
  • unsupported_existing_account_type
  • quota_exceeded

Règles métier importantes de l’envoi

  • l'unicité active est par email + structure ;
  • la structure doit pouvoir gérer ses adhérents ;
  • la capability users.invite_without_waitlist ne suffit pas seule : adherents.manage reste aussi requis ;
  • tant qu'une invitation active existe déjà pour cette structure et cet email, l'endpoint renvoie already_active_invitation ;
  • le batch consomme le quota au moment de l'envoi ;
  • le suivi quota est renvoyé immédiatement dans la réponse ;
  • il n'existe pas de revoke structure-side en V1.

Tracking structure-side

GET /structures/{idStructure}/adherent-invitations renvoie un StructureAdherentInvitationListResponse avec des items exposant notamment :

  • id
  • structureId
  • structureName
  • invitedByPersonId
  • email
  • status
  • targetPersonId
  • acceptedPersonId
  • waitlistEntryId
  • expiresAt
  • sentAt
  • acceptedAt
  • declinedAt

Tracking admin-side

GET /v1/web/api/admin/structure-adherent-invitations permet d'afficher un suivi backoffice global du même objet métier.

Ouverture publique du token

POST /v1/public/structure-adherent-invitations/resolve avec { "token": "..." } renvoie un StructureAdherentInvitationResolveResponse avec :

  • status
  • nextAction
  • canDecline
  • structureName
  • email
  • expiresAt

Valeurs attendues pour nextAction :

  • signup
  • login
  • unsupported_account
  • none

Décision front à partir du token

  • nextAction="signup"
  • afficher le flow de création de compte ;
  • nextAction="login"
  • demander une connexion au bon compte individual, puis appeler accept ;
  • nextAction="unsupported_account"
  • afficher un blocage produit clair ;
  • nextAction="none"
  • utiliser status pour informer l'utilisateur.

Le CTA de refus ne doit être affiché que si canDecline=true.

Signup depuis invitation

POST /v1/public/structure-adherent-invitations/signup

Payload :

{
  "token": "...",
  "pseudo": "alice-alpha",
  "challengeToken": "token"
}

Réponse :

  • 201 Created si le compte individual est créé ;
  • 200 OK si l'invitation était déjà acceptée.

Le backend renvoie un StructureAdherentInvitationActionResponse :

  • code
  • message
  • personId
  • pseudo

Codes utiles à gérer :

  • created
  • already_accepted

Acceptation sur compte existant

POST /v1/web/api/structure-adherent-invitations/accept

Préconditions :

  • utilisateur authentifié ;
  • compte individual correspondant.

Payload :

{
  "token": "..."
}

Réponse :

  • 200 OK
  • StructureAdherentInvitationActionResponse

Codes utiles à gérer :

  • accepted
  • already_accepted

Decline

POST /v1/public/structure-adherent-invitations/decline

Payload :

{
  "token": "..."
}

Réponse :

  • 200 OK
  • StructureAdherentInvitationActionResponse

Codes utiles à gérer :

  • declined
  • already_declined

Effet produit final

À l'acceptation réussie :

  • soit un compte individual est créé ;
  • soit un compte individual existant accepte ;
  • dans les deux cas, l'adhésion à la structure est créée ou réactivée automatiquement.

Point important lié à la waitlist

Si l'email invité était dans la waitlist :

  • l'entrée sort proprement de la waitlist ;
  • les anciens flows/tokens waitlist ne doivent plus être considérés comme utilisables.

Le front ne doit donc pas essayer de faire cohabiter un ancien parcours waitlist avec une invitation structure active sur le même email.

Résumé opérationnel pour le front

Avec les APIs actuelles, le front peut implémenter proprement :

  1. le choix du type de compte ;
  2. le signup pro ;
  3. l'inscription waitlist d'un futur compte pro ;
  4. l'état initial d'un compte pro sans structure ;
  5. la création d'une structure ;
  6. la revendication d'une structure catalogue existante ;
  7. la lecture canonique de "mes structures gérées" ;
  8. la lecture canonique des entitlements de structure ;
  9. la création et la gestion d'un event pro ;
  10. les audienceScope d'un event pro ;
  11. les invitations d'event pro ;
  12. le EventStaffMember d'un event pro ;
  13. les event proposals pro ;
  14. les invitations adhérent structure sans waitlist.

Avec les APIs actuelles, le front ne peut pas encore implémenter proprement, sans convention locale ou nouvelle API :

  1. le rattachement self-service à une structure déjà gérée.