pitch-connect.space / api/docs

API Reference

REST API · All responses are JSON · Auth via Bearer token

Pitch Connect API

Base URL: https://pitch-connect.space

Club and tournament data are read-enabled. Match scores, goals, highlights, and markers are write-enabled where the selected resource supports them.

🤖 AI Agents & LLMs: Building with AI coding assistants (Cursor, Antigravity, Claude, GPT, AutoGPT)? We provide dedicated machine-readable AI docs and specifications:
- LLM Index: /llms.txt (or /.well-known/llms.txt)
- Full LLM Spec & Tool Definitions: /llms-full.txt
- AI Agent Integration Guide: /api/docs/ai
- OpenAPI Specifications: /api/openapi.json (JSON) · /api/openapi.yaml (YAML)


Overview

The Pitch Connect API lets third-party apps read club squad data and fixtures, import tournament matches, write club game scores and goal scorers, and record granular match event markers for both pickup games and tournament matches.

Supported token types

Resource Token type Description Where to get it
Account Connected app token One token per connected user/app. It can access every club and tournament that user currently has permission to manage. Use it with explicit resource URLs such as /api/v1/clubs/:slug and /api/v1/tournaments/:slug. OAuth flow below
Club Static key A single shared key for the club. Good for scripts and direct integrations. Club → Integrations page
Tournament Static key A shared tournament key for direct integrations. Tournament integration flow
Club / Tournament Legacy per-resource app token Existing app connections created before the unified flow. Still supported by the generic /api/v1/club and /api/v1/tournament style endpoints. Existing integrations

OAuth-style App Connection

Use this when you're building an app and want your users to connect a Pitch Connect account without manually copying API keys.

Full flow

1. Your app  →  redirect user to:
                GET https://pitch-connect.space/api/auth/connect
                    ?app_name=MyApp
                    &redirect_uri=https://myapp.com/callback

2. User logs in to Pitch Connect (if not already signed in)
   A focused consent screen is shown — no app nav or footer.

3. User sees your app name and the clubs/tournaments they can access.
   They confirm the connection once.

4. Pitch Connect  →  redirects user back to your app with a
   fresh connected-app token unique to this user/app connection:
                GET https://myapp.com/callback
                    ?api_key=a1b2c3d4...
                    &resource_type=account
                    &user_name=Alex+Johnson
                    &club_count=2
                    &tournament_count=3

Step 1 — Redirect the user

GET https://pitch-connect.space/api/auth/connect?app_name=MyApp&redirect_uri=https://myapp.com/callback
Parameter Required Description
app_name Yes Your app's name, shown on the consent screen
redirect_uri Yes Where to send the user after connecting. Must be https:// in production (http://localhost allowed in development). Cannot point to pitch-connect.space.

Step 2 — Handle the callback

After the user confirms the connection, Pitch Connect redirects to your redirect_uri with:

Parameter Type Description
api_key string A fresh connected-app token. Store securely.
resource_type string Always account for the unified app flow
user_name string Human-readable Pitch Connect user name
club_count integer Number of clubs this token can currently access
tournament_count integer Number of tournaments this token can currently access

Callback example

https://myapp.com/callback?api_key=a1b2c3d4...&resource_type=account&user_name=Alex+Johnson&club_count=2&tournament_count=3

Step 3 — Discover accessible resources

Once you have the token, call:

curl https://pitch-connect.space/api/v1/resources \
  -H "Authorization: Bearer <your_connected_app_token>"

This returns the clubs and tournaments the token can use with explicit resource URLs.

Who can connect

Resource Role Can connect?
Club Club owner Yes
Club Club moderator Yes
Club Club member only No
Tournament Tournament organizer Yes
Tournament Host club moderator Yes
Tournament Participant only No
Any Pitch Connect admin Yes

Re-connecting

Each user/app connection has one shared connected-app token. Reconnecting returns the existing active token for that user/app pair.


Manual Key Generation

For scripts and direct integrations.

Club static key

  1. Go to your club page on Pitch Connect
  2. Click Manage → next to the API & Integrations summary in the sidebar
  3. Navigate to /clubs/{your-club-slug}/integrations
  4. Under Static API Key, click Generate Static Key
  5. Copy the key — treat it like a password

From the same page: Regenerate (rotates key, old one stops working immediately) or Revoke (removes access entirely).

Tournament static key

