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

# Planned endpoints

> The capture, games, and parsing endpoints we plan to expose — grounded in what the RinkNotes app already does, previewed for partner feedback.

These are the endpoints we plan to open first. Each is **grounded in a capability that already
powers the RinkNotes app internally**, so the paths, payloads, and responses shown are real
shapes — not guesses. What's not available yet is *commercial access* to them.

<Warning>
  **Preview — not commercially available yet.** There's no public gateway or API key today;
  access is provisioned with you as we roll out the API. Treat everything here as the shape to
  react to, not a live endpoint. The examples use the planned base URL
  `https://api.wispera.ai/functions/v1/`.
</Warning>

When the API opens, all calls will be `POST` and carry an org-scoped credential. Endpoints that
touch a team also require the caller to be a member of that team with capture permission
(otherwise `403`).

## Capture

### Submit a note

`POST /functions/v1/submit-note`

Sends a typed note. RinkNotes stores it as a capture event and kicks off classification into a
player observation. Planned rate limit: **60 / hour**.

<ParamField body="team_id" type="string" required>
  The team the note belongs to.
</ParamField>

<ParamField body="text" type="string" required>
  The note text. Trimmed; truncated at 2000 characters.
</ParamField>

<ParamField body="session_id" type="string">
  The game or practice session this note was captured during, if any.
</ParamField>

<ParamField body="player_id" type="string">
  The player the note is about, if known.
</ParamField>

<ParamField body="game_elapsed_ms" type="number">
  Milliseconds elapsed on the game clock when the note was captured.
</ParamField>

<ParamField body="period_id" type="string">
  The period this note falls in, if any.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl https://api.wispera.ai/functions/v1/submit-note \
    -H "Authorization: Bearer <credential>" \
    -H "Content-Type: application/json" \
    -d '{
      "team_id": "b1a7...c9",
      "text": "Great backcheck in the third, broke up a 2-on-1",
      "session_id": "5f2e...a1",
      "game_elapsed_ms": 2415000
    }'
  ```
</CodeGroup>

<ResponseField name="event_id" type="string">
  The id of the created capture event.
</ResponseField>

<ResponseField name="status" type="string">
  Always `"transcribed"` — the note is ready and classification has been triggered.
</ResponseField>

```json Response theme={null}
{ "event_id": "9c4d...02", "status": "transcribed" }
```

### Transcribe audio

`POST /functions/v1/transcribe-audio`

Sends an audio clip as `multipart/form-data` with a single file field named `audio`. The audio
is **ephemeral** — it's transcribed and discarded, never stored. Planned rate limit: **60 /
hour** (per file / mic bucket). Returns `502` if the upstream transcription service fails.

There are two modes, selected by headers.

<Tabs>
  <Tab title="Async (persist & classify)">
    Attaches the transcript to an existing capture event and triggers classification. The
    caller must be the user who captured that event, or the call returns `403`.

    **Headers**

    * `X-Capture-Event-Id` — the capture event to attach the transcript to. Required.
    * `X-Transcription-Source` — `file_upload` or `live_mic`. Required.

    ```bash Request theme={null}
    curl https://api.wispera.ai/functions/v1/transcribe-audio \
      -H "Authorization: Bearer <credential>" \
      -H "X-Capture-Event-Id: 9c4d...02" \
      -H "X-Transcription-Source: live_mic" \
      -F "audio=@clip.webm"
    ```

    ```json Response theme={null}
    { "event_id": "9c4d...02", "status": "transcribed" }
    ```
  </Tab>

  <Tab title="Transcribe-only">
    Returns the transcript text without persisting or classifying anything. Requires team
    membership with capture permission. Carries an additional per-day cap on top of the hourly
    limit.

    **Headers**

    * `X-Transcribe-Only: 1`
    * `X-Team-Id` — the team to attribute the transcription quota to. Required.

    ```bash Request theme={null}
    curl https://api.wispera.ai/functions/v1/transcribe-audio \
      -H "Authorization: Bearer <credential>" \
      -H "X-Transcribe-Only: 1" \
      -H "X-Team-Id: b1a7...c9" \
      -F "audio=@clip.webm"
    ```

    ```json Response theme={null}
    { "transcript": "great backcheck in the third broke up a two on one" }
    ```
  </Tab>
</Tabs>

## Games

### Finalize a game

`POST /functions/v1/finalize-game`

Closes out a game session and derives its stats and game events from the notes captured during
it. The session must belong to the given team and be a game session (not a practice), otherwise
you'll get `404` or `400`.

<ParamField body="team_id" type="string" required />

<ParamField body="session_id" type="string" required>
  The game session to finalize.
</ParamField>

```bash Request theme={null}
curl https://api.wispera.ai/functions/v1/finalize-game \
  -H "Authorization: Bearer <credential>" \
  -H "Content-Type: application/json" \
  -d '{ "team_id": "b1a7...c9", "session_id": "5f2e...a1" }'
