API Reference

Dutchie's Brackets provides a REST API for tournament organizers to programmatically manage their tournaments, participants, and matches.

Important Notes

Intended Audience: Tournament Organizers who want to integrate Dutchie's Brackets with their own systems

Access Scope: You can only access and modify tournaments that you created. You cannot access other users' tournaments or modify platform-wide settings.

Authentication: All API endpoints require authentication with your Dutchie's Brackets account.


Base URL

https://dutchieb.com/api

All API requests must be made to this base URL.


Authentication

API Token Authentication

The Dutchie's Brackets API uses API token authentication for external applications. You must be authenticated with a full account to use the API.

Generating an API Token:

  1. Sign in to your Dutchie's Brackets account
  2. Navigate to Account SettingsAPI Tokens
  3. Click Generate New Token
  4. Give your token a descriptive name (e.g., "Production Integration", "Tournament Manager Bot")
  5. Copy the token immediately - it will only be shown once
  6. Store it securely in your application's environment variables

Note: The API token generation UI is currently under development. For early access to API integration, please contact support at [email protected] with your use case.

Using Your API Token:

Include your API token in the Authorization header with the Bearer scheme for all API requests:

curl https://dutchieb.com/api/tournaments/create \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Tournament", ...}'

JavaScript/TypeScript:

const API_TOKEN = process.env.DUTCHIEB_API_TOKEN;

const response = await fetch('https://dutchieb.com/api/tournaments/create', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Summer Pool Championship',
    sport: 'POOL',
    // ... other fields
  })
});

Python:

import os
import requests

API_TOKEN = os.getenv('DUTCHIEB_API_TOKEN')

headers = {
    'Authorization': f'Bearer {API_TOKEN}',
    'Content-Type': 'application/json'
}

response = requests.post(
    'https://dutchieb.com/api/tournaments/create',
    json={'name': 'Summer Pool Championship', ...},
    headers=headers
)

Security Best Practices:

  • Never commit API tokens to version control
  • Store tokens in environment variables or secure vaults (AWS Secrets Manager, HashiCorp Vault, etc.)
  • Use different tokens for development, staging, and production environments
  • Rotate tokens periodically (recommended: every 90 days)
  • Revoke tokens immediately if compromised
  • Use token names to identify where each token is used
  • Monitor token usage in your account settings

Tournaments

Create Tournament

Create a new tournament in your account.

Endpoint: POST /api/tournaments/create

Authentication: Required

Request Body:

{
  "name": "Summer Pool Championship 2025",
  "description": "Annual pool tournament",
  "sport": "POOL",
  "bracketType": "DOUBLE_ELIMINATION",
  "maxPlayers": 16,
  "venueCount": 3,
  "visibility": "PRIVATE",
  "requireFullAccount": false,
  "startDate": "2025-07-15T10:00:00Z",
  "endDate": "2025-07-15T18:00:00Z"
}

Field Descriptions:

  • name (required): Tournament name, 1-100 characters
  • description (optional): Tournament description, max 500 characters
  • sport (required): Sport type (POOL, DARTS, CORNHOLE, PINGPONG, etc.)
  • bracketType (required): Tournament format
    • SINGLE_ELIMINATION: Traditional single-elimination bracket
    • DOUBLE_ELIMINATION: Loser's bracket format
    • ROUND_ROBIN: Everyone plays everyone
  • maxPlayers (required): Maximum participants, 2-128
  • venueCount (required): Number of simultaneous match locations, 1-50
  • visibility (required): Who can see the tournament
    • PUBLIC: Anyone can view
    • PRIVATE: Only accessible via invite link
  • requireFullAccount (optional): If true, participants must create an account to join (defaults to false)
  • startDate (optional): Planned start date/time (ISO 8601 format)
  • endDate (optional): Planned end date/time (ISO 8601 format)

Response: 201 Created

{
  "success": true,
  "tournament": {
    "id": "clx123abc",
    "name": "Summer Pool Championship 2025",
    "sport": "POOL",
    "status": "DRAFT",
    "visibility": "PRIVATE",
    "inviteCode": "ABC123"
  }
}

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 400 Bad Request: Validation error (invalid field values)

Get Tournament Details

Retrieve details for one of your tournaments.

Endpoint: GET /api/tournaments/:id

Authentication: Required (must be tournament creator)

Response: 200 OK

{
  "tournament": {
    "id": "clx123abc",
    "name": "Summer Pool Championship 2025",
    "description": "Annual pool tournament",
    "sport": "POOL",
    "type": "DOUBLE_ELIMINATION",
    "status": "IN_PROGRESS",
    "visibility": "PRIVATE",
    "maxPlayers": 16,
    "requireFullAccount": false,
    "inviteCode": "ABC123",
    "startDate": "2025-07-15T10:00:00Z",
    "endDate": "2025-07-15T18:00:00Z",
    "createdAt": "2025-07-01T14:30:00Z",
    "creator": {
      "name": "John Smith",
      "email": "[email protected]"
    },
    "participantCount": 12
  }
}

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 403 Forbidden: Not the tournament creator
  • 404 Not Found: Tournament doesn't exist