Tournament static keys are also supported by tournament endpoints and tournament match marker endpoints. They are passed the same way:

Authorization: Bearer <your_tournament_api_key>

Managing Integrations

The Integrations page (/clubs/{your-club-slug}/integrations) is the central hub for API access:

  • Connected Apps — lists every OAuth-connected app with its name, connection date, last-used time, and a Disconnect button to revoke that token
  • Static API Key — generate, regenerate, or revoke the shared key

Making API Requests

All endpoints are under /api/v1/ and require a token as a Bearer header.

Authorization: Bearer <your_api_key>

Recommended flow with a connected-app token:

  1. Call GET /api/v1/resources
  2. Pick a club slug or tournament slug from that response
  3. Call explicit resource endpoints such as:
    • GET /api/v1/clubs/:club_slug
    • GET /api/v1/tournaments/:tournament_slug
    • POST /api/v1/clubs/:club_slug/games/:slug/marker
    • POST /api/v1/tournaments/:tournament_slug/matches/:match_id/markers

Legacy generic endpoints such as GET /api/v1/club and GET /api/v1/tournament still work with static keys and older per-resource app tokens.


Endpoints

GET /api/v1/resources

Lists all clubs and tournaments accessible to a connected-app token.

curl https://pitch-connect.space/api/v1/resources \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"user": {
"id": 7,
"name": "Alex Johnson",
"email": "alex@example.com"
},
"clubs": [
{
"id": 12,
"name": "Westminster FC",
"slug": "westminster-fc",
"role": "owner"
}
],
"tournaments": [
{
"id": 18,
"name": "GMC Cup",
"slug": "gmc-cup",
"status": "group_stage",
"role": "organizer"
}
]
}


GET /api/v1/tournaments/:tournament_slug

Legacy alias for tournament static keys and older resource tokens: GET /api/v1/tournament

Returns tournament metadata, accepted teams, the tournament player list, and the current tournament match list.

curl https://pitch-connect.space/api/v1/tournaments/gmc-cup \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"id": 18,
"name": "GMC Cup",
"slug": "gmc-cup",
"status": "group_stage",
"format": "tournament",
"tournament_type": "inter_club",
"description": "Summer 7v7 tournament.",
"start_date": "2026-07-20",
"end_date": "2026-08-03",
"location": "Denver, CO",
"host_club": {
"id": 7,
"name": "GMC FC",
"slug": "gmc-fc"
},
"teams": [
{
"id": 101,
"name": "Home FC",
"club_id": 12,
"club_slug": "home-fc",
"group_name": "A",
"points": 3,
"matches_played": 1,
"wins": 1,
"draws": 0,
"losses": 0,
"goals_for": 2,
"goals_against": 1,
"goal_difference": 1
}
],
"players": [
{
"id": 42,
"slug": "alex-rivera",
"name": "Alex Rivera",
"avatar_url": "https://pitch-connect.space/rails/active_storage/...jpg",
"jersey_number": 10,
"team_position": "midfielder",
"preferred_position": "midfielder",
"secondary_positions": ["forward"],
"skill_level": "intermediate",
"captain": true,
"team_id": 101,
"team_name": "Home FC",
"club_id": 12,
"club_slug": "home-fc",
"profile_url": "https://pitch-connect.space/players/alex-rivera"
}
],
"upcoming_match_count": 2,
"completed_match_count": 1,
"matches": [
{
"id": 42,
"stage": "group",
"group_name": "A",
"round": 1,
"knockout_round": null,
"knockout_round_label": null,
"status": "completed",
"scheduled_at": "2026-07-20T18:00:00Z",
"played_at": "2026-07-20T18:00:00Z",
"started_at": "2026-07-20T18:01:00Z",
"ended_at": "2026-07-20T18:52:00Z",
"period": "full_time",
"field": "Field 1",
"pitch": "North",
"home_team": {
"id": 12,
"slug": "home-fc",
"name": "Home FC",
"placeholder": null
},
"away_team": {
"id": 15,
"slug": "away-fc",
"name": "Away FC",
"placeholder": null
},
"lineups": {
"home": {
"starters": [
{
"id": 45,
"slug": "alex-rivera",
"name": "Alex Rivera",
"avatar_url": "https://pitch-connect.space/rails/active_storage/blobs/...",
"jersey_number": 10,
"captain": true,
"position": "CAM",
"pitch_x": 50.0,
"pitch_y": 70.0
}
],
"substitutes": []
},
"away": {
"starters": [],
"substitutes": []
}
},
"score": {
"home": 2,
"away": 1,
"home_penalties": null,
"away_penalties": null
},
"winner_club_id": 12,
"goals": [
{
"team": "home",
"scorer": "Alex Rivera",
"minute": "9"
}
],
"cards": [],
"commentary": [],
"stream_url": null,
"notes": null
}
],
"url": "https://pitch-connect.space/tournaments/gmc-cup"
}


