> This page is for Expression Measurement.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://dev.hume.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://dev.hume.ai/_mcp/server.

# Sessions

**A session is one WebSocket connection to a realtime endpoint.** It starts when the server sends `session.created` and ends when the connection closes, normally right after `session.closed`. The audio and video endpoints share this protocol. [Audio realtime](/expression-measurement/docs/audio/realtime) and [Video realtime](/expression-measurement/docs/video/realtime) cover each endpoint's media format, configuration, and results.

## Messages

Text messages are JSON objects identified by a `type` field. Media travels as binary frames. Messages follow the [compatibility rules](/expression-measurement/docs/overview#compatibility).

<table>
  <thead>
    <tr>
      <th>
        Type
      </th>

      <th width="12%">
        Direction
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `session.created`
      </td>

      <td>
        receive
      </td>

      <td>
        The session ID and the default configuration, sent as soon as the connection opens.
      </td>
    </tr>

    <tr>
      <td>
        `session.update`
      </td>

      <td>
        send
      </td>

      <td>
        Replaces the configuration. Accepted only until the configuration locks at the first media frame.
      </td>
    </tr>

    <tr>
      <td>
        `session.updated`
      </td>

      <td>
        receive
      </td>

      <td>
        Confirms a 

        `session.update`

         with the full configuration now in effect.
      </td>
    </tr>

    <tr>
      <td>
        Binary frame
      </td>

      <td>
        send
      </td>

      <td>
        Audio, or one JPEG image on the video endpoint.
      </td>
    </tr>

    <tr>
      <td>
        `measurement.result`
      </td>

      <td>
        receive
      </td>

      <td>
        Scores for one window of speech, or for the faces in one image.
      </td>
    </tr>

    <tr>
      <td>
        `error`
      </td>

      <td>
        receive
      </td>

      <td>
        A message was rejected, or the session cannot continue. See 

        [Realtime errors](/expression-measurement/docs/errors#realtime-errors)

        .
      </td>
    </tr>

    <tr>
      <td>
        `session.close`
      </td>

      <td>
        send
      </td>

      <td>
        Asks the server to end the session.
      </td>
    </tr>

    <tr>
      <td>
        `session.closed`
      </td>

      <td>
        receive
      </td>

      <td>
        Why the session ended and totals of what was received and produced. The last message of every session that ends in an orderly way.
      </td>
    </tr>
  </tbody>
</table>

The audio endpoint also sends [`utterance.start` and `utterance.end`](/expression-measurement/docs/audio/realtime#utterances) to mark the start and end of each stretch of speech.

## Lifecycle

### The server sends `session.created`

It arrives before the client sends anything. Store the `session_id`; Hume support uses it to find the session, and it is the ID of the session's [run](/expression-measurement/docs/runs).

### The client sends `session.update`, if needed

The server confirms with `session.updated`, listing the full configuration now in effect. Skip this step to use the defaults. [Configuration](#configuration) has the rules.

### The client streams media and the server sends results

On the audio endpoint, the server replies only to a rejected frame, with an `error`. On the video endpoint, it replies to every frame with a `measurement.result`, or with an `error` if the frame was rejected or its measurement failed.

### The client sends `session.close`

The server delivers any results still in progress, sends `session.closed`, and closes the connection.

## Configuration

The example below changes one field. The confirmation shows the full configuration that results.

#### session.update (audio endpoint)

```json
{
  "type": "session.update",
  "measurement_timer_ms": 5000
}
```

#### session.updated (audio endpoint)

```json
{
  "type": "session.updated",
  "audio": {
    "type": "audio/pcm",
    "encoding": "s16le",
    "sample_rate": 16000,
    "channels": 1
  },
  "measurement_timer_ms": 5000
}
```

1. **Omitted fields return to their defaults.** `session.update` replaces the configuration rather than merging into it, so include every field you want in effect.
2. **The configuration locks at the first media frame the server takes.** A later `session.update` is rejected with `config_invalid`, and the session ends. Which frames lock it differs by endpoint; see [Audio configuration](/expression-measurement/docs/audio/realtime#configuration) and [Video configuration](/expression-measurement/docs/video/realtime#configuration).
3. **Unrecognized fields and unsupported values end the session** with `config_invalid`.
4. **Several updates may be sent until the configuration locks.** Each replaces the configuration and is confirmed with `session.updated`.

The fields each endpoint accepts are listed under [Audio configuration](/expression-measurement/docs/audio/realtime#configuration) and [Video configuration](/expression-measurement/docs/video/realtime#configuration).

## Closing

To end a session, send `session.close` and keep reading until `session.closed` arrives. Results still in progress are delivered before it. On the audio endpoint, if the final measurement fails, an `error` arrives first and `session.closed` carries `reason` set to `error`.

#### session.close

```json
{
  "type": "session.close"
}
```

`session.closed` states the `reason` the session ended and totals what the server received and produced. The totals differ by endpoint; see the [audio](/expression-measurement/docs/audio/realtime#session-summary) and [video](/expression-measurement/docs/video/realtime#session-summary) session summaries.

#### session.closed (audio endpoint)

```json
{
  "type": "session.closed",
  "session_id": "01926f3a-5b1c-7d2e-8f40-3a9b7c1d2e5f",
  "reason": "client_request",
  "received": {
    "audio_duration_ms": 26000,
    "frames": 260
  },
  "produced": {
    "utterances": 1,
    "measurements": 9
  }
}
```

After `session.closed`, the server closes the WebSocket with the code listed for the reason. [Close codes](/expression-measurement/docs/errors#close-codes) describes every code the server closes with.

<table>
  <thead>
    <tr>
      <th>
        Reason
      </th>

      <th width="12%">
        Close code
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `client_request`
      </td>

      <td>
        1000
      </td>

      <td>
        The client sent 

        `session.close`

        .
      </td>
    </tr>

    <tr>
      <td>
        `server_shutdown`
      </td>

      <td>
        1012
      </td>

      <td>
        The server is restarting. Open a new connection right away to start another session.
      </td>
    </tr>

    <tr>
      <td>
        `idle_timeout`
      </td>

      <td>
        1000
      </td>

      <td>
        The server received no media frame it could use for 30 seconds. Open a new connection to start another session.
      </td>
    </tr>

    <tr>
      <td>
        `error`
      </td>

      <td>
        1008
      </td>

      <td>
        An unrecoverable error, described in the 

        `error`

         message sent just before 

        `session.closed`

        .
      </td>
    </tr>
  </tbody>
</table>

If the server cannot continue a session, it closes the connection with code 1011 without sending `session.closed`, so no reason is given. See [Connection failures](/expression-measurement/docs/errors#connection-failures).

If the client closes the WebSocket instead of sending `session.close`, the session ends at once. The server sends nothing further, so results in progress and `session.closed` are lost.

## Connection behavior

1. **Messages arrive in order.** The server sends messages in the order it produces them, and results follow the order in which media was accepted.
2. **The server never sends pings.** It answers any ping from the client with a pong.
3. **A session closes after 30 seconds without a media frame the server can use.** It ends with `session.closed` and `reason` set to `idle_timeout`. Accepted frames reset the timer, as do audio frames rejected with `rate_limited`. Invalid frames, video frames rejected with `rate_limited`, text messages, and pings do not. To pause for longer, end the session and open a new one when you resume.
4. **A session cannot be resumed.** However it ended, open a new connection to start another.

## Time and identifiers

Time values are integers in milliseconds, measured from the start of the session's media. `session_id` is a UUID. The other identifiers (`measurement_id`, `utterance_id`, `frame_id`, and `face_id`) are integers that start at 0 in every session, increase as the session progresses, and mean nothing outside it. `face_id` is `null` for a face that was not measured.

## After a session

Every session is recorded as a run, and its `session_id` is the run's ID. The run endpoints return its status, the configuration it ran under, and the problems it reported. They do not return measurements, so keep the results you need from the session itself. See [Runs](/expression-measurement/docs/runs).

## Next steps

#### [Audio realtime](/expression-measurement/docs/audio/realtime)

Audio format, utterances, measurement timing, and audio configuration.

#### [Video realtime](/expression-measurement/docs/video/realtime)

JPEG frames, detection settings, face tracking, and video configuration.