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

# Python SDK

> API reference for interhumanai — the official Python client for auth, upload, stream, and real-time analysis.

First-party Python SDK for the Interhuman API.

Quickstart:

```python theme={null}
import asyncio
from interhumanai import InterhumanClient

async def main() -> None:
    client = InterhumanClient(key_id="...", key_secret="...")
    result = await client.upload.analyze("meeting.mp4")
    for signal in result.signals:
        print(signal.type.value, signal.start, signal.end)

asyncio.run(main())
```

## Clients & helpers

### AuthClient

Client for the token endpoints (`/v1/auth` and `/v1/client_tokens`).

Args:
environment: Named environment to call. Defaults to `production`.
base\_url: Explicit base URL override (e.g. `http://localhost:8080`).
http\_client: Optional shared `httpx.AsyncClient`. When omitted,
a client is created per request.

**Constructor**

```python theme={null}
AuthClient(*, environment: Optional[Literal['production', 'staging']] = None, base_url: str | None = None, http_client: httpx.AsyncClient | None = None) -> None
```

#### AuthClient.base\_url

```python theme={null}
@property
def base_url(self) -> str
```

The resolved HTTP base URL this client calls.

#### AuthClient.create\_client\_token()

```python theme={null}
async def create_client_token(self, *, api_key: str, scopes: collections.abc.Sequence[Scope | str] | None = None, expires_in: int | None = None, max_duration_seconds: int | None = None, max_bytes: int | None = None, max_concurrent: int | None = None, max_video_seconds: int | None = None, allowed_origins: collections.abc.Sequence[str] | None = None) -> ClientTokenResponse
```

Mint a short-lived, capped client token for direct end-user use.

Call this from a trusted server: the full API key travels in the
request body. Omitted options use the server defaults (scope
`interhumanai.stream`, 300 second lifetime clamped to 60-3600).

Args:
api\_key: The full API key (`ih_...`) minting the token.
scopes: Scopes the client token should carry.
expires\_in: Requested lifetime in seconds (server clamps to 60-3600).
max\_duration\_seconds: Cap on a single live session's duration.
max\_bytes: Cap on bytes accepted across the token's sessions.
max\_concurrent: Cap on concurrent sessions (server default 1).
max\_video\_seconds: Video-seconds budget across all surfaces.
allowed\_origins: Browser origins allowed to use the token.

Returns:
The minted client token and its effective caps.

#### AuthClient.create\_token()

```python theme={null}
async def create_token(self, *, key_id: str, key_secret: str, scopes: collections.abc.Sequence[Scope | str]) -> TokenResponse
```

Exchange API key credentials for a short-lived bearer access token.

Args:
key\_id: The API key id.
key\_secret: The API key secret.
scopes: Scopes to request; must be non-empty and held by the key.

Returns:
The minted token, its lifetime, and the granted scopes.

#### AuthClient.revoke\_client\_token()

```python theme={null}
async def revoke_client_token(self, *, api_key: str, token: str) -> None
```

Revoke a previously minted client token.

Revoking an already-expired token is a no-op and also succeeds.

Args:
api\_key: The API key that minted the token.
token: The client token to revoke.

### InterhumanClient

High-level client for the Interhuman API.

Authenticate either with API key credentials (`key_id` + `key_secret`,
exchanged for short-lived bearer tokens that are refreshed automatically)
or with a pre-issued `access_token` used as-is. Exactly one of the two
must be provided.

The client exposes every public surface: `auth` for token endpoints,
`upload` for complete files, and `stream` / `realtime`
for live WebSocket sessions.

Args:
key\_id: API key id (paired with `key_secret`).
key\_secret: API key secret.
access\_token: Pre-issued bearer token (JWT or client token).
scopes: Scopes requested when exchanging credentials. Defaults to
upload + stream. Ignored when `access_token` is used.
environment: Named environment to call. Defaults to `production`.
base\_url: Explicit base URL override (e.g. `http://localhost:8080`).
http\_client: Optional shared `httpx.AsyncClient` for the HTTP
surfaces. The caller owns its lifecycle.
refresh\_skew\_seconds: How long before expiry managed tokens refresh.

**Constructor**

```python theme={null}
InterhumanClient(*, key_id: str | None = None, key_secret: str | None = None, access_token: str | None = None, scopes: collections.abc.Sequence[Scope | str] | None = None, environment: Optional[Literal['production', 'staging']] = None, base_url: str | None = None, http_client: httpx.AsyncClient | None = None, refresh_skew_seconds: float = 30.0) -> None
```

#### InterhumanClient.get\_token()

```python theme={null}
async def get_token(self) -> str
```

Return the bearer token the client is currently using.

With managed credentials this mints or refreshes as needed; with a
pre-issued `access_token` it returns that token unchanged.

#### InterhumanClient.realtime()

```python theme={null}
def realtime(self) -> RealtimeClient
```

Create a client for one `WS /v0/realtime/analyze` session.

