Chaque opération part de https://gorally.games/v1 et exige une clé dans l’en-tête Authorization, sauf mention contraire.

Pas de bac à sable

Une clé rk_live_ agit sur les données réelles de ton organisation. Commence par une clé read, qui ne peut rien modifier.

Serveur à serveur seulement

L’API ne répond pas aux navigateurs : elle n’autorise aucune origine CORS. Ne mets jamais ta clé dans du code côté navigateur.

Forfait Ligues requis

Émettre une clé demande le forfait Ligues de ton organisation. Tu crées ensuite ta clé dans l’écran Intégrations.Voir le forfait Ligues

Explorateur interactif. Parcours le contrat dans une vue interactive. L’envoi de requêtes y est désactivé.

Compétition

Ligues, saisons, catégories, classements.

Ce document, en anglais.

GET /openapi.json

Aucune clé requise

Sans clé : le contrat se lit avant de s’intégrer.

Réponses

Réponses — GET /openapi.json
CodeDescription
200Le document OpenAPI, en anglais.object
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.

Exemple de réponse

{
  "openapi": "3.1.0",
  "info": {
    "title": "Rally Leagues API",
    "version": "1.0.0"
  }
}

Ce document, en français.

GET /openapi.fr.json

Aucune clé requise

Sans clé, comme la version anglaise.

Réponses

Réponses — GET /openapi.fr.json
CodeDescription
200Le document OpenAPI, en français.object
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.

Exemple de réponse

{
  "openapi": "3.1.0",
  "info": {
    "title": "Rally — API des ligues",
    "version": "1.0.0"
  }
}

Les ligues de l’organisation.

GET /leagues

Scope : read

Pagination par curseur sur une clé immuable : une ligne existante est vue exactement une fois, même sous mutation.

Paramètres

Paramètres — GET /leagues
NomEmplacementTypeRequisDescription
limitrequêteintegerminimum: 1maximum: 100default: 25NonTaille de page. Au-delà de 100, la requête est refusée plutôt que rognée en silence.
afterrequêtestringNonCurseur : l’identifiant du DERNIER élément de la page précédente. Un curseur inconnu est refusé — il ne se rattrape pas. Les trois listes paginées — événements, ligues, rencontres — suivent la MÊME règle : le parcours ordonne sur un numéro d’insertion interne qu’aucune modification ne déplace, et une page s’arrête avant toute écriture dont la transaction peut encore être en vol. Une ligne écrite par une transaction longue est donc RETARDÉE, jamais sautée : elle apparaît à sa place dès que sa transaction est commise, et exactement une fois. Renommer une ligue ou corriger une rencontre entre deux pages ne la fait ni disparaître ni revenir.

Réponses

Réponses — GET /leagues
CodeDescription
200Une page de ligues.list<League>
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "object": "list",
  "data": [
    {
      "id": "5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f",
      "name": "Ligue du mardi",
      "sport": "CURLING",
      "sportLabel": null,
      "profile": {
        "key": "CURLING",
        "label": "Curling",
        "version": 3,
        "resourceKind": "SHEET",
        "cadence": {
          "unitsPerMatch": 8,
          "minutesPerUnit": 15,
          "matchesPerNight": 1,
          "scoreTarget": null,
          "seasonWeeks": {
            "min": 10,
            "max": 30
          }
        },
        "vocabulary": {
          "resource": {
            "one": "feuille",
            "many": "feuilles",
            "genre": "F"
          },
          "unit": {
            "one": "bout",
            "many": "bouts",
            "genre": "M"
          },
          "match": {
            "one": "partie",
            "many": "parties",
            "genre": "F"
          },
          "scoreUnit": {
            "one": "point",
            "many": "points",
            "genre": "M"
          }
        }
      },
      "timeZone": "America/Toronto"
    }
  ],
  "has_more": false
}

Crée une ligue.

POST /leagues

Scope : write

Le droit commercial de l’organisation est consulté comme pour toute écriture.

Paramètres