GET /api/v1/tournaments/:tournament_slug/players

Legacy alias for tournament static keys and older resource tokens: GET /api/v1/tournament/players

Returns tournament players with team affiliation and profile image URLs.

For intra-club tournaments, this includes registration metadata such as jersey preferences and waiver acceptance timestamps.

curl https://pitch-connect.space/api/v1/tournaments/gmc-cup/players \
  -H "Authorization: Bearer <your_connected_app_token>"

Inter-club response
json
{
"tournament": {
"id": 18,
"name": "GMC Cup",
"slug": "gmc-cup",
"status": "group_stage",
"format": "tournament",
"tournament_type": "inter_club"
},
"players": [
{
"id": 42,
"slug": "alex-rivera",
"name": "Alex Rivera",
"avatar_url": "https://pitch-connect.space/rails/active_storage/...jpg",
"jersey_number": 10,
"team_position": "midfielder",
"preferred_position": "midfielder",
"secondary_positions": ["forward"],
"skill_level": "intermediate",
"captain": true,
"team_id": 101,
"team_name": "Home FC",
"club_id": 12,
"club_slug": "home-fc",
"profile_url": "https://pitch-connect.space/players/alex-rivera"
}
]
}

Intra-club response
json
{
"tournament": {
"id": 25,
"name": "Dashain Cup",
"slug": "dashain-cup",
"status": "registration",
"format": "tournament",
"tournament_type": "intra_club"
},
"players": [
{
"id": 88,
"slug": "nilo-makai",
"name": "Nilo Makai",
"avatar_url": "https://pitch-connect.space/rails/active_storage/...jpg",
"preferred_position": "forward",
"secondary_positions": ["midfielder"],
"skill_level": "beginner",
"registration_status": "registered",
"jersey_number": 5,
"team_position": "forward",
"jersey_size": "M",
"jersey_name": "Nilo",
"jersey_number_preferences": [5, 3],
"waiver_accepted_at": "2026-07-15T06:10:00Z",
"team_id": 201,
"team_name": "Dashain Cup - Team A",
"club_id": 9,
"club_slug": "himalayan-football-club-hfc",
"profile_url": "https://pitch-connect.space/players/nilo-makai"
}
]
}


GET /api/v1/tournaments/:tournament_slug/matches

Legacy alias for tournament static keys and older resource tokens: GET /api/v1/tournament/matches

Returns the tournament match list, suitable for "import all matches" workflows in third-party apps.

curl https://pitch-connect.space/api/v1/tournaments/gmc-cup/matches \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"tournament": {
"id": 18,
"name": "GMC Cup",
"slug": "gmc-cup",
"status": "group_stage",
"format": "tournament",
"tournament_type": "inter_club"
},
"matches": [
{
"id": 42,
"stage": "group",
"group_name": "A",
"round": 1,
"knockout_round": null,
"knockout_round_label": null,
"status": "completed",
"scheduled_at": "2026-07-20T18:00:00Z",
"played_at": "2026-07-20T18:00:00Z",
"started_at": "2026-07-20T18:01:00Z",
"ended_at": "2026-07-20T18:52:00Z",
"period": "full_time",
"field": "Field 1",
"pitch": "North",
"home_team": {
"id": 12,
"slug": "home-fc",
"name": "Home FC",
"placeholder": null
},
"away_team": {
"id": 15,
"slug": "away-fc",
"name": "Away FC",
"placeholder": null
},
"lineups": {
"home": {
"starters": [
{
"id": 45,
"slug": "alex-rivera",
"name": "Alex Rivera",
"avatar_url": "https://pitch-connect.space/rails/active_storage/blobs/...",
"jersey_number": 10,
"captain": true,
"position": "CAM",
"pitch_x": 50.0,
"pitch_y": 70.0
}
],
"substitutes": []
},
"away": {
"starters": [],
"substitutes": []
}
},
"score": {
"home": 2,
"away": 1,
"home_penalties": null,
"away_penalties": null
},
"winner_club_id": 12,
"goals": [
{
"team": "home",
"scorer": "Alex Rivera",
"minute": "9"
}
],
"cards": [],
"commentary": [],
"stream_url": null,
"notes": null
}
]
}

