Skip to content
CoachHelpr
CoachHelpr
Download

Connect to CoachHelpr

Show your team's start lineup in your own app, on the club site or on the scoreboard. You fetch it from one fixed address, with a key for your team.

What you can read

You get the start lineup of the match being played now, or else that of the next match. If that match has no lineup yet, you get the team's standard lineup.

For each player you get the first name, the initials, the shirt number, the position, whether that player is captain and the place on the pitch. You also see who starts on the bench.

We never pass on last names, notes or anything about fitness or injuries.

Get a key

A key belongs to one team. The coach of that team makes it in the app, through More, Teams, the team and Integrations, and gives it to you.

The key stops working as soon as that coach no longer coaches the team. Every coach of the team can revoke it, and so can we.

Keep the key on your server, never in a web page or app that others can open.

Questions about connecting go through the contact form.

Go to the contact form

Quick start

Send the key in the Authorization header, with Bearer in front:

curl https://coachhelpr.app/api/v1/integration/start-lineup \
  -H "Authorization: Bearer chk_..."

The same in JavaScript, on your server, with the key in the environment variable COACHHELPR_KEY:

const url = 'https://coachhelpr.app/api/v1/integration/start-lineup';
const response = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.COACHHELPR_KEY}` },
});
if (!response.ok) throw new Error(`CoachHelpr answered ${response.status}`);
const lineup = await response.json();

You then get this answer:

{
  "team": {
    "name": "MO14-1",
    "clubName": "HC De Linden"
  },
  "source": "match",
  "match": {
    "kickoffAt": "2026-10-10T10:30:00.000Z",
    "opponent": "HC Oosterpark MO14-2",
    "isHome": true,
    "pitch": "Veld 2",
    "running": false
  },
  "lineupStatus": "confirmed",
  "formation": "3-3-3-1",
  "slots": [
    {
      "slot": 1,
      "positionCode": "K",
      "line": "keeper",
      "x": 50,
      "y": 95,
      "player": {
        "firstName": "Noa",
        "initials": "NB",
        "shirtNumber": 1,
        "isCaptain": false
      }
    },
    {
      "slot": 2,
      "positionCode": "CA",
      "line": "defence",
      "x": 50,
      "y": 80,
      "player": {
        "firstName": "Amira",
        "initials": "AY",
        "shirtNumber": 4,
        "isCaptain": true
      }
    },
    {
      "slot": 3,
      "positionCode": "LA",
      "line": "defence",
      "x": 20,
      "y": 64,
      "player": {
        "firstName": "Fleur",
        "initials": "FJ",
        "shirtNumber": 3,
        "isCaptain": false
      }
    },
    {
      "slot": 4,
      "positionCode": "VS",
      "line": "defence",
      "x": 50,
      "y": 67,
      "player": {
        "firstName": "Lotte",
        "initials": "LV",
        "shirtNumber": 5,
        "isCaptain": false
      }
    },
    {
      "slot": 5,
      "positionCode": "RA",
      "line": "defence",
      "x": 80,
      "y": 64,
      "player": {
        "firstName": "Sara",
        "initials": "SK",
        "shirtNumber": 2,
        "isCaptain": false
      }
    },
    {
      "slot": 6,
      "positionCode": "LM",
      "line": "midfield",
      "x": 20,
      "y": 45,
      "player": {
        "firstName": "Julia",
        "initials": "JM",
        "shirtNumber": 8,
        "isCaptain": false
      }
    },
    {
      "slot": 7,
      "positionCode": "MM",
      "line": "midfield",
      "x": 50,
      "y": 48,
      "player": {
        "firstName": "Evi",
        "initials": "ED",
        "shirtNumber": 6,
        "isCaptain": false
      }
    },
    {
      "slot": 8,
      "positionCode": "RM",
      "line": "midfield",
      "x": 80,
      "y": 45,
      "player": {
        "firstName": "Tess",
        "initials": "TH",
        "shirtNumber": 7,
        "isCaptain": false
      }
    },
    {
      "slot": 9,
      "positionCode": "LV",
      "line": "attack",
      "x": 20,
      "y": 25,
      "player": {
        "firstName": "Mila",
        "initials": "MS",
        "shirtNumber": 11,
        "isCaptain": false
      }
    },
    {
      "slot": 10,
      "positionCode": "SP",
      "line": "attack",
      "x": 50,
      "y": 15,
      "player": {
        "firstName": "Zoë",
        "initials": "ZP",
        "shirtNumber": 9,
        "isCaptain": false
      }
    },
    {
      "slot": 11,
      "positionCode": "RV",
      "line": "attack",
      "x": 80,
      "y": 25,
      "player": null
    }
  ],
  "bench": [
    {
      "firstName": "Iris",
      "initials": "IW",
      "shirtNumber": 14,
      "isCaptain": false
    },
    {
      "firstName": "Yara",
      "initials": "YB",
      "shirtNumber": 16,
      "isCaptain": false
    }
  ],
  "serverTime": "2026-10-07T14:02:11.000Z"
}

Fields

Every field of the response. A path with [] applies to each item in that list.

FieldTypeMeaning
teamobjectThe key's team.
team.namestringThe team's name.
team.clubNamestring | nullThe club's name, or null without a club.
source"match" | "standard" | "none"Where the lineup comes from. match: the lineup of the match in the field match. standard: the team's standard lineup. none: there is no lineup.
matchobject | nullThe match being played now, or else the next one. Also filled when that match has no lineup yet; source is then standard or none. null without a match.
match.kickoffAtstring (date-time)When the match starts, in UTC.
match.opponentstringThe opponent.
match.isHomebooleantrue for a home match.
match.pitchstring | nullThe pitch, or null when it is not known.
match.runningbooleantrue when the match is being played now.
lineupStatus"confirmed" | "proposal" | "outdated" | nullOnly for match, else null. confirmed: the coach confirmed the lineup. proposal: a proposal the coach has not confirmed yet. outdated: something changed since the proposal, such as who can play.
formationstring | nullThe formation, such as 3-3-3-1. null for none.
slotsobject[]Every place of the formation, in order, including a place where nobody stands.
slots[].slotintegerThe place's number, from 1.
slots[].positionCodestringThe position's Dutch abbreviation, such as K (keeper), LA or SP.
slots[].line"keeper" | "defence" | "midfield" | "attack"The place's line.
slots[].xintegerWhere the place is across the pitch, from 0 (left) to 100 (right).
slots[].yintegerWhere the place is along the pitch, from 0 (at the opponent's goal) to 100 (at your own goal).
slots[].playerobject | nullThe player in this place, or null when the place is empty.
slots[].player.firstNamestringThe first name.
slots[].player.initialsstringThe initials.
slots[].player.shirtNumberinteger | nullThe shirt number, or null when it is not known.
slots[].player.isCaptainbooleantrue for the captain.
benchobject[]Who starts on the bench. Empty for standard and none.
bench[].firstNamestringThe first name.
bench[].initialsstringThe initials.
bench[].shirtNumberinteger | nullThe shirt number, or null when it is not known.
bench[].isCaptainbooleantrue for the captain.
serverTimestring (date-time)When we made the answer, in UTC.

Errors and limits

401 auth_required
No key, a wrong or revoked key, or a key of a coach who no longer coaches the team.
429 rate_limited
More than 60 requests in a minute with the same key. Wait a moment, then try again.
500 internal_error
Something went wrong on our side. Try again later.

Fetching once a minute is plenty.

Calls through MCP count toward the same 60 a minute. Connecting takes a few calls, and every time the assistant asks for the lineup takes one more.

The API sends no CORS headers, so a web page on another site cannot read the answer. Call it from your server.

Use with AI (MCP)

With MCP, an AI assistant such as Claude or Cursor can read your team's lineup for you. You use the same key, with the same limit. The tool takes no arguments: the key decides the team.

Address
https://coachhelpr.app/api/v1/integration/mcp
Tool
get_start_lineup

In Claude Code, add the server with:

claude mcp add --transport http coachhelpr \
  https://coachhelpr.app/api/v1/integration/mcp \
  --header "Authorization: Bearer chk_..."

In Cursor, put this in ~/.cursor/mcp.json:

{
  "mcpServers": {
    "coachhelpr": {
      "url": "https://coachhelpr.app/api/v1/integration/mcp",
      "headers": {
        "Authorization": "Bearer chk_..."
      }
    }
  }
}

Claude Desktop goes through mcp-remote, which needs Node.js. Put this in claude_desktop_config.json:

{
  "mcpServers": {
    "coachhelpr": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://coachhelpr.app/api/v1/integration/mcp",
        "--header",
        "Authorization:${COACHHELPR_AUTH}"
      ],
      "env": {
        "COACHHELPR_AUTH": "Bearer chk_..."
      }
    }
  }
}

More