Paramètres — POST /leagues
NomEmplacementTypeRequisDescription
Idempotency-Keyen-têtestringmaxLength: 255NonRejoue la réponse d’origine pendant 24 heures : un succès, ou un refus rendu pendant l’exécution. Un corps refusé avant (JSON mal formé, champ invalide) ne réserve pas la clé. Même clé et corps différent : 409 IDEMPOTENCY_KEY_REUSED. Une exécution encore en vol répond 409 IDEMPOTENT_REQUEST_IN_FLIGHT. Une 5xx n’est jamais figée : elle dit « réessaie ». Et si une exécution s’interrompt sans que son issue soit connue — une connexion perdue juste après le commit rend la même erreur qu’une perdue juste avant — le rejeu répond 409 IDEMPOTENCY_OUTCOME_UNKNOWN plutôt que de risquer un doublon : relis la ressource, puis reprends avec une clé neuve.

Corps de la requête

CreateLeague

Exemple de requête

{
  "name": "Ligue du mardi",
  "sport": "CURLING",
  "timeZone": "America/Toronto"
}

Réponses

Réponses — POST /leagues
CodeDescription
201La ligue créée.League
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
409Rejeu divergent de la clé d’idempotence.
413Corps au-delà de 16 Kio (BODY_TOO_LARGE). La façade publique ne transporte aucun fichier.
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "id": "5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f",
  "name": "Ligue du mardi",
  "sport": "CURLING",
  "sportLabel": null,
  "profile": {
    "key": "CURLING",
    "label": "Curling",
    "version": 3,
    "resourceKind": "SHEET",
    "cadence": {
      "unitsPerMatch": 8,
      "minutesPerUnit": 15,
      "matchesPerNight": 1,
      "scoreTarget": null,
      "seasonWeeks": {
        "min": 10,
        "max": 30
      }
    },
    "vocabulary": {
      "resource": {
        "one": "feuille",
        "many": "feuilles",
        "genre": "F"
      },
      "unit": {
        "one": "bout",
        "many": "bouts",
        "genre": "M"
      },
      "match": {
        "one": "partie",
        "many": "parties",
        "genre": "F"
      },
      "scoreUnit": {
        "one": "point",
        "many": "points",
        "genre": "M"
      }
    }
  },
  "timeZone": "America/Toronto"
}

Une ligue et ses saisons.

GET /leagues/{leagueId}

Scope : read

Paramètres

Paramètres — GET /leagues/{leagueId}
NomEmplacementTypeRequisDescription
leagueIdcheminstringOui

Réponses

Réponses — GET /leagues/{leagueId}
CodeDescription
200La ligue et ses saisons.LeagueDetail
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "object": "league",
  "id": "5b0c2f9e-8a41-4d6b-9f3e-1a2b3c4d5e6f",
  "name": "Ligue du mardi",
  "sport": "CURLING",
  "sportLabel": null,
  "profile": {
    "key": "CURLING",
    "label": "Curling",
    "version": 3,
    "resourceKind": "SHEET",
    "cadence": {
      "unitsPerMatch": 8,
      "minutesPerUnit": 15,
      "matchesPerNight": 1,
      "scoreTarget": null,
      "seasonWeeks": {
        "min": 10,
        "max": 30
      }
    },
    "vocabulary": {
      "resource": {
        "one": "feuille",
        "many": "feuilles",
        "genre": "F"
      },
      "unit": {
        "one": "bout",
        "many": "bouts",
        "genre": "M"
      },
      "match": {
        "one": "partie",
        "many": "parties",
        "genre": "F"
      },
      "scoreUnit": {
        "one": "point",
        "many": "points",
        "genre": "M"
      }
    }
  },
  "timeZone": "America/Toronto",
  "cadences": [
    {
      "id": "7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f",
      "key": "HIVER",
      "label": "Hiver",
      "position": 1
    }
  ],
  "seasons": [
    {
      "id": "c3a1e5d7-9b2f-4a6c-8e0d-1f3b5d7a9c2e",
      "name": "Hiver 2027",
      "cadenceId": "7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f",
      "ordinal": 1,
      "status": "SETUP",
      "sportProfile": "CURLING",
      "sportProfileVersion": 3,
      "resourceKind": "SHEET",
      "cadence": {
        "unitsPerMatch": 8,
        "minutesPerUnit": 15,
        "matchesPerNight": 1,
        "scoreTarget": null,
        "seasonWeeks": {
          "min": 10,
          "max": 30
        }
      },
      "vocabulary": {
        "resource": {
          "one": "feuille",
          "many": "feuilles",
          "genre": "F"
        },
        "unit": {
          "one": "bout",
          "many": "bouts",
          "genre": "M"
        },
        "match": {
          "one": "partie",
          "many": "parties",
          "genre": "F"
        },
        "scoreUnit": {
          "one": "point",
          "many": "points",
          "genre": "M"
        }
      }
    }
  ]
}