Each call returns a fresh, unconnected client; a client handles a
single session.

#### InterhumanClient.stream()

```python theme={null}
def stream(self) -> StreamClient
```

Create a client for one `WS /v1/stream/analyze` session.

Each call returns a fresh, unconnected client; a client handles a
single session.

### RealtimeClient

One live session against `WS /v0/realtime/analyze`.

The real-time endpoint is a temporary `v0` public release; it accepts
either the `interhumanai.stream` or the `interhumanai.realtime` scope.
It never emits `engagement.updated` or `conversation_quality.updated`
events, and adds transcript and feedback output on top of the stream
protocol. The previous hyphenated spelling (`/v0/real-time/analyze`) still
works as a temporary compatibility alias, but new integrations should use
`/v0/realtime/analyze`.

#### RealtimeClient.send\_transcript()

```python theme={null}
async def send_transcript(self, transcript: collections.abc.Sequence[TranscriptSegment]) -> None
```

Send the latest client-side transcript of the conversation.

Each call fully replaces any previously sent transcript; the latest
transcript is rendered into subsequent feedback output.

Args:
transcript: Ordered transcript segments (speaker ids are
zero-based).

#### RealtimeClient.update\_config()

```python theme={null}
async def update_config(self, *, analysis_groups: collections.abc.Sequence[AnalysisGroup | str] | None = None, realtime_feedback_frequency: RealtimeFeedbackFrequency | str | None = None, realtime_feedback_instructions: str | None = None, goal_dimensions: collections.abc.Sequence[GoalDimension | str] | None = None) -> None
```

Replace the session configuration.

Each update fully replaces the previous configuration; omitted options
reset to their server defaults. The server acknowledges with a
`session.updated` event.

Args:
analysis\_groups: Analysis tracks to run (must be non-empty when
provided). Server default when omitted: audio and visual.
realtime\_feedback\_frequency: How often feedback output is generated.
realtime\_feedback\_instructions: Non-empty instructions that enable feedback;
when omitted or empty, feedback is disabled.
goal\_dimensions: Goal dimensions that enable feedback generation.

### SessionSocketClient

One live analysis session over a WebSocket.

Connect with `connect` (or `async with`), send binary video chunks
with `send_video`, and consume typed server events by iterating the
client with `async for`. Iteration ends when the connection closes;
`close_info` then holds the close code and reason.

Args:
token\_provider: Source of bearer tokens for authentication.
environment: Named environment to connect to. Defaults to
`production`.
base\_url: Explicit HTTP base URL override; converted to `ws(s)://`.

**Constructor**

```python theme={null}
SessionSocketClient(*, token_provider: TokenProvider, environment: Optional[Literal['production', 'staging']] = None, base_url: str | None = None) -> None
```

#### SessionSocketClient.close()

```python theme={null}
async def close(self, code: int = 1000, reason: str = '') -> None
```

Close the WebSocket immediately, discarding in-flight analysis.

Args:
code: WebSocket close code to send.
reason: Optional close reason.

#### SessionSocketClient.close\_info

```python theme={null}
@property
def close_info(self) -> CloseInfo | None
```

Close code and reason once the connection has ended, else `None`.

#### SessionSocketClient.connect()

```python theme={null}
async def connect(self) -> 'SessionSocketClient[EventT]'
```

Open the WebSocket connection and start receiving events.

Returns:
This client, for chaining.

Raises:
InterhumanConfigError: If the client was already connected.
InterhumanError: If the handshake fails (bad credentials or scope,
unreachable host).

#### SessionSocketClient.is\_open

```python theme={null}
@property
def is_open(self) -> bool
```

Whether the connection is currently open.

#### SessionSocketClient.request\_close()

```python theme={null}
async def request_close(self) -> None
```

Ask the server to drain in-flight analysis and end the session.

The server acknowledges with `session.closing`, emits any final
events, sends `session.ended`, and closes the socket normally. Keep
iterating to observe the drain; for an immediate teardown use
`close` instead.

#### SessionSocketClient.send\_video()

```python theme={null}
async def send_video(self, chunk: bytes | bytearray | memoryview) -> None
```

Send one binary video chunk.

The first chunk must carry the container's init header; later chunks
are continuation fragments of the same WebM or fragmented-MP4 stream.
The SDK does not enforce a chunk size; the server rejects chunks above
its limit (32 MB by default, reported in `session.ready` as
`max_segment_size_bytes`) with an `ih6002` error.

Args:
chunk: The raw video bytes to send.

#### SessionSocketClient.url

```python theme={null}
@property
def url(self) -> str
```

The WebSocket URL this client connects to.

#### SessionSocketClient.wait\_closed()

```python theme={null}
async def wait_closed(self) -> CloseInfo
```

Wait until the connection has fully closed.

Returns:
The session's close code and reason.

#### SessionSocketClient.wait\_for\_session\_ready()

