openapi: 3.0.3
info:
title: 'ASSOVIX API'
description: "API REST multi-tenant pour la gestion d'associations africaines (tontines, ONG, mutuelles, syndicats).\n\n## Identification du tenant\n\nToutes les routes `/api/v1/*` (sauf `register`, `login` et `login/select-tenant`) nécessitent le header **`X-Tenant-Slug`** :\n\n```\nX-Tenant-Slug: mon-association\n```\n\nLe slug est l'identifiant unique de l'association, choisi lors de l'inscription.\n\n## Format de réponse uniforme\n\nToutes les réponses suivent ce format :\n\n```json\n{\n \"success\": true,\n \"message\": \"Description lisible.\",\n \"data\": {},\n \"meta\": { \"timestamp\": \"2026-04-27T10:00:00+00:00\", \"version\": \"v1\" }\n}\n```\n\nEn cas d'erreur, le champ `errors` s'ajoute et `data` vaut `null`.\n\n## Codes d'erreur\n\n| Code | Signification |\n|------|---------------|\n| 200 | Succès |\n| 201 | Ressource créée |\n| 401 | Token manquant ou expiré |\n| 403 | Accès interdit (tenant suspendu) |\n| 404 | Ressource ou tenant introuvable |\n| 422 | Validation échouée |\n| 429 | Trop de requêtes (100/min API · 10/min auth) |\n| 500 | Erreur serveur interne |"
version: 1.0.0
servers:
-
url: 'https://api-assovix.gestiem.com'
tags:
-
name: Authentication
description: ''
-
name: 'Tenant (Admin)'
description: ''
-
name: 'Member Groups'
description: ''
-
name: Members
description: ''
components:
securitySchemes:
default:
type: http
scheme: bearer
description: 'Obtenez votre token via **POST /api/v1/auth/login**, puis passez-le en header : `Authorization: Bearer {token}`'
security:
-
default: []
paths:
/api/v1/auth/logout:
post:
summary: 'Se déconnecter'
operationId: seDconnecter
description: "Révoque **tous** les tokens Sanctum de l'utilisateur connecté (toutes les sessions)."
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
200:
description: 'Déconnexion réussie'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Déconnexion réussie.'
data: null
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Déconnexion réussie.'
data:
type: string
example: null
nullable: true
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Token manquant ou expiré'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- Authentication
/api/v1/auth/me:
get:
summary: 'Profil et permissions'
operationId: profilEtPermissions
description: "Retourne le profil complet de l'utilisateur connecté et\nla liste de ses permissions dans le contexte du tenant courant."
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
200:
description: Succès
content:
application/json:
schema:
type: object
example:
success: true
message: 'Utilisateur courant.'
data:
user:
id: 550e8400-e29b-41d4-a716-446655440000
tenant_id: 660e8400-e29b-41d4-a716-446655440000
first_name: Kofi
last_name: Mensah
email: kofi@example.com
phone: '+22997000000'
avatar_url: null
locale: fr
is_active: true
last_login_at: '2026-04-27T10:00:00+00:00'
roles:
-
name: admin
guard_name: web
permissions:
- manage-members
- manage-cotisations
- view-reports
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Utilisateur courant.'
data:
type: object
properties:
user:
type: object
properties:
id:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
tenant_id:
type: string
example: 660e8400-e29b-41d4-a716-446655440000
first_name:
type: string
example: Kofi
last_name:
type: string
example: Mensah
email:
type: string
example: kofi@example.com
phone:
type: string
example: '+22997000000'
avatar_url:
type: string
example: null
nullable: true
locale:
type: string
example: fr
is_active:
type: boolean
example: true
last_login_at:
type: string
example: '2026-04-27T10:00:00+00:00'
roles:
type: array
example:
-
name: admin
guard_name: web
items:
type: object
properties:
name:
type: string
example: admin
guard_name:
type: string
example: web
permissions:
type: array
example:
- manage-members
- manage-cotisations
- view-reports
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Token manquant ou expiré'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- Authentication
/api/v1/auth/refresh:
post:
summary: 'Rafraîchir le token'
operationId: rafrachirLeToken
description: "Révoque le token courant et émet un nouveau token Sanctum valide 24h.\nUtile pour prolonger une session sans redemander les credentials."
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
200:
description: 'Token rafraîchi'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Token rafraîchi.'
data:
token: 3|assovix_newFreshToken789xyz
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Token rafraîchi.'
data:
type: object
properties:
token:
type: string
example: 3|assovix_newFreshToken789xyz
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Token manquant ou expiré'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- Authentication
/api/v1/auth/register:
post:
summary: 'Créer un compte association'
operationId: crerUnCompteAssociation
description: "Crée simultanément un nouveau tenant (association) et son premier utilisateur\navec le rôle `admin`. Retourne un token Sanctum immédiatement utilisable.\nCe endpoint ne nécessite pas le header `X-Tenant-Slug` car le tenant n'existe pas encore."
parameters: []
responses:
201:
description: 'Inscription réussie'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Inscription réussie.'
data:
token: 1|assovix_K2c3QTLQgDywEmIa0qaXL1ivbosnIr4
user:
id: 550e8400-e29b-41d4-a716-446655440000
tenant_id: 660e8400-e29b-41d4-a716-446655440000
first_name: Kofi
last_name: Mensah
email: kofi@example.com
phone: '+22997000000'
avatar_url: null
locale: fr
is_active: true
last_login_at: null
tenant:
id: 660e8400-e29b-41d4-a716-446655440000
name: 'Tontine Les Amis'
association_type: tontine
slug: tontine-les-amis
country: BJ
currency: XOF
plan: free
status: trial
trial_ends_at: '2026-05-11T10:00:00+00:00'
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Inscription réussie.'
data:
type: object
properties:
token:
type: string
example: 1|assovix_K2c3QTLQgDywEmIa0qaXL1ivbosnIr4
user:
type: object
properties:
id:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
tenant_id:
type: string
example: 660e8400-e29b-41d4-a716-446655440000
first_name:
type: string
example: Kofi
last_name:
type: string
example: Mensah
email:
type: string
example: kofi@example.com
phone:
type: string
example: '+22997000000'
avatar_url:
type: string
example: null
nullable: true
locale:
type: string
example: fr
is_active:
type: boolean
example: true
last_login_at:
type: string
example: null
nullable: true
tenant:
type: object
properties:
id:
type: string
example: 660e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Tontine Les Amis'
association_type:
type: string
example: tontine
slug:
type: string
example: tontine-les-amis
country:
type: string
example: BJ
currency:
type: string
example: XOF
plan:
type: string
example: free
status:
type: string
example: trial
trial_ends_at:
type: string
example: '2026-05-11T10:00:00+00:00'
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Slug déjà utilisé'
type: object
example:
success: false
message: 'Données invalides.'
data: null
errors:
slug:
- 'The slug has already been taken.'
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Données invalides.'
data:
type: string
example: null
nullable: true
errors:
type: object
properties:
slug:
type: array
example:
- 'The slug has already been taken.'
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
-
description: 'Email invalide'
type: object
example:
success: false
message: 'Données invalides.'
data: null
errors:
email:
- 'The email field must be a valid email address.'
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Données invalides.'
data:
type: string
example: null
nullable: true
errors:
type: object
properties:
email:
type: array
example:
- 'The email field must be a valid email address.'
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
-
description: 'Mot de passe trop court'
type: object
example:
success: false
message: 'Données invalides.'
data: null
errors:
password:
- 'The password field must be at least 8 characters.'
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Données invalides.'
data:
type: string
example: null
nullable: true
errors:
type: object
properties:
password:
type: array
example:
- 'The password field must be at least 8 characters.'
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
association_name:
type: string
description: "Nom de l'association."
example: 'Tontine Les Amis'
association_type:
type: string
description: "Type d'association : tontine, mutuelle, ong, syndicat, club_culturel, association_sportive, communaute_religieuse."
example: tontine
slug:
type: string
description: 'Identifiant URL unique (minuscules, chiffres, tirets).'
example: tontine-les-amis
country:
type: string
description: 'Code pays ISO 3166-1 alpha-2.'
example: BJ
currency:
type: string
description: 'Devise : XOF, XAF, GNF, USD, EUR.'
example: XOF
first_name:
type: string
description: "Prénom de l'administrateur."
example: Kofi
last_name:
type: string
description: 'Nom de famille.'
example: Mensah
email:
type: string
description: 'Adresse email.'
example: kofi@example.com
phone:
type: string
description: 'Numéro de téléphone (optionnel).'
example: '+22997000000'
nullable: true
password:
type: string
description: 'Mot de passe (min. 8 caractères).'
example: Secret@2026!
password_confirmation:
type: string
description: 'Confirmation du mot de passe.'
example: Secret@2026!
required:
- association_name
- association_type
- slug
- country
- currency
- first_name
- last_name
- email
- password
- password_confirmation
security: []
/api/v1/auth/login:
post:
summary: 'Se connecter'
operationId: seConnecter
description: "Authentifie un utilisateur **sans header `X-Tenant-Slug`**. Le système\nidentifie automatiquement l'association en croisant `email` et `password`.\n- Cas standard (un seul match) : retourne un token Sanctum, l'utilisateur,\n le tenant et les permissions.\n- Cas ambigu (l'email existe dans plusieurs associations actives) : retourne\n un `login_intent` court-vie (5 min) et la liste des associations à choisir.\n Aucun token Sanctum n'est émis tant que la sélection n'est pas faite via\n `POST /api/v1/auth/login/select-tenant`."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Connexion directe (un seul tenant)'
type: object
example:
success: true
message: 'Connexion réussie.'
data:
token: 2|assovix_4xK2c3QTLQgDywEmIa0qaXL1ivbosnIr4
user:
id: 550e8400-...
email: kofi@example.com
is_active: true
tenant:
id: 660e8400-...
name: 'Tontine Les Amis'
slug: tontine-les-amis
permissions:
- manage-members
meta:
timestamp: '2026-05-04T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Connexion réussie.'
data:
type: object
properties:
token:
type: string
example: 2|assovix_4xK2c3QTLQgDywEmIa0qaXL1ivbosnIr4
user:
type: object
properties:
id:
type: string
example: 550e8400-...
email:
type: string
example: kofi@example.com
is_active:
type: boolean
example: true
tenant:
type: object
properties:
id:
type: string
example: 660e8400-...
name:
type: string
example: 'Tontine Les Amis'
slug:
type: string
example: tontine-les-amis
permissions:
type: array
example:
- manage-members
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T10:00:00+00:00'
version:
type: string
example: v1
-
description: "Sélection d'association requise"
type: object
example:
success: true
message: 'Plusieurs associations correspondent. Sélectionnez-en une.'
data:
requires_tenant_selection: true
login_intent: lit_9f3a2b81e4c64d9bb9a1d0e2f1c8b3a7
expires_at: '2026-05-04T10:05:00+00:00'
associations:
-
id: 660e8400-...
name: 'Tontine Les Amis'
slug: tontine-les-amis
association_type: tontine
logo_url: null
country: BJ
meta:
timestamp: '2026-05-04T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Plusieurs associations correspondent. Sélectionnez-en une.'
data:
type: object
properties:
requires_tenant_selection:
type: boolean
example: true
login_intent:
type: string
example: lit_9f3a2b81e4c64d9bb9a1d0e2f1c8b3a7
expires_at:
type: string
example: '2026-05-04T10:05:00+00:00'
associations:
type: array
example:
-
id: 660e8400-...
name: 'Tontine Les Amis'
slug: tontine-les-amis
association_type: tontine
logo_url: null
country: BJ
items:
type: object
properties:
id:
type: string
example: 660e8400-...
name:
type: string
example: 'Tontine Les Amis'
slug:
type: string
example: tontine-les-amis
association_type:
type: string
example: tontine
logo_url:
type: string
example: null
nullable: true
country:
type: string
example: BJ
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T10:00:00+00:00'
version:
type: string
example: v1
422:
description: 'Identifiants incorrects'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Données invalides.'
data: null
errors:
email:
- 'Identifiants incorrects.'
meta:
timestamp: '2026-05-04T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Données invalides.'
data:
type: string
example: null
nullable: true
errors:
type: object
properties:
email:
type: array
example:
- 'Identifiants incorrects.'
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T10:00:00+00:00'
version:
type: string
example: v1
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: "Adresse email de l'utilisateur."
example: kofi@example.com
password:
type: string
description: 'Mot de passe.'
example: Secret@2026!
required:
- email
- password
security: []
/api/v1/auth/login/select-tenant:
post:
summary: 'Sélectionner une association après login multi-tenant'
operationId: slectionnerUneAssociationAprsLoginMultiTenant
description: "Consomme un `login_intent` émis par `POST /auth/login` quand l'email\nmatche plusieurs associations, et retourne un token Sanctum complet\npour le tenant choisi. Le `login_intent` est **one-shot** : un second\nappel échoue. TTL 5 min."
parameters: []
responses:
200:
description: 'Sélection réussie'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Connexion réussie.'
data:
token: 3|assovix_freshTokenForSelectedTenant
user:
id: 550e8400-...
email: kofi@example.com
tenant:
id: 660e8400-...
name: 'Tontine Les Amis'
slug: tontine-les-amis
permissions:
- manage-members
meta:
timestamp: '2026-05-04T10:00:30+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Connexion réussie.'
data:
type: object
properties:
token:
type: string
example: 3|assovix_freshTokenForSelectedTenant
user:
type: object
properties:
id:
type: string
example: 550e8400-...
email:
type: string
example: kofi@example.com
tenant:
type: object
properties:
id:
type: string
example: 660e8400-...
name:
type: string
example: 'Tontine Les Amis'
slug:
type: string
example: tontine-les-amis
permissions:
type: array
example:
- manage-members
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T10:00:30+00:00'
version:
type: string
example: v1
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Intent expiré ou inconnu'
type: object
example:
success: false
message: 'Données invalides.'
data: null
errors:
login_intent:
- 'Lien expiré, recommencez la connexion.'
meta:
timestamp: '2026-05-04T10:05:30+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Données invalides.'
data:
type: string
example: null
nullable: true
errors:
type: object
properties:
login_intent:
type: array
example:
- 'Lien expiré, recommencez la connexion.'
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T10:05:30+00:00'
version:
type: string
example: v1
-
description: 'Tenant non autorisé pour cet intent'
type: object
example:
success: false
message: 'Données invalides.'
data: null
errors:
tenant_id:
- 'Sélection invalide.'
meta:
timestamp: '2026-05-04T10:00:30+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Données invalides.'
data:
type: string
example: null
nullable: true
errors:
type: object
properties:
tenant_id:
type: array
example:
- 'Sélection invalide.'
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T10:00:30+00:00'
version:
type: string
example: v1
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
login_intent:
type: string
description: 'Identifiant retourné par /auth/login.'
example: lit_9f3a2b81e4c64d9bb9a1d0e2f1c8b3a7
tenant_id:
type: string
description: "UUID de l'association choisie."
example: 660e8400-e29b-41d4-a716-446655440000
required:
- login_intent
- tenant_id
security: []
/api/admin/tenants:
get:
summary: 'Lister les tenants'
operationId: listerLesTenants
description: "Retourne la liste paginée de tous les tenants (25 par page), triée par date de création décroissante.\nFiltres disponibles : `status` et `search` (recherche dans `name` et `slug`)."
parameters:
-
in: query
name: status
description: 'Filtre par statut. Valeurs : `active`, `suspended`, `trial`.'
example: active
required: false
schema:
type: string
description: 'Filtre par statut. Valeurs : `active`, `suspended`, `trial`.'
example: active
-
in: query
name: search
description: 'Recherche dans le nom ou le slug du tenant.'
example: tontine
required: false
schema:
type: string
description: 'Recherche dans le nom ou le slug du tenant.'
example: tontine
-
in: query
name: page
description: 'Numéro de page. Défaut: 1.'
example: 1
required: false
schema:
type: integer
description: 'Numéro de page. Défaut: 1.'
example: 1
responses:
200:
description: 'Liste paginée'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Liste des tenants.'
data:
-
id: 660e8400-e29b-41d4-a716-446655440000
name: 'Tontine Les Amis'
slug: tontine-les-amis
country: BJ
currency: XOF
plan: free
status: trial
trial_ends_at: '2026-05-11T10:00:00+00:00'
created_at: '2026-04-27T10:00:00+00:00'
meta:
current_page: 1
last_page: 3
per_page: 25
total: 72
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Liste des tenants.'
data:
type: array
example:
-
id: 660e8400-e29b-41d4-a716-446655440000
name: 'Tontine Les Amis'
slug: tontine-les-amis
country: BJ
currency: XOF
plan: free
status: trial
trial_ends_at: '2026-05-11T10:00:00+00:00'
created_at: '2026-04-27T10:00:00+00:00'
items:
type: object
properties:
id:
type: string
example: 660e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Tontine Les Amis'
slug:
type: string
example: tontine-les-amis
country:
type: string
example: BJ
currency:
type: string
example: XOF
plan:
type: string
example: free
status:
type: string
example: trial
trial_ends_at:
type: string
example: '2026-05-11T10:00:00+00:00'
created_at:
type: string
example: '2026-04-27T10:00:00+00:00'
meta:
type: object
properties:
current_page:
type: integer
example: 1
last_page:
type: integer
example: 3
per_page:
type: integer
example: 25
total:
type: integer
example: 72
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Non authentifié'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- 'Tenant (Admin)'
post:
summary: 'Créer un tenant'
operationId: crerUnTenant
description: "Crée manuellement un nouveau tenant sans créer d'utilisateur associé.\nRéservé au super admin de la plateforme."
parameters: []
responses:
201:
description: 'Tenant créé'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Tenant créé.'
data:
id: 770e8400-e29b-41d4-a716-446655440000
name: 'ONG Solidarité Bénin'
slug: ong-solidarite-benin
country: SN
currency: XOF
plan: free
status: trial
created_at: '2026-04-27T10:00:00+00:00'
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Tenant créé.'
data:
type: object
properties:
id:
type: string
example: 770e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'ONG Solidarité Bénin'
slug:
type: string
example: ong-solidarite-benin
country:
type: string
example: SN
currency:
type: string
example: XOF
plan:
type: string
example: free
status:
type: string
example: trial
created_at:
type: string
example: '2026-04-27T10:00:00+00:00'
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Non authentifié'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
422:
description: 'Slug dupliqué'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Données invalides.'
data: null
errors:
slug:
- 'The slug has already been taken.'
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Données invalides.'
data:
type: string
example: null
nullable: true
errors:
type: object
properties:
slug:
type: array
example:
- 'The slug has already been taken.'
items:
type: string
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- 'Tenant (Admin)'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: "Nom de l'association."
example: 'ONG Solidarité Bénin'
slug:
type: string
description: 'Identifiant URL unique (minuscules, chiffres, tirets).'
example: ong-solidarite-benin
country:
type: string
description: 'Code pays ISO 3166-1 alpha-2.'
example: SN
nullable: true
currency:
type: string
description: 'Devise : `XOF`, `XAF`, `GNF`, `USD`, `EUR`.'
example: XOF
nullable: true
plan:
type: string
description: 'Plan : `free`, `starter`, `pro`, `enterprise`.'
example: free
nullable: true
status:
type: string
description: 'Statut : `active`, `suspended`, `trial`.'
example: trial
nullable: true
required:
- name
- slug
'/api/admin/tenants/{id}':
put:
summary: 'Modifier un tenant'
operationId: modifierUnTenant
description: "Met à jour les informations d'un tenant existant.\nSeuls les champs envoyés sont modifiés (comportement PATCH sur PUT)."
parameters: []
responses:
200:
description: 'Tenant mis à jour'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Tenant mis à jour.'
data:
id: 660e8400-e29b-41d4-a716-446655440000
name: 'Tontine Les Amis v2'
slug: tontine-les-amis
country: CI
currency: XOF
plan: starter
status: trial
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Tenant mis à jour.'
data:
type: object
properties:
id:
type: string
example: 660e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Tontine Les Amis v2'
slug:
type: string
example: tontine-les-amis
country:
type: string
example: CI
currency:
type: string
example: XOF
plan:
type: string
example: starter
status:
type: string
example: trial
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Non authentifié'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
404:
description: 'Tenant introuvable'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Ressource introuvable.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Ressource introuvable.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- 'Tenant (Admin)'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: "Nouveau nom de l'association."
example: 'Tontine Les Amis v2'
country:
type: string
description: 'Code pays ISO 3166-1 alpha-2.'
example: CI
currency:
type: string
description: 'Devise : `XOF`, `XAF`, `GNF`, `USD`, `EUR`.'
example: XOF
plan:
type: string
description: 'Plan : `free`, `starter`, `pro`, `enterprise`.'
example: starter
locale:
type: string
description: 'Locale BCP 47.'
example: fr
delete:
summary: 'Supprimer un tenant'
operationId: supprimerUnTenant
description: "Soft-delete du tenant. Les données sont conservées en base (`deleted_at` non null).\nCette action est réversible via une restauration manuelle."
parameters: []
responses:
200:
description: 'Tenant supprimé'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Tenant supprimé.'
data: null
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Tenant supprimé.'
data:
type: string
example: null
nullable: true
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Non authentifié'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
404:
description: 'Tenant introuvable'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Ressource introuvable.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Ressource introuvable.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- 'Tenant (Admin)'
parameters:
-
in: path
name: id
description: 'UUID du tenant.'
example: 660e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/admin/tenants/{id}/suspend':
put:
summary: 'Suspendre un tenant'
operationId: suspendreUnTenant
description: "Passe le statut du tenant à `suspended`.\nLes utilisateurs du tenant recevront une erreur 403 sur chaque requête."
parameters: []
responses:
200:
description: 'Tenant suspendu'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Tenant suspendu.'
data:
id: 660e8400-e29b-41d4-a716-446655440000
name: 'Tontine Les Amis'
slug: tontine-les-amis
status: suspended
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Tenant suspendu.'
data:
type: object
properties:
id:
type: string
example: 660e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Tontine Les Amis'
slug:
type: string
example: tontine-les-amis
status:
type: string
example: suspended
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Non authentifié'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
404:
description: 'Tenant introuvable'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Ressource introuvable.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Ressource introuvable.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- 'Tenant (Admin)'
parameters:
-
in: path
name: id
description: 'UUID du tenant.'
example: 660e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/admin/tenants/{id}/activate':
put:
summary: 'Activer un tenant'
operationId: activerUnTenant
description: "Passe le statut du tenant à `active`.\nRétablit l'accès complet à l'API pour les utilisateurs du tenant."
parameters: []
responses:
200:
description: 'Tenant activé'
content:
application/json:
schema:
type: object
example:
success: true
message: 'Tenant activé.'
data:
id: 660e8400-e29b-41d4-a716-446655440000
name: 'Tontine Les Amis'
slug: tontine-les-amis
status: active
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Tenant activé.'
data:
type: object
properties:
id:
type: string
example: 660e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Tontine Les Amis'
slug:
type: string
example: tontine-les-amis
status:
type: string
example: active
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
401:
description: 'Non authentifié'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Non authentifié.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Non authentifié.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
404:
description: 'Tenant introuvable'
content:
application/json:
schema:
type: object
example:
success: false
message: 'Ressource introuvable.'
data: null
errors: []
meta:
timestamp: '2026-04-27T10:00:00+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Ressource introuvable.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-04-27T10:00:00+00:00'
version:
type: string
example: v1
tags:
- 'Tenant (Admin)'
parameters:
-
in: path
name: id
description: 'UUID du tenant.'
example: 660e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
/api/v1/member-groups:
get:
summary: 'Liste des groupes'
operationId: listeDesGroupes
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- 'Member Groups'
post:
summary: 'Créer un groupe'
operationId: crerUnGroupe
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- 'Member Groups'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Nom du groupe.'
example: Bureau
description:
type: string
description: ''
example: 'Rerum non doloribus aspernatur ad eius molestiae accusantium.'
nullable: true
color:
type: string
description: 'Couleur hex (ex: #059669).'
example: '#059669'
nullable: true
parent_id:
type: string
description: 'UUID du groupe parent.'
example: null
nullable: true
required:
- name
'/api/v1/member-groups/{id}':
put:
summary: 'Modifier un groupe'
operationId: modifierUnGroupe
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- 'Member Groups'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: validation.max.
example: vwgshl
description:
type: string
description: ''
example: 'Rerum non doloribus aspernatur ad eius molestiae accusantium.'
nullable: true
color:
type: string
description: 'Must match the regex /^#[0-9A-Fa-f]{6}$/.'
example: '#057Fce'
nullable: true
parent_id:
type: string
description: 'validation.uuid The id of an existing record in the member_groups table. Must not be one of .'
example: a00372d2-8e4a-326e-8cb5-f40c121e1465
nullable: true
delete:
summary: 'Supprimer un groupe'
operationId: supprimerUnGroupe
description: 'Refusé si le groupe contient des membres (actifs ou supprimés).'
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- 'Member Groups'
parameters:
-
in: path
name: id
description: 'UUID du groupe.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/v1/member-groups/{id}/members':
get:
summary: "Membres d'un groupe"
operationId: membresDunGroupe
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- 'Member Groups'
parameters:
-
in: path
name: id
description: 'UUID du groupe.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
/api/v1/members/stats:
get:
summary: 'Statistiques des membres'
operationId: statistiquesDesMembres
description: 'Retourne le total, la répartition par statut et par groupe, les nouveaux ce mois/année.'
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
/api/v1/members/export:
get:
summary: 'Exporter les membres en CSV'
operationId: exporterLesMembresEnCSV
description: ''
parameters:
-
in: query
name: status
description: 'Filtre statut.'
example: active
required: false
schema:
type: string
description: 'Filtre statut.'
example: active
-
in: query
name: group_id
description: 'Filtre groupe.'
example: null
required: false
schema:
type: string
description: 'Filtre groupe.'
example: null
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
/api/v1/members/template:
get:
summary: "Télécharger le template CSV d'import"
operationId: tlchargerLeTemplateCSVDimport
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
/api/v1/members/import:
post:
summary: 'Importer des membres depuis CSV'
operationId: importerDesMembresDepuisCSV
description: "Importe des membres depuis un fichier CSV. Max 1000 lignes.\nColonnes requises : first_name*, last_name*. Toutes les autres sont optionnelles."
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: 'Fichier CSV (max 5MB).'
required:
- file
/api/v1/members:
get:
summary: 'Liste des membres'
operationId: listeDesMembres
description: "Retourne la liste paginée des membres du tenant courant.\nSupporte la recherche, le filtrage et le tri."
parameters:
-
in: query
name: search
description: 'Recherche sur nom, prénom, email, numéro.'
example: Kofi
required: false
schema:
type: string
description: 'Recherche sur nom, prénom, email, numéro.'
example: Kofi
-
in: query
name: status
description: 'Filtre statut : active, inactive, pending, suspended, honorary, deceased.'
example: active
required: false
schema:
type: string
description: 'Filtre statut : active, inactive, pending, suspended, honorary, deceased.'
example: active
-
in: query
name: group_id
description: 'UUID du groupe.'
example: null
required: false
schema:
type: string
description: 'UUID du groupe.'
example: null
-
in: query
name: sponsor_id
description: 'UUID du parrain.'
example: null
required: false
schema:
type: string
description: 'UUID du parrain.'
example: null
-
in: query
name: joined_from
description: 'Date début adhésion (YYYY-MM-DD).'
example: '2026-01-01'
required: false
schema:
type: string
description: 'Date début adhésion (YYYY-MM-DD).'
example: '2026-01-01'
-
in: query
name: joined_to
description: 'Date fin adhésion (YYYY-MM-DD).'
example: '2026-12-31'
required: false
schema:
type: string
description: 'Date fin adhésion (YYYY-MM-DD).'
example: '2026-12-31'
-
in: query
name: sort
description: 'Champ de tri : member_number, last_name, joined_at.'
example: member_number
required: false
schema:
type: string
description: 'Champ de tri : member_number, last_name, joined_at.'
example: member_number
-
in: query
name: order
description: 'Ordre : asc ou desc.'
example: asc
required: false
schema:
type: string
description: 'Ordre : asc ou desc.'
example: asc
-
in: query
name: per_page
description: 'Résultats par page (max 100).'
example: 25
required: false
schema:
type: integer
description: 'Résultats par page (max 100).'
example: 25
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
post:
summary: 'Créer un membre'
operationId: crerUnMembre
description: "Crée un nouveau membre. Le numéro de membre est généré automatiquement.\nEnvoyer les fichiers en multipart/form-data."
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
first_name:
type: string
description: Prénom.
example: Kofi
last_name:
type: string
description: 'Nom de famille.'
example: Mensah
email:
type: string
description: 'Email (unique par tenant).'
example: kofi@example.com
nullable: true
phone:
type: string
description: Téléphone.
example: '+22997000000'
nullable: true
gender:
type: string
description: 'Genre : M, F, other.'
example: M
nullable: true
birth_date:
type: string
description: 'Date de naissance (YYYY-MM-DD).'
example: '1990-01-15'
nullable: true
address:
type: string
description: validation.max.
example: wgshlz
nullable: true
city:
type: string
description: validation.max.
example: aedjxa
nullable: true
country:
type: string
description: 'Code pays ISO alpha-2.'
example: BJ
nullable: true
profession:
type: string
description: validation.max.
example: izxgnx
nullable: true
status:
type: string
description: Statut.
example: pending
nullable: true
group_id:
type: string
description: 'UUID du groupe.'
example: null
nullable: true
sponsor_id:
type: string
description: 'validation.uuid The id of an existing record in the members table.'
example: 4b9c5595-1c58-3e6a-9a54-8e9361992634
nullable: true
joined_at:
type: string
description: validation.date.
example: '2026-05-04T13:50:09'
nullable: true
notes:
type: string
description: ''
example: architecto
nullable: true
photo:
type: string
format: binary
description: 'Photo membre (jpg/png, max 2MB).'
nullable: true
id_card:
type: string
format: binary
description: "Pièce d'identité (jpg/png/pdf, max 5MB)."
nullable: true
required:
- first_name
- last_name
'/api/v1/members/{id}':
get:
summary: "Détail d'un membre"
operationId: dtailDunMembre
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
put:
summary: 'Modifier un membre'
operationId: modifierUnMembre
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
requestBody:
required: false
content:
multipart/form-data:
schema:
type: object
properties:
first_name:
type: string
description: validation.max.
example: vwgshl
last_name:
type: string
description: validation.max.
example: zaedjx
email:
type: string
description: 'validation.email validation.max.'
example: rosenbaum.javonte@example.com
nullable: true
phone:
type: string
description: validation.max.
example: xgnxor
nullable: true
gender:
type: string
description: ''
example: F
enum:
- M
- F
- other
nullable: true
birth_date:
type: string
description: 'validation.date validation.before.'
example: '2026-03-12'
nullable: true
address:
type: string
description: validation.max.
example: wgshlz
nullable: true
city:
type: string
description: validation.max.
example: aedjxa
nullable: true
country:
type: string
description: validation.size.
example: uu
nullable: true
profession:
type: string
description: validation.max.
example: izxgnx
nullable: true
group_id:
type: string
description: 'validation.uuid The id of an existing record in the member_groups table.'
example: 5b0c0231-0539-3fcd-86ff-38b36e835b89
nullable: true
sponsor_id:
type: string
description: 'validation.uuid The id of an existing record in the members table. Must not be one of .'
example: 4b9c5595-1c58-3e6a-9a54-8e9361992634
nullable: true
joined_at:
type: string
description: validation.date.
example: '2026-05-04T13:50:09'
nullable: true
notes:
type: string
description: ''
example: architecto
nullable: true
photo:
type: string
format: binary
description: 'Must be a file. validation.max.'
nullable: true
id_card:
type: string
format: binary
description: 'Must be a file. validation.max.'
nullable: true
delete:
summary: 'Supprimer un membre (soft delete)'
operationId: supprimerUnMembresoftDelete
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
parameters:
-
in: path
name: id
description: 'UUID du membre.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/v1/members/{id}/restore':
post:
summary: 'Restaurer un membre supprimé'
operationId: restaurerUnMembreSupprim
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
parameters:
-
in: path
name: id
description: 'UUID du membre.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/v1/members/{id}/status':
put:
summary: "Changer le statut d'un membre"
operationId: changerLeStatutDunMembre
description: ''
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: 'Nouveau statut : active, inactive, suspended, honorary, deceased.'
example: active
reason:
type: string
description: 'Raison du changement (optionnel).'
example: 'Cotisations à jour'
nullable: true
required:
- status
parameters:
-
in: path
name: id
description: 'UUID du membre.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/v1/members/{id}/card':
get:
summary: 'Télécharger la carte membre PDF'
operationId: tlchargerLaCarteMembrePDF
description: "Génère et retourne la carte membre au format PDF (A6 paysage).\nLe QR code intégré pointe vers l'endpoint de vérification publique."
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
200:
description: 'Carte PDF'
content:
text/plain:
schema:
type: string
example: 'file {"description": "Fichier PDF de la carte membre"}'
tags:
- Members
parameters:
-
in: path
name: id
description: 'UUID du membre.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/v1/members/{id}/card/regenerate':
post:
summary: 'Régénérer le token QR code de la carte'
operationId: rgnrerLeTokenQRCodeDeLaCarte
description: "Invalide l'ancien QR code et génère un nouveau token."
parameters:
-
in: header
name: X-Tenant-Slug
description: ''
example: demo
schema:
type: string
responses:
500:
description: ''
content:
application/json:
schema:
type: object
example:
success: false
message: 'Erreur serveur.'
data: null
errors: []
meta:
timestamp: '2026-05-04T13:50:09+00:00'
version: v1
properties:
success:
type: boolean
example: false
message:
type: string
example: 'Erreur serveur.'
data:
type: string
example: null
nullable: true
errors:
type: array
example: []
meta:
type: object
properties:
timestamp:
type: string
example: '2026-05-04T13:50:09+00:00'
version:
type: string
example: v1
tags:
- Members
parameters:
-
in: path
name: id
description: 'UUID du membre.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string