Participants

Get Tournament Participants

Retrieve all participants registered for your tournament.

Endpoint: GET /api/tournaments/:id/participants

Authentication: Required (must be tournament creator)

Response: 200 OK

{
  "participants": [
    {
      "id": "clp123xyz",
      "displayName": "Alice Johnson",
      "participantType": "FULL_ACCOUNT",
      "status": "ACTIVE",
      "seed": 1,
      "createdAt": "2025-07-10T09:15:00Z",
      "userName": "Alice Johnson",
      "userEmail": "[email protected]"
    },
    {
      "id": "clp456abc",
      "displayName": "Bob Smith",
      "participantType": "QUICK_SESSION",
      "status": "ACTIVE",
      "seed": 2,
      "createdAt": "2025-07-10T09:20:00Z",
      "userName": null,
      "userEmail": null
    }
  ]
}

Participant Types:

  • FULL_ACCOUNT: User with a Dutchie's Brackets account
  • QUICK_SESSION: Temporary participant (joined via quick join)

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 403 Forbidden: Not the tournament creator
  • 404 Not Found: Tournament doesn't exist

Bracket Management

Get Tournament Bracket

Retrieve the complete bracket structure with all rounds and matches for your tournament.

Endpoint: GET /api/tournaments/:id/bracket

Authentication: Required (must be tournament creator)

Response: 200 OK

{
  "rounds": [
    {
      "id": "clr123",
      "tournamentId": "clx123abc",
      "roundNumber": 1,
      "name": "Round 1",
      "status": "COMPLETED",
      "matches": [
        {
          "id": "clm456",
          "roundId": "clr123",
          "matchNumber": 1,
          "player1Id": "clp123xyz",
          "player2Id": "clp456abc",
          "player1": {
            "id": "clp123xyz",
            "displayName": "Alice Johnson"
          },
          "player2": {
            "id": "clp456abc",
            "displayName": "Bob Smith"
          },
          "player1Score": 5,
          "player2Score": 3,
          "winnerId": "clp123xyz",
          "status": "COMPLETED",
          "completedAt": "2025-07-15T10:45:00Z"
        }
      ]
    }
  ]
}

Match Statuses:

  • PENDING: Not yet started
  • IN_PROGRESS: Currently being played
  • COMPLETED: Finished with results

Round Statuses:

  • PENDING: Not yet started
  • IN_PROGRESS: Some matches completed
  • COMPLETED: All matches completed

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 403 Forbidden: Not the tournament creator
  • 404 Not Found: Tournament doesn't exist or bracket not generated

Generate Bracket

Generate the tournament bracket based on registered participants. This can only be done once per tournament.

Endpoint: POST /api/tournaments/:id/generate-bracket

Authentication: Required (must be tournament creator)

Request Body: None required

Response: 200 OK

{
  "success": true,
  "message": "Bracket generated successfully",
  "roundCount": 4,
  "matchCount": 15
}

Requirements:

  • Tournament must be in DRAFT or REGISTRATION_OPEN status
  • Must have at least 2 participants
  • Bracket cannot already be generated

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 403 Forbidden: Not the tournament creator
  • 400 Bad Request: Insufficient participants (minimum 2 required)
  • 409 Conflict: Bracket already generated
  • 404 Not Found: Tournament doesn't exist

Match Management

Update Match Score

Enter or update the score for a match in your tournament.

Endpoint: POST /api/tournaments/:tournamentId/matches/:matchId/score

Authentication: Required (must be tournament creator)

Request Body:

{
  "winnerId": "clp123xyz",
  "player1Score": 5,
  "player2Score": 3
}

Field Descriptions:

  • winnerId (required): ID of the winning participant
  • player1Score (required): Score for player 1
  • player2Score (required): Score for player 2

Response: 200 OK

{
  "success": true,
  "match": {
    "id": "clm456",
    "winnerId": "clp123xyz",
    "player1Score": 5,
    "player2Score": 3,
    "status": "COMPLETED",
    "completedAt": "2025-07-15T10:45:00Z"
  }
}

Behavior:

  • Automatically updates match status to COMPLETED
  • Advances winner to next round (if applicable)
  • Triggers real-time updates via WebSocket to all connected clients
  • Updates tournament status if this was the final match

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 403 Forbidden: Not the tournament creator
  • 400 Bad Request: Invalid scores or winner ID
  • 404 Not Found: Tournament or match doesn't exist