Lineups object

Each tournament match contains a lineups object with home and away keys. Each side contains starters and substitutes arrays of player objects:

Field Type Description
id integer User ID
slug string Player slug
name string Full name of player
avatar_url string \ null
jersey_number integer \ null
captain boolean true if player is team captain
position string \ null
pitch_x float \ null
pitch_y float \ null

GET /api/v1/clubs/:club_slug

Legacy alias for club static keys and older resource tokens: GET /api/v1/club

Returns the full club profile: details, all active players, and next 20 upcoming games in one call.

curl https://pitch-connect.space/api/v1/clubs/westminster-fc \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"id": 12,
"name": "Westminster FC",
"slug": "westminster-fc",
"city": "London",
"location": "Westminster, London",
"description": "Sunday league side, all levels welcome.",
"member_count": 18,
"is_private": false,
"created_at": "2024-09-01T10:00:00Z",
"badge_url": "https://pitch-connect.space/rails/active_storage/...jpg",
"cover_photo_url": "https://pitch-connect.space/rails/active_storage/...jpg",
"players": [ ... ],
"upcoming_games": [ ... ]
}

Field Type Notes
badge_url string \ null
cover_photo_url string \ null

GET /api/v1/clubs/:club_slug/players

Legacy alias for club static keys and older resource tokens: GET /api/v1/club/players

Active squad members with jersey numbers, positions, and skill levels.

Jersey numbers and club positions are set by club staff in the app (Members page) and read here.

curl https://pitch-connect.space/api/v1/clubs/westminster-fc/players \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"club": "Westminster FC",
"players": [
{
"id": 42,
"slug": "alex-johnson",
"name": "Alex Johnson",
"avatar_url": "https://pitch-connect.space/rails/active_storage/...jpg",
"jersey_number": 10,
"club_position": "midfielder",
"effective_position": "midfielder",
"preferred_position": "forward",
"secondary_positions": ["defender"],
"skill_level": "intermediate",
"role": "Owner",
"joined_at": "2024-09-01T10:00:00Z",
"profile_url": "https://pitch-connect.space/players/alex-johnson"
}
]
}

Player fields

Field Type Notes
id integer User ID — use this to match players in game team lists
slug string URL-friendly identifier
name string Full display name
jersey_number integer Squad number (1–99). Omitted if not assigned
club_position string Position assigned by the club: goalkeeper · defender · midfielder · forward. Omitted if not set
effective_position string club_position if set, otherwise preferred_position. Use this for lineup and team formation
preferred_position string Player's own preferred position
secondary_positions string[] Other positions the player covers. May be empty
skill_level string beginner · intermediate · advanced
role string Owner · Moderator · Member
joined_at ISO 8601 UTC timestamp when they joined the club
profile_url string Link to their Pitch Connect profile

GET /api/v1/clubs/:clubslug/upcominggames

Legacy alias for club static keys and older resource tokens: GET /api/v1/club/upcoming_games

Next 20 games ordered by kick-off time with full team breakdowns and match pairings.

curl https://pitch-connect.space/api/v1/clubs/westminster-fc/upcoming_games \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"club": "Westminster FC",
"games": [
{
"id": 301,
"slug": "westminster-sunday-kickabout",
"title": "Westminster Sunday Kickabout",
"matchup": "Reds vs Blues · Greens vs Yellows",
"date_time": "2026-06-01T10:00:00Z",
"timezone": "London",
"location": "Battersea Park, London",
"field": "Pitch 3",
"pitch": "5-a-side",
"description": "Friendly match, bring water.",
"notes": "Gates open 15 mins before kick-off.",
"max_players": 10,
"players_joined": 7,
"spots_left": 3,
"skill_level": "intermediate",
"cost_per_player": 5.0,
"is_public": true,
"organizer": { "id": 1, "name": "Alex Johnson" },
"teams": [
{
"number": 1,
"name": "Reds",
"players": [
{
"id": 42,
"name": "Alex Johnson",
"is_guest": false,
"player_type": "user",
"is_captain": true,
"is_substitute": false,
"pitch_role": "goalkeeper",
"checked_in": true
}
],
"maybe_players": [
{
"id": 43,
"name": "Sam Lee",
"is_guest": false,
"player_type": "user",
"is_captain": false,
"is_substitute": false,
"pitch_role": null,
"checked_in": false
}
]
}
],
"matchups": [
{ "match_id": "301_0", "match_index": 0, "home_team": 1, "home_name": "Reds", "away_team": 2, "away_name": "Blues" },
{ "match_id": "301_1", "match_index": 1, "home_team": 3, "home_name": "Greens", "away_team": 4, "away_name": "Yellows" }
],
"unassigned_players": [],
"maybe_unassigned_players": [],
"score": "Reds 2 · Blues 1",
"url": "https://pitch-connect.space/games/westminster-sunday-kickabout"
}
]
}

