← NewTon DC Tournament Manager

REST API

Optional server API for shared tournament hosting. Base URL: /api

Overview

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.


Architecture

Server Requirements

Docker Deployment (Recommended)

docker 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.

Directory Structure

/var/www/html/
├── api/
│   ├── list-tournaments.php
│   ├── upload-tournament.php
│   └── delete-tournament.php
├── tournaments/
│   └── [tournament JSON files]
└── [application files]

Data Storage


Endpoints

1. List Tournaments

GET /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.


2. Upload Tournament

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:

ParameterTypeRequiredDescription
filenamestringYesMust end with .json
dataobjectYesTournament 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:

StatusErrorCause
400Invalid JSON payloadRequest body is not valid JSON
400Missing filename or dataRequired fields absent
400Filename must end with .jsonInvalid extension
400Invalid filename - contains dangerous charactersPath traversal attempt
409Tournament file already existsFile exists, no overwrite flag
500Failed to save tournament fileFilesystem write error

3. Delete Tournament

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:

StatusErrorCause
400Missing filenameRequired field absent
400Filename must end with .jsonInvalid extension
400Invalid filename - contains dangerous charactersPath traversal attempt
404Tournament file not foundFile doesn't exist
500Failed to delete tournament fileFilesystem error

CORS

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.


Client Integration

Feature Detection

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.

Conditional UI

Import Validation

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.


Data Format

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.


Security

Built-in Protections

Recommended Practices


Troubleshooting

Upload Button Doesn't Appear

  1. Open the browser console and look for [Shared Tournaments] messages
  2. Verify /api/list-tournaments.php returns 200 OK
  3. Check NEWTON_API_ENABLED is set to true
  4. Verify PHP is executing (not returning raw PHP source code)

502 Bad Gateway

PHP-FPM is not running or misconfigured. Check the nginx fastcgi_pass settings and verify PHP-FPM is running inside the container.

Tournaments Not Appearing

Check that /tournaments exists and is readable (755 for directory, 644 for files). Verify the JSON files are valid.

API Returns 403 Forbidden

NEWTON_API_ENABLED is set to false. Restart the container after changing environment variables.

Tournaments Not Persisting After Restart

Verify the volume mount is configured: -v ./tournaments:/var/www/html/tournaments. Check host directory permissions.

View Container Logs

docker compose logs -f

Access Container Shell

docker compose exec newton-tournament sh