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

# Architecture

> How StandIn (the hosted media bridge), the HMAC WebSocket, the plugin, and your agent fit together.

Joining a Teams call needs a media bridge that speaks Teams' real-time audio/video. **You don't run
that** - it's **StandIn**, a hosted service at [standin.komaa.com](https://standin.komaa.com). You
subscribe (free package), connect your Teams bot, and StandIn's managed bridge talks to your plugin.
The plugin is the cross-platform **brain**: it hosts a WebSocket StandIn connects to, runs the
dialogue + perception, and emits avatar driver cues. **StandIn bridges + renders; the plugin drives.**

```mermaid theme={null}
flowchart LR
    A["Your agent<br/>OpenClaw / Hermes"]
    P["Teams Voice Plugin<br/>(WebSocket server)"]
    S["StandIn<br/>hosted media bridge"]
    T["Microsoft Teams<br/>call / meeting"]

    A <-->|"consult · tasks"| P
    P <-->|"HMAC WebSocket"| S
    S <-->|"Teams calling media"| T

    style A fill:#1e293b,stroke:#475569,color:#e2e8f0
    style P fill:#166534,stroke:#22c55e,color:#dcfce7
    style S fill:#0e7490,stroke:#06b6d4,color:#cffafe
    style T fill:#3730a3,stroke:#6366f1,color:#e0e7ff
```

The **plugin is the WebSocket server** (default port `:9442` on OpenClaw, `:8443` on Hermes); the
**hosted StandIn bridge is the client** that connects to it. You register your plugin's WebSocket URL
and shared secret in your StandIn dashboard - expose it so the hosted bridge can reach it (a public
URL, or a tunnel such as Tailscale).

## Call flow

A single call: the HMAC handshake, the recording-status gate, then the per-turn dialogue loop.

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor C as Caller
    participant S as StandIn
    participant P as Plugin
    participant A as Agent

    C->>S: joins / places a Teams call
    S->>P: WS upgrade + HMAC(timestamp.callId)
    Note over S,P: ±60s window · single-use · replay-guarded
    S->>P: session.start
    S->>P: recording.status
    Note over P: gate - no media processed until recording is active
    loop each turn
        S->>P: audio.frame / video.frame
        P->>A: consult / task
        A-->>P: result
        P-->>S: audio.frame + expression / speech.marks
        S-->>C: avatar speaks
    end
    S->>P: session.end
```

## Who does what

| Component                               | Role                                                                                                                                                                                                         |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Your agent** (OpenClaw / Hermes)      | the dialogue brain - answers consults, runs background tasks, produces vision/image results                                                                                                                  |
| **Teams Voice Plugin** (WS server)      | hosts the bridge · realtime / streaming dialogue · vision ring + budget · emits `expression` / `speech.marks` / `display.image` cues                                                                         |
| **StandIn** (hosted, standin.komaa.com) | fully-managed media bridge - joins the Teams call, renders the avatar tile, samples inbound A/V, forwards DTMF, enforces the recording-status gate, places outbound calls. No infrastructure for you to run. |
| **Microsoft Teams**                     | the live call / meeting and its media plane                                                                                                                                                                  |

<Note>
  Subscribe to **StandIn** at [standin.komaa.com](https://standin.komaa.com) (free package) and connect
  your agent. There is no media server, VM, or worker for you to host.
</Note>

## Wire contract

The plugin and StandIn speak a fixed contract over the HMAC WebSocket - the same one both the OpenClaw
and Hermes plugins implement:

| Aspect            | Value                                                                                                                                                                                                                                                                  |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Handshake**     | `HMAC-SHA256(sharedSecret, "{timestampMs}.{callId}")`, lowercase hex, sent as `X-StandIn-Timestamp` / `-Signature` (legacy `X-OpenClawTeamsBridge-*` still accepted) headers on the WS upgrade. ±60 s window; each `(callId, ts, sig)` is single-use (replay-guarded). |
| **Path**          | `/voice/msteams/stream/{callId}`                                                                                                                                                                                                                                       |
| **Audio**         | PCM 16 kHz, 16-bit, mono, little-endian; 20 ms / 640-byte frames, base64                                                                                                                                                                                               |
| **Inbound msgs**  | `session.start` · `session.end` · `recording.status` · `audio.frame` · `video.frame` · `participants` · `dtmf` · `ping`                                                                                                                                                |
| **Outbound msgs** | `audio.frame` · `expression` · `speech.marks` · `display.image` · `assistant.cancel` · `pong`                                                                                                                                                                          |

<Warning>
  The plugin's `sharedSecret` **must byte-match** the value registered for your bot in StandIn, or the
  HMAC handshake fails and no call connects.
</Warning>

## Microsoft Graph permissions

You bring your own Teams bot (BYO): register an **Azure AD app + Azure Bot resource** in your tenant,
admin-consent the required **application** permissions, and point its calling webhook at StandIn (the URL
is shown in your StandIn dashboard). These apply to both the OpenClaw and Hermes plugins.

See the canonical [Graph permissions table](/teams/azure-bot#8-grant-graph-permissions) on the Create the Azure bot page.