Crée une saison dans une ligue.

POST /leagues/{leagueId}/seasons

Scope : write

Paramètres

Paramètres — POST /leagues/{leagueId}/seasons
NomEmplacementTypeRequisDescription
leagueIdcheminstringOui
Idempotency-Keyen-têtestringmaxLength: 255NonRejoue la réponse d’origine pendant 24 heures : un succès, ou un refus rendu pendant l’exécution. Un corps refusé avant (JSON mal formé, champ invalide) ne réserve pas la clé. Même clé et corps différent : 409 IDEMPOTENCY_KEY_REUSED. Une exécution encore en vol répond 409 IDEMPOTENT_REQUEST_IN_FLIGHT. Une 5xx n’est jamais figée : elle dit « réessaie ». Et si une exécution s’interrompt sans que son issue soit connue — une connexion perdue juste après le commit rend la même erreur qu’une perdue juste avant — le rejeu répond 409 IDEMPOTENCY_OUTCOME_UNKNOWN plutôt que de risquer un doublon : relis la ressource, puis reprends avec une clé neuve.

Corps de la requête

CreateSeason

Exemple de requête

{
  "name": "Hiver 2027",
  "cadenceId": "7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f",
  "startsAt": "2027-01-05T00:00:00.000Z",
  "endsAt": "2027-04-27T00:00:00.000Z"
}

Réponses

Réponses — POST /leagues/{leagueId}/seasons
CodeDescription
201La saison créée.Season
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
409Rejeu divergent de la clé d’idempotence.
413Corps au-delà de 16 Kio (BODY_TOO_LARGE). La façade publique ne transporte aucun fichier.
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "id": "c3a1e5d7-9b2f-4a6c-8e0d-1f3b5d7a9c2e",
  "name": "Hiver 2027",
  "cadenceId": "7e4d2c1b-0a9f-4e8d-b7c6-5a4b3c2d1e0f",
  "ordinal": 1,
  "status": "SETUP",
  "sportProfile": "CURLING",
  "sportProfileVersion": 3,
  "resourceKind": "SHEET",
  "cadence": {
    "unitsPerMatch": 8,
    "minutesPerUnit": 15,
    "matchesPerNight": 1,
    "scoreTarget": null,
    "seasonWeeks": {
      "min": 10,
      "max": 30
    }
  },
  "vocabulary": {
    "resource": {
      "one": "feuille",
      "many": "feuilles",
      "genre": "F"
    },
    "unit": {
      "one": "bout",
      "many": "bouts",
      "genre": "M"
    },
    "match": {
      "one": "partie",
      "many": "parties",
      "genre": "F"
    },
    "scoreUnit": {
      "one": "point",
      "many": "points",
      "genre": "M"
    }
  }
}

Les catégories d’une saison.

GET /seasons/{seasonId}/categories

Scope : read

Paramètres

Paramètres — GET /seasons/{seasonId}/categories
NomEmplacementTypeRequisDescription
seasonIdcheminstringOui

Réponses

Réponses — GET /seasons/{seasonId}/categories
CodeDescription
200Les catégories de la saison, rendues entières.list<Category>
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "object": "list",
  "data": [
    {
      "id": "e2b4d6f8-1a3c-4e5b-9d7f-0c2e4a6b8d1f",
      "seasonId": "c3a1e5d7-9b2f-4a6c-8e0d-1f3b5d7a9c2e",
      "name": "Mardi soir",
      "phase": "REGULAR",
      "revision": 6,
      "policyRevision": 1,
      "matchCount": 28,
      "unplayedMatchCount": 20,
      "unplayedRegularMatchCount": 20,
      "registrationCapacity": 8
    }
  ]
}

Le classement d’une catégorie.

GET /categories/{categoryId}/standings

Scope : read

Le classement est CALCULÉ à la lecture et n’est jamais stocké : c’est pourquoi il n’existe aucun webhook standings.updated. Un consommateur qui veut le suivre écoute match.completed et relit ceci.

Paramètres