```python theme={null}
async def wait_for_session_ready(self) -> EventT
```

Wait for the server's `session.ready` event.

The event is also delivered through iteration; this helper simply
awaits it (or returns it if it already arrived).

Returns:
The `session.ready` event.

Raises:
InterhumanError: If the connection closes before the session
becomes ready (typically an auth or scope failure).

### StaticTokenProvider

Token provider that always returns the same pre-issued token.

**Constructor**

```python theme={null}
StaticTokenProvider(token: str) -> None
```

#### StaticTokenProvider.get\_token()

```python theme={null}
async def get_token(self) -> str
```

Return the configured token.

### StreamClient

One live session against `WS /v1/stream/analyze`.

Requires the `interhumanai.stream` scope. Send WebM or fragmented-MP4
chunks with `send_video`; consume typed events with `async for`.

#### StreamClient.update\_config()

```python theme={null}
async def update_config(self, *, include: collections.abc.Sequence[IncludeFlag | str] | None = None, goal_dimensions: collections.abc.Sequence[GoalDimension | str] | None = None) -> None
```

Replace the session configuration.

Each update fully replaces the previous configuration; omitted options
reset to their server defaults. The server acknowledges with a
`session.updated` event.

Args:
include: Optional sections to include in quality updates.
goal\_dimensions: Goal dimensions that enable feedback generation.

### TokenManager

Mints bearer tokens from API key credentials and refreshes them early.

Tokens are cached until `expires_in - refresh_skew_seconds` elapses;
concurrent callers share a single in-flight mint.

Args:
auth\_client: The `AuthClient` used to mint tokens.
key\_id: The API key id.
key\_secret: The API key secret.
scopes: Scopes to request on every mint.
refresh\_skew\_seconds: Seconds before expiry at which the cached token
is considered stale.
clock: Monotonic clock returning seconds; injectable for tests.

**Constructor**

```python theme={null}
TokenManager(*, auth_client: AuthClient, key_id: str, key_secret: str, scopes: collections.abc.Sequence[Scope | str] = (<Scope.UPLOAD: 'interhumanai.upload'>, <Scope.STREAM: 'interhumanai.stream'>), refresh_skew_seconds: float = 30.0, clock: Callable[[], float] = <built-in function monotonic>) -> None
```

#### TokenManager.get\_token()

```python theme={null}
async def get_token(self) -> str
```

Return a valid access token, minting or refreshing when needed.

#### TokenManager.invalidate()

```python theme={null}
def invalidate(self) -> None
```

Drop the cached token so the next call mints a fresh one.

### TokenProvider

Anything that can produce a bearer access token on demand.

**Constructor**

```python theme={null}
TokenProvider(*args, **kwargs)
```

#### TokenProvider.get\_token()

```python theme={null}
async def get_token(self) -> str
```

Return a currently valid bearer access token.

### UploadClient

Client for analyzing complete video files.

Args:
token\_provider: Source of bearer tokens for authentication.
environment: Named environment to call. Defaults to `production`.
base\_url: Explicit base URL override.
http\_client: Optional shared `httpx.AsyncClient`. When omitted,
a client is created per request.

**Constructor**

```python theme={null}
UploadClient(*, token_provider: TokenProvider, environment: Optional[Literal['production', 'staging']] = None, base_url: str | None = None, http_client: httpx.AsyncClient | None = None) -> None
```

#### UploadClient.analyze()

```python theme={null}
async def analyze(self, file: Union[bytes, bytearray, memoryview, IO[bytes], str, os.PathLike[str]], *, filename: str | None = None, content_type: str | None = None, include: collections.abc.Sequence[IncludeFlag | str] | None = None, goal_dimensions: collections.abc.Sequence[GoalDimension | str] | None = None, conversation_context: str | None = None) -> AnalysisResult
```

Analyze a complete video file and return the detected signals.

The video must be at least 3 seconds long and at most 32 MB, in one of
the supported containers (mp4, avi, mov, mkv, mpeg-ts, webm).

Args:
file: The video to analyze - raw bytes, an open binary file
object, or a filesystem path.
filename: Filename reported to the API. Defaults to the path or
file object's name, else `video`.
content\_type: MIME type of the file (e.g. `video/mp4`).
include: Optional response sections to include (conversation
quality overall and/or timeline).
goal\_dimensions: Goal dimensions that trigger interaction
feedback. Ignored when `conversation_context` is set.
conversation\_context: Free-text description of the interaction;
when set, it drives feedback generation.

Returns:
The analysis result. Optional sections are only present when
requested.

## Data models

### AnalysisResult

Response of `POST /v1/upload/analyze`.

Optional sections are only present when requested: `feedback` when goal
dimensions or a conversation context were supplied, `conversation_quality`
when requested via include flags.

