Skip to content

Submit a match result

POST
/matches
curl --request POST \
--url https://api.krkplaystennis.example/v1/matches \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: 5f0c6a2e-8d47-4b8e-9a51-2b3c7e1d9f04' \
--data '{ "competition_id": "cmp_6tw2hn8rxe", "venue_id": "ven_7hk2p9wq4m", "court_number": 2, "played_at": "2026-10-10T18:00:00+02:00", "player_ids": [ "plr_2b7xq9mhk4", "plr_9fz3tr6wn1" ], "status": "completed", "score": { "sets": [ { "games": [ 6, 4 ] }, { "games": [ 3, 6 ] } ], "match_tiebreak": [ 10, 7 ] } }'

Submits the result of a singles match. Only the organizer of the match’s competition can submit it.

The API rates the match asynchronously. The response has rating_status: pending, and the status changes to rated when both players’ ratings are updated, usually within a few minutes. Walkovers aren’t rated and get rating_status: not_rated.

Send a unique Idempotency-Key with each new result. If a request fails because of a network error, retry it with the same key: the API returns the original response instead of creating a second match.

Idempotency-Key
required
string
>= 1 characters <= 255 characters

A unique value for this result, such as a UUID. The API keeps each key for 24 hours.

Example

5f0c6a2e-8d47-4b8e-9a51-2b3c7e1d9f04
Media typeapplication/json
One of: discriminator: status
Completed match

A match played to the end. Send the full score.

object
competition_id
required

A competition ID. Starts with cmp_.

string
/^cmp_[a-z0-9]{10}$/
venue_id
required

A venue ID. Starts with ven_.

string
/^ven_[a-z0-9]{10}$/
court_number

The court number at the venue. Optional.

integer
>= 1
played_at
required

When the match started, with the UTC offset.

string format: date-time
player_ids
required

The two players. Every score pair uses this order.

Array<string>
>= 2 items <= 2 items
status
required
Allowed value: completed
score
required

The score, set by set. Every pair of numbers is in the same order as player_ids.

Valid set scores depend on the competition’s match_format.games_per_set. When match_format.final_set is match_tiebreak, a match tiebreak replaces the third set. In a retired match, the last set can be unfinished.

object
sets
required
Array<object>
>= 1 items <= 3 items
object
games
required

Two non-negative integers, in the same order as player_ids.

Array<integer>
>= 2 items <= 2 items
tiebreak

Tiebreak points, only for a set that ended 7-6.

Array<integer>
>= 2 items <= 2 items
match_tiebreak

Match tiebreak points, only when the competition’s match_format.final_set is match_tiebreak.

Array<integer>
>= 2 items <= 2 items

Examples

Completed match decided in a match tiebreak

{
"competition_id": "cmp_6tw2hn8rxe",
"venue_id": "ven_7hk2p9wq4m",
"court_number": 2,
"played_at": "2026-10-10T18:00:00+02:00",
"player_ids": [
"plr_2b7xq9mhk4",
"plr_9fz3tr6wn1"
],
"status": "completed",
"score": {
"sets": [
{
"games": [
6,
4
]
},
{
"games": [
3,
6
]
}
],
"match_tiebreak": [
10,
7
]
}
}

The match is recorded. Rating happens asynchronously.

Media typeapplication/json
object
id
required

A match ID. Starts with mat_.

string
/^mat_[a-z0-9]{10}$/
competition_id
required

A competition ID. Starts with cmp_.

string
/^cmp_[a-z0-9]{10}$/
venue_id
required

A venue ID. Starts with ven_.

string
/^ven_[a-z0-9]{10}$/
court_number
required

The court number at the venue. null if the organizer didn’t send it.

integer | null
>= 1
played_at
required

When the match started.

string format: date-time
player_ids
required

The two players. Every score pair uses this order.

Array<string>
>= 2 items <= 2 items
status
required

How the match ended:

  • completed: the match was played to the end.
  • retired: a player stopped during the match. The match is rated on the games completed.
  • walkover: a player didn’t show up or withdrew before the match. The match isn’t rated.