Tournament Status Management

Update Tournament Status

Change the status of your tournament (e.g., open registration, start tournament, complete tournament).

Endpoint: POST /api/tournaments/:id/status

Authentication: Required (must be tournament creator)

Request Body:

{
  "status": "REGISTRATION_OPEN"
}

Valid Status Transitions:

  • DRAFTREGISTRATION_OPEN: Open registration for participants
  • REGISTRATION_OPENIN_PROGRESS: Start the tournament
  • IN_PROGRESSCOMPLETED: End the tournament
  • Any status → CANCELLED: Cancel the tournament

Response: 200 OK

{
  "success": true,
  "tournament": {
    "id": "clx123abc",
    "status": "REGISTRATION_OPEN"
  }
}

Error Responses:

  • 401 Unauthorized: Not authenticated
  • 403 Forbidden: Not the tournament creator
  • 400 Bad Request: Invalid status transition
  • 404 Not Found: Tournament doesn't exist

Error Handling

Standard Error Response

All errors follow this format:

{
  "error": "Human-readable error message",
  "details": {
    "field": ["Specific validation error"]
  }
}

HTTP Status Codes

CodeMeaningWhen Used
200OKSuccessful GET/POST request
201CreatedResource successfully created
400Bad RequestValidation error or invalid data
401UnauthorizedNot authenticated (missing/invalid session)
403ForbiddenAuthenticated but not authorized (not tournament creator)
404Not FoundResource doesn't exist
409ConflictResource state conflict (e.g., bracket already generated)
500Server ErrorInternal server error

Code Examples

JavaScript/TypeScript

Create Tournament and Generate Bracket:

async function createAndSetupTournament() {
  // 1. Create tournament
  const createResponse = await fetch('/api/tournaments/create', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name: 'Summer Pool Championship',
      sport: 'POOL',
      bracketType: 'DOUBLE_ELIMINATION',
      maxPlayers: 16,
      venueCount: 3,
      visibility: 'PRIVATE',
      requireFullAccount: false
    }),
  });

  if (!createResponse.ok) {
    const error = await createResponse.json();
    throw new Error(error.error);
  }

  const { tournament } = await createResponse.json();
  console.log('Created tournament:', tournament.id);

  // 2. Wait for participants to register...
  // (They join via the invite link: https://dutchieb.com/join/${tournament.inviteCode})

  // 3. Generate bracket when ready
  const generateResponse = await fetch(`/api/tournaments/${tournament.id}/generate-bracket`, {
    method: 'POST'
  });

  if (!generateResponse.ok) {
    const error = await generateResponse.json();
    throw new Error(error.error);
  }

  const { roundCount, matchCount } = await generateResponse.json();
  console.log(`Bracket generated: ${roundCount} rounds, ${matchCount} matches`);

  return tournament;
}

Get Bracket and Update Match Scores:

async function updateMatchScore(tournamentId: string, matchId: string) {
  // 1. Get current bracket
  const bracketResponse = await fetch(`/api/tournaments/${tournamentId}/bracket`);
  const { rounds } = await bracketResponse.json();

  // 2. Update match score
  const scoreResponse = await fetch(
    `/api/tournaments/${tournamentId}/matches/${matchId}/score`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        winnerId: 'clp123xyz',
        player1Score: 5,
        player2Score: 3
      }),
    }
  );

  if (!scoreResponse.ok) {
    const error = await scoreResponse.json();
    throw new Error(error.error);
  }

  const { match } = await scoreResponse.json();
  console.log('Match completed:', match);
}

Python

Create Tournament:

import os
import requests

def create_tournament(api_token):
    """Create a new tournament using API token authentication"""
    url = "https://dutchieb.com/api/tournaments/create"
    headers = {
        "Authorization": f"Bearer {api_token}",
        "Content-Type": "application/json"
    }
    data = {
        "name": "Summer Pool Championship",
        "sport": "POOL",
        "bracketType": "DOUBLE_ELIMINATION",
        "maxPlayers": 16,
        "venueCount": 3,
        "visibility": "PRIVATE",
        "requireFullAccount": False
    }

    response = requests.post(url, json=data, headers=headers)
    response.raise_for_status()

    tournament = response.json()["tournament"]
    print(f"Created tournament: {tournament['id']}")
    print(f"Invite code: {tournament['inviteCode']}")
    print(f"Share link: https://dutchieb.com/join/{tournament['inviteCode']}")

    return tournament

# Usage - Load token from environment variable
api_token = os.getenv("DUTCHIEB_API_TOKEN")
if not api_token:
    raise ValueError("DUTCHIEB_API_TOKEN environment variable not set")

tournament = create_tournament(api_token)

Monitor Tournament and Update Scores:

import os
import requests