```

<ResponseField name="finalized" type="object">
  `session_id`, plus `stats` and `events` booleans indicating whether each derivation ran.
</ResponseField>

```json Response theme={null}
{ "finalized": { "session_id": "5f2e...a1", "stats": true, "events": true } }
```

Derivation of stats and game events runs internally after you call this — you don't call those
steps directly.

## Parsing helpers

These turn free text into a structured object using RinkNotes' hockey model. They **persist
nothing** — no side effects, nothing stored. Planned rate limit: **30 / hour**. Every response
includes a `confidence` between 0 and 1.

### Parse a game

`POST /functions/v1/parse-new-game`

<ParamField body="transcript" type="string" required>
  The free text describing the game.
</ParamField>

<ParamField body="today" type="string">
  Today's date as `YYYY-MM-DD`, used to resolve relative dates ("this Saturday").
</ParamField>

```json Response theme={null}
{
  "opponent": "Northshore Kings",
  "starts_at": "2026-07-19T18:30:00Z",
  "home_away": "home",
  "confidence": 0.82
}
```

`home_away` is `"home"`, `"away"`, or `null`. `starts_at` is ISO-8601 or `null`.

### Parse a practice

`POST /functions/v1/parse-new-practice`

<ParamField body="transcript" type="string" required />

<ParamField body="today" type="string" />

```json Response theme={null}
{ "starts_at": "2026-07-18T17:00:00Z", "label": "Power-play work", "confidence": 0.74 }
```

### Parse a calendar event

`POST /functions/v1/parse-new-event`

<ParamField body="transcript" type="string" required />

<ParamField body="today" type="string" />

```json Response theme={null}
{
  "title": "Season-end team dinner",
  "event_type": "team_dinner",
  "starts_at": "2026-07-25T23:00:00Z",
  "ends_at": null,
  "location": "The Lakeview Grill",
  "notes": null,
  "confidence": 0.9
}
```

`event_type` is one of `tournament`, `team_dinner`, `meeting`, `tryout`, `social`, `other`, or
`null`. All fields except `confidence` may be `null`.

### Parse an in-game event

`POST /functions/v1/parse-game-event`

<ParamField body="transcript" type="string" required />

<ParamField body="position_group" type="string" required>
  `goalie` or `skater` — selects which event vocabulary applies.
</ParamField>

```json Response theme={null}
{
  "event_type": "shot_against",
  "zone": "DZ",
  "strength_state": "PK",
  "detail": { "saveType": "glove", "dangerLevel": "high", "rebound": false }
}
```

`zone` is `OZ` / `NZ` / `DZ` / `null`; `strength_state` is `EV` / `PP` / `PK` / `null`; `detail`
is an object of recognized fields (or `null`).

## Data model

For reference, the client-safe shape of the objects the API works with. These are the fields
you'd see on a capture and its derived outputs — not a full schema.

| Object            | Client-safe fields                                                                                                                                                                                             |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **capture event** | `id`, `team_id`, `session_id`, `player_id`, `kind`, `transcript`, `status`, `needs_review`, `created_at`, `game_elapsed_ms`, `period_id`, `trigger_kind` (`manual`/`energy`/`wakeWord`), `dimension`, `detail` |
| **session**       | `id`, `team_id`, `kind` (e.g. `game`/`practice`), `opponent`, `starts_at`, `status` (`pending`/`active`/`ended`), `label`                                                                                      |
| **game event**    | `id`, `session_id`, `team_id`, `event_type`, `side` (`for`/`against`), `player_id`, `period_id`, `game_elapsed_ms`, `detail`                                                                                   |
| **game stat**     | `id`, `player_id`, `session_id`, `metric`, `value`                                                                                                                                                             |
| **team player**   | `id`, `team_id`, `full_name`, `jersey_number`, `birth_year`, `position`                                                                                                                                        |

<Note>
  Reading these objects back out programmatically — client read/GET endpoints — is on the
  [roadmap](/developers/roadmap), not part of this first surface.
</Note>
