Give every user an agent,right inside your app.

Your users ask in plain words. Bash does the job through your own API and checks with them before it books, pays or cancels anything. You write the functions, we run the agent.

npm install ./vestra-sdk-0.1.7.tgz
server.ts
import { Vestra } from '@vestra/sdk';
// get_booking is your API. It only reads, so Bash calls it freely.
const vestra = new Vestra({ tools: { get_booking } });

const run = vestra.user('traveller-42')
  .ask('My 6am flight got cancelled. Get me on the next one.');

for await (const event of run) {
  // Bash stops here for your user's yes
  const asked = event.interaction;
  if (asked?.type !== 'approval') continue;
  const yes = await app.confirm(event.user, asked.summary);
  await event.answer({ approve: yes, present_user: true });
}
console.log(await run.text());
from vestra import Vestra

# get_booking is your API. It only reads, so Bash calls it freely.
vestra = Vestra(tools={"get_booking": lookup_booking})
run = vestra.user("traveller-42").ask("My 6am flight got cancelled. Get me on the next one.")

for event in run:
    # Bash stops here for your user's yes
    asked = event.interaction
    if asked and asked.type == "approval":
        yes = app.confirm(event.user, asked.summary)
        event.answer(approve=yes, present_user=True)

print(run.text())
# Paste your test key once; vestra keeps it on this computer.
$ vestra login

# tools.mjs exports a function per tool your setup declares: get_booking here.
$ vestra ask "My 6am flight got cancelled. Get me on the next one." --user traveller-42 --tools ./tools.mjs

# Bash asks here before it pays. From a script or another terminal, answer by the id it names.
$ vestra answer int_3j9 --approve --present-user
# stream: true answers this turn's events as they happen.
curl -N "$VESTRA_BASE_URL/v1/conversations" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "message": "My 6am flight got cancelled. Get me on the next one.", "stream": true }'

# interaction.created: Bash stops here for your user's yes. Show it, then answer with what they chose.
curl "$VESTRA_BASE_URL/v1/interactions/int_3j9/answer" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -d '{ "approve": true, "present_user": true }'
// mcp.json: any MCP client, your key as the bearer
{ "mcpServers": { "vestra": { "type": "http", "url": "https://api.vestra.ai/v1/mcp",
    "headers": { "Authorization": "Bearer vk_test_..." } } } }

// then the ask, as a tool call
{ "method": "tools/call", "params": { "name": "ask",
    "arguments": { "user": "traveller-42", "message": "My 6am flight got cancelled. Get me on the next one." } } }

// and the yes, once your user saw it
{ "method": "tools/call", "params": { "name": "answer", "arguments": { "interaction": "int_3j9", "approve": true, "present_user": true } } }

What traveller-42 sees in your app

Bash asks traveller-42waits for a yes

Book the 09:15 tomorrow, Delhi to Pune, so you land before your meeting.

Flight
6E 2171, departs 09:15, gate B4
Fare
Rs 5,420 on the card ending 4412
Refund
Rs 4,980 from the cancelled 6E 2134, back in 5 days

You're on the 09:15 tomorrow, seat 14C. Your refund of Rs 4,980 is on its way.

Your code grows a line at a time.

One traveller, from the first ask to your launch. Each step adds a line or two, and shows what Bash does with it.

server.tsvestra.yaml

$ vestra test

Testing travel-help version 3.

passed: (all 5)

$ vestra deploy --live

Bash, for traveller-42Their own Bash, made just now
test mode
My 6am flight got cancelled. Get me on the next one.

task.startedRebook the flight
Bash needs traveller-42's okayShown in your app, answered by them
Booking

Book the 09:15 to Mumbai, seat 14C?

The fare difference is ₹1,200. Nothing is booked until they say yes.

Booked. traveller-42 said yes.interaction.resolved, approve: true
Bash called get_booking
{ ref: 'TN-48213' }
Your function answered, on your server
{ from: 'DEL', to: 'BOM', leaves: '06:10', status: 'cancelled' }
Bash carried on
The next flight to Mumbai is the 09:15. Shall I book it?

Bash sees what your function returns, and nothing else from your systems.

travel-help, version 3Tested in test mode, judged against each expect
  • Everyday baggage question
  • Flight time with no reference
  • Cancellation request with approval
  • Delayed flight question
  • Pressure to promise a refund
Testing…vestra deploy --live

Guide 1 walks this path with your own key, and it ran end to end on our staging servers on 1 October. Open guide 1

Five doors. The same ask.

TypeScript, Python, the CLI, plain HTTP or MCP. Every door takes the same words and brings back the same events.

ask.ts

What comes back

Bashfor traveller-42

  • conversation.status
  • message.delta
  • task.started
  • interaction.created
The same reply and the same event names, whichever door you use.

One card. Eighteen commands.

Learn these and you can guess the rest. Every door, guide and reference is organised by them.

The Vestra cardOpen the card

Every command has its own page, with all five doors.

Give every customer
a Bash of their own.

Get a test key, follow the quickstart, and watch your first yes come back.