Game fields

Field Type Notes
matchup string All pairings in one string: "Reds vs Blues · Greens vs Yellows". Bye shown as "Team X (bye)"
date_time ISO 8601 Kick-off time in UTC
timezone string Timezone the game was scheduled in
location string \ null
field string \ null
pitch string \ null
description string \ null
notes string \ null
max_players integer Total player capacity
players_joined integer Confirmed players
spots_left integer max_players − players_joined
skill_level string \ null
cost_per_player number \ null
organizer object { id, name }
teams array See team object below
matchups array [{ home_team, home_name, away_team, away_name }]. away_* are null for a bye
unassigned_players array Confirmed players not yet on a team
maybe_unassigned_players array Maybe RSVPs not yet on a team
score string \ null
url string Link to the game on Pitch Connect

Team object

Field Type Notes
number integer Team number
name string Custom name or "Team N"
players array Confirmed players on this team
maybe_players array Maybe RSVPs on this team

Player-in-game object

Field Type Notes
id integer \ null
name string Display name
is_guest boolean true if not a registered user
player_type string user for registered players, junior for saved junior members, or guest for unnamed plus-ones
is_captain boolean Team captain
is_substitute boolean On the bench
pitch_role string \ null
checked_in boolean Whether they checked in

GET /api/v1/clubs/:clubslug/pastgames {#get-api-v1-clubs-clubslug-pastgames}

Legacy alias for club static keys and older resource tokens: GET /api/v1/club/past_games

Recently completed games in reverse chronological order (newest first). Each game includes the full score breakdown, individual goal events, and aggregated scorer totals.
Each game object also includes the same RSVP breakdown fields as upcoming games: teams[].maybe_players and maybe_unassigned_players.

Query parameters

Parameter Type Default Description
limit integer 20 Games per page (max 50)
page integer 1 Page number
curl "https://pitch-connect.space/api/v1/clubs/westminster-fc/past_games?limit=10&page=1" \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"club": "Westminster FC",
"page": 1,
"limit": 10,
"games": [
{
"id": 300,
"slug": "westminster-sunday-kickabout-2026-06-15",
"title": "Westminster Sunday Kickabout",
"date_time": "2026-06-15T10:00:00Z",
"location": "Battersea Park, London",
"score": "Reds 3 · Blues 1",
"result": {
"scores": [
{ "team": "Reds", "score": 3 },
{ "team": "Blues", "score": 1 }
],
"scorers": [
{ "name": "Alex Johnson", "goals": 2 },
{ "name": "Sam Lee", "goals": 1 }
],
"goals": [
{ "scorer": "Alex Johnson", "assister": "Sam Lee", "team": "1", "own_goal": false },
{ "scorer": "Sam Lee", "assister": null, "team": "1", "own_goal": false },
{ "scorer": "Alex Johnson", "assister": null, "team": "1", "own_goal": false },
{ "scorer": "Chris Evans", "assister": null, "team": "2", "own_goal": false }
]
}
}
]
}

result object

Field Type Notes
scores array [{ team, score }] — one entry per team
scorers array Aggregated totals per player, sorted by goals desc
goals array Individual goal events in recorded order
goals[].own_goal boolean true if the goal was credited as an own goal

All other fields are identical to the upcoming games response, including the RSVP fields.


PATCH /api/v1/clubs/:club_slug/games/:slug/scores

Legacy alias for club static keys and older resource tokens: PATCH /api/v1/games/:slug/scores

Update the score for each team. Only works for games belonging to the authenticated club.

