API Partenaire · Accès restreint

PTM Partner API

API REST exposée par Padel Tournoi Manager pour les partenaires intégrés. Authentification par clé API. Tous les endpoints retournent du JSON.

Base URL STAGING https://coavlxhpthncsnbnfstw.supabase.co/functions/v1
Format JSON
·
Auth x-api-key header
·
Version v1
·
Mis à jour Juin 2026
01

Authentification

Header requis

Toutes les requêtes doivent inclure la clé API dans le header x-api-key. La clé est fournie par PTM lors de l'onboarding partenaire.

Exemple de requête authentifiée
HTTP
GET /partner-tournaments-list x-api-key: ptm_live_xxxxxxxxxxxxxxxxxxxx Content-Type: application/json
⚠ Sécurité

Ne jamais exposer la clé API côté client. Tous les appels doivent transiter par votre backend.

02

Codes d'erreur

CodeSignificationDescription
200OKSuccès, y compris les cas métier (not_found, multiple…)
201CreatedInscription créée (statut confirmed, pending ou waitlist)
400Bad RequestParamètre requis manquant ou mal formé (email, téléphone)
401UnauthorizedClé API absente, invalide ou désactivée
404Not FoundRessource inexistante ou non publiée sur Pista
409ConflictDoublon d'inscription (paire déjà inscrite)
500Server ErrorErreur interne PTM
Convention

Les cas métier de /partner-players-verify (joueur introuvable, homonymie…) retournent toujours HTTP 200 avec un champ status dans le body. Seules les erreurs d'infrastructure utilisent les codes 4xx/5xx.

03

Liste des tournois

GET /partner-tournaments-list Tournois ouverts aux inscriptions

Retourne la liste des tournois publiés sur Pista avec les inscriptions ouvertes (registration_open = true). Tous les filtres sont optionnels et cumulables.

Paramètres optionnels
ParamètreTypeDescription
date_fromdate (YYYY-MM-DD)Tournois à partir de cette date
date_todate (YYYY-MM-DD)Tournois jusqu'à cette date
genderstringFiltrer par genre : Masculin, Féminin, Mixte
levelstringFiltrer par niveau : P25, P100, P250
club_iduuidRestreindre aux tournois d'un club spécifique
latnumberLatitude du point de référence (requis avec lng et radius_km)
lngnumberLongitude du point de référence
radius_kmnumberRayon de recherche en km autour du point lat/lng
Requête
HTTP
GET /partner-tournaments-list?date_from=2026-06-01&date_to=2026-07-31&gender=Masculin&level=P25 x-api-key: votre_clé_api
Réponse 200 - Succès
JSON
{ "tournaments": [ { "id": "uuid", "name": "Tournoi P25 ON Padel", "date": "2026-06-20", "start_time": "09:00", "level": "P25", "gender": "Masculin", "club_name": "ON Padel Escalquens", "city": "Escalquens", "address": "12 Rue du Padel, 31750 Escalquens", "latitude": 43.5421, "longitude": 1.5987, "total_pairs": 16, "registered_pairs": 4, "available_spots": 12, "registration_open": true } ] }
Note

available_spots peut être null si le tournoi n'a pas de limite de paires définie. La réponse est limitée à 100 résultats. Une pagination cursor-based (?cursor=) sera disponible prochainement.

date_from
date_to
gender
level
club_id
radius_km
lat
lng
04

Détail d'un tournoi

GET /partner-tournaments-detail?id={uuid} Détail complet
Paramètres
ParamètreTypeRequisDescription
iduuidrequisIdentifiant du tournoi
Requête
HTTP
GET /partner-tournaments-detail?id=550e8400-e29b-41d4-a716-446655440000 x-api-key: votre_clé_api
Réponse 200 - Succès
JSON
{ "id": "550e8400-e29b-41d4-a716-446655440000", "name": "Tournoi P25 ON Padel", "date": "2026-06-20", "level": "P25", "gender": "Masculin", "format": "TMC", "club_name": "ON Padel Escalquens", "city": "Escalquens", "address": "12 Rue du Padel, 31750 Escalquens", "total_pairs": 16, "registered_pairs": 4, "available_spots": 12, "registration_open": true, "entry_fee": 30 }
Réponse 400 - Paramètre manquant
JSON
{ "error": "id is required" }
Réponse 404 - Tournoi introuvable
JSON
{ "error": "Tournament not found" }
id *
05

Vérification de licence

⚠ Déprécié

