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

# Run the example

> A step-by-step walkthrough of the basic-bridge example: clone, configure, expose, connect to StandIn, and put a gpt-realtime agent on a Teams call.

The fastest way to understand the bridge is to run its example project - a minimal, working
embedding you can copy straight into your own repo. Budget about **10 minutes** once the
[Teams setup](/teams/overview) is done.

<Card title="examples/basic-bridge" icon="play" href="https://github.com/komaa-com/openai-msteams-bridge/tree/main/examples/basic-bridge">
  `loadConfig()` + `startServer()` in a few lines, with a custom vision hook (your own model answers
  the agent's `look` tool) and a custom `lookup_order` function tool the bridge executes. Env-file
  config and graceful shutdown are built in.
</Card>

## What you need first

* **Node.js `>= 20`**.
* An **OpenAI API key** with Realtime access
  ([platform.openai.com](https://platform.openai.com/api-keys)). No agent to create - the bridge
  configures each `gpt-realtime` session itself.
* A **StandIn identity** with its **shared secret** (from
  [pairing](/quickstart#connect-your-agent-by-pairing-recommended) or the
  [dashboard](https://standin.komaa.com/dashboard)). The [sandbox](/community) works too if you have
  no Teams bot yet.

## 1. Clone and install

```bash theme={null}
git clone https://github.com/komaa-com/openai-msteams-bridge
```

```bash theme={null}
cd openai-msteams-bridge/examples/basic-bridge
```

```bash theme={null}
npm install
```

## 2. Configure the environment

The example reads the same env file as the CLI - copy the fully commented template from the repo
root:

```bash theme={null}
cp ../../.env.example .env
```

Open `.env` and fill in the two required values:

| Variable               | What to put there                                                       |
| ---------------------- | ----------------------------------------------------------------------- |
| `OPENAI_API_KEY`       | Your OpenAI API key (server-side only; never sent to the Teams side).   |
| `WORKER_SHARED_SECRET` | The shared secret from StandIn pairing - both sides must match exactly. |

Worth setting while you are there: `OPENAI_VOICE` (e.g. `marin`), `OPENAI_INSTRUCTIONS` (the
agent's personality), and `OPENAI_FIRST_MESSAGE` (a deterministic greeting / AI disclosure). The
example still boots with dummy values, so you can check the wiring before the credentials are real.
Everything else (`PORT`, `MAX_CALL_MINUTES`, goodbye behavior, VAD mode) has sensible defaults -
see the [configuration reference](/openai/configuration).

## 3. Start it

```bash theme={null}
npm start
```

The bridge prints the WebSocket URL to give StandIn:

```
Point your StandIn identity's agent WebSocket URL at ws://<this-host>:8080/voice/msteams/stream
```

## 4. Expose port 8080

StandIn connects **from the internet**, so the port needs a public `wss://` URL. Any tunnel works:

<Tabs>
  <Tab title="Tailscale Funnel">
    ```bash theme={null}
    tailscale funnel --bg --https=8080 8080
    ```

    Your URL: `wss://<machine>.<tailnet>.ts.net:8080/voice/msteams/stream`
  </Tab>

  <Tab title="cloudflared">
    ```bash theme={null}
    cloudflared tunnel --url http://localhost:8080
    ```

    Use the printed hostname: `wss://<hostname>/voice/msteams/stream`
  </Tab>

  <Tab title="ngrok">
    ```bash theme={null}
    ngrok http 8080
    ```

    Use the printed hostname: `wss://<hostname>/voice/msteams/stream`
  </Tab>
</Tabs>

## 5. Connect it to StandIn and call

1. In your [StandIn dashboard](https://standin.komaa.com/dashboard), set the identity's
   **Agent voice URL** to the `wss://` URL from step 4.
2. Make sure the identity's shared secret equals `WORKER_SHARED_SECRET`.
3. Place a Teams call to your bot (or join the sandbox meeting). StandIn joins, connects to the
   bridge, and your `gpt-realtime` agent answers.

## The vision hook

The example ships a custom `VisionDescriber` in `index.mjs`. When your agent calls its built-in
`look` tool, the bridge hands your function the current camera or screen-share frame and returns
your text description to the agent - the raw frame never leaves your process. The example answers
with `gpt-4o-mini`; swap in any vision-capable model. If you prefer configuration over code, the
`VISION_API_URL` / `VISION_API_KEY` / `VISION_MODEL` variables do the same against any
OpenAI-compatible endpoint - see [Vision](/openai/configuration#vision-the-look-tool).

## The custom tool

The example also registers a `lookup_order` function tool:

```js theme={null}
const tools = [{
  name: "lookup_order",
  description: "Look up the status of a customer order by its order number.",
  parameters: {
    type: "object",
    properties: { orderNumber: { type: "string", description: "The order number, e.g. KO-1234." } },
    required: ["orderNumber"],
  },
  async handler({ orderNumber }, ctx) {
    // call your own backend here; the returned string goes to the agent
    return `Order ${orderNumber} shipped yesterday and arrives tomorrow.`;
  },
}];

startServer(cfg, undefined, describeFrame, { handleSignals: true, tools });
```

Ask the agent on a call "where is order KO-12?" and it calls your handler, then speaks the answer.
This is the pattern for wiring the agent to your own systems (CRM lookups, transfers, bookings);
remote **MCP servers** are the config-only alternative - see
[Extending the agent's tools](https://komaa-com.github.io/openai-msteams-bridge/extending-tools/).

## From example to your own project

Depend on the published package instead of the local checkout:

```bash theme={null}
npm install @komaa/openai-msteams-bridge
```

Then copy the example's `index.mjs` as your starting point. The
[library API](https://komaa-com.github.io/openai-msteams-bridge/library-api/) documents the
programmatic surface (config, server options, tools, hooks).

## If something does not work

* **WebSocket rejected with `401`** - `WORKER_SHARED_SECRET` does not match the secret in StandIn.
* **Bot joins the call but stays silent** - the Agent voice URL is unreachable from the internet or
  points at the wrong port; re-check the tunnel.
* **Call connects, then ends with `agent-unavailable`** - the OpenAI key is invalid, lacks Realtime
  access, or `OPENAI_REALTIME_MODEL` names a model your account cannot use; the log line carries the
  underlying error.
* More: [Troubleshooting](/troubleshooting).

## Links

<CardGroup cols={3}>
  <Card title="Example README" icon="github" href="https://github.com/komaa-com/openai-msteams-bridge/blob/main/examples/basic-bridge/README.md" />

  <Card title="Configuration" icon="sliders" href="/openai/configuration" />

  <Card title="Library API" icon="book" href="https://komaa-com.github.io/openai-msteams-bridge/library-api/" />
</CardGroup>
