Modelo de datos
El árbol de dominio de SETTO — organización, torneo, categoría, ronda, grupo, cuadro y partido — más las convenciones de identificadores y los valores de enum sobre los que vas a ramificar.
Todo lo que devuelve la API externa cuelga de un mismo árbol: una organización posee torneos; un torneo tiene categorías (divisions) y equipos; una categoría tiene rondas; una ronda contiene grupos (pools, con clasificación) para el juego de todos contra todos y cuadros (draws) para la eliminación directa; ambos terminan conteniendo partidos, y un partido terminado tiene un marcador (score) con sus sets. Aprende esta forma una vez y los seis endpoints dejan de parecer seis cosas sin relación.
El árbol
En prosa:
- Una Organization posee torneos. Tu token está ligado a exactamente una.
- Un Tournament es una competencia. Lleva el formato (
type), el deporte, elstatusdel ciclo de vida, las fechas y unapublicUrl. - Una Division es lo que organizadores y jugadores llaman categoría: un
segmento por nivel y rama dentro del torneo (
Cuarta Fuerza Varonil). Es dueña de las rondas y del precio. - Un Round es una fase — juego de grupos o una etapa de eliminación.
- Un Pool es un grupo dentro de una ronda de todos contra todos; contiene los equipos de ese grupo y su clasificación (standings).
- Un Draw es un cuadro. Contiene draw rounds (
Quarterfinals,Semifinals, …) en suorder, y cada draw round contiene sus partidos. - Un Game es un partido. Los partidos terminados llevan un score, que a su vez lleva sus sets.
- Un Team es una inscripción registrada: una pareja en dobles, un jugador único en singles, el equipo completo en fútbol, y en las ligas por equipos una escuadra ligada a un league team.
Identificadores
Cada recurso expone un UUID v4 como idx. Ese es el id que pasas en una ruta
(/v1/external/tournaments/{idx}) y el id que guardas de tu lado. Las llaves
primarias numéricas que SETTO usa internamente nunca se exponen.
Los torneos llevan dos identificadores extra:
| Campo | Ejemplo | Uso |
|---|---|---|
slug | torneo-apertura-2026 | Legible, único, estable |
publicUrl | https://www.setto.io/t/torneo-apertura-2026 | Página pública canónica, lista para enlazar |
Los endpoints toman idx, no slug. Guarda el idx de la llamada de listado;
usa el slug para mostrar y la publicUrl para enlazar.
Torneo
| Campo | Notas |
|---|---|
type | Formato de competencia — ver abajo |
sport | PADEL, TENNIS, PICKLEBALL o FUTBOL |
status | Ciclo de vida — ver abajo |
startDate, endDate | Marcas de tiempo ISO 8601 en UTC |
timezone | Zona IANA en la que se armó el calendario (America/Monterrey), nullable |
isPublic | Si la página pública es accesible — false no la oculta de la API |
isFormatComplete | Si el organizador terminó de configurar el formato |
matchDays | Número de jornadas, para torneos con forma de liga |
standingColumns | Qué estadísticas muestra la tabla de clasificación — en fútbol, las de goles |
sportSettings | Solo fútbol: modalidad, tiempo extra, walkover — ver abajo. null en el resto |
publicUrl | https://www.setto.io/t/{slug} |
organization | { idx, name, slug } — siempre presente |
type
Los cuatro formatos que te vas a encontrar más seguido:
| Valor | Forma |
|---|---|
TOURNAMENT | Un evento único: fase de grupos y/o un cuadro |
LEAGUE | Una temporada de jornadas entre parejas |
TEAM_LEAGUE | Una temporada entre escuadras; una ronda puede tener division: null |
INDIVIDUAL_LEAGUE | Una temporada entre jugadores individuales |
Los formatos especializados (Americano, express/lucky-loser y las ligas
individuales de pickleball) llevan sus propios valores de type. Trata type
como una cadena abierta: ramifica sobre los valores que te importen y deja pasar
el resto, en lugar de dar por hecho que el conjunto está cerrado.
status
El ciclo de vida corre en una sola dirección:
DRAFT → UPCOMING → IN_PROGRESS → COMPLETED → ARCHIVEDLos cinco se devuelven. Los torneos DRAFT y ARCHIVED son visibles para tu
token aunque el sitio público los oculte — fíltralos con el parámetro de consulta
status, o descártalos del lado del cliente, antes de renderizar nada público.
sportSettings
Un torneo de fútbol (soccer) lleva un bloque sportSettings con las reglas que
eligió el organizador. En PADEL, TENNIS y PICKLEBALL el campo es null.
| Campo | Notas |
|---|---|
modality | F5, F7, F9, F11 o FUTSAL — tamaño de equipo y cancha |
extraTime | Si un empate en eliminatoria se va a tiempo extra antes de los penales |
matchMinutes | Minutos de tiempo reglamentario |
h2hRequiresCompleteSubset | Omitir la mini-liga si algún par empatado aún no se enfrentó |
forfeitScore | Goles que se le otorgan al equipo que sí se presentó, en un walkover |
fairPlayWeights | { yellow, secondYellow, directRed, yellowThenRed }, nullable |
Los torneos de fútbol siempre son type: TOURNAMENT, siempre se registran por
equipo (un capitán inscribe a la escuadra) y nunca forman parte de un circuito:
circuit y circuitCategory quedan en null.
Categoría (division)
| Campo | Notas |
|---|---|
name | Cuarta Fuerza Varonil |
number | Orden de despliegue; las listas se ordenan por él, ascendente |
color | Color hexadecimal que eligió el organizador, usado en toda la UI de SETTO |
isDoubles | true para parejas, false para singles y para equipos de fútbol |
isVisible | false significa que el organizador ocultó esta categoría en el sitio público |
price | Precio de inscripción de esta categoría, nullable |
minParticipants, maxParticipants, maxRegistrations | Cupo, todos nullable |
tournament | Referencia inversa { idx } |
Lo oculto se devuelve, no se filtra
Una categoría oculta regresa con isVisible: false en lugar de omitirse. El
trabajo de la API es decirte la verdad; decidir qué mostrar es tuyo. Lo mismo
aplica a las rondas.
Ronda
| Campo | Notas |
|---|---|
name | Pool Play, Playoffs o Qualifiers |
type | ROUND_ROBIN_ROUND, SINGLE_ELIMINATION_ROUND o QUALIFIERS_ROUND |
number | Orden dentro de la categoría |
isVisible | Misma semántica que en una categoría |
isSetup | Si el organizador ya generó los partidos de la ronda |
numberOfSets, proSets, playAllSets, pointsPerSet | Reglas de puntuación |
drawSize, numQualifiers, placements | Dimensiones del cuadro |
division | { idx, name } — nullable: una ronda de TEAM_LEAGUE puede abarcar todo el torneo |
Una ronda de todos contra todos llena pools; una ronda de eliminación directa
llena draws. Muchas rondas llenan tanto games (la lista plana) como la
estructura anidada del cuadro — así que el mismo partido puede aparecer dos veces
en un payload, una bajo games y otra bajo draws[].rounds[].games[]. Elimina
duplicados por game.idx.
Una ronda de fútbol siempre es de todos contra todos o de eliminación directa,
nunca de clasificación, y siempre es de un solo set: numberOfSets es 1,
proSets y playAllSets son false, y pointsPerSet es null.
Grupo, clasificación y estadísticas de equipo
Un pool es un grupo: { idx, name, number }, más teams y standings
cuando los populas. Una standing lleva rank, awardedPoints, su referencia
team, y un objeto teamStats con points, wins, losses, ties,
forfeits, pointsFor / pointsAgainst, setsPointsFor /
setsPointsAgainst y las dos diferencias calculadas.
En fútbol esos mismos contadores son goles: pointsFor son los goles a favor,
pointsAgainst los goles en contra y pointsDifference la diferencia de goles.
Las estadísticas de sets — setsPointsFor, setsPointsAgainst y
setsPointsDifference — nunca se escriben en fútbol y se quedan en 0.
tournament.standingColumns elige cuáles de esas estadísticas muestra la tabla.
Un torneo de fútbol agrega GOALS_FOR, GOALS_AGAINST y
GOAL_DIFFERENCE — tres columnas que leen los campos pointsFor /
pointsAgainst / pointsDifference de arriba — en lugar de las columnas
SETS_* / POINTS_*, que no puede usar. Los contadores de partidos quedan
igual: un torneo de fútbol nuevo nace con GAMES_PLAYED, GAMES_WON,
GAMES_TIED, GAMES_LOST y esas tres columnas de goles. Los deportes de
raqueta conservan sus columnas de sets y puntos, y nunca reciben las de goles.
Sobre rank
rank es el rango almacenado, tal como SETTO lo calculó por última vez. El
sitio público aplica encima el head-to-head y los demás desempates
configurados al renderizar una tabla de clasificación, así que una tabla que
armes solo con rank puede diferir de la de setto.io. Si necesitas paridad
exacta, ordena con las reglas de desempate del propio torneo
(populate=rankedTiebreakRules en el torneo) en lugar de hacerlo por rank.
El fútbol agrega un criterio a ese orden: H2HL, la mini-liga de
enfrentamientos directos. Los equipos que siguen empatados se reordenan con una
sub-tabla armada solo con los partidos que jugaron entre ellos — primero puntos,
luego diferencia de goles y luego goles a favor dentro de esa sub-tabla. Se
omite mientras esos equipos no se hayan enfrentado todos, salvo que el
organizador desactive sportSettings.h2hRequiresCompleteSubset.
Cuadro y ronda de cuadro
Un draw es un cuadro: { idx, placement }. placement distingue el cuadro
principal de los cuadros de consolación y de tercer lugar. Sus draw rounds
llevan un label (Quarterfinals, Semifinals, Final) y un order; ordena
por order para acomodar el cuadro de izquierda a derecha.
Partido
| Campo | Notas |
|---|---|
number, displayNumber | Numeración del partido; displayNumber es la que muestran los organizadores |
status | UPCOMING, FINISHED, CANCELLED, FORFEIT o BYE |
time | Hora programada de inicio, ISO 8601 en UTC, nullable |
courtNumber, court | Número de cancha y, cuando se popula, el objeto de la cancha |
matchDay | Número de jornada, para ligas |
homeTeam, awayTeam, winner | Referencias { idx, name }, cada una nullable |
homeTeamDetails, awayTeamDetails | La etiqueta del lugar mientras no se conoce su equipo — ver abajo |
score | { home, away, sets[] } cuando se popula — ver abajo |
isRetirement | Si el partido terminó por retiro |
Etiquetas de los lugares del cuadro
En un cuadro, un partido suele existir antes de que se conozcan sus
participantes. Entonces homeTeam es null y homeTeamDetails lleva la
etiqueta del lugar, que dice de dónde va a salir el equipo:
{
"label": "Winner of Game 3",
"labelEs": "Ganador del juego 3",
"teamNumber": null,
"poolNumber": null,
"poolRank": null,
"sourceGameNumber": 3,
"sourceResult": "WINNER"
}label es la clave almacenada, casi siempre en inglés; labelEs es la misma
etiqueta tal como la escribe el panel de SETTO en español («Ganador del juego
3», «1ro Grupo A»). Para otro idioma, ármala con sourceGameNumber /
sourceResult (o poolNumber / poolRank) en lugar de imprimir label.
Marcador y sets
{
"idx": "1b9d7c36-4e58-40af-92c1-6d3f8a05b7e2",
"home": 2,
"away": 1,
"sets": [
{ "idx": "…", "number": 1, "home": 6, "away": 4, "isTieBreak": false },
{ "idx": "…", "number": 2, "home": 3, "away": 6, "isTieBreak": false },
{ "idx": "…", "number": 3, "home": 7, "away": 5, "isTieBreak": false }
]
}score.home / score.away son sets ganados, no puntos. Los puntos por set
viven en sets[], ordenados por number.
Marcadores de fútbol
Un resultado de fútbol reutiliza esa misma forma con otra lectura. score.home
y score.away son goles del tiempo reglamentario — el marcador al final
del reglamentario, no el del partido si hubo tiempo extra — y sets trae
exactamente un elemento que los repite, con isTieBreak: false. Ese set
sintético es a propósito: una integración que suma sets[] sigue funcionando,
nada más que suma goles en lugar de puntos por set.
{
"idx": "7d2c9a41-0b63-4f18-9e57-3a4c8d015f6b",
"home": 2,
"away": 2,
"sets": [
{ "idx": "…", "number": 1, "home": 2, "away": 2, "isTieBreak": false }
],
"extraTime": { "home": 1, "away": 0 },
"penalties": null,
"decisionMethod": "EXTRA_TIME"
}Tres campos que solo llena el fútbol; en un marcador de raqueta los tres vienen
en null:
| Campo | Notas |
|---|---|
extraTime | { home, away } — goles anotados durante el tiempo extra, o null |
penalties | { home, away } — penales convertidos en la tanda, o null |
decisionMethod | REGULATION, EXTRA_TIME, SHOOTOUT o WALKOVER |
El tiempo extra no se acumula, los penales no son goles
extraTime cuenta solo lo que se anotó en el tiempo extra, así que súmalo a
score.home / score.away para el total: el ejemplo de arriba es 2-2 al
final del tiempo reglamentario y una victoria 3-2 tras el tiempo extra. Y la
tanda de penales no es anotación — penalties nunca llega a la tabla de
clasificación, así que ganar 4-3 en penales deja el pointsFor de los dos
equipos igual.
Un partido de fútbol de fase de grupos puede terminar empatado: el resultado
queda como empate y winner es null. Uno de eliminatoria no — el empate se va
a tiempo extra (si el organizador lo activó) y luego a penales, y SETTO deriva
el winner del desempate que lo resolvió. Lee winner y decisionMethod en
lugar de comparar score.home con score.away, que se quedan en los goles del
tiempo reglamentario.
Equipo
| Campo | Notas |
|---|---|
name | Álvarez / Peña en pareja, el nombre del jugador en singles, el equipo en fútbol |
seed, seedNumber | Siembra; nullable, y ordenada con los nulos al final en las respuestas de lista |
division | { idx, name }, nullable |
players | [{ idx, firstName, lastName, avatar }] cuando se popula — nunca datos de contacto |
captainProfileIdx | El idx del perfil de jugador del capitán, cuando se popula |
leagueTeam | { idx, name, color, logo, position, pointsAdjustment } para ligas por equipos |
isWildCard, qualifierStatus, drawAssignment | Control de wildcards y clasificados |
picture, club, ranking, description | Campos opcionales de presentación |
En un TEAM_LEAGUE, un equipo pertenece a un league team — la escuadra
(Alemania, Brasil) que acumula puntos a lo largo de la temporada.
Fechas y zonas horarias
Cada marca de tiempo es ISO 8601 en UTC con sufijo Z
(2026-09-18T17:00:00.000Z). El campo timezone del torneo te dice en qué zona
IANA armó el organizador el calendario; conviértela antes de mostrar una hora de
inicio, o un partido de las 19:00 en Monterrey se leerá como la 01:00 del día
siguiente.
Autenticación
Cómo funcionan los tokens de API de SETTO — tokens personales y de organización, la cabecera bearer, los prefijos live y test, los seis permisos, qué alcanza cada token, expiración, rotación y revocación, y la diferencia entre 401 y 403.
Populate
Cómo el parámetro de consulta populate incrusta relaciones, la lista blanca exacta por endpoint, los valores por defecto de cada endpoint y las reglas para las rutas anidadas.