curl -X PATCH https://pitch-connect.space/api/v1/clubs/westminster-fc/games/thursday-kickabout/scores \
  -H "Authorization: Bearer <your_connected_app_token>" \
  -H "Content-Type: application/json" \
  -d '{ "scores": { "1": 2, "2": 1 } }'
Field Type Description
scores object Map of team number (string key) to score (integer). E.g. { "1": 2, "2": 0, "3": 1 }

Response
json
{
"message": "Scores updated.",
"game": {
"id": 301,
"slug": "thursday-kickabout",
"title": "Thursday Kickabout",
"team_scores": { "1": 2, "2": 1 },
"score": "Reds 2 · Blues 1",
"goals": [ ... ]
}
}


PATCH /api/v1/clubs/:club_slug/games/:slug/goals

Legacy alias for club static keys and older resource tokens: PATCH /api/v1/games/:slug/goals

Replace the full goal list. Sends the complete array — overwrites all existing goals.

curl -X PATCH https://pitch-connect.space/api/v1/clubs/westminster-fc/games/thursday-kickabout/goals \
  -H "Authorization: Bearer <your_connected_app_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "goals": [
      {
        "team": "1",
        "scorer": "Alex Johnson",
        "user_id": 42,
        "minute": "23",
        "penalty": false,
        "own_goal": false,
        "assister": "Sam Smith",
        "assister_user_id": 15
      }
    ]
  }'

Goal object

Field Required Notes
team Yes Team number as string: "1", "2", etc.
scorer Yes Scorer display name
user_id No Scorer's user ID from /players. Omit for guests
minute No Minute of goal, e.g. "45"
penalty No true if penalty
own_goal No true if own goal
assister No Assister display name
assister_user_id No Assister user ID from /players

Note: Scores and goals are independent. Call both endpoints to keep them in sync, or derive scores by counting goals in your app.


POST /api/v1/clubs/:club_slug/games/:slug/marker

Legacy alias for club static keys and older resource tokens: POST /api/v1/games/:slug/marker

Record a single match event marker. Idempotent — sending the same id again updates the existing marker rather than creating a duplicate.

The game must belong to the club identified by your API token.

curl -X POST https://pitch-connect.space/api/v1/clubs/westminster-fc/games/thursday-kickabout/marker \
  -H "Authorization: Bearer <your_connected_app_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "e305e94b-4b10-4bc3-be9d-472d2fb2d2e1",
    "timestamp": "2026-06-02T14:15:22Z",
    "type": "Save",
    "primary_player_pc_id": "8432",
    "primary_player_type": "junior",
    "secondary_player_pc_id": "9021",
    "secondary_player_type": "user",
    "label": "Tipped over crossbar"
  }'

Request body

Field Required Description
id Yes Client-generated UUID — prevents duplicate markers (upsert key)
match_id Yes The match_id from the matchup in /club/upcoming_games. Format: {game_id}_{match_index} e.g. 301_0. Required when the game has multiple match pairings.
timestamp Yes ISO-8601 datetime of the event
type Yes Event type — see allowed values below
primary_player_pc_id No Player id from the game roster for the primary player (scorer, shooter, outgoing sub…)
primary_player_type No The roster player's player_type: user or junior. Recommended for juniors to prevent numeric ID ambiguity.
secondary_player_pc_id No Player id from the game roster for the secondary player (assister, chance creator, incoming sub…)
secondary_player_type No The roster player's player_type: user or junior. Recommended for juniors to prevent numeric ID ambiguity.
label No Optional free-text note

For backward compatibility, omitted player types are inferred from the game's participants. Integrations should send the corresponding player_type returned by the game roster, especially for juniors, because user IDs and junior IDs use separate numeric namespaces.

Allowed type values