Paramètres — GET /categories/{categoryId}/standings
NomEmplacementTypeRequisDescription
categoryIdcheminstringOui

Réponses

Réponses — GET /categories/{categoryId}/standings
CodeDescription
200Le classement, ordonné et rendu entier.list<StandingRow>
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "object": "list",
  "data": [
    {
      "leagueTeamId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "name": "Les Pierres",
      "rank": 1,
      "tied": false,
      "played": 4,
      "wins": 3,
      "draws": 0,
      "losses": 1,
      "scoreFor": 29,
      "scoreAgainst": 18,
      "diff": 11,
      "gamePointsFor": 0,
      "gamePointsAgainst": 0,
      "points": 9
    },
    {
      "leagueTeamId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
      "name": "Les Balais",
      "rank": 2,
      "tied": false,
      "played": 4,
      "wins": 2,
      "draws": 1,
      "losses": 1,
      "scoreFor": 24,
      "scoreAgainst": 21,
      "diff": 3,
      "gamePointsFor": 0,
      "gamePointsAgainst": 0,
      "points": 7
    }
  ]
}

Résultats

Rencontres et saisie de résultat.

Les rencontres de l’organisation.

GET /matches

Scope : read

revision est le verrou optimiste à annoncer pour écrire un résultat.

Paramètres

Paramètres — GET /matches
NomEmplacementTypeRequisDescription
limitrequêteintegerminimum: 1maximum: 100default: 25NonTaille de page. Au-delà de 100, la requête est refusée plutôt que rognée en silence.
afterrequêtestringNonCurseur : l’identifiant du DERNIER élément de la page précédente. Un curseur inconnu est refusé — il ne se rattrape pas. Les trois listes paginées — événements, ligues, rencontres — suivent la MÊME règle : le parcours ordonne sur un numéro d’insertion interne qu’aucune modification ne déplace, et une page s’arrête avant toute écriture dont la transaction peut encore être en vol. Une ligne écrite par une transaction longue est donc RETARDÉE, jamais sautée : elle apparaît à sa place dès que sa transaction est commise, et exactement une fois. Renommer une ligue ou corriger une rencontre entre deux pages ne la fait ni disparaître ni revenir.

Réponses

Réponses — GET /matches
CodeDescription
200Une page de rencontres.list<Match>
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "object": "list",
  "data": [
    {
      "id": "f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c",
      "categoryId": "e2b4d6f8-1a3c-4e5b-9d7f-0c2e4a6b8d1f",
      "status": "SCHEDULED",
      "teamAId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "teamBId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
      "scoreA": null,
      "scoreB": null,
      "sets": null,
      "startsAt": "2027-01-13T00:15:00.000Z",
      "revision": 3
    }
  ],
  "has_more": true
}

Écrit ou corrige le résultat d’une rencontre.

PATCH /matches/{matchId}

Scope : write

expectedRevision est OBLIGATOIRE : c’est la comparaison-et-échange qui empêche deux programmes d’écraser silencieusement le résultat l’un de l’autre. Une révision périmée rend 409 — relis la rencontre, ne réessaie pas à l’aveugle. Pas d’Idempotency-Key ici : le verrou de révision fait déjà ce travail, et deux mécanismes se contrediraient.

Paramètres

Paramètres — PATCH /matches/{matchId}
NomEmplacementTypeRequisDescription
matchIdcheminstringOui

Corps de la requête

MatchResult

Exemple de requête

{
  "expectedRevision": 3,
  "event": "COMPLETER",
  "score": {
    "a": 7,
    "b": 5,
    "unitsPlayed": 8
  }
}

Réponses

Réponses — PATCH /matches/{matchId}
CodeDescription
200La rencontre après écriture.object
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
409Révision périmée, ou état qui n’admet pas cette commande.
413Corps au-delà de 16 Kio (BODY_TOO_LARGE). La façade publique ne transporte aucun fichier.
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "id": "f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c",
  "status": "COMPLETED",
  "scoreA": 7,
  "scoreB": 5,
  "resultSide": "A",
  "sets": null,
  "revision": 4
}

Effectifs

Sous scopes members:* uniquement — la seule surface qui porte des noms de personnes.

L’effectif d’une inscription.

GET /registrations/{registrationId}/roster

Scope : members:read

