This commit completes the first phase of feature parity implementation and resolves all API integration issues to match the backend API format. ## API Integration Fixes - Remove all hardcoded default values from transformers (tick_rate, kast, player_count, steam_updated) - Update TypeScript types to make fields optional where backend doesn't guarantee them - Update Zod schemas to validate optional fields correctly - Fix mock data to match real API response format (plain arrays, not wrapped objects) - Update UI components to handle undefined values with proper fallbacks - Add comprehensive API documentation for Match and Player endpoints ## Phase 1 Features Implemented (3/6) ### 1. Player Tracking System ✅ - Created TrackPlayerModal.svelte with auth code input - Integrated track/untrack player API endpoints - Added UI for providing optional share code - Displays tracked status on player profiles - Full validation and error handling ### 2. Share Code Parsing ✅ - Created ShareCodeInput.svelte component - Added to matches page for easy match submission - Real-time validation of share code format - Parse status feedback with loading states - Auto-redirect to match page on success ### 3. VAC/Game Ban Status ✅ - Added VAC and game ban count/date fields to Player type - Display status badges on player profile pages - Show ban count and date when available - Visual indicators using DaisyUI badge components ## Component Improvements - Modal.svelte: Added Svelte 5 Snippet types, actions slot support - ThemeToggle.svelte: Removed deprecated svelte:component usage - Tooltip.svelte: Fixed type safety with Snippet type - All new components follow Svelte 5 runes pattern ($state, $derived, $bindable) ## Type Safety & Linting - Fixed all ESLint errors (any types → proper types) - Fixed form label accessibility issues - Replaced error: any with error: unknown + proper type guards - Added Snippet type imports where needed - Updated all catch blocks to use instanceof Error checks ## Static Assets - Migrated all files from public/ to static/ directory per SvelteKit best practices - Moved 200+ map icons, screenshots, and other assets - Updated all import paths to use /images/ (served from static/) ## Documentation - Created IMPLEMENTATION_STATUS.md tracking all 15 missing features - Updated API.md with optional field annotations - Created MATCHES_API.md with comprehensive endpoint documentation - Added inline comments marking optional vs required fields ## Testing - Updated mock fixtures to remove default values - Fixed mock handlers to return plain arrays like real API - Ensured all components handle undefined gracefully ## Remaining Phase 1 Tasks - [ ] Add VAC status column to match scoreboard - [ ] Create weapons statistics tab for matches - [ ] Implement recently visited players on home page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
24 KiB
CSGOWTFD Backend API Documentation
Version: 1.0
Base URL: http://localhost:8000 (default)
Backend Repository: https://somegit.dev/CSGOWTF/csgowtfd
Framework: Go + Gin + Ent ORM
Database: PostgreSQL
Table of Contents
- Overview
- API Endpoints
- Data Models
- Integration Guide
- Error Handling
- CS2 Migration Notes
- Rate Limiting
- Caching Strategy
- Testing
- Swagger Documentation
- Support & Updates
Overview
The CSGOWTFD backend is a REST API service that provides Counter-Strike match statistics, player profiles, and demo parsing capabilities. It uses:
- Web Framework: Gin (Go)
- ORM: Ent (Entity Framework for Go)
- Database: PostgreSQL with pgx driver
- Cache: Redis
- Steam Integration: Steam API + demo parsing
Configuration
Default backend configuration:
httpd:
listen: ':8000'
cors_allow_domains: ['*']
database:
driver: 'pgx'
connection: 'postgres://username:password@localhost:5432/database_name'
redis:
addr: 'localhost:6379'
CORS
The backend supports CORS with configurable allowed domains. By default, all origins are allowed in development.
API Endpoints
For detailed documentation on the matches API specifically, see MATCHES_API.md.
API Endpoints
Player Endpoints
1. Get Player Profile
Retrieves comprehensive player statistics and match history.
Endpoint: GET /player/:id
Alternative: GET /player/:id/next/:time
Parameters:
id(path, required): Steam ID (uint64)time(path, optional): Unix timestamp for pagination (get matches before this time)
Response (200 OK):
{
"id": 76561198012345678,
"name": "PlayerName",
"avatar": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/...",
"vanity_url": "custom-url",
"vanity_url_real": "custom-url",
"steam_updated": "2024-11-04T10:30:00Z", // Optional: may not always be provided
"profile_created": "2015-03-12T00:00:00Z",
"wins": 1250,
"looses": 980,
"ties": 45,
"vac_count": 0,
"vac_date": null,
"game_ban_count": 0,
"game_ban_date": null,
"oldest_sharecode_seen": "CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
"matches": [
{
"match_id": 3589487716842078322,
"map": "de_inferno",
"date": "2024-11-01T18:45:00Z",
"score_team_a": 13,
"score_team_b": 10,
"duration": 2456,
"match_result": 1,
"max_rounds": 24,
"demo_parsed": true,
"vac_present": false,
"gameban_present": false,
"tick_rate": 64.0, // Optional: not always provided by API
"stats": {
"team_id": 2,
"kills": 24,
"deaths": 18,
"assists": 6,
"headshot": 12,
"mvp": 3,
"score": 56,
"kast": 78, // Optional: not always provided by API
"rank_old": 18500,
"rank_new": 18650,
"dmg_enemy": 2450,
"dmg_team": 120,
"flash_assists": 4,
"flash_duration_enemy": 15.6,
"flash_total_enemy": 8,
"ud_he": 450,
"ud_flames": 230,
"ud_flash": 5,
"ud_smoke": 3,
"avg_ping": 25.5,
"color": "yellow"
}
}
]
}
Use Case: Display player profile page with career stats and recent matches.
2. Get Player Metadata
Retrieves lightweight player metadata (recent matches summary).
Endpoint: GET /player/:id/meta
Alternative: GET /player/:id/meta/:limit
Parameters:
id(path, required): Steam IDlimit(path, optional): Number of recent matches to include (default: 10)
Response (200 OK):
{
"id": 76561198012345678,
"name": "PlayerName",
"avatar": "...",
"recent_matches": 25,
"last_match_date": "2024-11-01T18:45:00Z",
"avg_kills": 21.3,
"avg_deaths": 17.8,
"avg_kast": 75.2,
"win_rate": 56.5
}
Use Case: Quick player card/preview without full match data.
3. Track Player
Adds a player to the tracking system for automatic match updates.
Endpoint: POST /player/:id/track
Parameters:
id(path, required): Steam ID
Request Body:
{
"auth_code": "XXXX-XXXXX-XXXX"
}
Response (200 OK):
{
"success": true,
"message": "Player added to tracking queue"
}
Use Case: Allow users to track their own profile for automatic updates.
4. Untrack Player
Removes a player from the tracking system.
Endpoint: DELETE /player/:id/track
Parameters:
id(path, required): Steam ID
Response (200 OK):
{
"success": true,
"message": "Player removed from tracking"
}
Match Endpoints
5. Parse Match from Share Code
Triggers parsing of a CS:GO/CS2 match from a share code.
Endpoint: GET /match/parse/:sharecode
Parameters:
sharecode(path, required): CS:GO match share code (e.g.,CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)
Response (200 OK):
{
"match_id": 3589487716842078322,
"status": "parsing",
"message": "Demo download and parsing initiated"
}
Response (202 Accepted):
{
"match_id": 3589487716842078322,
"status": "queued",
"estimated_time": 120
}
Use Case: User submits match share code for analysis.
6. Get Match Details
Retrieves full match details including all player statistics.
Endpoint: GET /match/:id
Parameters:
id(path, required): Match ID (uint64)
Response (200 OK):
{
"match_id": 3589487716842078322,
"share_code": "CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
"map": "de_inferno",
"date": "2024-11-01T18:45:00Z",
"score_team_a": 13,
"score_team_b": 10,
"duration": 2456,
"match_result": 1,
"max_rounds": 24,
"demo_parsed": true,
"vac_present": false,
"gameban_present": false,
"tick_rate": 64.0,
"players": [
{
"id": 76561198012345678,
"name": "Player1",
"avatar": "...",
"team_id": 2,
"kills": 24,
"deaths": 18,
"assists": 6,
"headshot": 12,
"mvp": 3,
"score": 56,
"kast": 78,
"rank_old": 18500,
"rank_new": 18650,
"dmg_enemy": 2450,
"dmg_team": 120,
"flash_assists": 4,
"flash_duration_enemy": 15.6,
"flash_duration_team": 2.3,
"flash_duration_self": 1.2,
"flash_total_enemy": 8,
"flash_total_team": 1,
"flash_total_self": 1,
"ud_he": 450,
"ud_flames": 230,
"ud_flash": 5,
"ud_smoke": 3,
"ud_decoy": 0,
"mk_2": 4,
"mk_3": 2,
"mk_4": 1,
"mk_5": 0,
"avg_ping": 25.5,
"color": "yellow",
"crosshair": "CSGO_XXXXXXXXXXXX"
}
]
}
Use Case: Display match overview page with scoreboard.
7. Get Match Weapons
Retrieves weapon statistics for all players in a match.
Endpoint: GET /match/:id/weapons
Parameters:
id(path, required): Match ID
Response (200 OK):
{
"match_id": 3589487716842078322,
"weapons": [
{
"player_id": 76561198012345678,
"weapon_stats": [
{
"eq_type": 7,
"weapon_name": "AK-47",
"kills": 12,
"damage": 1450,
"hits": 48,
"hit_groups": {
"head": 8,
"chest": 25,
"stomach": 8,
"left_arm": 3,
"right_arm": 2,
"left_leg": 1,
"right_leg": 1
}
}
]
}
]
}
Use Case: Display detailed weapon performance page for match.
8. Get Match Rounds
Retrieves round-by-round statistics for a match.
Endpoint: GET /match/:id/rounds
Parameters:
id(path, required): Match ID
Response (200 OK):
{
"match_id": 3589487716842078322,
"rounds": [
{
"round": 1,
"winner": 2,
"win_reason": "elimination",
"players": [
{
"player_id": 76561198012345678,
"bank": 800,
"equipment": 650,
"spent": 650,
"kills_in_round": 2,
"damage_in_round": 234
}
]
}
]
}
Use Case: Display economy tab with round-by-round breakdown.
9. Get Match Chat
Retrieves in-game chat messages from a match.
Endpoint: GET /match/:id/chat
Parameters:
id(path, required): Match ID
Response (200 OK):
{
"match_id": 3589487716842078322,
"messages": [
{
"player_id": 76561198012345678,
"player_name": "Player1",
"message": "nice shot!",
"tick": 15840,
"round": 8,
"all_chat": true,
"timestamp": "2024-11-01T19:12:34Z"
}
]
}
Use Case: Display chat log page for match.
Matches Listing
10. Get Matches List
Retrieves a paginated list of matches.
Endpoint: GET /matches
Alternative: GET /matches/next/:time
Parameters:
time(path, optional): Unix timestamp for pagination (use with/matches/next/:time)- Query parameters:
limit(optional): Number of matches to return (default: 50, max: 100)map(optional): Filter by map name (e.g.,de_inferno)player_id(optional): Filter by player Steam ID
Response (200 OK):
IMPORTANT: This endpoint returns a plain array, not an object with properties.
[
{
"match_id": "3589487716842078322",
"map": "de_inferno",
"date": 1730487900,
"score": [13, 10],
"duration": 2456,
"match_result": 1,
"max_rounds": 24,
"parsed": true,
"vac": false,
"game_ban": false
}
]
Field Descriptions:
match_id: Unique match identifier (uint64 as string)map: Map name (can be empty string if not parsed)date: Unix timestamp (seconds since epoch)score: Array with two elements[team_a_score, team_b_score]duration: Match duration in secondsmatch_result: 0 = tie, 1 = team_a win, 2 = team_b winmax_rounds: Maximum rounds (24 for MR12, 30 for MR15)parsed: Whether the demo has been parsedvac: Whether any player has a VAC bangame_ban: Whether any player has a game ban
Pagination:
- To get the next page, use the
datefield from the last match in the array - Request
/matches/next/{timestamp}where{timestamp}is the Unix timestamp - Continue until the response returns fewer matches than requested
Use Case: Display matches listing page with filters.
Sitemap
11. Get Sitemap Index
Returns XML sitemap index for SEO.
Endpoint: GET /sitemap.xml
Response (200 OK):
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://csgow.tf/sitemap/1</loc>
<lastmod>2024-11-04</lastmod>
</sitemap>
</sitemapindex>
12. Get Sitemap Pages
Returns XML sitemap for specific page range.
Endpoint: GET /sitemap/:id
Parameters:
id(path, required): Sitemap page number
Response (200 OK):
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://csgow.tf/match/3589487716842078322</loc>
<lastmod>2024-11-01</lastmod>
<changefreq>never</changefreq>
<priority>0.8</priority>
</url>
</urlset>
Data Models
Match
interface Match {
match_id: number; // uint64, unique identifier
share_code: string; // CS:GO share code
map: string; // Map name (e.g., "de_inferno")
date: string; // ISO 8601 timestamp
score_team_a: number; // Final score for team A (T/CT)
score_team_b: number; // Final score for team B (CT/T)
duration: number; // Match duration in seconds
match_result: number; // 0 = tie, 1 = team_a win, 2 = team_b win
max_rounds: number; // Max rounds (24 for MR12, 30 for MR15)
demo_parsed: boolean; // Demo parsing status
vac_present: boolean; // Any VAC bans in match
gameban_present: boolean; // Any game bans in match
tick_rate: number; // Server tick rate (64 or 128)
players?: MatchPlayer[]; // Array of player stats
}
Player
interface Player {
id: number; // uint64, Steam ID
name: string; // Steam display name
avatar: string; // Avatar URL
vanity_url?: string; // Custom Steam URL
vanity_url_real?: string; // Actual vanity URL
steam_updated: string; // Last Steam profile update
profile_created?: string; // Steam account creation date
wins?: number; // Total competitive wins
looses?: number; // Total competitive losses (note: typo in backend)
ties?: number; // Total ties
vac_count?: number; // Number of VAC bans
vac_date?: string; // Date of last VAC ban
game_ban_count?: number; // Number of game bans
game_ban_date?: string; // Date of last game ban
oldest_sharecode_seen?: string; // Oldest match on record
matches?: Match[]; // Recent matches
}
MatchPlayer
interface MatchPlayer {
match_stats: number; // Match ID reference
player_stats: number; // Player ID reference
team_id: number; // 2 = T, 3 = CT
// Performance
kills: number;
deaths: number;
assists: number;
headshot: number; // Headshot kills
mvp: number; // MVP stars
score: number; // In-game score
kast: number; // KAST percentage (0-100)
// Rank
rank_old?: number; // Rating before match (CS2: 0-30000)
rank_new?: number; // Rating after match
// Damage
dmg_enemy?: number; // Damage to enemies
dmg_team?: number; // Team damage
// Multi-kills
mk_2?: number; // Double kills
mk_3?: number; // Triple kills
mk_4?: number; // Quad kills
mk_5?: number; // Ace (5k)
// Utility damage
ud_he?: number; // HE grenade damage
ud_flames?: number; // Molotov/Incendiary damage
ud_flash?: number; // Flash grenades used
ud_smoke?: number; // Smoke grenades used
ud_decoy?: number; // Decoy grenades used
// Flash statistics
flash_assists?: number; // Assists from flashes
flash_duration_enemy?: number; // Total enemy blind time
flash_duration_team?: number; // Total team blind time
flash_duration_self?: number; // Self-flash time
flash_total_enemy?: number; // Enemies flashed count
flash_total_team?: number; // Teammates flashed count
flash_total_self?: number; // Self-flash count
// Other
crosshair?: string; // Crosshair settings
color?: 'green' | 'yellow' | 'purple' | 'blue' | 'orange' | 'grey';
avg_ping?: number; // Average ping
}
RoundStats
interface RoundStats {
round: number; // Round number (1-24 for MR12)
bank: number; // Money at round start
equipment: number; // Equipment value purchased
spent: number; // Total money spent
match_player_id: number; // Reference to MatchPlayer
}
Weapon
interface Weapon {
victim: number; // Player ID who was hit/killed
dmg: number; // Damage dealt
eq_type: number; // Weapon type ID
hit_group: number; // Hit location (1=head, 2=chest, etc.)
match_player_id: number; // Reference to MatchPlayer
}
Message
interface Message {
message: string; // Chat message content
all_chat: boolean; // true = all chat, false = team chat
tick: number; // Game tick when sent
match_player_id: number; // Reference to MatchPlayer
}
Integration Guide
Frontend Setup
- Install HTTP Client
npm install axios
# or use native fetch
- Create API Client (
src/lib/api/client.ts)
import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000';
const API_TIMEOUT = import.meta.env.VITE_API_TIMEOUT || 10000;
class APIClient {
private client: AxiosInstance;
constructor() {
this.client = axios.create({
baseURL: API_BASE_URL,
timeout: API_TIMEOUT,
headers: {
'Content-Type': 'application/json'
}
});
// Response interceptor for error handling
this.client.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
// Server responded with error
console.error('API Error:', error.response.status, error.response.data);
} else if (error.request) {
// Request made but no response
console.error('Network Error:', error.message);
}
return Promise.reject(error);
}
);
}
async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.get<T>(url, config);
return response.data;
}
async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.post<T>(url, data, config);
return response.data;
}
async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.delete<T>(url, config);
return response.data;
}
}
export const apiClient = new APIClient();
- Create API Endpoints (
src/lib/api/players.ts)
import { apiClient } from './client';
import type { Player, Match } from '$lib/types';
export const playersAPI = {
async getPlayer(steamId: string | number, beforeTime?: number): Promise<Player> {
const url = beforeTime ? `/player/${steamId}/next/${beforeTime}` : `/player/${steamId}`;
return apiClient.get<Player>(url);
},
async getPlayerMeta(steamId: string | number, limit: number = 10) {
return apiClient.get(`/player/${steamId}/meta/${limit}`);
},
async trackPlayer(steamId: string | number, authCode: string) {
return apiClient.post(`/player/${steamId}/track`, { auth_code: authCode });
},
async untrackPlayer(steamId: string | number) {
return apiClient.delete(`/player/${steamId}/track`);
}
};
- Create API Endpoints (
src/lib/api/matches.ts)
import { apiClient } from './client';
import type { Match } from '$lib/types';
export const matchesAPI = {
async getMatch(matchId: string | number): Promise<Match> {
return apiClient.get<Match>(`/match/${matchId}`);
},
async getMatchWeapons(matchId: string | number) {
return apiClient.get(`/match/${matchId}/weapons`);
},
async getMatchRounds(matchId: string | number) {
return apiClient.get(`/match/${matchId}/rounds`);
},
async getMatchChat(matchId: string | number) {
return apiClient.get(`/match/${matchId}/chat`);
},
async parseMatch(shareCode: string) {
return apiClient.get(`/match/parse/${shareCode}`);
},
async getMatches(params?: {
limit?: number;
before_time?: number;
map?: string;
player_id?: string;
}) {
const url = params?.before_time ? `/matches/next/${params.before_time}` : '/matches';
const limit = params?.limit || 50;
// API returns a plain array, not an object
const data = await apiClient.get<LegacyMatchListItem[]>(url, {
params: {
limit: limit + 1, // Request one extra to check if there are more
map: params?.map,
player_id: params?.player_id
}
});
// Check if there are more matches
const hasMore = data.length > limit;
const matches = hasMore ? data.slice(0, limit) : data;
// Get timestamp for next page
const lastMatch = matches[matches.length - 1];
const nextPageTime =
hasMore && lastMatch ? Math.floor(new Date(lastMatch.date).getTime() / 1000) : undefined;
// Transform to new format
return transformMatchesListResponse(matches, hasMore, nextPageTime);
}
};
- Use in SvelteKit Routes (
src/routes/player/[id]/+page.ts)
import { playersAPI } from '$lib/api/players';
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params }) => {
try {
const player = await playersAPI.getPlayer(params.id);
return { player };
} catch (err) {
throw error(404, 'Player not found');
}
};
Error Handling
HTTP Status Codes
| Status | Meaning | Common Causes |
|---|---|---|
| 200 | Success | Request completed successfully |
| 202 | Accepted | Request queued (async operations) |
| 400 | Bad Request | Invalid parameters or malformed request |
| 404 | Not Found | Player/match doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Backend issue |
| 503 | Service Unavailable | Backend down or maintenance |
Error Response Format
{
"error": "Match not found",
"code": "MATCH_NOT_FOUND",
"details": {
"match_id": 3589487716842078322
}
}
Retry Strategy
For transient errors (500, 503), implement exponential backoff:
async function retryRequest<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = baseDelay * Math.pow(2, i);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
CS2 Migration Notes
Rank System Changes
CS:GO used 18 ranks (Silver I to Global Elite):
- Values: 0-18
CS2 uses Premier Rating:
- Values: 0-30,000
- No traditional ranks in Premier mode
Backend Compatibility:
rank_oldandrank_newfields now store Premier rating (0-30000)- Frontend must detect game version and display accordingly
MR12 vs MR15
CS:GO: MR15 (max 30 rounds) CS2: MR12 (max 24 rounds)
Check max_rounds field in Match data:
24= MR12 (CS2)30= MR15 (CS:GO)
Game Mode Detection
To determine if a match is CS:GO or CS2:
- Check
datefield (CS2 released Sept 2023) - Check
max_rounds(24 = likely CS2) - Backend may add
game_versionfield in future updates
Rate Limiting
Current Limits (may vary by deployment):
- Steam API: 1 request per second (backend handles this)
- Demo Parsing: Max 6 concurrent parses
- Frontend API: No explicit limit, but use reasonable request rates
Best Practices:
- Implement client-side caching (5-15 minutes for match data)
- Use debouncing for search inputs (300ms)
- Batch requests when possible
Caching Strategy
Backend Caching (Redis)
The backend uses Redis for:
- Steam API responses (7 days)
- Match data (permanent until invalidated)
- Player profiles (7 days)
Frontend Caching Recommendations
// In-memory cache with TTL
class DataCache<T> {
private cache = new Map<string, { data: T; expires: number }>();
set(key: string, data: T, ttlMs: number) {
this.cache.set(key, {
data,
expires: Date.now() + ttlMs
});
}
get(key: string): T | null {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() > entry.expires) {
this.cache.delete(key);
return null;
}
return entry.data;
}
}
// Usage
const matchCache = new DataCache<Match>();
const cachedMatch = matchCache.get(`match:${matchId}`);
if (!cachedMatch) {
const match = await matchesAPI.getMatch(matchId);
matchCache.set(`match:${matchId}`, match, 15 * 60 * 1000); // 15 min
}
Testing
Mock Data
Use MSW (Mock Service Worker) for testing:
// src/mocks/handlers/matches.ts
import { http, HttpResponse } from 'msw';
export const matchHandlers = [
http.get('/match/:id', ({ params }) => {
return HttpResponse.json({
match_id: parseInt(params.id as string),
map: 'de_inferno',
date: '2024-11-01T18:45:00Z',
score_team_a: 13,
score_team_b: 10
// ... rest of mock data
});
})
];
Swagger Documentation
The backend provides interactive API documentation at:
URL: {API_BASE_URL}/api/swagger
This Swagger UI allows you to:
- Explore all endpoints
- Test API calls directly
- View request/response schemas
- See authentication requirements
Support & Updates
- Backend Issues: https://somegit.dev/CSGOWTF/csgowtfd/issues
- Frontend Issues: https://somegit.dev/CSGOWTF/csgowtf/issues
- API Changes: Watch the backend repository for updates
Document Version: 1.0 Last Updated: 2025-11-04 Maintained By: CSGOW.TF Development Team