ESFA Backend API Documentation Help

Get Match

Retrieve detailed information about a specific game match and its players.

Endpoint Details

Method: GET
Path: /api/v1/games/{gameCode}/matches/{matchId}
Authentication: Required (Bearer token)

Parameters

Path Parameters

Parameter

Type

Required

Description

gameCode

string

Yes

Code identifying the game type (e.g., "TTT" for Tic-Tac-Toe)

matchId

string

Yes

Unique identifier for the match (UUID format)

Headers

Header

Type

Required

Description

Authorization

string

Yes

Bearer token obtained from Authentication Service

Request Examples

cURL

curl -H "Authorization: Bearer eyJhbGciOi..." \ https://api.your-service.tld/api/v1/games/TTT/matches/a3c6e5f2-1a2b-4c5d-8e9f-0123456789ab

JavaScript (fetch)

const response = await fetch('/api/v1/games/TTT/matches/a3c6e5f2-1a2b-4c5d-8e9f-0123456789ab', { headers: { 'Authorization': 'Bearer ' + token } }); const matchData = await response.json();

Python

import requests headers = {'Authorization': f'Bearer {token}'} response = requests.get( 'https://api.your-service.tld/api/v1/games/TTT/matches/a3c6e5f2-1a2b-4c5d-8e9f-0123456789ab', headers=headers ) match_data = response.json()

Response

Success Response (200 OK)

{ "matchId": "a3c6e5f2-1a2b-4c5d-8e9f-0123456789ab", "gameCode": "TTT", "players": [ { "id": "p1", "name": "Alice", "status": "Active", "imgAvatarUrl": "https://api.dicebear.com/9.x/adventurer/svg?seed=Alice" }, { "id": "p2", "name": "Bob", "status": "Pending", "imgAvatarUrl": "https://api.dicebear.com/9.x/adventurer/svg?seed=Bob" } ] }

Response Fields

Field

Type

Description

matchId

string

Unique identifier for the match

gameCode

string

Code identifying the game type

players

array

Array of player objects in the match

players[].id

string

Unique identifier for the player

players[].name

string

Display name of the player

players[].status

string

Current status of the player in the match

players[].imgAvatarUrl

string

URL to player's avatar image (optional)

Player Status Values

Status

Description

Active

Player is actively participating in the match

Pending

Player has joined but is not yet active

Inactive

Player is temporarily inactive

Disconnected

Player has lost connection

Error Responses

401 Unauthorized

Missing or invalid authentication token.

{ "errors": [ { "i18N": "UNAUTHORIZED", "error": "Invalid or missing authentication token", "type": "Unauthorized" } ] }

Solutions:

  • Verify the Authorization header is present and formatted correctly

  • Ensure the Bearer token is valid and not expired

  • Obtain a fresh token from the Authentication Service

404 Not Found

The specified match does not exist.

{ "errors": [ { "i18N": "MATCH_NOT_FOUND", "error": "Match not found", "type": "NotFound" } ] }

Common causes:

  • Invalid matchId (doesn't exist in the system)

  • Invalid gameCode (game type not supported)

  • Match may have been deleted

410 Gone

The match has expired and is no longer available.

{ "errors": [ { "i18N": "MATCH_EXPIRED", "error": "Match has expired", "type": "Gone" } ] }

Common causes:

  • Match exceeded its time-to-live (TTL)

  • Match was completed and archived

  • System cleanup removed old matches

500 Internal Server Error

Temporary server issue.

{ "errors": [ { "i18N": "SERVER_ERROR", "error": "Internal server error", "type": "ServerError" } ] }

Recommended action: Retry the request after a brief delay.

Usage Patterns

Basic Match Retrieval

async function getMatch(gameCode, matchId, token) { try { const response = await fetch(`/api/v1/games/${gameCode}/matches/${matchId}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } catch (error) { console.error('Failed to get match:', error); throw error; } }

Match Polling

async function pollMatchUntilActive(gameCode, matchId, token, maxAttempts = 30) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { const match = await getMatch(gameCode, matchId, token); // Check if all players are active const allActive = match.players.every(player => player.status === 'Active'); if (allActive) { return match; } // Wait before next poll await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error) { if (error.response?.status === 404) { throw new Error('Match not found'); } // Continue polling on other errors } } throw new Error('Match polling timed out'); }

Error Handling with Retry

async function getMatchWithRetry(gameCode, matchId, token, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const response = await fetch(`/api/v1/games/${gameCode}/matches/${matchId}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { return await response.json(); } // Don't retry client errors (4xx) if (response.status >= 400 && response.status < 500) { const errorData = await response.json(); throw new Error(`Client error: ${errorData.errors[0]?.error || response.statusText}`); } // Retry server errors (5xx) and network issues if (attempt === maxRetries) { throw new Error(`Failed after ${maxRetries} attempts`); } // Exponential backoff await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); } catch (error) { if (attempt === maxRetries) { throw error; } } } }

Integration Tips

Performance Optimization

  • Cache match data when appropriate (consider match state volatility)

  • Use conditional requests with ETags if supported

  • Implement connection pooling for multiple requests

Real-time Updates

  • Consider WebSocket connections for real-time match updates

  • Implement polling with backoff for simpler real-time scenarios

  • Use Server-Sent Events (SSE) if supported

State Management

class MatchManager { constructor(apiClient) { this.apiClient = apiClient; this.matchCache = new Map(); } async getMatch(gameCode, matchId, options = {}) { const cacheKey = `${gameCode}:${matchId}`; // Check cache if enabled if (!options.skipCache && this.matchCache.has(cacheKey)) { const cached = this.matchCache.get(cacheKey); if (Date.now() - cached.timestamp < 30000) { // 30 second cache return cached.data; } } // Fetch fresh data const match = await this.apiClient.getMatch(gameCode, matchId); // Update cache this.matchCache.set(cacheKey, { data: match, timestamp: Date.now() }); return match; } }
05 September 2025