# Put Bash inside your app

> Your signed-in user talks to Bash in your own page: chat, approvals, its screen and voice, from a session your server opens.

Your user talks to Bash without leaving your app. Your server opens a session for them, and your page shows it.

About 10 minutes. It uses [`embed`](https://vestra.ai/developers/embed.md), [`watch`](https://vestra.ai/developers/watch.md), [`talk`](https://vestra.ai/developers/talk.md).

## 1. Open a session

On your server, for the user signed in to your app. Its token lasts 15 minutes and reaches only that user ([`embed`](https://vestra.ai/developers/embed.md)).

**TypeScript**

```ts
// On your server, for the user signed in to your app.
const session = await vestra.user('traveller-42').embed();

// Hand session.url to your page, for its iframe.
console.log(session.url, session.expires_at);
```

**Python**

```python
# On your server, for the user signed in to your app.
session = vestra.user("traveller-42").embed()

# Hand session.url to your page, for its iframe.
print(session.url, session.expires_at)
```

**CLI**

```bash
# A preview of what your user sees. In your app, your server opens one per visit.
vestra embed --user traveller-42
```

**HTTP**

```bash
# On your server, for the user signed in to your app.
curl "$VESTRA_BASE_URL/v1/sessions" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "chat": true, "screen": "view", "voice": true }'
```

**MCP**

```json
{
  "method": "tools/call",
  "params": {
    "name": "embed",
    "arguments": {
      "user": "traveller-42"
    }
  }
}
```

**What happens:** Bash answers with a `url` for this user, and `session.started` reaches your webhooks.

## 2. Show it in your page

Put the `url` in an iframe with `allow="microphone"`, so voice works. Name the pages that may frame it in `allowed_embed_origins`, with `PATCH /v1/org`.

`embed.html`:

```html
<!-- In your page: the url your server opened for this user. -->
<iframe src="{{ session.url }}" allow="microphone"
  title="Travel help" style="width: 100%; height: 640px; border: 0">
</iframe>
```

**What happens:** Your user chats, approves in place and watches Bash work, with no Vestra sign-in.

## 3. Or show just its screen

For the screen alone, in a tab or a support console, opened in the user's conversation. With control, a person can take the wheel and give it back ([`watch`](https://vestra.ai/developers/watch.md)).

**TypeScript**

```ts
// Its screen alone, in the user's conversation.
// With control, a person may take the wheel.
const traveller = vestra.user('traveller-42');
const screen = await traveller.watch({
  conversation: 'conv_8f2k',
  control: true,
});
console.log(screen.url);
```

**Python**

```python
# Its screen alone, in the user's conversation.
# With control, a person may take the wheel.
screen = vestra.user("traveller-42").watch(
    conversation="conv_8f2k", control=True
)
print(screen.url)
```

**CLI**

```bash
# Its screen alone, in the user's conversation. With --control, whoever opens it may take the wheel.
vestra watch --user traveller-42 --conversation conv_8f2k --control
```

**HTTP**

```bash
# Its screen alone, in the user's conversation. With "control", a person may take the wheel.
curl "$VESTRA_BASE_URL/v1/sessions" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "conversation_id": "conv_8f2k", "screen": "control" }'
```

**MCP**

```json
{
  "method": "tools/call",
  "params": {
    "name": "watch",
    "arguments": {
      "user": "traveller-42",
      "conversation": "conv_8f2k",
      "control": true
    }
  }
}
```

**What happens:** The page shows Bash's computer, live, and `screen.control` on the conversation's stream says who is driving.

## 4. Or start a voice call

A call in the user's conversation ([`talk`](https://vestra.ai/developers/talk.md)).

**TypeScript**

```ts
// A call in the user's conversation.
const traveller = vestra.user('traveller-42');
const call = await traveller.talk({ conversation: 'conv_8f2k' });
console.log(call.url);
```

**Python**

```python
# A call in the user's conversation.
call = vestra.user("traveller-42").talk(conversation="conv_8f2k")
print(call.url)
```

**CLI**

```bash
# A call in the user's conversation.
vestra talk --user traveller-42 --conversation conv_8f2k
```

**HTTP**

```bash
# A call in the user's conversation.
curl "$VESTRA_BASE_URL/v1/sessions" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "conversation_id": "conv_8f2k", "voice": true }'
```

**MCP**

```json
{
  "method": "tools/call",
  "params": {
    "name": "talk",
    "arguments": { "user": "traveller-42", "conversation": "conv_8f2k" }
  }
}
```

**What happens:** The page joins the call, and what is said lands in the conversation as text. When it ends, `session.ended` carries the transcript.

## 5. End it

When your user signs out, end their session by its id.

**TypeScript**

```ts
// When your user signs out, by the id you kept.
await vestra.sessions.end('ses_8h2');
```

**Python**

```python
# When your user signs out, by the id you kept.
vestra.sessions.end("ses_8h2")
```

**CLI**

The CLI's sessions are previews, good for 15 minutes. End one from your server, with an SDK or HTTP.

**HTTP**

```bash
# When your user signs out, by the id you kept.
curl -X DELETE "$VESTRA_BASE_URL/v1/sessions/ses_8h2" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**

Sessions end from your server, with an SDK or HTTP.

**What happens:** Its token stops working at once, its streams close, and `session.ended` says so.

## 6. The whole thing

Open, show, call and end, in one program.

**TypeScript**

```ts
import { Vestra } from '@vestra/sdk';

const vestra = new Vestra();
const traveller = vestra.user('traveller-42');

// Open a session
const session = await traveller.embed();
console.log(session.url);

// Or show just its screen
const screen = await traveller.watch({
  conversation: 'conv_8f2k',
  control: true,
});

// Or start a voice call
const call = await traveller.talk({ conversation: 'conv_8f2k' });
console.log(screen.url, call.url);

// End it
await vestra.sessions.end(session.id);
```

**Python**

```python
from vestra import Vestra

vestra = Vestra()
traveller = vestra.user("traveller-42")

# Open a session
session = traveller.embed()
print(session.url)

# Or show just its screen
screen = traveller.watch(conversation="conv_8f2k", control=True)

# Or start a voice call
call = traveller.talk(conversation="conv_8f2k")
print(screen.url, call.url)

# End it
vestra.sessions.end(session.id)
```

**CLI**

```bash
# Open a session: a preview of what your user sees
vestra embed --user traveller-42

# Or show just its screen
vestra watch --user traveller-42 --conversation conv_8f2k --control

# Or start a voice call
vestra talk --user traveller-42 --conversation conv_8f2k
```

**HTTP**

```bash
# Open a session
curl "$VESTRA_BASE_URL/v1/sessions" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "chat": true, "screen": "view", "voice": true }'

# Or show just its screen
curl "$VESTRA_BASE_URL/v1/sessions" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "conversation_id": "conv_8f2k", "screen": "control" }'

# Or start a voice call
curl "$VESTRA_BASE_URL/v1/sessions" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "conversation_id": "conv_8f2k", "voice": true }'

# End it
curl -X DELETE "$VESTRA_BASE_URL/v1/sessions/ses_8h2" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**

An agent opens each with the `embed`, `watch` and `talk` tools. Ending one stays with your server.

**What happens:** One session per visit, and your user never leaves your app.

Next: [Test before your users see it](https://vestra.ai/developers/guides/testing.md)