C’est la SEULE famille de routes qui rend des libellés de personnes, et une organisation peut la refuser à une clé sans lui retirer le reste. Aucun autre point de la façade — webhooks compris — ne les copie.

Paramètres

Paramètres — GET /registrations/{registrationId}/roster
NomEmplacementTypeRequisDescription
registrationIdcheminstringOui

Réponses

Réponses — GET /registrations/{registrationId}/roster
CodeDescription
200L’effectif.Roster
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "registrationId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "label": "Les Pierres",
  "memberships": [
    {
      "personId": "3c5e7a9b-1d2f-4b6c-8a0e-2d4f6b8c0e1a",
      "label": {
        "kind": "NAME",
        "value": "Camille Roy"
      },
      "role": "CAPTAIN",
      "jerseyNumber": 7,
      "endedAt": null
    }
  ]
}

Ajoute une personne à un effectif.

POST /registrations/{registrationId}/roster

Scope : members:write

Paramètres

Paramètres — POST /registrations/{registrationId}/roster
NomEmplacementTypeRequisDescription
registrationIdcheminstringOui
Idempotency-Keyen-têtestringmaxLength: 255NonRejoue la réponse d’origine pendant 24 heures : un succès, ou un refus rendu pendant l’exécution. Un corps refusé avant (JSON mal formé, champ invalide) ne réserve pas la clé. Même clé et corps différent : 409 IDEMPOTENCY_KEY_REUSED. Une exécution encore en vol répond 409 IDEMPOTENT_REQUEST_IN_FLIGHT. Une 5xx n’est jamais figée : elle dit « réessaie ». Et si une exécution s’interrompt sans que son issue soit connue — une connexion perdue juste après le commit rend la même erreur qu’une perdue juste avant — le rejeu répond 409 IDEMPOTENCY_OUTCOME_UNKNOWN plutôt que de risquer un doublon : relis la ressource, puis reprends avec une clé neuve.

Corps de la requête

AddMembership

Exemple de requête

{
  "personId": "3c5e7a9b-1d2f-4b6c-8a0e-2d4f6b8c0e1a",
  "role": "CAPTAIN",
  "jerseyNumber": 7
}

Réponses

Réponses — POST /registrations/{registrationId}/roster
CodeDescription
200L’effectif après ajout.Roster
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
409Rejeu divergent de la clé d’idempotence, ou conflit d’état.
413Corps au-delà de 16 Kio (BODY_TOO_LARGE). La façade publique ne transporte aucun fichier.
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "registrationId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "label": "Les Pierres",
  "memberships": [
    {
      "personId": "3c5e7a9b-1d2f-4b6c-8a0e-2d4f6b8c0e1a",
      "label": {
        "kind": "NAME",
        "value": "Camille Roy"
      },
      "role": "CAPTAIN",
      "jerseyNumber": 7,
      "endedAt": null
    }
  ]
}

Événements

Le même flux que les webhooks, relisible et rejouable.

Les événements structurels, du plus ancien au plus récent.

GET /events

Scope : read

LE RATTRAPAGE : c’est ce qui évite « on a perdu deux jours de webhooks ». Le flux est exactement celui des webhooks — même liste blanche, même charge mince, mêmes identifiants (webhook-id vaut id). Aucun événement de personne, de gouvernance ou de paiement n’y figure.

Paramètres

Paramètres — GET /events
NomEmplacementTypeRequisDescription
limitrequêteintegerminimum: 1maximum: 100default: 25NonTaille de page. Au-delà de 100, la requête est refusée plutôt que rognée en silence.
afterrequêtestringNonCurseur : l’identifiant du DERNIER élément de la page précédente. Un curseur inconnu est refusé — il ne se rattrape pas. Les trois listes paginées — événements, ligues, rencontres — suivent la MÊME règle : le parcours ordonne sur un numéro d’insertion interne qu’aucune modification ne déplace, et une page s’arrête avant toute écriture dont la transaction peut encore être en vol. Une ligne écrite par une transaction longue est donc RETARDÉE, jamais sautée : elle apparaît à sa place dès que sa transaction est commise, et exactement une fois. Renommer une ligue ou corriger une rencontre entre deux pages ne la fait ni disparaître ni revenir.

Réponses