class TournamentManager:
    """Manage tournaments via the Dutchie's Brackets API"""

    def __init__(self, api_token=None):
        """
        Initialize the tournament manager

        Args:
            api_token: Your API token. If not provided, will read from
                      DUTCHIEB_API_TOKEN environment variable
        """
        self.base_url = "https://dutchieb.com/api"
        self.api_token = api_token or os.getenv("DUTCHIEB_API_TOKEN")

        if not self.api_token:
            raise ValueError("API token required. Set DUTCHIEB_API_TOKEN environment variable or pass token to constructor")

        self.headers = {
            "Authorization": f"Bearer {self.api_token}",
            "Content-Type": "application/json"
        }

    def get_participants(self, tournament_id):
        """Get all participants for a tournament"""
        url = f"{self.base_url}/tournaments/{tournament_id}/participants"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()["participants"]

    def generate_bracket(self, tournament_id):
        """Generate the tournament bracket"""
        url = f"{self.base_url}/tournaments/{tournament_id}/generate-bracket"
        response = requests.post(url, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def get_bracket(self, tournament_id):
        """Get current bracket state"""
        url = f"{self.base_url}/tournaments/{tournament_id}/bracket"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()["rounds"]

    def update_match_score(self, tournament_id, match_id, winner_id, p1_score, p2_score):
        """Update score for a match"""
        url = f"{self.base_url}/tournaments/{tournament_id}/matches/{match_id}/score"
        data = {
            "winnerId": winner_id,
            "player1Score": p1_score,
            "player2Score": p2_score
        }
        response = requests.post(url, json=data, headers=self.headers)
        response.raise_for_status()
        return response.json()

# Usage - API token loaded from environment variable
manager = TournamentManager()  # Reads from DUTCHIEB_API_TOKEN env var

# Or explicitly pass the token
# manager = TournamentManager(api_token="your-api-token-here")

# Get participants
participants = manager.get_participants("clx123abc")
print(f"Total participants: {len(participants)}")

# Generate bracket
result = manager.generate_bracket("clx123abc")
print(f"Bracket generated: {result['matchCount']} matches")

# Update first match
manager.update_match_score(
    tournament_id="clx123abc",
    match_id="clm456",
    winner_id="clp123xyz",
    p1_score=5,
    p2_score=3
)
print("Match score updated successfully")

Best Practices

1. Error Handling

Always check response status codes and handle errors appropriately:

const API_TOKEN = process.env.DUTCHIEB_API_TOKEN;

const response = await fetch('https://dutchieb.com/api/tournaments/create', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ /* tournament data */ })
});

if (!response.ok) {
  const error = await response.json();

  if (response.status === 401) {
    // Invalid or expired API token
    console.error('Authentication failed: Invalid or expired API token');
    console.error('Generate a new token at https://dutchieb.com/settings/api');
  } else if (response.status === 403) {
    // Not authorized for this resource
    console.error('Forbidden: You do not have access to this tournament');
  } else if (response.status === 400) {
    // Validation error
    console.error('Validation error:', error.details);
  } else {
    // Other error
    console.error('API Error:', error.error);
  }
  return;
}

2. Authentication Security

Protect your API tokens:

  • Never commit API tokens to version control (add .env to .gitignore)
  • Store tokens in environment variables or secure secret managers
  • Use different tokens for development, staging, and production
  • Rotate tokens every 90 days
  • Revoke compromised tokens immediately through your account settings
  • Always use HTTPS for API requests (never HTTP)
  • Monitor token usage in your account dashboard

3. Rate Limiting

While rate limiting is not currently enforced, plan for future limits:

  • Implement exponential backoff for retries
  • Cache responses when appropriate
  • Batch operations when possible

4. Real-Time Updates

For live tournament updates, use WebSockets instead of polling:

  • Don't repeatedly call /api/tournaments/:id/bracket
  • Use WebSocket events for live match updates
  • See WebSocket Events for details

5. Tournament Workflow

Follow this recommended sequence:

  1. Create tournament via API
  2. Share invite code with participants (they join via web UI)
  3. Monitor participant registration
  4. Generate bracket when ready
  5. Update match scores as they complete
  6. Monitor bracket progression via WebSocket events

Limitations

Current Limitations:

  • Cannot delete tournaments via API (use web UI)
  • Cannot modify participants via API (they self-register)
  • Cannot customize bracket seeding via API (automatic)
  • No bulk operations (must update matches individually)

Planned Features:

  • Webhooks for tournament events
  • Bulk match score updates
  • Advanced bracket customization
  • Tournament templates

Support

Need Help?

Security Issues: If you discover a security vulnerability, please email [email protected] instead of using public issue trackers.

Was this page helpful?

Help us improve our documentation. Found a typo or have a suggestion?

Report an Issue