AI PadelAPI documentation Back to site

Court server: data ingest

Match results, stats, movement, clips, live score, americano, tournament bracket, levels and player passes. Access: the club token in X-Club-Token.

Base URL: https://your-server/api · Guide for this section: Court server and cloud

Match result from the court #

POST/v1/ingest/match-result

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Creates a match from the court score and recalculates the participants' rating. The response holds matchId: stats, movement, clips and rallies refer to it. A player is given by user_id, phone or a pass code; an unknown phone creates a new profile, and the player sees the match after signing in with that number. If the sets are level (score not kept or not finished), the match is accepted but does not count for rating (voided=true). If more points were entered by a person (points_manual) than by cameras (points_auto), the match is treated as manual and weighs half in the rating. The booking can be given in booking_id, otherwise the cloud finds it by court, day and time. The method is not idempotent: every successful call creates a new match.

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
courtstringnoCourt name
daystringyesMatch date YYYY-MM-DD
timestringyesStart time HH:MM
setsstringyesScore by sets “team1:team2”, comma-separated
pattern ^\d+:\d+(,\d+:\d+)*$
playersarray [object]yesLine-up: 2–4 players, both teams
items from 2; items up to 4
players[].user_idstring (uuid), or nullnoPlayer id (from QR or binding)
players[].phonestring, or nullnoPlayer phone if the id is unknown
players[].teamintegeryesTeam: 1 or 2
min 1; max 2
players[].namestringnoName from the terminal — for a new profile
players[].pass_codestring, or nullnoQR pass code the court could not verify itself
length up to 120
booking_idstring (uuid), or nullnoBooking the session was opened from; omitted — the cloud finds it by court and time
points_autointeger, or nullnoPoints scored by cameras
min 0
points_manualinteger, or nullnoPoints entered by a person
min 0

Example request