Réponses — GET /events
CodeDescription
200Une page d’événements.list<Event>
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "object": "list",
  "data": [
    {
      "id": "8f6e4d2c-0b9a-4c7e-a5d3-1f9e7c5a3b0d",
      "type": "match.completed",
      "created": "2027-01-13T03:12:45.000Z",
      "organization": "9d3f5a7c-2b1e-4c8d-a6f0-3e2d1c0b9a87",
      "data": {
        "object": "match",
        "id": "f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c",
        "revision": 4
      }
    }
  ],
  "has_more": false
}

Relit un événement reçu par webhook.

GET /events/{eventId}

Scope : read

Un événement hors liste blanche est INTROUVABLE, pas interdit : il n’appartient pas au contrat public.

Paramètres

Paramètres — GET /events/{eventId}
NomEmplacementTypeRequisDescription
eventIdcheminstringOui

Réponses

Réponses — GET /events/{eventId}
CodeDescription
200L’événement.Event
400Requête malformée (INVALID_INPUT).
401Clé absente, mal formée, révoquée ou expirée (UNAUTHORIZED). Les cookies de session ne valent rien ici.
403La clé n’a pas le scope requis (FORBIDDEN), ou l’abonnement de l’organisation ne permet pas cet accès (ENTITLEMENT_DENIED).
404Ressource introuvable dans l’organisation de la clé (NOT_FOUND).
429Trop de requêtes (RATE_LIMITED), par adresse ou par clé. Attends le nombre de secondes de Retry-After, puis reprends avec un recul exponentiel bruité.
503Indisponible. LEAGUES_UNAVAILABLE (panne ou délai de 10 s dépassé) porte Retry-After: 30 ; une écriture peut alors ne pas être confirmée, vérifie son état avant de la répéter. ENTITLEMENT_UNAVAILABLE et HOLDER_UNINITIALIZED : droit commercial indisponible ou titulaire non initialisé.

Exemple de réponse

{
  "id": "8f6e4d2c-0b9a-4c7e-a5d3-1f9e7c5a3b0d",
  "type": "match.completed",
  "created": "2027-01-13T03:12:45.000Z",
  "organization": "9d3f5a7c-2b1e-4c8d-a6f0-3e2d1c0b9a87",
  "data": {
    "object": "match",
    "id": "f4e3d2c1-b0a9-4f8e-8d7c-6b5a4f3e2d1c",
    "revision": 4
  }
}

Schémas

Les objets que les opérations reçoivent et rendent.

Error

Error
NomTypeRequisDescription
errorstringOuiMessage lisible, en français. Dérivé du seul code : il ne porte jamais d’identifiant interne.
codestringOuiValeurs : UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, INVALID_INPUT, IDEMPOTENCY_KEY_REUSED, IDEMPOTENT_REQUEST_IN_FLIGHT, IDEMPOTENCY_OUTCOME_UNKNOWN, ENTITLEMENT_DENIED, ENTITLEMENT_UNAVAILABLE, HOLDER_UNINITIALIZED, BODY_TOO_LARGE, WEBHOOK_VAULT_UNAVAILABLE, ROTATION_EN_COURS, LEAGUES_UNAVAILABLE, RATE_LIMITED
request_idstringOuiL’identifiant de CETTE requête, également rendu en en-tête X-Request-Id sur toute réponse — succès compris. Recopie-le dans un ticket : c’est ce qui permet de retrouver ta requête dans nos journaux. Si tu en envoies un toi-même dans X-Request-Id, il est repris tel quel (8 à 64 caractères de A-Za-z0-9_.:-).

SportTerm

SportTerm
NomTypeRequisDescription
onestringOui
manystringOui
genrestringOuiValeurs : M, F

SportCadence

Repères modifiables du profil ; les valeurs nulles ne prétendent aucun défaut.

SportCadence
NomTypeRequisDescription
unitsPerMatchinteger | nullminimum: 1Oui
minutesPerUnitinteger | nullminimum: 1Oui
matchesPerNightinteger | nullminimum: 1Oui
scoreTargetinteger | nullminimum: 1Oui
seasonWeeksobject | nullOui
seasonWeeks.minintegerminimum: 1Oui
seasonWeeks.maxintegerminimum: 1Oui

SportProfile

Le profil sportif RÉSOLU de la ligue. label est le libellé du catalogue, ou le sport saisi par le club quand la clé vaut AUTRE. version est celle du catalogue qui a rendu ce profil : elle avance quand une valeur rendue change, jamais rétroactivement.