| Field                  | Type                               | Required |
| ---------------------- | ---------------------------------- | -------- |
| `signals`              | `list[Signal]`                     | No       |
| `engagement_state`     | `list[EngagementStateEntry]`       | No       |
| `feedback`             | `Optional[Feedback \| NoFeedback]` | No       |
| `conversation_quality` | `ConversationQuality \| None`      | No       |

### ClientTokenResponse

Response of `POST /v1/client_tokens`.

| Field                  | Type                | Required |
| ---------------------- | ------------------- | -------- |
| `access_token`         | `str`               | Yes      |
| `token_type`           | `str`               | Yes      |
| `expires_in`           | `int`               | Yes      |
| `scope`                | `str`               | Yes      |
| `max_duration_seconds` | `int \| None`       | No       |
| `max_bytes`            | `int \| None`       | No       |
| `max_concurrent`       | `int \| None`       | No       |
| `max_video_seconds`    | `int \| None`       | No       |
| `allowed_origins`      | `list[str] \| None` | No       |

### CloseInfo

Close code and reason of a finished WebSocket session.

| Field    | Type  | Required |
| -------- | ----- | -------- |
| `code`   | `int` | Yes      |
| `reason` | `str` | Yes      |

### ConversationQuality

Conversation-quality section of an analysis result.

| Field      | Type                                     | Required |
| ---------- | ---------------------------------------- | -------- |
| `overall`  | `ConversationQualityValues`              | Yes      |
| `timeline` | `list[ConversationQualityTimelineEntry]` | No       |

### ConversationQualityTimelineEntry

Conversation-quality scores for one slice of the video.

| Field    | Type                        | Required |
| -------- | --------------------------- | -------- |
| `start`  | `float`                     | Yes      |
| `end`    | `float`                     | Yes      |
| `values` | `ConversationQualityValues` | Yes      |

### ConversationQualityUpdatedData

Latest conversation-quality scores (sections follow the include flags).

| Field      | Type                                             | Required |
| ---------- | ------------------------------------------------ | -------- |
| `overall`  | `ConversationQualityValues \| None`              | No       |
| `timeline` | `list[ConversationQualityTimelineEntry] \| None` | No       |

### ConversationQualityUpdatedEvent

New conversation-quality scores are available.

| Field            | Type                                      | Required |
| ---------------- | ----------------------------------------- | -------- |
| `timestamp`      | `datetime`                                | Yes      |
| `correlation_id` | `str`                                     | Yes      |
| `type`           | `Literal['conversation_quality.updated']` | Yes      |
| `data`           | `ConversationQualityUpdatedData`          | Yes      |

### ConversationQualityValues

Conversation-quality scores (0-100; 50 means no evidence either way).

| Field           | Type    | Required |
| --------------- | ------- | -------- |
| `quality_index` | `float` | Yes      |
| `clarity`       | `float` | Yes      |
| `authority`     | `float` | Yes      |
| `energy`        | `float` | Yes      |
| `rapport`       | `float` | Yes      |
| `learning`      | `float` | Yes      |

### CoverageDroppedData

Video ranges dropped under backpressure (not billed).

| Field    | Type                  | Required |
| -------- | --------------------- | -------- |
| `ranges` | `list[CoverageRange]` | No       |

### CoverageDroppedEvent

Some video was dropped without being analyzed.

| Field            | Type                          | Required |
| ---------------- | ----------------------------- | -------- |
| `timestamp`      | `datetime`                    | Yes      |
| `correlation_id` | `str`                         | Yes      |
| `type`           | `Literal['coverage.dropped']` | Yes      |
| `data`           | `CoverageDroppedData`         | Yes      |

### CoverageRange

A time range of video that was not analyzed.

| Field   | Type    | Required |
| ------- | ------- | -------- |
| `start` | `float` | Yes      |
| `end`   | `float` | Yes      |

### EngagementStateEntry

An engagement state over a time range.

| Field   | Type              | Required |
| ------- | ----------------- | -------- |
| `state` | `EngagementLevel` | Yes      |
| `start` | `float`           | Yes      |
| `end`   | `float`           | Yes      |

### EngagementUpdatedData

The engagement state entered at `start`.

| Field   | Type              | Required |
| ------- | ----------------- | -------- |
| `state` | `EngagementLevel` | Yes      |
| `start` | `float`           | Yes      |

### EngagementUpdatedEvent

The subject's engagement state changed.

| Field            | Type                            | Required |
| ---------------- | ------------------------------- | -------- |
| `timestamp`      | `datetime`                      | Yes      |
| `correlation_id` | `str`                           | Yes      |
| `type`           | `Literal['engagement.updated']` | Yes      |
| `data`           | `EngagementUpdatedData`         | Yes      |

### ErrorData

A session error notice (fatal errors also close the socket).

| Field     | Type          | Required |
| --------- | ------------- | -------- |
| `code`    | `str`         | Yes      |
| `message` | `str`         | Yes      |
| `link`    | `str \| None` | No       |
| `segment` | `int \| None` | No       |

### ErrorEvent

The server reported an error for this session.

