Skip to main content

standin-sdk

One package. StandIn in Python, core and every plugin, in a single import.

StandIn is the hosted bridge that joins a Microsoft Teams call. It owns the Microsoft side entirely, the bot registration, Graph, media negotiation, the avatar tile, and talks to your worker over one authenticated socket per call. This package is that socket's other end.

from standin import CallServer, CallSession, ChatChannel, FrameAligner
from standin.plugins.livekit import TeamsCall

Install

pip install standin-sdk

That one line is already enough for ElevenLabs, Deepgram and Cartesia: they are reached over an ordinary WebSocket, so they need nothing beyond aiohttp, which is the only thing the base install pulls.

STANDIN_SECRET=... ELEVENLABS_API_KEY=... ELEVENLABS_AGENT_ID=... \
  python -m standin.plugins.elevenlabs

Add an extra only for a framework that runs inside your process:

pip install "standin-sdk[livekit]"
pip install "standin-sdk[hermes-agent]"

Each plugin has a runnable example at the root of the repo: ElevenLabs, Deepgram, Cartesia, LiveKit and Hermes Agent. Start a custom plugin from echo. OpenAI and OpenClaw are TypeScript, in the other half of the repo.

One package, on purpose

A call surface is never done: screen share, call back, camera, chat, managed chat, adaptive cards. Every one of them has to reach every framework StandIn supports. Split across a wheel per framework, each surface costs N hand-threaded releases and N version matrices; here it costs one directory under standin/plugins/ and one line in standin/__init__.py.

The base install stays small anyway, because that is what extras are for:

Install You get
pip install standin-sdk The core, and every plugin reached over a socket: echo, ElevenLabs, Deepgram and Cartesia. aiohttp is the only dependency.
pip install "standin-sdk[livekit]" The above, plus livekit-agents.
pip install "standin-sdk[hermes-agent]" The above, plus the Hermes adapter. Hermes Agent itself ships the host and loads the adapter in-process.
pip install "standin-sdk[all]" Everything.

import standin never imports a framework. Plugins load the first time you name one, so LiveKit code on disk costs a Hermes user nothing, and a missing extra raises PluginNotInstalled with the install line in it, not a ModuleNotFoundError from inside somebody else's package.

What it gives you

CallServer Answers the socket StandIn dials. Owns the HMAC handshake and its replay guard, capacity and draining, the wire protocol, sequence numbers and the audio timeline, and the watchdogs that end a call nobody closed.
CallHandler The five-method seam a plugin implements. Every method optional.
VideoFrame One frame of what the caller is showing, on the vision lane.
ChatChannel The Microsoft Teams messages lane. Dialed out from your worker, so chat needs no listener, no open port, and no bot credential of your own.

Writing a plugin

The whole contract is five methods, and you implement only the ones you need:

from standin import CallServer, CallSession


class EchoHandler:
    async def on_start(self, session: CallSession) -> None:
        self._call = session

    async def on_caller_audio(self, pcm: bytes) -> None:
        await self._call.send_audio(pcm)  # PCM16, 16 kHz, mono


server = CallServer(handler_factory=EchoHandler)
await server.start()

This illustrates the handler contract. echo adds the runnable entry point and keeps the listener alive:

STANDIN_SECRET=... python -m standin.plugins.echo

Call your number and you hear yourself. Run that before you suspect your own agent: if the echo answers, your secret, your tunnel and your StandIn identity are all correct.

Everything that is the same for every framework lives in CallServer, which is why plugins stay small. Everything that differs, what runs the agent, is yours.

Configuration

Environment only, matching how the plugins read their keys.

Variable Default Meaning
STANDIN_SECRET (required) Connection secret from the StandIn portal. Arms the listener.
STANDIN_PORT 9442 Port the call listener binds.
STANDIN_HOST 0.0.0.0 Bind address. Use 127.0.0.1 when only a local tunnel should reach it.
STANDIN_WS_PATH /msteams/calling Path StandIn dials.
STANDIN_CHAT_URL wss://teams.standin.komaa.com/api/chat/channel Chat channel the worker dials out to.

The listener authenticates WebSocket upgrades with HMAC. Terminate TLS at your public ingress so StandIn can reach it over wss://.

Signing control requests

Use sign_request for HTTP control requests. HMAC v2 binds the method, request path and hash of the entire body, including tenantId:

from standin import SIGNATURE_V2_HEADER, TIMESTAMP_HEADER, now_ms, sign_request

timestamp = str(now_ms())
headers = {
    TIMESTAMP_HEADER: timestamp,
    SIGNATURE_V2_HEADER: sign_request(secret, timestamp, "POST", "/api/calls", raw_body),
}

Serialize the body once and send those same raw_body bytes. These helpers prepare signatures; they do not send HTTP requests. sign_body / verify_body are for chat POST bodies, with a 300-second replay window. WebSocket call and chat-channel handshakes keep sign_handshake / verify_handshake and their separate 60-second window.

Audio

PCM16, 16 kHz, mono, little-endian, both directions. The server owns the outbound sequence number and timeline, so a handler that swaps or re-publishes its audio source cannot make timestamps jump backwards.

The layout

standin/
  __init__.py        the public API, and the lazy hook that keeps it cheap
  call_server.py  handler.py  chat.py  audio.py  protocol.py  ...
  vision.py          what the caller shows you, and what you show back
  avatar.py          the face the caller sees: expression and lip-sync
  fetch.py           fetching a URL a model chose, safely
  plugins/
    echo/            answers a call with the caller's own voice. No extra.
    elevenlabs/      an ElevenLabs agent takes the call. No extra.
    deepgram/        a Deepgram Voice Agent takes the call. No extra.
    cartesia/        a Cartesia Line agent takes the call. No extra.
    livekit/         a LiveKit Agent takes the call.
    hermes/          a Hermes agent takes the call, in the Hermes process.

Links

MIT.

Release files for standin-sdk 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for standin-sdk 0.1.1
File Size Uploaded
standin_sdk-0.1.1.tar.gz 332.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for standin-sdk 0.1.1
File Interpreter ABI Platform
standin_sdk-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 611.3 kB

Release files / standin_sdk-0.1.1.tar.gz

Download URL standin_sdk-0.1.1.tar.gz
Size 332.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3f6c1e7d1fddfb17e0ea57ae7bc2614510ff139e2d8a40540cee446b041dcfa5
BLAKE2b-256 checksum
How to use checksums
3b645d6db49c8c33d57d457fbe154df2094c6e6c1d86beb3dbd0706fde2962f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release files / standin_sdk-0.1.1-py3-none-any.whl

Download URL standin_sdk-0.1.1-py3-none-any.whl
Size 278.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
840d7b9ac0be086c8fba777c866b74eafcb8b935679691d1fc81ed5f47bdc086
BLAKE2b-256 checksum
How to use checksums
892a857038aa6a9f3f3131fa5a011393c220a990f6aa6166f72be19a57af5f3d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page