> ## 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.

# Cartesia plugin

> Answer Microsoft Teams calls with a Cartesia Line agent, using the StandIn TypeScript SDK.

`@komaa/standin-sdk/cartesia` puts [Cartesia](https://docs.cartesia.ai/line) on a real Microsoft Teams call. StandIn answers the call and dials your worker; this plugin answers that dial, opens one session per call, and relays the audio both ways.

## Install and run

There is nothing to install beyond the SDK. Cartesia is reached over an ordinary WebSocket, so this plugin adds no dependency.

```bash theme={null}
npm install @komaa/standin-sdk
```

```bash theme={null}
export STANDIN_SECRET=...        # your StandIn connection secret
export CARTESIA_API_KEY=...
export CARTESIA_AGENT_ID=...
npx standin-cartesia
```

Expose port `9442` at the `/msteams/calling` path and register the public `wss://` URL as your StandIn identity's agent voice URL. The full walkthrough is in the [Quickstart](/typescript-sdk/quickstart), and there is a runnable example at [examples/cartesia-msteams-connector](https://github.com/komaa-com/standin/tree/main/examples/cartesia-msteams-connector).

## The audio format

Audio is pinned to `pcm_16000` in both directions, which is exactly what a Microsoft Teams call carries.

## Your agent stays your agent

Cartesia runs the agent, so this plugin is transport and nothing else. There are no call tools to declare here, because whatever the agent does it does in its own code on Cartesia's platform.

Caller details reach that code as stream metadata, call context arrives as `custom` events, and a key press arrives as a real `dtmf` event.

<Warning>
  `CARTESIA_SYSTEM_PROMPT` is optional on purpose. Left unset, the agent keeps exactly the prompt you wrote on Cartesia's side. Set it and the caller's details are appended to yours. Nothing here ever silently rewrites a deployed prompt.
</Warning>

## Inside your own worker

The plugin is a `CallHandler` like any other:

```ts theme={null}
import { CallServer } from "@komaa/standin-sdk";
import { CartesiaHandler, cartesiaConfigFromEnv } from "@komaa/standin-sdk/cartesia";

const config = cartesiaConfigFromEnv();
const server = new CallServer({
  handlerFactory: () => new CartesiaHandler({ config }),
});
await server.start();
```

Read the configuration **once**, then close over it. `handlerFactory` runs once per call, so
`new CartesiaHandler()` with nothing passed reads the environment again on every call, and a key
removed after startup would fail the next caller rather than failing you. `serve()`, which is what
`npx standin-cartesia` runs, does exactly this, then waits for `SIGINT` or `SIGTERM` before calling
`server.aclose()`. `server.start()` returns as soon as the listener is bound, so a script that
ends there exits before a single call arrives, and `aclose()` is what drains live calls and
releases the port.

## Configuration

| Variable            | What it is                                                                                                       |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `CARTESIA_API_KEY`  | Your API key. Used only to mint a per-call token over HTTPS, so the long-lived key never rides the agent socket. |
| `CARTESIA_AGENT_ID` | Which Line agent answers the call.                                                                               |

<Accordion title="Optional settings">
  | Variable                 | What it is                                                                                              |
  | ------------------------ | ------------------------------------------------------------------------------------------------------- |
  | `CARTESIA_VOICE_ID`      | Override the agent's configured voice.                                                                  |
  | `CARTESIA_INTRODUCTION`  | What the agent says first.                                                                              |
  | `CARTESIA_SYSTEM_PROMPT` | Appended to, never replacing, the prompt on Cartesia's side. Leave it unset and nothing is added.       |
  | `CARTESIA_VERSION`       | API version header. Defaults to `2025-04-16`. Pin it when Cartesia ships a version you have not tested. |
  | `CARTESIA_API_HOST`      | Defaults to `api.cartesia.ai`. Must be a cartesia.ai host.                                              |
</Accordion>

<Note>
  The configuration is read once when the worker starts, not per call, so a missing key stops the worker at startup rather than surprising the first caller. `CARTESIA_API_HOST` is checked against the cartesia.ai suffix, because your API key travels to it: a wrong host would be credential leakage rather than a failed call. The reasoning behind that shape is on [Configuration](/typescript-sdk/configuration).
</Note>

## Next

<CardGroup cols={2}>
  <Card title="Realtime providers" icon="bolt" href="/typescript-sdk/realtime-providers">
    The startup buffer, the echo guard and barge-in that every provider plugin sits on.
  </Card>

  <Card title="Configuration" icon="sliders" href="/typescript-sdk/configuration">
    Every `STANDIN_` variable, and the helpers for reading your own.
  </Card>
</CardGroup>