| Field            | Type               | Required |
| ---------------- | ------------------ | -------- |
| `timestamp`      | `datetime`         | Yes      |
| `correlation_id` | `str`              | Yes      |
| `type`           | `Literal['error']` | Yes      |
| `data`           | `ErrorData`        | Yes      |

### Feedback

Actionable interaction feedback generated from the analysis.

| Field               | Type                  | Required                  |
| ------------------- | --------------------- | ------------------------- |
| `type`              | `Literal['feedback']` | No (default `'feedback'`) |
| `message`           | `str`                 | Yes                       |
| `active_dimensions` | `list[GoalDimension]` | No                        |
| `primary_signal`    | `SignalType \| None`  | No                        |

### FeedbackGeneratedData

Feedback text generated from the active goal dimensions.

| Field      | Type  | Required |
| ---------- | ----- | -------- |
| `feedback` | `str` | Yes      |

### FeedbackGeneratedEvent

Interaction feedback was generated.

| Field            | Type                            | Required |
| ---------------- | ------------------------------- | -------- |
| `timestamp`      | `datetime`                      | Yes      |
| `correlation_id` | `str`                           | Yes      |
| `type`           | `Literal['feedback.generated']` | Yes      |
| `data`           | `FeedbackGeneratedData`         | Yes      |

### NoFeedback

Explicit indication that no feedback was warranted, with the reason.

| Field    | Type                     | Required                     |
| -------- | ------------------------ | ---------------------------- |
| `type`   | `Literal['no_feedback']` | No (default `'no_feedback'`) |
| `reason` | `str`                    | Yes                          |

### RealtimeFeedbackGeneratedData

Generated guidance for the analyzed window.

`text` is the literal `NO_GUIDANCE` when the model had nothing to
suggest for this window.

| Field   | Type    | Required |
| ------- | ------- | -------- |
| `text`  | `str`   | Yes      |
| `start` | `float` | Yes      |
| `end`   | `float` | Yes      |

### RealtimeFeedbackGeneratedEvent

New feedback output is available.

| Field            | Type                                     | Required |
| ---------------- | ---------------------------------------- | -------- |
| `timestamp`      | `datetime`                               | Yes      |
| `correlation_id` | `str`                                    | Yes      |
| `type`           | `Literal['realtime_feedback.generated']` | Yes      |
| `data`           | `RealtimeFeedbackGeneratedData`          | Yes      |

### RealtimeSessionConfigOptions

Session-config options the real-time endpoint supports.

| Field                            | Type                              | Required |
| -------------------------------- | --------------------------------- | -------- |
| `realtime_feedback_instructions` | `str`                             | Yes      |
| `realtime_feedback_frequency`    | `list[RealtimeFeedbackFrequency]` | Yes      |
| `analysis_groups`                | `list[AnalysisGroup]`             | Yes      |

### RealtimeSessionReadyData

Limits and supported options of a newly opened real-time session.

| Field                              | Type                           | Required |
| ---------------------------------- | ------------------------------ | -------- |
| `session_idle_timeout_seconds`     | `int`                          | Yes      |
| `session_max_duration_seconds`     | `int`                          | Yes      |
| `max_segment_duration_seconds`     | `float \| None`                | No       |
| `min_segment_size_bytes`           | `int`                          | Yes      |
| `max_segment_size_bytes`           | `int`                          | Yes      |
| `supported_session_config_options` | `RealtimeSessionConfigOptions` | Yes      |

### RealtimeSessionReadyEvent

The session is accepted and ready to receive video.

| Field            | Type                       | Required |
| ---------------- | -------------------------- | -------- |
| `timestamp`      | `datetime`                 | Yes      |
| `correlation_id` | `str`                      | Yes      |
| `type`           | `Literal['session.ready']` | Yes      |
| `data`           | `RealtimeSessionReadyData` | Yes      |

### RealtimeSessionUpdatedData

The real-time session configuration now in effect.

| Field                            | Type                                | Required |
| -------------------------------- | ----------------------------------- | -------- |
| `realtime_feedback_instructions` | `str \| None`                       | No       |
| `realtime_feedback_frequency`    | `RealtimeFeedbackFrequency \| None` | No       |
| `analysis_groups`                | `list[AnalysisGroup]`               | Yes      |

### RealtimeSessionUpdatedEvent

Acknowledgment of a session-config update.

| Field            | Type                         | Required |
| ---------------- | ---------------------------- | -------- |
| `timestamp`      | `datetime`                   | Yes      |
| `correlation_id` | `str`                        | Yes      |
| `type`           | `Literal['session.updated']` | Yes      |
| `data`           | `RealtimeSessionUpdatedData` | Yes      |

### SessionClosingData

Drain window granted after a graceful close request.

| Field               | Type  | Required |
| ------------------- | ----- | -------- |
| `max_drain_seconds` | `int` | Yes      |

### SessionClosingEvent

The server accepted a graceful close and is draining.