Category Types Effect
Match lifecycle Start Begins the match / half. Starts the match clock.
Break Pauses play (half-time, injury time). Pauses clock.
Resume Restarts play after a break. Resumes clock.
End Ends the match or half.
PenaltyShootout Marks the start of a penalty shootout period.
ResetMatch Clears all scores, goals, cards, and markers. If match_id is given resets only that pairing; otherwise resets the full game.
Keeper Establishes the goalkeeper for each team. primary_player_pc_id = home keeper, secondary_player_pc_id = away keeper. Send before match start and again if keeper changes. GK is referenced by subsequent Save markers.
Goals Goal Regular goal. Increments scorer's team score. Own goal detected via label containing "own goal" or "og".
PenaltyScored Penalty kick scored. Increments scorer's team score.
PenaltyMissed Penalty kick missed. No score change.
Shots Shot Shot off target (missed the frame). Counted in Shots off Target stats.
Miss Shot that missed the goal entirely (completely off-frame). Counted as Missed Chances.
Save Goalkeeper save. primary_player_pc_id = shooter (not GK), secondary_player_pc_id = chance creator. GK is determined from the last Keeper marker for the match. Counted in Shots on Target for the shooter's team and GK Saves for the defending team.
ChanceCreated Key pass or through ball that creates a shooting opportunity. primary_player_pc_id = chance creator. Counted in Chances Created stats.
Discipline YellowCard Yellow card issued to primary_player_pc_id.
RedCard Red card issued to primary_player_pc_id.
Foul Foul committed. Counted in Fouls stats.
Defensive Defense Defensive contribution (clearance, block, interception). primary_player_pc_id = defender. Counted in Defensive Contributions stats.
Tactical Substitution Player substitution. primary_player_pc_id = outgoing, secondary_player_pc_id = incoming. No score change.
Penalty Structural foul leading to a penalty (different from PenaltyScored). Records fouler/fouled.
Other Event Free-form event (e.g. injury stoppage, VAR review). Stored for the timeline only.

Player field mapping by type

Type primary_player_pc_id secondary_player_pc_id
Goal Scorer Assister (optional)
PenaltyScored Penalty taker
PenaltyMissed GK (if saved) / Taker (if missed) Taker (if saved) / —
Shot / Miss Shooter Chance creator (optional)
Save Saving goalkeeper
YellowCard / RedCard Booked player
Foul Foul committer Fouled player
Penalty Fouled player Foul committer
Substitution Outgoing player Incoming player
PenaltyShootout / Start / Break / Resume / End / Event / ResetMatch

How shots are counted in stats

