> ## Documentation Index
> Fetch the complete documentation index at: https://huddleup-sports.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> How to authenticate to the Whistle partner API and MCP server with a bearer API key, what your key's source scope covers, and how to handle, rotate, and revoke keys safely.

Every request to the REST API and the MCP endpoint requires a partner API key in the `Authorization` header:

```bash theme={null}
Authorization: Bearer whk_your_key_here
```

Keys look like `whk_` followed by 48 hex characters. Whistle stores only a hash of your key — it can never be shown to you again after issuance.

## Getting a key

Keys are issued by the Whistle team: email [tommy@huddleup-sports.com](mailto:tommy@huddleup-sports.com) with your integration's name. There is no self-service key portal yet. The plaintext key is delivered once.

## What your key can access

Partner keys are **scoped principals**, bound to a `source` namespace (e.g. `"huddleup"`) when minted:

* You can read and mutate only leagues whose `source` matches your key, plus any leagues explicitly granted to your key by the Whistle team.
* Rows you create are stamped with your key's `source` automatically — you may omit `source` from request bodies, or echo your own value. Sending any *other* value returns `400 {"error":"source_mismatch"}`; identity can't be spoofed via the body.
* Webhook endpoints are owned by your `source` too: you list, delete, and read deliveries only for your own endpoints, and you receive events only for leagues in your scope.

<Note>
  **Out-of-scope looks like nonexistent — by design.** A league, game, or webhook endpoint outside your scope returns the exact same `404` and error code as one that doesn't exist (`league_not_found`, `game_not_found`, `endpoint_not_found`). You cannot distinguish "not yours" from "not there", so don't treat a 404 as proof a resource is absent — only that it's not accessible to you.
</Note>

## Failure behavior

Any authentication failure — missing header, malformed header, revoked or unknown key — returns the same response: status `401` with body

```json theme={null}
{"error": "unauthorized"}
```

The API never returns `403` (authorization failures surface as the 404s described above). A `401` on a previously working integration almost always means the key was revoked or the header is no longer being sent — it is **not retriable**; fix the credential.

## Handling your key safely

* Store the key in a secret manager or environment variable (`WHISTLE_API_KEY` in these docs' examples). Never commit it, log it, or embed it in client-side code.
* The API is server-to-server only (no CORS), so there is never a legitimate reason for the key to reach a browser or mobile app.
* Even though keys are source-scoped, a leaked key can still read and rewrite everything in your namespace — including reopening claimed positions and deleting your webhooks. Give each integration its own key so one can be revoked without breaking the others.

## Rotation and revocation

There is no rotation endpoint. To rotate: request a new key from [tommy@huddleup-sports.com](mailto:tommy@huddleup-sports.com), deploy it, then ask for the old key to be revoked. Revocation takes effect immediately — subsequent requests get `401`.

If you suspect a key has leaked, email support immediately to revoke it.

## Verifying your key

The cheapest authenticated call is the API index:

```bash theme={null}
curl -s https://whistle.huddleup-sports.com/api/v1 \
  -H "Authorization: Bearer $WHISTLE_API_KEY"
```

A `200` with `{"data":{"version":"v1", …}}` means the key works. See the [REST quickstart](/developers/quickstart-rest).