| Field            | Type                         | Required |
| ---------------- | ---------------------------- | -------- |
| `timestamp`      | `datetime`                   | Yes      |
| `correlation_id` | `str`                        | Yes      |
| `type`           | `Literal['session.closing']` | Yes      |
| `data`           | `SessionClosingData`         | Yes      |

### SessionConfigOptions

Session-config options the stream endpoint supports.

| Field             | Type                          | Required |
| ----------------- | ----------------------------- | -------- |
| `include`         | `list[IncludeFlag]`           | No       |
| `goal_dimensions` | `list[GoalDimension] \| None` | No       |

### SessionEndedData

Why the session ended.

| Field    | Type  | Required |
| -------- | ----- | -------- |
| `reason` | `str` | Yes      |

### SessionEndedEvent

Final envelope of a gracefully ended session.

| Field            | Type                       | Required |
| ---------------- | -------------------------- | -------- |
| `timestamp`      | `datetime`                 | Yes      |
| `correlation_id` | `str`                      | Yes      |
| `type`           | `Literal['session.ended']` | Yes      |
| `data`           | `SessionEndedData`         | Yes      |

### SessionReadyData

Limits and supported options of a newly opened stream session.

| Field                              | Type                   | Required |
| ---------------------------------- | ---------------------- | -------- |
| `session_idle_timeout_seconds`     | `int`                  | Yes      |
| `session_max_duration_seconds`     | `int`                  | Yes      |
| `max_segment_duration_seconds`     | `float \| None`        | No       |
| `min_segment_size_bytes`           | `int`                  | Yes      |
| `max_segment_size_bytes`           | `int`                  | Yes      |
| `supported_session_config_options` | `SessionConfigOptions` | Yes      |

### SessionReadyEvent

The session is accepted and ready to receive video.

| Field            | Type                       | Required |
| ---------------- | -------------------------- | -------- |
| `timestamp`      | `datetime`                 | Yes      |
| `correlation_id` | `str`                      | Yes      |
| `type`           | `Literal['session.ready']` | Yes      |
| `data`           | `SessionReadyData`         | Yes      |

### SessionUpdatedData

The session configuration now in effect.

| Field             | Type                          | Required |
| ----------------- | ----------------------------- | -------- |
| `include`         | `list[IncludeFlag]`           | No       |
| `goal_dimensions` | `list[GoalDimension] \| None` | No       |

### SessionUpdatedEvent

Acknowledgment of a session-config update.

| Field            | Type                         | Required |
| ---------------- | ---------------------------- | -------- |
| `timestamp`      | `datetime`                   | Yes      |
| `correlation_id` | `str`                        | Yes      |
| `type`           | `Literal['session.updated']` | Yes      |
| `data`           | `SessionUpdatedData`         | Yes      |

### Signal

A detected social signal over a time range.

| Field         | Type                  | Required |
| ------------- | --------------------- | -------- |
| `type`        | `SignalType`          | Yes      |
| `start`       | `float`               | Yes      |
| `end`         | `float`               | Yes      |
| `probability` | `Probability \| None` | No       |
| `rationale`   | `str \| None`         | No       |

### SignalDetectedData

A newly detected signal (its `end` is not known yet).

| Field         | Type                  | Required |
| ------------- | --------------------- | -------- |
| `signal_type` | `SignalType`          | Yes      |
| `start`       | `float`               | Yes      |
| `probability` | `Probability \| None` | No       |
| `rationale`   | `str \| None`         | No       |

### SignalDetectedEvent

A social signal was detected.

| Field            | Type                         | Required |
| ---------------- | ---------------------------- | -------- |
| `timestamp`      | `datetime`                   | Yes      |
| `correlation_id` | `str`                        | Yes      |
| `type`           | `Literal['signal.detected']` | Yes      |
| `data`           | `SignalDetectedData`         | Yes      |

### SignalEndedData

End time of a signal that is no longer active.

| Field         | Type         | Required |
| ------------- | ------------ | -------- |
| `signal_type` | `SignalType` | Yes      |
| `end`         | `float`      | Yes      |

### SignalEndedEvent

An active signal ended.

| Field            | Type                      | Required |
| ---------------- | ------------------------- | -------- |
| `timestamp`      | `datetime`                | Yes      |
| `correlation_id` | `str`                     | Yes      |
| `type`           | `Literal['signal.ended']` | Yes      |
| `data`           | `SignalEndedData`         | Yes      |

### SignalUpdatedData

Updated details for a signal that is still active.

| Field         | Type                  | Required |
| ------------- | --------------------- | -------- |
| `signal_type` | `SignalType`          | Yes      |
| `start`       | `float`               | Yes      |
| `probability` | `Probability \| None` | No       |
| `rationale`   | `str \| None`         | No       |

### SignalUpdatedEvent

An active signal's details changed.