Cet endpoint est déprécié et sera retiré dans une prochaine version. La vérification FFT est désormais réalisée côté PTM, automatiquement, au moment de l'inscription (partner-register) — aucun pré-appel n'est nécessaire.

À ne pas intégrer pour de nouveaux développements. Documenté ici uniquement pour référence.

GET /partner-players-verify Vérification FFT via TenUp
Paramètres
ParamètreTypeRequisDescription
first_namestringrequisPrénom du joueur
last_namestringrequisNom du joueur
licencestringoptionnelNuméro de licence FFT. Si fourni, affine la recherche et détecte les incohérences.
Requête - Recherche par nom
HTTP
GET /partner-players-verify?first_name=Jean&last_name=Dupont x-api-key: votre_clé_api
Requête - Recherche avec licence
HTTP
GET /partner-players-verify?first_name=Jean&last_name=Dupont&licence=1234567 x-api-key: votre_clé_api
Cas de réponse (tous HTTP 200)
status: found - Joueur trouvé (résultat unique)
{ "status": "found", "player": { "licence_number": "1234567", "first_name": "Jean", "last_name": "Dupont", "ranking": "9783", "club": "ON Padel Escalquens" } }
status: multiple - Plusieurs joueurs (homonymie)
{ "status": "multiple", "players": [ { "licence_number": "1234567", "first_name": "Jean", "last_name": "Dupont", "ranking": "9783", "club": "ON Padel Escalquens" }, { "licence_number": "7654321", "first_name": "Jean", "last_name": "Dupont", "ranking": "8234", "club": "Padel Club Toulouse" } ] } // → Afficher la liste, le joueur sélectionne, renvoyer un second appel avec licence.
status: not_found - Introuvable dans FFT
{ "status": "not_found" }
status: licence_mismatch - Licence ≠ Nom
{ "status": "licence_mismatch" } // → La licence existe dans FFT mais ne correspond pas au nom fourni.
status: error - Service FFT indisponible
{ "status": "error", "message": "Player verification service unavailable" }
Réponse 400 - Paramètre manquant
JSON
{ "error": "first_name and last_name are required" }
first_name *
last_name *
licence
06

Inscription d'une paire

Vérification & logique automatique

À l'inscription, PTM vérifie chaque joueur auprès de la FFT. Trois issues possibles :

  • status: confirmed — joueurs vérifiés et places disponibles : la paire est ajoutée au tableau.
  • status: waitlist — tournoi complet : la paire est placée en liste d'attente (avec sa position).
  • status: pending — un joueur n'a pas pu être vérifié (introuvable FFT, licence incorrecte, homonymie, ou service FFT temporairement indisponible) : la place est réservée mais l'inscription attend une validation manuelle du juge-arbitre.

Les champs requis manquants ou mal formés (email, téléphone) sont rejetés en 400 avant toute création.

POST /partner-register Inscription paire au tournoi
Body (JSON)
JSON
{ "tournament_id": "550e8400-e29b-41d4-a716-446655440000", "player1": { "licence_number": "1234567", "first_name": "Jean", "last_name": "Dupont", "phone": "0612345678", "email": "jean.dupont@email.com" }, "player2": { "licence_number": "7654321", "first_name": "Marie", "last_name": "Martin", "phone": "0687654321", "email": "marie.martin@email.com" } }
Champs requis
ChampTypeDescription
tournament_iduuidID du tournoi cible
player1.licence_numberstringLicence FFT joueur 1
player1.first_namestringPrénom joueur 1
player1.last_namestringNom joueur 1
player1.phonestringTéléphone joueur 1 (≥ 6 chiffres)
player1.emailstringEmail joueur 1 (format validé)
player2.licence_numberstringLicence FFT joueur 2
player2.first_namestringPrénom joueur 2
player2.last_namestringNom joueur 2
player2.phonestringTéléphone joueur 2 (≥ 6 chiffres)
player2.emailstringEmail joueur 2 (format validé)

L'email (format) et le téléphone (au moins 6 chiffres) sont validés ; une valeur mal formée renvoie un 400. La licence est requise (présence) ; son exactitude est vérifiée auprès de la FFT après l'inscription (peut produire status: pending).

Réponse 201 - Confirmé
JSON
{ "registration_id": "uuid", "status": "confirmed", "message": "Registration confirmed" }
Réponse 201 - Liste d'attente
JSON
{ "registration_id": "uuid", "status": "waitlist", "waitlist_position": 2, "message": "Tournament is full, you have been added to the waitlist" }
Réponse 201 - En attente de validation (vérification FFT échouée)
JSON
{ "registration_id": "uuid", "status": "pending", "message": "Registration received, pending verification" }
Réponse 400 - Champ manquant ou invalide
JSON
{ "error": "missing_field", "field": "player1.phone_invalid" }

