Optional server API for shared tournament hosting. Base URL: /api
The REST API is an optional feature for shared tournament hosting. It enables tournament sharing, browsing, and management across multiple users when deployed to a PHP-enabled web server.
NEWTON_API_ENABLED=false on demo or public-facing instances/tournaments directorydocker run -d \
--name newton-tournament \
-p 8080:2020 \
-v ./tournaments:/var/www/html/tournaments \
ghcr.io/skrodahl/newton:latest
The Docker image includes Alpine Linux with nginx + PHP 8.2-FPM, all API endpoints pre-configured, and persistent tournament storage via volume mount. See Docker Quick Start for the full setup guide.
/var/www/html/
├── api/
│ ├── list-tournaments.php
│ ├── upload-tournament.php
│ └── delete-tournament.php
├── tournaments/
│ └── [tournament JSON files]
└── [application files]
/tournaments{TournamentName}_{YYYY-MM-DD}.jsonGET /api/list-tournaments.php
Returns a list of all tournaments in the /tournaments directory.
Success response (200):
{
"tournaments": [
{
"filename": "Summer_League_2025-08-15.json",
"name": "Summer League",
"date": "2025-08-15",
"players": 16,
"status": "completed"
}
],
"count": 1
}
Returns an empty array if no tournaments exist. Skips files that can't be read or have invalid JSON.
POST /api/upload-tournament.php
Uploads a tournament JSON file to the server.
Request body:
{
"filename": "MyTournament_2025-10-02.json",
"data": { ... tournament object ... }
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filename | string | Yes | Must end with .json |
data | object | Yes | Tournament data object |
Add ?overwrite=true to the URL to replace an existing file. Without it, uploading to an existing filename returns 409 Conflict.
Success response (200):
{
"success": true,
"filename": "MyTournament_2025-10-02.json",
"path": "/tournaments/MyTournament_2025-10-02.json",
"message": "Tournament uploaded successfully"
}
Error responses:
| Status | Error | Cause |
|---|---|---|
| 400 | Invalid JSON payload | Request body is not valid JSON |
| 400 | Missing filename or data | Required fields absent |
| 400 | Filename must end with .json | Invalid extension |
| 400 | Invalid filename - contains dangerous characters | Path traversal attempt |
| 409 | Tournament file already exists | File exists, no overwrite flag |
| 500 | Failed to save tournament file | Filesystem write error |
POST /api/delete-tournament.php
Deletes a tournament file from the server.
Request body:
{
"filename": "MyTournament_2025-10-02.json"
}
Success response (200):
{
"success": true,
"filename": "MyTournament_2025-10-02.json",
"message": "Tournament deleted successfully"
}
Error responses:
| Status | Error | Cause |
|---|---|---|
| 400 | Missing filename | Required field absent |
| 400 | Filename must end with .json | Invalid extension |
| 400 | Invalid filename - contains dangerous characters | Path traversal attempt |
| 404 | Tournament file not found | File doesn't exist |
| 500 | Failed to delete tournament file | Filesystem error |
All endpoints include CORS headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
For production deployments, restrict Access-Control-Allow-Origin to your specific domain.
The application detects server availability on page load by calling /api/list-tournaments.php. If the server responds, API features (upload button, shared tournaments list, delete buttons) are shown. If not, they are silently hidden — the app works fully without them.
Any file downloaded from the server must pass the Tournament Manager's import validation before it can be loaded. Files that don't match the expected tournament structure are rejected by the client — they cannot affect your local tournament data.
Tournaments are stored as JSON with the following top-level structure:
{
"id": 1234567890,
"name": "My Tournament",
"date": "2025-10-02",
"created": "2025-10-02T10:30:00.000Z",
"status": "completed",
"bracketSize": 16,
"players": [ ... ],
"matches": [ ... ],
"placements": { "1": 1, "2": 2 },
"exportedAt": "2025-10-02T15:45:00.000Z"
}
Match IDs follow the format FS-1-1 (Frontside Round 1 Match 1) and BS-FINAL (Backside Final). See the export format for the full schema.
basename() — prevents directory traversal.., /, \, or null bytesAccess-Control-Allow-Origin to your domain in production/tournaments directory regularlyNEWTON_API_ENABLED=false if you don't need sharing[Shared Tournaments] messages/api/list-tournaments.php returns 200 OKNEWTON_API_ENABLED is set to truePHP-FPM is not running or misconfigured. Check the nginx fastcgi_pass settings and verify PHP-FPM is running inside the container.
Check that /tournaments exists and is readable (755 for directory, 644 for files). Verify the JSON files are valid.
NEWTON_API_ENABLED is set to false. Restart the container after changing environment variables.
Verify the volume mount is configured: -v ./tournaments:/var/www/html/tournaments. Check host directory permissions.
docker compose logs -f
docker compose exec newton-tournament sh