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.

Referees manage the live state of a match: they acquire an exclusive lock before making any changes, record match events as play progresses, and finalize the result when the final whistle blows. The system updates team standings, player statistics, and porra (prediction) points automatically on finalization.

How match locking works

To prevent two referees from editing the same match simultaneously, FutsalManager uses a database-level lock. A lock is valid for two minutes and must be renewed by the client. If a lock expires, another referee can acquire it.
All endpoints that modify match state — status changes, event recording, observations, and finalization — require the caller to hold the current lock. Requests without a valid lock return a 403.

Typical referee workflow

1

Acquire the match lock

Before touching anything, call the lock endpoint. A successful lock means no one else can edit the match until you release it or it expires.
POST /matches/:id/lock
Successful response:
{ "message": "Bloqueo obtenido/renovado", "success": true }
If someone else holds the lock you receive a 409 with the other referee’s username:
{
  "message": "El partido está siendo editado por árbitro2",
  "success": false,
  "owner": "árbitro2"
}
Renew the lock every 90 seconds by calling this endpoint again. The same user can re-acquire their own lock without a conflict.
2

Set status to en_curso

Move the match from pendiente to en_curso to signal that play has started.
PUT /matches/:id/status
Content-Type: application/json

{ "status": "en_curso" }
Valid status values are pendiente, en_curso, and finalizado. Transitions are not enforced by the API — you can move between any valid status, but the expected flow is pendienteen_cursofinalizado.
3

Record match events

Add a goal, card, or penalty shootout event as it happens.
POST /matches/:id/events
Content-Type: application/json

{
  "type": "gol",
  "playerId": 23,
  "teamSide": "home"
}
Event types:
typeEffect on match record
golIncrements home_goals or away_goals depending on teamSide.
tarjeta_amarillaRecords a yellow card; increments player and team yellow card stats.
tarjeta_rojaRecords a red card; increments player and team red card stats.
penalti_tanda_marcadoIncrements home_penalty_goals or away_penalty_goals. No effect on regular stats.
penalti_tanda_falladoRecords the miss; no score counters are changed.
playerId and teamSide ("home" or "away") are required for every event type.All three fields — type, playerId, teamSide — are required. The operation runs in a transaction: the event row and the stat updates are committed together or rolled back entirely.
4

Correct a mistake — delete an event

If an event was recorded in error, delete it. All stat changes from that event are reversed atomically.
DELETE /matches/:matchId/events/:eventId
The reversal logic mirrors the addition logic:
  • A deleted gol decrements the appropriate goal counter (floor of 0).
  • A deleted penalti_tanda_marcado decrements the penalty goal counter.
  • A deleted tarjeta_amarilla or tarjeta_roja decrements the card counters on both the player and the team.
You must hold the match lock to delete events. The deletion runs inside a transaction and rolls back on any error.
5

Add observations (optional)

Record any notes about the match — incidents, suspensions, or clarifications — before finalizing.
PUT /matches/:id/observations
Content-Type: application/json

{ "observations": "Partido suspendido en el minuto 38 por lluvia." }
Send null to clear existing observations.
6

Finalize the match

Finalizing triggers a cascade of automatic updates in a single database transaction:
  • Match status is set to finalizado.
  • The lock (locked_by, locked_at) is cleared.
  • Observations are saved.
  • matches_played is incremented for every player who appears in an event.
  • Team standings (played, won, drawn, lost, goals_for, goals_against, points) are updated for both teams.
  • Porra (prediction) votes are evaluated: users who predicted the correct result each receive one point.
PUT /matches/:id/finish
Content-Type: application/json

{ "observations": "Good game." }
Successful response:
{
  "message": "Partido finalizado, clasificación y porra actualizadas"
}
Calling this endpoint on an already-finalized match returns an error. To correct results after finalization you must contact a database administrator directly, as there is no undo endpoint.
7

Release the lock

Once finalization is complete the lock is cleared automatically. If you need to release it manually (for example, you locked a match but will not referee it), call the unlock endpoint.
POST /matches/:id/unlock
A referee can only release their own lock. An admin can release any lock by appending ?force=true.
POST /matches/:id/unlock?force=true

Standings calculation reference

When PUT /matches/:id/finish is called, points are awarded as follows:
ResultHome teamAway team
Home win (home_goals > away_goals)3 points, 1 win1 loss
Away win (home_goals < away_goals)1 loss3 points, 1 win
Draw (home_goals == away_goals)1 point, 1 draw1 point, 1 draw
Standings are only updated when the match has both a home_team_id and an away_team_id, and belongs to a group (group_id is not null). Penalty shootout goals do not affect regular standings — they are stored separately in home_penalty_goals / away_penalty_goals for display purposes only.

Build docs developers (and LLMs) love