BashBrainTeammatesPricingDevelopersGet a test key

Put Bash inside your app

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

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

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

TypeScript
// 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
# 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
# A preview of what your user sees. In your app, your server opens one per visit.vestra embed --user traveller-42
HTTP
# 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
{  "method": "tools/call",  "params": {    "name": "embed",    "arguments": {      "user": "traveller-42"    }  }}

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.

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

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

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

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

TypeScript
// 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
# 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
# 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
# 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
{  "method": "tools/call",  "params": {    "name": "watch",    "arguments": {      "user": "traveller-42",      "conversation": "conv_8f2k",      "control": true    }  }}

Or start a voice call

A call in the user's conversation (talk).

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.

TypeScript
// 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
# A call in the user's conversation.call = vestra.user("traveller-42").talk(conversation="conv_8f2k")print(call.url)
CLI
# A call in the user's conversation.vestra talk --user traveller-42 --conversation conv_8f2k
HTTP
# 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
{  "method": "tools/call",  "params": {    "name": "talk",    "arguments": { "user": "traveller-42", "conversation": "conv_8f2k" }  }}

End it

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

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

TypeScript
// When your user signs out, by the id you kept.await vestra.sessions.end('ses_8h2');
Python
# When your user signs out, by the id you kept.vestra.sessions.end("ses_8h2")
HTTP

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

# 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"
HTTP
# 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"
HTTP

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

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

The whole thing

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

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

TypeScript
import { Vestra } from '@vestra/sdk'; const vestra = new Vestra();const traveller = vestra.user('traveller-42'); // Open a sessionconst session = await traveller.embed();console.log(session.url); // Or show just its screenconst screen = await traveller.watch({  conversation: 'conv_8f2k',  control: true,}); // Or start a voice callconst call = await traveller.talk({ conversation: 'conv_8f2k' });console.log(screen.url, call.url); // End itawait vestra.sessions.end(session.id);
Python
from vestra import Vestra vestra = Vestra()traveller = vestra.user("traveller-42") # Open a sessionsession = traveller.embed()print(session.url) # Or show just its screenscreen = traveller.watch(conversation="conv_8f2k", control=True) # Or start a voice callcall = traveller.talk(conversation="conv_8f2k")print(screen.url, call.url) # End itvestra.sessions.end(session.id)
CLI
# Open a session: a preview of what your user seesvestra embed --user traveller-42 # Or show just its screenvestra watch --user traveller-42 --conversation conv_8f2k --control # Or start a voice callvestra talk --user traveller-42 --conversation conv_8f2k
HTTP
# Open a sessioncurl "$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 screencurl "$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 callcurl "$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 itcurl -X DELETE "$VESTRA_BASE_URL/v1/sessions/ses_8h2" \  -H "Authorization: Bearer $VESTRA_API_KEY"
HTTP

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

# Open a sessioncurl "$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 screencurl "$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 callcurl "$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 itcurl -X DELETE "$VESTRA_BASE_URL/v1/sessions/ses_8h2" \  -H "Authorization: Bearer $VESTRA_API_KEY"