> ## Documentation Index
> Fetch the complete documentation index at: https://developers.teambuilder.ge/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Make your first request in five minutes: verify a key, read a leaderboard and render it.

This walkthrough uses a `tb_test_` key, which reads your sandbox company of sample employees. Every request works unchanged with a `tb_live_` key once you go live.

<Steps>
  <Step title="Get a test key">
    Keys are issued by TeamBuilder during onboarding. Email [contact@teambuilder.ge](mailto:contact@teambuilder.ge) from your company domain and ask for a test key; a live key follows when you are ready.

    The key is shown once. Put it in an environment variable rather than in code:

    ```bash theme={null}
    export TEAMBUILDER_API_KEY="tb_test_..."
    ```
  </Step>

  <Step title="Verify the key">
    `GET /v1/ping` works with any key and tells you which company it reads, its scopes and its mode.

    <CodeGroup>
      ```bash curl theme={null}
      curl https://api.teambuilder.ge/v1/ping \
        -H "Authorization: Bearer $TEAMBUILDER_API_KEY"
      ```

      ```javascript Node.js theme={null}
      const res = await fetch("https://api.teambuilder.ge/v1/ping", {
        headers: { Authorization: `Bearer ${process.env.TEAMBUILDER_API_KEY}` },
      });
      console.log(res.status, await res.json());
      ```

      ```python Python theme={null}
      import os, requests

      res = requests.get(
          "https://api.teambuilder.ge/v1/ping",
          headers={"Authorization": f"Bearer {os.environ['TEAMBUILDER_API_KEY']}"},
          timeout=10,
      )
      print(res.status_code, res.json())
      ```
    </CodeGroup>

    ```json Response theme={null}
    {
      "company_id": "0b6c1e2f-4a8d-4f3e-9b71-6d2e8c5a1f04",
      "key_prefix": "tb_test_",
      "scopes": ["employees:read", "leaderboard:read", "departments:read", "shop:read"],
      "mode": "test"
    }
    ```

    A `401` means the key is missing, mistyped, revoked or expired. See [Authentication](/authentication).
  </Step>

  <Step title="Read the leaderboard">
    Fetch the top ten for the current month:

    <CodeGroup>
      ```bash curl theme={null}
      curl "https://api.teambuilder.ge/v1/leaderboard?period=this_month&limit=10" \
        -H "Authorization: Bearer $TEAMBUILDER_API_KEY"
      ```

      ```javascript Node.js theme={null}
      const res = await fetch(
        "https://api.teambuilder.ge/v1/leaderboard?period=this_month&limit=10",
        { headers: { Authorization: `Bearer ${process.env.TEAMBUILDER_API_KEY}` } },
      );
      if (!res.ok) {
        const { error } = await res.json();
        throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
      }
      const { data: leaders } = await res.json();
      ```

      ```python Python theme={null}
      import os, requests

      res = requests.get(
          "https://api.teambuilder.ge/v1/leaderboard",
          params={"period": "this_month", "limit": 10},
          headers={"Authorization": f"Bearer {os.environ['TEAMBUILDER_API_KEY']}"},
          timeout=10,
      )
      res.raise_for_status()
      leaders = res.json()["data"]
      ```
    </CodeGroup>

    Each row has the employee's company-wide `rank`, their `xp` for the period, their all-time `level` and their department.
  </Step>

  <Step title="Render it">
    Turn the rows into markup on your server, then cache the result: rankings can be up to 30 seconds old, so polling more often gains nothing.

    <CodeGroup>
      ```javascript Node.js theme={null}
      const escape = (s) =>
        String(s).replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);

      const html = `<ol class="leaderboard">${leaders
        .map(
          (row) => `<li>
            <span class="rank">${row.rank}</span>
            <span class="name">${escape(row.name ?? "New teammate")}</span>
            <span class="dept">${escape(row.department?.name ?? "")}</span>
            <span class="xp">${row.xp.toLocaleString("en-GB")} XP</span>
          </li>`,
        )
        .join("")}</ol>`;
      ```

      ```python Python theme={null}
      from html import escape

      items = "".join(
          f'<li><span class="rank">{row["rank"]}</span>'
          f'<span class="name">{escape(row["name"] or "New teammate")}</span>'
          f'<span class="dept">{escape((row["department"] or {}).get("name", ""))}</span>'
          f'<span class="xp">{row["xp"]:,} XP</span></li>'
          for row in leaders
      )
      html = f'<ol class="leaderboard">{items}</ol>'
      ```
    </CodeGroup>

    <Note>
      `name` can be `null` for someone who has not signed in to TeamBuilder yet and was not added by a roster sync. Always render a fallback.
    </Note>
  </Step>
</Steps>

<Warning>
  Call the API from your server only. Keys grant access to employee names and emails, so they must never reach a browser, a mobile app or a public repository. The API does not accept cross-origin browser requests, so browser calls fail by design.
</Warning>

## Where next

<CardGroup cols={2}>
  <Card title="Leaderboards guide" icon="trophy" href="/guides/leaderboards">
    Periods, department views and refresh strategy.
  </Card>

  <Card title="Roster sync guide" icon="arrows-rotate" href="/guides/roster-sync">
    Keep employees, departments and job titles in sync.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.