curl -X POST "https://your-server/api/v1/ingest/match-result" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{
  "court": "court4",
  "day": "2026-10-02",
  "time": "18:00",
  "sets": "6:4,3:6,7:5",
  "players": [
    {"user_id": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83", "team": 1},
    {"phone": "+79000000001", "team": 1, "name": "Oleg"},
    {"user_id": "a52e7c14-90bd-4e3f-8c61-7d2f0b9e4a15", "team": 2},
    {"phone": "+79000000002", "team": 2, "name": "Maria"}
  ],
  "points_auto": 54,
  "points_manual": 3
}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
400bad_day_or_timeInvalid date or time (YYYY-MM-DD and HH:MM)
400both_teams_requiredPlayers of both teams are required
404player_not_found: …Player not found (the id or phone follows in the text)
422[…]Parameter or body validation error: detail is a list of errors

Example response

{
  "matchId": "8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20",
  "winnerTeam": 1,
  "ratingDeltas": {
    "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83": 12,
    "a52e7c14-90bd-4e3f-8c61-7d2f0b9e4a15": -12
  },
  "bookingId": "b71c2e94-0a3f-4d58-8e26-9f1d3c5a7b40",
  "voided": false
}

Match stats #

POST/v1/ingest/match-stats

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Per-player match stats: top ball speed, distance, shots by type. The player is given by user_id or phone and must be in the match line-up. Players with a non-zero ball speed get an “analysis ready” push. A repeat overwrites the values.

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
match_idstring (uuid)yesmatchId from the match-result response
avg_rally_shotsnumber, or nullnoAverage shots per rally
playersarray [object]yesPer-player stats
players[].user_idstring (uuid), or nullnoPlayer
players[].phonestring, or nullnoPhone if the id is unknown
players[].max_ball_kmhinteger, or nullnoTop ball speed, km/h
players[].run_minteger, or nullnoDistance, m
players[].shotsarray [object]noShots by type: objects with class, count, avgKmh

Example request

curl -X POST "https://your-server/api/v1/ingest/match-stats" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{
  "match_id": "8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20",
  "avg_rally_shots": 6.8,
  "players": [
    {
      "user_id": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83",
      "max_ball_kmh": 112,
      "run_m": 2350,
      "shots": [{"class": "smash", "count": 7, "avgKmh": 104}]
    }
  ]
}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
404match_not_foundMatch not found, belongs to another club, or you are not a participant
404player_not_in_match: …The player is not in the match
404player_not_found: …Player not found (the id or phone follows in the text)
422[…]Parameter or body validation error: detail is a list of errors

Example response

{"ok": true}

Player movement #

POST/v1/ingest/match-movement

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Movement of each player from 3D court tracking: distance, work rate, top speed, sprints, zone shares, coverage, presence grid. Resending updates the rows without duplicates; an empty field does not erase a known value. A player recognized by phone but missing from the line-up is added without a rating change (joined). A guest without a phone is stored by name.

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
match_idstring (uuid)yesmatchId
sourcestringnoTracking source
length up to 16; default "court3d"
playersarray [object]yesPlayers, 1–8
items from 1; items up to 8
players[].user_idstring (uuid), or nullnoPlayer
players[].phonestring, or nullnoPhone if the id is unknown
players[].namestringnoName (for a guest without a phone)
players[].teamintegeryesTeam: 1 or 2
min 1; max 2
players[].distance_mnumber, or nullnoDistance, m
min 0
players[].work_rate_m_per_minnumber, or nullnoWork rate, m/min
min 0
players[].top_speed_msnumber, or nullnoTop speed, m/s
min 0
players[].sprintsinteger, or nullnoSprint count
min 0
players[].zonesobject, or nullnoTime share by zone, %: net, mid, back
players[].coverage_percentnumber, or nullnoCourt coverage, % (0–100)
min 0; max 100
players[].gridarray [array [number]]noPresence grid: [x, y, n], up to 4000 cells
items up to 4000

Example request

curl -X POST "https://your-server/api/v1/ingest/match-movement" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{
  "match_id": "8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20",
  "source": "court3d",
  "players": [
    {
      "user_id": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83",
      "team": 1,
      "distance_m": 2410.5,
      "work_rate_m_per_min": 38.2,
      "top_speed_ms": 5.9,
      "sprints": 14,
      "zones": {"net": 42.0, "mid": 31.0, "back": 27.0},
      "coverage_percent": 63.0,
      "grid": [[2.5, 4.0, 18]]
    },
    {"name": "Guest", "team": 2, "distance_m": 1980.0}
  ]
}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
404match_not_foundMatch not found, belongs to another club, or you are not a participant
404player_not_found: …Player not found (the id or phone follows in the text)
422[…]Parameter or body validation error: detail is a list of errors

Example response

{"ok": true, "count": 4, "added": 4, "updated": 0, "joined": 0}

Clip metadata #

POST/v1/ingest/clips

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Match clip metadata. A clip is sent twice: right after the match — metadata, then, once the video reaches storage — with object_key. Rows are matched by clip_id, so a repeat updates the clip instead of creating a duplicate; empty fields do not erase known values. Angles of the same moment share one group.

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
match_idstring (uuid)yesmatchId
clipsarray [object]yesClips
clips[].clip_idstringnoClip id on the court server — a repeat updates the clip by it
clips[].kindstringnoKind: highlight, highlight_angle (angle), highlight_vertical (9:16 copy); empty — keep
clips[].titlestringnoTitle
clips[].urlstringnoPublic link if any; empty — the video is still on its way
clips[].object_keystringnoStorage object key — comes in the second message
clips[].preview_urlstringnoPreview link
clips[].duration_snumbernoDuration, s (fractions allowed)
default 0
clips[].groupstringnoShared moment key for angles
clips[].camerastringnoCamera label for the player switch

Example request

curl -X POST "https://your-server/api/v1/ingest/clips" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{
  "match_id": "8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20",
  "clips": [
    {
      "clip_id": "rally-012-cam1",
      "kind": "highlight",
      "title": "Smash at the net",
      "duration_s": 12.5,
      "group": "rally-012",
      "camera": "Camera 1"
    },
    {
      "clip_id": "rally-012-cam2",
      "kind": "highlight_angle",
      "duration_s": 12.5,
      "group": "rally-012",
      "camera": "Camera 2"
    }
  ]
}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
404match_not_foundMatch not found, belongs to another club, or you are not a participant
422[…]Parameter or body validation error: detail is a list of errors

Example response

{"ok": true, "count": 2, "added": 2, "updated": 0}

Upload clip file #

PUT/v1/ingest/clips/file

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Uploads the clip file (mp4) to the cloud when the cloud stores video itself. The body is raw mp4, not JSON. The key looks like “<club id>/<match>/<name>.mp4”: the first segment must equal the token club. After the upload send POST /v1/ingest/clips with this object_key — the cloud will hand players a short-lived link.

Query parameters

NameTypeRequiredDescription
keystringyesObject key “<club id>/<match>/<name>.mp4”

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body

Request body — the whole mp4 file (binary, not JSON).

Example request

curl -X PUT "https://your-server/api/v1/ingest/clips/file?key=c1/8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20/clip-001.mp4" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: video/mp4" \
  --data-binary @clip-001.mp4

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
400local_storage_disabledThe cloud does not store video locally — file upload is disabled
403bad_keyThe object key does not start with the token club id or contains ..
413clip_too_largeThe file exceeds the size limit
507disk_fullNo disk space in the cloud
507write_failedThe file was not written
422[…]Parameter or body validation error: detail is a list of errors

Example response

{
  "ok": true,
  "key": "c1/8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20/clip-001.mp4",
  "bytes": 18734211
}

Player highlight during the match #

POST/v1/ingest/player-clip

Access: Club token Header X-Club-Token: <club token> — the club court server token.

A close-up of a player that is already in storage is pushed to the player right away, without waiting for the end of the match. The player is given by user_id or phone (an unknown phone creates a profile). The link in the push is valid for one week.

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
user_idstring (uuid), or nullnoPlayer
phonestring, or nullnoPhone if the id is unknown
namestringnoName for a new profile
object_keystringyesObject key, starts with “<club id>/”
titlestringnoTitle in the push
courtstringnoCourt — for the caption
duration_snumbernoDuration, s
default 0

Example request

curl -X POST "https://your-server/api/v1/ingest/player-clip" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{
  "user_id": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83",
  "name": "",
  "object_key": "c1/8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20/zoom-001.mp4",
  "title": "",
  "court": "court4",
  "duration_s": 9.0
}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
403bad_keyThe object key does not start with the token club id or contains ..
400no_playerNeither user_id nor phone given
409clip_not_storedThe video for this key is not in storage yet
404player_not_found: …Player not found (the id or phone follows in the text)
422[…]Parameter or body validation error: detail is a list of errors

Example response

{"ok": true, "user_id": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83"}

Live court score #

POST/v1/ingest/live

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Current court score for scoreboards and streams. Kept for 180 seconds and expires by itself unless updated. Read with the public GET /v1/live/{club_id}.

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
courtstringyesCourt name
scorestringnoScore line for the scoreboard
teamsstringnoLine-up line
set_nointeger, or nullnoSet number
on_airbooleannoWhether the court video stream is on air
default false

Example request

curl -X POST "https://your-server/api/v1/ingest/live" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{
  "court": "court4",
  "score": "6:4 3:2 · 30-15",
  "teams": "Ivan/Oleg — Anna/Maria",
  "set_no": 2,
  "on_air": true
}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
422[…]Parameter or body validation error: detail is a list of errors

Example response

{"ok": true, "ttl_s": 180}

Current americano game on a court #

GET/v1/ingest/quick-games

Access: Club token Header X-Club-Token: <club token> — the club court server token.

The game of the current americano or mexicano round assigned to this court and still without a score. No such game — {"game": null}.

Query parameters

NameTypeRequiredDescription
courtstringyesCourt name as on the court server

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Example request

curl -X GET "https://your-server/api/v1/ingest/quick-games?court=court4" \
  -H "X-Club-Token: <club token>"

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
422[…]Parameter or body validation error: detail is a list of errors

Example response

{
  "game": {
    "gameId": 17,
    "qtId": "6e2b9f41-c7a0-4d3e-9b15-8f4c2a7d0e93",
    "round": 2,
    "points": 24,
    "format": "americano",
    "team1": [
      {"userId": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83", "name": "Ivan P."},
      {"userId": null, "name": "Guest"}
    ],
    "team2": [
      {"userId": "a52e7c14-90bd-4e3f-8c61-7d2f0b9e4a15", "name": "Anna S."},
      {"userId": null, "name": "Oleg"}
    ]
  }
}

Americano game score from the court #

POST/v1/ingest/quick-games/{game_id}/score

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Americano game score from the court server (rallies from machine-vision cameras). Only a game of the token's club. Returns the full current state of the americano.

Path parameters

NameTypeRequiredDescription
game_idintegeryesgameId from GET /v1/ingest/quick-games

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
score1integeryesTeam 1 points, 0–64
min 0; max 64
score2integeryesTeam 2 points, 0–64
min 0; max 64

Example request

curl -X POST "https://your-server/api/v1/ingest/quick-games/17/score" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{"score1": 14, "score2": 10}'

Responses

CodedetailMeaning
200—Success
401bad_club_tokenInvalid club token
404game_not_foundGame not found or belongs to another club
409finishedThe americano is already finished
422[…]Parameter or body validation error: detail is a list of errors

Bracket match on a court #

GET/v1/ingest/bracket

Access: Club token Header X-Club-Token: <club token> — the club court server token.

The next unplayed bracket match of a live club tournament assigned to this court. None — {"match": null}.

Query parameters

NameTypeRequiredDescription
courtstringyesCourt name as on the court server

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Example request

curl -X GET "https://your-server/api/v1/ingest/bracket?court=court4" \
  -H "X-Club-Token: <club token>"

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
422[…]Parameter or body validation error: detail is a list of errors

Example response

{
  "match": {
    "matchId": "8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20",
    "tournamentId": "6e2b9f41-c7a0-4d3e-9b15-8f4c2a7d0e93",
    "title": "Club cup",
    "round": 1,
    "startAt": "2026-10-04T12:00:00",
    "team1": [
      {"userId": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83", "name": "Ivan P."},
      {"userId": null, "name": "Guest"}
    ],
    "team2": [
      {"userId": "a52e7c14-90bd-4e3f-8c61-7d2f0b9e4a15", "name": "Anna S."},
      {"userId": null, "name": "Guest"}
    ]
  }
}

Bracket match result from the court #

POST/v1/ingest/bracket/{match_id}/result

Access: Club token Header X-Club-Token: <club token> — the club court server token.

Bracket match result from automatic scoring. A repeat after a connection drop is safe: if the winner is already recorded, the response is {"ok": true, "already": true} and nothing changes.

Path parameters

NameTypeRequiredDescription
match_idstring (uuid)yesMatch id

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
scorestringyesScore by sets “6:4,3:6,7:5”
pattern ^\d+:\d+(,\d+:\d+)*$
winnerintegeryesWinning team: 1 or 2
min 1; max 2

Example request

curl -X POST "https://your-server/api/v1/ingest/bracket/8c0f4a52-2d1e-4b7a-9f63-1a5e7d9b3c20/result" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{"score": "6:4,3:6,7:5", "winner": 1}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
404match_not_foundMatch not found, belongs to another club, or you are not a participant
422[…]Parameter or body validation error: detail is a list of errors

Example response

{"ok": true}

Player levels for the terminal #

GET/v1/ingest/levels

Access: Club token Header X-Club-Token: <club token> — the club court server token.

AISOLUS rating of line-up players, shown next to the name on the court terminal. Up to 16 ids and up to 16 phones, comma-separated. The response is a map where the same level is stored under both the id and the phone; players without a rating are omitted. A number that lost its “+” in the URL is restored to “+7…”.

Query parameters

NameTypeRequiredDescription
idsstringnoPlayer ids, comma-separated, up to 16
phonesstringnoPhones, comma-separated, up to 16

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Example request

curl -X GET "https://your-server/api/v1/ingest/levels?ids=3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83&phones=%2B79000000001" \
  -H "X-Club-Token: <club token>"

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
422[…]Parameter or body validation error: detail is a list of errors

Example response

{
  "levels": {
    "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83": {"value": 3.25, "step": "<step name>", "games": 14},
    "+79000000001": {"value": 3.25, "step": "<step name>", "games": 14}
  }
}

Check a player pass in the cloud #

POST/v1/ingest/player-pass

Access: Club token Header X-Club-Token: <club token> — the club court server token.

The court terminal could not verify a player QR pass itself (no day key in the license) — the cloud checks it. Only the id and a short name are returned.

Headers

NameTypeRequiredDescription
x-club-tokenstringyesCourt server club token

Request body application/json

FieldTypeRequiredDescription
codestringyesCode from the player's QR pass
length up to 120

Example request

curl -X POST "https://your-server/api/v1/ingest/player-pass" \
  -H "X-Club-Token: <club token>" \
  -H "Content-Type: application/json" \
  -d '{"code": "<pass code>"}'

Responses

CodedetailMeaning
200—Success; example below
401bad_club_tokenInvalid club token
404pass_not_foundPass not recognized
410pass_expiredThe pass has expired
422[…]Parameter or body validation error: detail is a list of errors

Example response

{"userId": "3d6e9a10-5b2c-4f8e-a1d7-6c4b2e0f9a83", "name": "Ivan P."}