field identifie le problème : nom du champ manquant (ex. player2.last_name) ou suffixe _invalid pour un format incorrect (player1.email_invalid, player1.phone_invalid).

Réponse 404 - Tournoi non disponible
JSON
{ "error": "Tournament not found or not open for registration" }
Réponse 409 - Paire déjà inscrite
JSON
{ "error": "This pair is already registered" }
Body JSON
07

Désinscription d'une paire

Restrictions

La désinscription n'est possible que pour les inscriptions créées via Pista (source: pista). Elle est bloquée si le tournoi est en cours ou terminé. La promotion automatique de la liste d'attente n'est pas gérée, le JA continue à promouvoir manuellement.

POST /partner-unregister Annuler une inscription paire
Body (JSON)
JSON
{ "registration_id": "550e8400-e29b-41d4-a716-446655440000" }
Champs requis
ChampTypeDescription
registration_iduuidID de l'inscription à annuler
Réponse 200 - Annulation confirmée
JSON
{ "status": "cancelled", "message": "Registration successfully cancelled" }
Réponse 400 - Paramètre manquant
JSON
{ "error": "registration_id is required" }
Réponse 403 - Inscription non-Pista
JSON
{ "error": "Cannot unregister a non-Pista registration" }
Réponse 404 - Inscription introuvable
JSON
{ "error": "Registration not found" }
Réponse 409 - Tournoi en cours ou inscription déjà annulée
JSON
{ "error": "Tournament already in progress, unregistration not allowed" } { "error": "Registration already cancelled" }
Body JSON
08

Webhooks · disponible (staging)

Implémenté sur staging

PTM notifie des changements sur le cycle de vie des inscriptions et des tournois (et non sur les attributs des joueurs : classement/contact ne sont pas synchronisés — servis par GET tournaments/:id). L'émission est active sur staging ; il reste à échanger l'URL d'abonnement et le secret de signature, et à confirmer le mapping de l'annulation (voir plus bas).

Chaque événement est envoyé en POST vers l'URL d'abonnement, signé en HMAC-SHA256 du corps brut (header X-PTM-Signature, hex). Un header X-PTM-Event-Id (UUID) permet la déduplication idempotente. En cas d'échec, réessais en backoff exponentiel (jusqu'à 8 tentatives, puis abandon). Seuls les changements visibles côté partenaire sont émis (pas de notification si le statut projeté ne change pas) ; un changement initié par le partenaire n'est pas réémis vers lui.

RegistrationStatusUpdated — changement de statut d'une inscription
JSON
{ "event": "RegistrationStatusUpdated", "registration_id": "uuid", "tournament_id": "uuid", "partner_booking_id": "votre-booking-id", "previous_status": "pending", "new_status": "confirmed", "roster": [ { "licence": "...", "first_name": "...", "last_name": "...", "ranking": 1234, "email": "...", "phone": "..." }, { "licence": "...", "first_name": "...", "last_name": "...", "ranking": 5678, "email": "...", "phone": "..." } ], "timestamp": "2026-06-15T10:00:00Z" }

Émis sur les transitions de statut projeté confirmed / pending / waitlist / rejected : ex. le juge-arbitre valide une inscription en anomalie (pending → confirmed), promeut une liste d'attente (waitlist → confirmed ou → pending), ou rejette une inscription (→ rejected). roster reflète la paire courante (peut être null en liste d'attente). N'est émis que pour les inscriptions d'origine partenaire (porteuses d'un partner_booking_id).

TournamentUpdated — changement sur un tournoi
JSON
{ "event": "TournamentUpdated", "tournament_id": "uuid", "status": "CANCELLED", "previous_status": "SETUP", "max_pairs": 16, "available_spots": 6, "registration_close_at": "2026-06-20T18:00:00Z", "timestamp": "2026-06-15T10:00:00Z" }

Émis sur changement de status du tournoi (notamment → CANCELLED : toutes les inscriptions du tournoi sont à retirer côté partenaire). N'est émis que pour les tournois ayant au moins une inscription d'origine partenaire.

Points encore à valider avec Pista

Mapping exact de l'annulation (un cancelled PTM est projeté provisoirement en rejected — à confirmer : rejected vs événement de désinscription dédié) ; identifiant joueur opaque pour la réconciliation par slot lors d'un remplacement ; URL d'abonnement + secret de signature à échanger.