Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Danielsl4/TFG_DAM_2526/llms.txt

Use this file to discover all available pages before exploring further.

The Matches API is the operational core of FutsalLeague Manager. It exposes a complete set of endpoints for browsing the calendar, recording live events such as goals and cards, managing editorial locks so that only one referee edits a match at a time, and finalizing results — which automatically updates the standings and distributes prediction points to users.

Public endpoints

List matches

GET /matches Returns all active matches for the requested season (or the currently active season if no season_id is provided). Each object includes team details, the current score, field information, phase, group name, aggregated voting statistics, and the authenticated user’s own vote when a valid token is present.
season_id
number
Filter matches by season. Omit to return matches for the currently active season.
Response — array of match objects.
[
  {
    "id": 42,
    "date": "2025-11-15T19:00:00",
    "homeTeam": {
      "id": 3,
      "name": "FC Rayo",
      "logoUrl": "https://res.cloudinary.com/example/image.webp"
    },
    "awayTeam": {
      "id": 7,
      "name": "Atlético Sur",
      "logoUrl": "https://res.cloudinary.com/example/image.webp"
    },
    "homeTeamPlaceholder": null,
    "awayTeamPlaceholder": null,
    "homeGoals": null,
    "awayGoals": null,
    "homePenaltyGoals": null,
    "awayPenaltyGoals": null,
    "field": {
      "id": 1,
      "name": "Pabellón Municipal",
      "location": "Calle Mayor 12"
    },
    "status": "pendiente",
    "phase": "fase_de_grupos",
    "groupName": "Grupo A",
    "dayOfWeek": "Sábado",
    "observations": null,
    "userVote": null,
    "votingStats": {
      "local": 5,
      "draw": 2,
      "away": 3,
      "total": 10
    }
  }
]
Responses are cached in Redis per season and per user. The cache is invalidated automatically when match data changes.

Get match detail

GET /matches/:id Returns the full match object plus an ordered array of events. The userVote field is populated when a valid Authorization header is included.
id
number
required
Match ID.
Response — single match object with an additional events field.
id
number
Match identifier.
date
string
ISO 8601 datetime string.
homeTeam
object
awayTeam
object
homeTeamPlaceholder
string | null
Label shown when the home team slot is not yet filled (knockout rounds).
awayTeamPlaceholder
string | null
Label shown when the away team slot is not yet filled (knockout rounds).
homeGoals
number | null
Goals scored by the home team. null before the match starts.
awayGoals
number | null
Goals scored by the away team.
homePenaltyGoals
number | null
Penalty shootout goals for the home side.
awayPenaltyGoals
number | null
Penalty shootout goals for the away side.
field
object
status
string
pendiente | en_curso | finalizado
phase
string
fase_de_grupos or knockout phase identifier.
groupName
string | null
Group name, e.g. "Grupo A".
observations
string | null
Referee notes.
userVote
string | null
local | empate | visitante | null
votingStats
object
events
array
Errors
StatusMeaning
404Match not found.
curl https://api.example.com/matches/42

Get last activity timestamp

GET /matches/last-activity Returns the Unix timestamp (milliseconds) of the most recent change to any match. Useful for polling-based live updates without fetching the full calendar. Response
{ "timestamp": 1731700800000 }

Vote on a match

POST /matches/:id/vote Cast or update a prediction vote for a pending match. A user can vote only once; submitting again overwrites the previous vote. Authentication is required.
id
number
required
Match ID.
vote
string
required
"local" — home win, "empate" — draw, "visitante" — away win.
Response
{
  "message": "Voto registrado correctamente",
  "votingStats": {
    "local": 6,
    "draw": 2,
    "away": 3,
    "total": 11
  }
}
Errors
StatusMeaning
400Invalid vote value.
400Match has already started or finished.
400One or both teams are not yet defined (placeholder match).
401No valid token provided.
404Match not found.
Voting is only allowed while status is "pendiente" and both teams are real (non-placeholder) entities.
curl -X POST https://api.example.com/matches/42/vote \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "vote": "local" }'

Referee / admin endpoints

These endpoints require a valid token with at least referee role. Operations that modify live match state also require an active edit lock on the match.

Claim edit lock

POST /matches/:id/lock Claim exclusive editing rights for a match. The lock expires automatically after 2 minutes of inactivity, after which any referee can claim it. Calling this endpoint again renews the lock.
id
number
required
Match ID.
Response (success)
{ "message": "Bloqueo obtenido/renovado", "success": true }
Response (conflict — match locked by another user)
{ "message": "El partido está siendo editado por referee42", "success": false, "owner": "referee42" }
StatusMeaning
409Match is currently locked by another user.

Release edit lock

