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

# Python SDK

> Install and use the official Python SDK for server-side movement detection.

# Python SDK

Use the Python SDK for server-side integrations, local video replay, and local camera workflows.

If you are still evaluating the product, start with the guided [guest Workbench demo](https://app.kynasmith.dev/workbench?demo=squat) or the [public catalog](https://app.kynasmith.dev/catalog). This page is the integration path once you are ready to run your own backend workflow.

## Step 1: Install

```bash theme={null}
pip install kynasmith
```

Requires Python 3.12 or newer. Install the `media` extra for local pose detection:
`pip install "kynasmith[media]"`.

## Step 2: Initialize the client

```python theme={null}
from kynasmith import KynasmithClient

client = KynasmithClient(
    api_key="ks_key_123:secret_123",
)
```

The SDK infers your project from the API key.

## Step 3: Create and release a MoveSpec

Define a MoveSpec using YAML that describes the movement you want to detect, then validate it, version it, and release it:

```python theme={null}
from kynasmith.models import MoveSpecCreate, MoveSpecDraftUpdate

# Create a new MoveSpec
movespec = client.movespecs.create(
    MoveSpecCreate(name="Squat counter")
)

# Update the draft with a valid MoveSpec YAML definition
client.movespecs.update_draft(
    movespec.movespec_id,
    MoveSpecDraftUpdate(yaml_source="""
name: squat
version: "1.0"
detection:
  type: repetition
  movement:
    name: squat
    phases:
      - name: standing
        criteria:
          - joint: knee
            angle_min: 160
            angle_max: 180
      - name: bottom
        criteria:
          - joint: knee
            angle_min: 60
            angle_max: 100
    sequence:
      - standing
      - bottom
      - standing
"""),
)

# Validate the draft
validation = client.movespecs.validate_draft(movespec.movespec_id)
assert validation.ok is True

# Snapshot to an immutable version and release it
version = client.movespecs.create_version(movespec.movespec_id)
client.movespecs.release_version(
    movespec.movespec_id,
    version.movespec_version_id,
)
```

See [MoveSpec](/movespec/overview) for the full YAML authoring guide.

## Step 4: Replay a video

```python theme={null}
from kynasmith.models import DetectionSessionCreate

result = client.process_video(
    DetectionSessionCreate(
        movespec_version_id=version.movespec_version_id,
        mode="replay",
    ),
    video_path="squat-demo.mp4",
)

print(f"Accepted frames: {result.accepted_frames_total}")
print(f"Completed events: {result.completed_event_counts}")
```

<Note>
  Replace `squat-demo.mp4` with the path to your own video file. If you are working inside this repo, the deterministic squat replay fixture lives at `activities_golden_set/videos/squat.mp4`.
</Note>

Raw video stays local. The SDK extracts pose landmarks with MediaPipe and sends only pose frames to Kynasmith.

## Step 5: Run a live camera session

```python theme={null}
live_result = client.start_camera(
    DetectionSessionCreate(
        movespec_version_id=version.movespec_version_id,
        mode="live",
    ),
    camera_index=0,
    max_frames=300,
)
```

`start_camera()` captures up to `max_frames` camera frames, finalizes the session, and returns the result.

## Manual session control

Use the lower-level session helpers when you need full control over realtime flow:

```python theme={null}
session = client.detection.create_session(
    DetectionSessionCreate(
        movespec_version_id=version.movespec_version_id,
        mode="live",
    )
)

with client.detection.connect_session(session) as realtime:
    for event in realtime.iter_events():
        print(event.type)
```

<Accordion title="Alternative credential formats">
  ### Separate credential fields

  ```python theme={null}
  client = KynasmithClient(
      api_key_id="key_123",
      api_key_secret="secret_123",
  )
  ```

  ### Service account authentication

  ```python theme={null}
  client = KynasmithClient(
      service_account_id="svc_123",
      service_account_secret="svc_secret_123",
  )
  ```

  ### Pre-minted access token

  ```python theme={null}
  client = KynasmithClient(
      access_token="ks_at_...",
  )
  ```

  When using a static access token, you are responsible for refresh and rotation.

  ### Explicit project ID

  When your credential is scoped to multiple projects, pass `project_id` in request payloads:

  ```python theme={null}
  result = client.process_video(
      DetectionSessionCreate(
          project_id="proj_456",
          movespec_version_id=version.movespec_version_id,
          mode="replay",
      ),
      video_path="squat-demo.mp4",
  )
  ```
</Accordion>

## Typed errors

The Python SDK raises typed exceptions for structured error handling:

* `AuthError` — authentication failures (invalid or expired credentials)
* `AuthorizationError` — permission or scope errors
* `CompatibilityError` — SDK version is not compatible (update your SDK)
* `SessionStateError` — session lifecycle violations
* `ValidationError` — request or MoveSpec validation failures
* `RateLimitError` — rate limit exceeded (HTTP 429)

## Limitations

* **Synchronous only**: The SDK is fully synchronous. In async frameworks, wrap SDK calls with `asyncio.to_thread()` to avoid blocking the event loop:

  ```python theme={null}
  import asyncio

  result = await asyncio.to_thread(
      client.process_video,
      DetectionSessionCreate(
          movespec_version_id=version.movespec_version_id,
          mode="replay",
      ),
      "squat-demo.mp4",
  )
  ```

* **First-run model download**: The first detector run downloads the default MediaPipe `.task` model (\~100 MB) into the local cache. Pass `model_path` to use a pre-downloaded model.

## Environment notes

* Python 3.12 or newer required
* Default install includes `mediapipe` and `opencv-python-headless`
* Tested compatibility line: `mediapipe==0.10.32`
* Default model asset: `pose_landmarker_full/float16/1/pose_landmarker_full.task`
