Developer Platform

Open Scoreboard API

Technical documentation for the current v1 REST API, with copyable examples, route-by-route notes, and authenticated personal API key management.

Public developer docs

Teams

Teams expose reusable grouped rosters with names, logos, jersey colors, and mapped players under partner-owned external IDs.

Available in v1

The team surface mirrors player lists: PUT /teams for idempotent full sync, GET for reads, and PATCH /teams/{externalID} for integration-managed incremental roster edits.

Collection routeGET/PUT /teams
Record routeGET/PATCH /teams/{externalID}
Roster patchupsert + removeExternalIDs

Implementation notes

  • PATCH preserves manually managed team members while updating players mapped by the active connection.
  • Use teams when the external system already groups players into club, school, or lineup entities.

Endpoints

OpenAPI
PUT/teams

Create or replace a team

Synchronize a team and its current mapped roster.

teams.write
Response

Returns the synchronized team resource.

curl
curl -X PUT https://your-domain.example/api/v1/teams \
  -H "Authorization: Bearer osb_test_your_key_here" \
  -H "Content-Type: application/json" \
  -H "OpenScoreboard-Request-ID: demo-request-001" \
  -d '{
    "externalID": "club-101",
    "name": "Northshore TTC",
    "logoURL": "https://cdn.partner.example/logos/northshore.png",
    "jerseyColor": "#1b4dff",
    "players": [
      { "externalID": "player-1001", "firstName": "Ava", "lastName": "Nguyen" },
      { "externalID": "player-1002", "firstName": "Noah", "lastName": "Patel" }
    ]
  }'
JavaScript
const baseURL = "https://your-domain.example/api/v1";
const apiKey = "osb_test_your_key_here";

async function api(path, init = {}) {
  const response = await fetch(`${baseURL}${path}`, {
    ...init,
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      "OpenScoreboard-Request-ID": "demo-request-001",
      ...(init.headers || {})
    }
  });

  const body = await response.json();
  if (!response.ok) {
    throw new Error(body.error?.message || "Request failed");
  }

  return body.data;
}

await api("/teams", {
  method: "PUT",
  body: JSON.stringify({
    externalID: "club-101",
    name: "Northshore TTC",
    logoURL: "https://cdn.partner.example/logos/northshore.png",
    jerseyColor: "#1b4dff",
    players: [
      { externalID: "player-1001", firstName: "Ava", lastName: "Nguyen" },
      { externalID: "player-1002", firstName: "Noah", lastName: "Patel" }
    ]
  })
});
PATCH/teams/{externalID}

Incrementally adjust a team roster

Upsert or remove mapped roster members and update top-level team metadata.

teams.write
Response

Returns the updated team.

curl
curl -X PATCH https://your-domain.example/api/v1/teams/club-101 \
  -H "Authorization: Bearer osb_test_your_key_here" \
  -H "Content-Type: application/json" \
  -H "OpenScoreboard-Request-ID: demo-request-001" \
  -d '{
    "name": "Northshore Table Tennis Club",
    "upsert": [
      { "externalID": "player-1003", "firstName": "Mia", "lastName": "Fernandez" }
    ],
    "removeExternalIDs": ["player-1002"]
  }'
JavaScript
const baseURL = "https://your-domain.example/api/v1";
const apiKey = "osb_test_your_key_here";

async function api(path, init = {}) {
  const response = await fetch(`${baseURL}${path}`, {
    ...init,
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      "OpenScoreboard-Request-ID": "demo-request-001",
      ...(init.headers || {})
    }
  });

  const body = await response.json();
  if (!response.ok) {
    throw new Error(body.error?.message || "Request failed");
  }

  return body.data;
}

await api("/teams/club-101", {
  method: "PATCH",
  body: JSON.stringify({
    name: "Northshore Table Tennis Club",
    upsert: [
      { externalID: "player-1003", firstName: "Mia", lastName: "Fernandez" }
    ],
    removeExternalIDs: ["player-1002"]
  })
});