SportProfile
NomTypeRequisDescription
keystringOuiValeurs : CURLING, CORNHOLE, HOCKEY_RECREATIF, VOLLEYBALL, PICKLEBALL, BALLE_MOLLE, AUTRE
labelstringOui
versionintegerOui
resourceKindstringOuiValeurs : SHEET, COURT, FIELD, SURFACE
cadenceSportCadenceOui
vocabularyobjectOuiLes mots du sport : la ressource qu’un créneau réserve (feuille, planche, terrain), l’unité qu’une rencontre compte (bout, manche, période), ce qu’on joue et ce qu’on marque.
vocabulary.resourceSportTermOui
vocabulary.unitSportTermOui
vocabulary.matchSportTermOui
vocabulary.scoreUnitSportTermOui

League

League
NomTypeRequisDescription
idstringOui
namestringOui
sportstringOuiValeurs : CURLING, CORNHOLE, HOCKEY_RECREATIF, VOLLEYBALL, PICKLEBALL, BALLE_MOLLE, AUTRE
sportLabelstring | nullOuiLe sport saisi par le club quand sport vaut AUTRE ; null sinon.
profileSportProfileOui
timeZonestringOui

LeagueDetail

LeagueDetail
NomTypeRequisDescription
objectstringOuiValeurs : league
idstringOui
namestringOui
sportstringOuiValeurs : CURLING, CORNHOLE, HOCKEY_RECREATIF, VOLLEYBALL, PICKLEBALL, BALLE_MOLLE, AUTRE
sportLabelstring | nullOui
profileSportProfileOui
timeZonestringOui
cadencesCadence[]Oui
seasonsSeason[]Oui

Cadence

Cadence
NomTypeRequisDescription
idstringOui
keystringOui
labelstringOui
positionintegerOui

CreateLeague

CreateLeague
NomTypeRequisDescription
namestringmaxLength: 200Oui
sportstringOuiUne clé du catalogue de profils sportifs. AUTRE pour un sport non préréglé.Valeurs : CURLING, CORNHOLE, HOCKEY_RECREATIF, VOLLEYBALL, PICKLEBALL, BALLE_MOLLE, AUTRE
sportLabelstring | nullmaxLength: 60NonLe nom du sport, OBLIGATOIRE quand sport vaut AUTRE et refusé sinon.
timeZonestringOuiFuseau IANA, par exemple America/Toronto.

Season

Season
NomTypeRequisDescription
idstringOui
namestringOui
cadenceIdstringNon
ordinalintegerNon
statusstringNonValeurs : SETUP, REGULAR, PLAYOFFS, CLOSED, ABANDONED
sportProfilestring | nullOuiClé littérale figée à la création ; null avant le catalogue. Une clé future reste rendue.
sportProfileVersioninteger | nullminimum: 1Oui
resourceKindstringOuiValeurs : SHEET, COURT, FIELD, SURFACE
cadenceSportCadenceOui
vocabularyobjectOui
vocabulary.resourceSportTermOui
vocabulary.unitSportTermOui
vocabulary.matchSportTermOui
vocabulary.scoreUnitSportTermOui

CreateSeason

CreateSeason
NomTypeRequisDescription
namestringmaxLength: 200Oui
cadenceIdstringOui
startsAtstring | nullformat: date-timeNon
endsAtstring | nullformat: date-timeNon

Category

Category
NomTypeRequisDescription
idstringOui
seasonIdstringOui
namestringOui
phasestringOuiValeurs : SETUP, REGULAR, PLAYOFFS, CLOSED, ABANDONED
revisionintegerOui
policyRevisionintegerNon
matchCountintegerNon
unplayedMatchCountintegerNon
unplayedRegularMatchCountintegerNon
registrationCapacityinteger | nullNon

StandingRow

StandingRow
NomTypeRequisDescription
leagueTeamIdstringNon
namestringNon
rankintegerNon
tiedbooleanNon
playedintegerNon
winsintegerNon
drawsintegerNon
lossesintegerNon
scoreForintegerNon
scoreAgainstintegerNon
diffintegerNon
gamePointsForintegerNonPoints de jeu marqués, cumulés sur le détail des sets. 0 hors de la forme de résultat SETS.
gamePointsAgainstintegerNon
pointsintegerNon