| Field            | Type                        | Required |
| ---------------- | --------------------------- | -------- |
| `timestamp`      | `datetime`                  | Yes      |
| `correlation_id` | `str`                       | Yes      |
| `type`           | `Literal['signal.updated']` | Yes      |
| `data`           | `SignalUpdatedData`         | Yes      |

### TokenResponse

Response of `POST /v1/auth`.

| Field          | Type  | Required |
| -------------- | ----- | -------- |
| `access_token` | `str` | Yes      |
| `token_type`   | `str` | Yes      |
| `expires_in`   | `int` | Yes      |
| `scope`        | `str` | Yes      |

### TranscriptGeneratedEvent

The server transcribed a slice of the session's audio.

| Field            | Type                              | Required |
| ---------------- | --------------------------------- | -------- |
| `timestamp`      | `datetime`                        | Yes      |
| `correlation_id` | `str`                             | Yes      |
| `type`           | `Literal['transcript.generated']` | Yes      |
| `data`           | `TranscriptSegment`               | Yes      |

### TranscriptSegment

One segment of a conversation transcript.

| Field     | Type    | Required         |
| --------- | ------- | ---------------- |
| `start`   | `float` | Yes              |
| `end`     | `float` | Yes              |
| `text`    | `str`   | Yes              |
| `speaker` | `int`   | No (default `0`) |

### UnknownEvent

A server envelope whose `type` this SDK version does not know.

Newer API versions may add event types; they are surfaced as-is instead of
failing the session.

Attributes:
type: The envelope's `type` discriminator.
raw: The full envelope payload as received.

| Field  | Type             | Required |
| ------ | ---------------- | -------- |
| `type` | `str`            | Yes      |
| `raw`  | `dict[str, Any]` | Yes      |

## Enumerations

### AnalysisGroup

Analysis track groups selectable on the real-time API.

| Member   | Value      |
| -------- | ---------- |
| `VIDEO`  | `'video'`  |
| `VISUAL` | `'visual'` |
| `AUDIO`  | `'audio'`  |

### EngagementLevel

Coarse engagement state of the analyzed subject.

| Member       | Value          |
| ------------ | -------------- |
| `ENGAGED`    | `'engaged'`    |
| `NEUTRAL`    | `'neutral'`    |
| `DISENGAGED` | `'disengaged'` |

### GoalDimension

Interaction-goal dimensions used for feedback and conversation quality.

| Member      | Value         |
| ----------- | ------------- |
| `CLARITY`   | `'clarity'`   |
| `AUTHORITY` | `'authority'` |
| `ENERGY`    | `'energy'`    |
| `RAPPORT`   | `'rapport'`   |
| `LEARNING`  | `'learning'`  |

### IncludeFlag

Optional response sections selectable on upload and stream analysis.

| Member                          | Value                             |
| ------------------------------- | --------------------------------- |
| `CONVERSATION_QUALITY_OVERALL`  | `'conversation_quality_overall'`  |
| `CONVERSATION_QUALITY_TIMELINE` | `'conversation_quality_timeline'` |

### Probability

Confidence band attached to a detected signal.

| Member   | Value      |
| -------- | ---------- |
| `HIGH`   | `'high'`   |
| `MEDIUM` | `'medium'` |
| `LOW`    | `'low'`    |

### RealtimeFeedbackFrequency

How often the real-time API generates feedback output.

`HIGH` is roughly every 10 seconds of analyzed video, `MEDIUM` every
20 seconds, and `LOW` every 30 seconds.

| Member   | Value      |
| -------- | ---------- |
| `HIGH`   | `'high'`   |
| `MEDIUM` | `'medium'` |
| `LOW`    | `'low'`    |

### Scope

OAuth-style scopes accepted by the Interhuman API.

| Member     | Value                     |
| ---------- | ------------------------- |
| `UPLOAD`   | `'interhumanai.upload'`   |
| `STREAM`   | `'interhumanai.stream'`   |
| `REALTIME` | `'interhumanai.realtime'` |

### SignalType

Social signals the Inter-1 model can detect.

| Member          | Value             |
| --------------- | ----------------- |
| `AGREEMENT`     | `'agreement'`     |
| `CONFIDENCE`    | `'confidence'`    |
| `CONFUSION`     | `'confusion'`     |
| `DISAGREEMENT`  | `'disagreement'`  |
| `DISENGAGEMENT` | `'disengagement'` |
| `ENGAGEMENT`    | `'engagement'`    |
| `FRUSTRATION`   | `'frustration'`   |
| `HESITATION`    | `'hesitation'`    |
| `INTEREST`      | `'interest'`      |
| `SKEPTICISM`    | `'skepticism'`    |
| `STRESS`        | `'stress'`        |
| `UNCERTAINTY`   | `'uncertainty'`   |

## Exceptions

### InterhumanAPIError

An error response from the Interhuman API, or a transport failure.