POST /matches/:id/unlock Release the lock on a match. Admins can force-release any lock.
id
number
required
Match ID.
force
boolean
default:"false"
Set to true to override another user’s lock (admin only).
# Admin force-unlock
curl -X POST "https://api.example.com/matches/42/unlock?force=true" \
  -H "Authorization: Bearer <admin-token>"

Update match status

PUT /matches/:id/status Change the match lifecycle status. Requires an active lock.
id
number
required
Match ID.
status
string
required
"pendiente" | "en_curso" | "finalizado"
To officially close a match and update standings, use PUT /matches/:id/finish instead of setting status to finalizado directly.

Update match observations

PUT /matches/:id/observations Save free-text referee notes on a match. Requires an active lock.
id
number
required
Match ID.
observations
string
Free-text notes. Send null or omit to clear.

Add a match event

POST /matches/:id/events Record a goal, card, or penalty shootout event. Automatically increments the corresponding score field and updates player_stats and team_stats. Requires an active lock.
id
number
required
Match ID.
type
string
required
"gol" | "tarjeta_amarilla" | "tarjeta_roja" | "penalti_tanda_marcado" | "penalti_tanda_fallado"
playerId
number
required
ID of the player who performed the action.
teamSide
string
required
"home" or "away" — determines which score column is incremented.
Response
{ "message": "Evento registrado correctamente" }
curl -X POST https://api.example.com/matches/42/events \
  -H "Authorization: Bearer <referee-token>" \
  -H "Content-Type: application/json" \
  -d '{ "type": "gol", "playerId": 15, "teamSide": "home" }'

Remove a match event

DELETE /matches/:matchId/events/:eventId Reverses a previously recorded event, decrementing the relevant score and stat counters. Requires an active lock.
matchId
number
required
Match ID.
eventId
number
required
Event ID to remove.

Finalize match

PUT /matches/:id/finish Closes the match, sets status to finalizado, releases the lock, recalculates standings (team_stats), and distributes prediction points to users who voted correctly. This action is atomic — all changes roll back if any step fails.
id
number
required
Match ID.
observations
string
Optional final referee notes saved alongside the result.
Response
{ "message": "Partido finalizado, clasificación y porra actualizadas" }
StatusMeaning
400Match is already finalized.

Admin-only endpoints

Create match

POST /matches Create a new match entry. Either homeTeamId/awayTeamId or placeholder strings must be provided for knockout-round matches where teams are not yet known.
date
string
required
ISO 8601 datetime string.
homeTeamId
number
Home team ID. Omit for placeholder matches.
awayTeamId
number
Away team ID. Omit for placeholder matches.
fieldId
number
required
Field (venue) ID.
groupId
number
Group ID for group-stage matches.
seasonId
number
required
Season ID.
phase
string
default:"fase_de_grupos"
Phase identifier, e.g. "cuartos", "semifinal", "final".
homePlaceholder
string
Display label when home team is unknown, e.g. "Ganador Grupo A".
awayPlaceholder
string
Display label when away team is unknown.
Response
{ "message": "Partido creado correctamente", "id": 43 }

Update team assignments

PUT /matches/:id/teams Assign or update real team IDs for knockout-round matches once the qualifying teams are known.
id
number
required
Match ID.
homeTeamId
number
Home team ID.
awayTeamId
number
Away team ID.
homePlaceholder
string
Clears the home placeholder when a real team is assigned. Pass null to remove.
awayPlaceholder
string
Clears the away placeholder when a real team is assigned. Pass null to remove.

Soft-delete match

DELETE /matches/:id Moves a match to the trash (is_active = false). The match no longer appears in public endpoints but can be restored.

Restore match from trash

POST /matches/:id/restore Restores a soft-deleted match.

Permanently delete match

DELETE /matches/:id/permanent Physically removes a match and all its events from the database. The match must already be in the trash (is_active = false).
This operation is irreversible. All associated match_events are also deleted.

List trashed matches

GET /matches/admin/trash Returns soft-deleted matches with basic team and season information. Response — array of objects:
[
  {
    "id": 10,
    "date": "2025-10-01T18:00:00",
    "home_team_name": "FC Rayo",
    "away_team_name": "Atlético Sur",
    "home_team_placeholder": null,
    "away_team_placeholder": null,
    "season_name": "Temporada 2025/26"
  }
]

Season match report

GET /matches/admin/report Returns all finalized matches for a season, each with its full events list. Intended for generating official season reports.
season_id
number
Filter by season. Returns finalized matches across all seasons if omitted.
Response — array of match objects with an embedded events array.
curl https://api.example.com/matches/admin/report?season_id=2 \
  -H "Authorization: Bearer <admin-token>"

Build docs developers (and LLMs) love