Match

Match
NomTypeRequisDescription
idstringOui
categoryIdstringNon
statusstringNon
teamAIdstring | nullNon
teamBIdstring | nullNon
scoreAinteger | nullNon
scoreBinteger | nullNon
setsSetScore[] | nullNonDétail des sets sous la forme de résultat SETS ; null ailleurs, tableau vide sur un forfait. scoreA et scoreB portent alors les SETS gagnés.
startsAtstring | nullformat: date-timeNon
revisionintegerOuiLe verrou optimiste à annoncer dans expectedRevision.

MatchResult

MatchResult
NomTypeRequisDescription
expectedRevisionintegerOui
eventstringOuiValeurs : COMPLETER, ATTRIBUER, CORRIGER_SCORE, CORRIGER_ATTRIBUTION
scoreobjectNon
score.aintegerNon
score.bintegerNon
score.unitsPlayedintegerNon
setsSetScore[]maxItems: 15NonDétail des sets, exigé sous la forme de résultat SETS et refusé ailleurs. Le score de match ne s’envoie pas : il se dérive du détail. Une politique ne peut pas exiger plus de 15 sets, donc aucun résultat n'en porte davantage.
resultSidestring | nullNonValeurs : A, B, DRAW
forfeitingSidestring | nullNonValeurs : A, B, BOTH
outcomestring | nullNonValeurs : PLAYED, CONCEDED, FORFEIT, DOUBLE_FORFEIT, ABANDONED
reasonstring | nullminLength: 1maxLength: 500Non

SetScore

SetScore
NomTypeRequisDescription
aintegerOui
bintegerOui

Roster

Roster
NomTypeRequisDescription
registrationIdstringNon
labelstringNon
membershipsobject[]Non

AddMembership

AddMembership
NomTypeRequisDescription
personIdstringOui
rolestringNonValeurs : PLAYER, CAPTAIN, COACH
jerseyNumberinteger | nullminimum: 0maximum: 999Non

Event

Event
NomTypeRequisDescription
idstringOuiVaut l’en-tête webhook-id de la livraison correspondante.
typestringOuiValeurs : league.created, season.created, season.closed, season.reopened, season.abandoned, season.renewed, category.created, category.updated, registration.submitted, registration.withdrawn, registration.reinstated, schedule.published, match.rescheduled, match.completed, match.awarded, match.corrected, match.cancelled, match.voided, series.opened, series.updated
createdstringformat: date-timeOui
organizationstringOui
dataobjectOuiCHARGE MINCE, toujours : un objet, son identifiant, sa révision. Jamais un nom, une adresse ni une date de naissance — une URL de webhook est un point de sortie vers un tiers, souvent en clair dans SES journaux. Le consommateur relit l’état courant, il ne rejoue pas un état vieux de six heures.
data.objectstringNon
data.idstringNon
data.revisioninteger | nullNon
data.previousobjectNon
data.scheduledobjectNonmatch.rescheduled UNIQUEMENT — la place visée, quand previous porte celle qu’elle remplace : resourceId, startsAt, endsAt. C’est le seul événement à porter deux valeurs, parce que c’est le seul fait qu’un consommateur ne peut pas reconstituer sans avoir gardé son propre état. Il est émis à la PUBLICATION de l’horaire, une fois par rencontre dont la place publiée a changé : un brouillon de déplacement n’a été communiqué à personne et ne s’annonce donc jamais.

Codes d’erreur

Le champ code d’une erreur prend une de ces valeurs. Elles ne changent pas : branche ta logique dessus.

  • UNAUTHORIZED
  • FORBIDDEN
  • NOT_FOUND
  • CONFLICT
  • INVALID_INPUT
  • IDEMPOTENCY_KEY_REUSED
  • IDEMPOTENT_REQUEST_IN_FLIGHT
  • IDEMPOTENCY_OUTCOME_UNKNOWN
  • ENTITLEMENT_DENIED
  • ENTITLEMENT_UNAVAILABLE
  • HOLDER_UNINITIALIZED
  • BODY_TOO_LARGE
  • WEBHOOK_VAULT_UNAVAILABLE
  • ROTATION_EN_COURS
  • LEAGUES_UNAVAILABLE
  • RATE_LIMITED