Stat Counted from
Shots on Target Goal + PenaltyScored (scorer's team) + Save (shooter's team — primary_player is the shooter)
Shots off Target Shot — shot that missed the frame or was blocked (shooter's team)
Missed Chances Miss — completely off-target attempt (shooter's team)
Chances Created ChanceCreated (creator's team)
GK Saves Save — attributed to the defending team (opposite of shooter's team)
Defensive Contributions Defense (defending player's team)
Fouls Foul (fouling player's team)

Response
json
{ "status": "success", "marker_id": "e305e94b-4b10-4bc3-be9d-472d2fb2d2e1" }

Returns 201 Created for new markers, 200 OK for updates.


GET /api/v1/clubs/:club_slug/games/:slug/markers

Legacy alias for club static keys and older resource tokens: GET /api/v1/games/:slug/markers

Returns all markers for a game ordered by timestamp.

curl https://pitch-connect.space/api/v1/clubs/westminster-fc/games/thursday-kickabout/markers \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"game": { "id": 301, "slug": "thursday-kickabout" },
"game_mode": false,
"markers": [
{
"id": "e305e94b-...",
"timestamp": "2026-06-02T14:15:22Z",
"type": "Save",
"primary_player_pc_id": "8432",
"primary_player_type": "junior",
"secondary_player_pc_id": "9021",
"secondary_player_type": "user",
"label": "Tipped over crossbar",
"primary_player": { "id": 8432, "name": "Jamie Junior", "type": "junior" },
"secondary_player": { "id": 9021, "name": "Sam Smith", "type": "user" }
}
]
}

game_mode: true means the game is in Game Mode On — only Start, Goal, YellowCard, RedCard, Substitution, Break, Resume, PenaltyScored, PenaltyMissed, PenaltyShootout, ResetMatch will be present. The base Penalty marker (with fouler/fouled details) is not transmitted in game mode.


DELETE /api/v1/clubs/:club_slug/games/:slug/markers/:id

Deletes a game marker by its client id. If the deleted marker is a Goal or PenaltyScored marker, the associated goal is automatically removed and the team score is reverted.

curl -X DELETE https://pitch-connect.space/api/v1/clubs/westminster-fc/games/thursday-kickabout/markers/marker-goal-1 \
  -H "Authorization: Bearer <your_connected_app_token>"

Response
json
{
"status": "success",
"message": "Marker deleted and stats updated."
}


POST /api/v1/tournaments/:tournamentslug/matches/:matchid/markers

Records a tournament match marker. This endpoint supports live tournament control markers such as:

  • Start
  • Break
  • HalfTime
  • Resume
  • ExtraTime1
  • ExtraTime2
  • End
  • FullTime
  • Goal
  • PenaltyScored
  • YellowCard
  • RedCard
  • commentary-style event markers
curl -X POST https://pitch-connect.space/api/v1/tournaments/gmc-cup/matches/42/markers \
  -H "Authorization: Bearer <your_tournament_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "marker-123",
    "timestamp": "2026-07-14T18:45:00Z",
    "type": "Goal",
    "team": "home",
    "scorer": "Alex",
    "minute": "12"
  }'

Tournament tokens can use this endpoint directly. Club tokens can also use it if they are authorized for the tournament match.

Response
json
{
"status": "success",
"marker_id": "marker-123"
}


DELETE /api/v1/tournaments/:tournamentslug/matches/:matchid/markers/:id

Deletes a tournament match marker by its client id. If the deleted marker is a Goal or PenaltyScored marker, the associated goal is automatically removed and match scores are recalculated and reverted.

curl -X DELETE https://pitch-connect.space/api/v1/tournaments/gmc-cup/matches/42/markers/marker-goal-1 \
  -H "Authorization: Bearer <your_tournament_api_key>"

Response
json
{
"status": "success",
"message": "Marker deleted and match score updated.",
"match": {
"id": 42,
"status": "live",
"home_team_name": "Home FC",
"away_team_name": "Away FC"
}
}


GET /api/v1/tournaments/:tournamentslug/matches/:matchid/markers

Lists tournament match markers in chronological order.

curl https://pitch-connect.space/api/v1/tournaments/gmc-cup/matches/42/markers \
  -H "Authorization: Bearer <your_tournament_api_key>"

Response
json
{
"tournament": {
"id": 18,
"slug": "gmc-cup",
"name": "GMC Cup"
},
"match": {
"id": 42,
"status": "live",
"home_team_name": "Home FC",
"away_team_name": "Away FC"
},
"markers": [
{
"id": "marker-start-1",
"timestamp": "2026-07-20T18:01:00Z",
"type": "Start",
"primary_player_pc_id": null,
"secondary_player_pc_id": null,
"label": null,
"primary_player": null,
"secondary_player": null
},
{
"id": "marker-goal-1",
"timestamp": "2026-07-20T18:10:00Z",
"type": "Goal",
"primary_player_pc_id": "42",
"secondary_player_pc_id": null,
"label": "Home FC",
"primary_player": {
"id": 42,
"name": "Alex Rivera"
},
"secondary_player": null
}
]
}


Error Responses

HTTP Status Meaning
401 Unauthorized Missing or invalid token
403 Forbidden Valid token, but wrong resource type or not allowed for that club/tournament
404 Not Found Resource not found or doesn't belong to the connected resource
422 Unprocessable Invalid request body

401 example
json
{
"error": "Invalid API token.",
"hint": "Use a club API key, connected club app token, or tournament API key."
}


Notes

  • All timestamps are UTC ISO 8601
  • GET /api/v1/club returns players + games in one call — use the focused endpoints if you only need one
  • Only active members appear in the players list — pending and former members are excluded
  • Jersey numbers and club positions are set in the app by club staff (Members page) and read via the API
  • Use effective_position for lineup/formation — it returns club_position if set, otherwise falls back to preferred_position
  • Games are capped at 20, ordered by kick-off time ascending
  • Updating goals does not auto-update team_scores — call both endpoints or count goals in your app
  • Event markers (POST /game/:slug/marker) are idempotent — send the same client id to update without creating a duplicate
  • Deleting a marker (DELETE /markers/:id) automatically removes associated goal entries and reverts team scores & match stats for Goal or PenaltyScored markers
  • When game_mode is true, only simplified types are sent: Start, Goal, YellowCard, RedCard, Substitution, Break, Resume, PenaltyScored, PenaltyMissed, PenaltyShootout, ResetMatch — the structural Penalty marker is not transmitted in game mode
  • primary_player_pc_id / secondary_player_pc_id are the player's id from the /players endpoint
  • Write game endpoints only accept games belonging to the club identified by the token
  • Tournament endpoints require a tournament token, except tournament match marker writes which also accept authorized club tokens
  • Per-app tokens can be revoked individually without affecting other connections