string
Allowed values: completed retired walkover
score
required
One of:

The score, set by set. Every pair of numbers is in the same order as player_ids.

Valid set scores depend on the competition’s match_format.games_per_set. When match_format.final_set is match_tiebreak, a match tiebreak replaces the third set. In a retired match, the last set can be unfinished.

object
sets
required
Array<object>
>= 1 items <= 3 items
object
games
required

Two non-negative integers, in the same order as player_ids.

Array<integer>
>= 2 items <= 2 items
tiebreak

Tiebreak points, only for a set that ended 7-6.

Array<integer>
>= 2 items <= 2 items
match_tiebreak

Match tiebreak points, only when the competition’s match_format.final_set is match_tiebreak.

Array<integer>
>= 2 items <= 2 items
winner_id
required

A player ID. Starts with plr_.

string
/^plr_[a-z0-9]{10}$/
retired_player_id
required
One of:

A player ID. Starts with plr_.

string
/^plr_[a-z0-9]{10}$/
rating_status
required

Whether the match has affected ratings:

  • pending: the result is recorded, and the rating update hasn’t run yet.
  • rated: both players’ ratings are updated.
  • not_rated: the match doesn’t affect ratings, for example a walkover.
string
Allowed values: pending rated not_rated
created_at
required

When the result was submitted.

string format: date-time

Examples

Examplecompleted

Completed match waiting to be rated

{
"id": "mat_4nc8xq2vbt",
"competition_id": "cmp_6tw2hn8rxe",
"venue_id": "ven_7hk2p9wq4m",
"court_number": 2,
"played_at": "2026-10-10T18:00:00+02:00",
"player_ids": [
"plr_2b7xq9mhk4",
"plr_9fz3tr6wn1"
],
"status": "completed",
"score": {
"sets": [
{
"games": [
6,
4
]
},
{
"games": [
3,
6
]
}
],
"match_tiebreak": [
10,
7
]
},
"winner_id": "plr_2b7xq9mhk4",
"retired_player_id": null,
"rating_status": "pending",
"created_at": "2026-10-10T20:41:00Z"
}
Location
string format: uri

URL of the new match.

Example
https://api.krkplaystennis.example/v1/matches/mat_4nc8xq2vbt

The request is malformed, for example a query parameter has the wrong type.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

ExampleinvalidRequest

Limit out of range

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The request has 1 invalid parameter.",
"instance": "/v1/players",
"errors": [
{
"pointer": "#/limit",
"detail": "limit must be between 1 and 100."
}
]
}

The credentials are missing, invalid, or expired.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

Exampleunauthenticated

Missing API key

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#unauthenticated",
"title": "Authentication required",
"status": 401,
"detail": "Send a valid API key in the X-API-Key header.",
"instance": "/v1/players/plr_2b7xq9mhk4"
}

The access token can’t submit this result.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

Token without the matches:write scope

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This operation requires the matches:write scope.",
"instance": "/v1/matches"
}

The request conflicts with an earlier request or an existing match.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

Idempotency key reused with a different body

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#idempotency-conflict",
"title": "Idempotency key conflict",
"status": 409,
"detail": "The Idempotency-Key 5f0c6a2e-8d47-4b8e-9a51-2b3c7e1d9f04 was already used with a different request body.",
"instance": "/v1/matches"
}

The request is well formed, but the result isn’t valid.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

Same player on both sides

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#validation-failed",
"title": "Validation failed",
"status": 422,
"detail": "The request has 1 invalid field.",
"instance": "/v1/matches",
"errors": [
{
"pointer": "/player_ids",
"detail": "The two players must be different."
}
]
}

You sent too many requests. Wait for the number of seconds in Retry-After, then retry.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

ExamplerateLimited

Rate limit reached

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#rate-limited",
"title": "Rate limit reached",
"status": 429,
"detail": "This API key is limited to 60 requests per minute. Retry after 30 seconds.",
"instance": "/v1/matches"
}
Retry-After
integer
>= 1

Number of seconds to wait before you retry.

Example
30

© 2026 Saverio Cirillo · KRKplaysTennis is a fictional API · Built with Starlight