Attributes:
status: HTTP status code, or `0` when the request never reached the
API (network failure).
error\_id: Machine-readable error code from the response body (e.g.
`ih2001`), when the API supplied one.
correlation\_id: Correlation id of the failed request, when supplied.
link: Documentation link for the error, when supplied.
body: The parsed JSON error body, when one was returned.

### InterhumanConfigError

Raised for client-side misuse, before any network call is made.

Examples: missing credentials, sending on a socket that is not open, or
connecting a client that is already connected.

### InterhumanError

Base class for every error raised by the Interhuman SDK.

## Functions

### http\_to\_ws\_base\_url()

```python theme={null}
def http_to_ws_base_url(http_base_url: str) -> str
```

Derive the WebSocket base URL from an HTTP base URL.

`https://` becomes `wss://` and `http://` becomes `ws://`; any path
is preserved and a trailing slash is trimmed.

Args:
http\_base\_url: The HTTP base URL to convert.

Returns:
The WebSocket base URL without a trailing slash.

### parse\_realtime\_event()

```python theme={null}
def parse_realtime_event(payload: dict[str, Any]) -> Union[RealtimeSessionReadyEvent, RealtimeSessionUpdatedEvent, SessionClosingEvent, SessionEndedEvent, SignalDetectedEvent, SignalUpdatedEvent, SignalEndedEvent, TranscriptGeneratedEvent, RealtimeFeedbackGeneratedEvent, CoverageDroppedEvent, ErrorEvent, UnknownEvent]
```

Parse one real-time envelope into its typed event model.

Args:
payload: The decoded JSON envelope (must carry a string `type`).

Returns:
The matching typed event, or `UnknownEvent` for a `type`
this SDK version does not know.

### parse\_stream\_event()

```python theme={null}
def parse_stream_event(payload: dict[str, Any]) -> Union[SessionReadyEvent, SessionUpdatedEvent, SessionClosingEvent, SessionEndedEvent, SignalDetectedEvent, SignalUpdatedEvent, SignalEndedEvent, EngagementUpdatedEvent, ConversationQualityUpdatedEvent, FeedbackGeneratedEvent, CoverageDroppedEvent, ErrorEvent, UnknownEvent]
```

Parse one stream envelope into its typed event model.

Args:
payload: The decoded JSON envelope (must carry a string `type`).

Returns:
The matching typed event, or `UnknownEvent` for a `type`
this SDK version does not know.

### resolve\_http\_base\_url()

```python theme={null}
def resolve_http_base_url(*, base_url: str | None = None, environment: Optional[Literal['production', 'staging']] = None) -> str
```

Resolve the HTTP base URL from an explicit override or a named environment.

Args:
base\_url: Explicit base URL (e.g. `http://localhost:8080`). Takes
precedence over `environment` when provided.
environment: Named environment. Defaults to `production`.

Returns:
The base URL without a trailing slash.

## Constants

### DEFAULT\_REFRESH\_SKEW\_SECONDS

Type: `float`

```python theme={null}
DEFAULT_REFRESH_SKEW_SECONDS = 30.0
```

### DEFAULT\_SCOPES

Type: `tuple`

```python theme={null}
DEFAULT_SCOPES = (<Scope.UPLOAD: 'interhumanai.upload'>, <Scope.STREAM: 'interhumanai.stream'>)
```

### Environment

Type: `type alias`

```python theme={null}
Environment = Literal['production', 'staging']
```

### HTTP\_BASE\_URLS

Type: `dict`

```python theme={null}
HTTP_BASE_URLS = {'production': 'https://api.interhuman.ai', 'staging': 'https://staging-api.interhuman.ai'}
```

### InteractionFeedback

Type: `type alias`

```python theme={null}
InteractionFeedback = Feedback | NoFeedback
```

### NO\_GUIDANCE

Type: `str`

```python theme={null}
NO_GUIDANCE = 'NO_GUIDANCE'
```

### RealtimeEvent

Type: `type alias`

```python theme={null}
RealtimeEvent = Union[RealtimeSessionReadyEvent, RealtimeSessionUpdatedEvent, SessionClosingEvent, SessionEndedEvent, SignalDetectedEvent, SignalUpdatedEvent, SignalEndedEvent, TranscriptGeneratedEvent, RealtimeFeedbackGeneratedEvent, CoverageDroppedEvent, ErrorEvent, UnknownEvent]
```

### StreamEvent

Type: `type alias`

```python theme={null}
StreamEvent = Union[SessionReadyEvent, SessionUpdatedEvent, SessionClosingEvent, SessionEndedEvent, SignalDetectedEvent, SignalUpdatedEvent, SignalEndedEvent, EngagementUpdatedEvent, ConversationQualityUpdatedEvent, FeedbackGeneratedEvent, CoverageDroppedEvent, ErrorEvent, UnknownEvent]
```

### VideoInput

Type: `type alias`

```python theme={null}
VideoInput = Union[bytes, bytearray, memoryview, IO[bytes], str, os.PathLike[str]]
```
