BashBrainTeammatesPricingDevelopersGet a test key

ask

Send a job in plain words to Bash, the AI agent Vestra runs for you. The job can be for one of your users, for many users at once, or for your own company. Bash can reply right away, act in apps, call your tools, or start a task (a longer piece of work) on its own computer. You can follow every step as it happens.

Ask for one of your users

Name them with your own id. Their Bash is created the first time you do.

TypeScript
import { Vestra } from '@vestra/sdk';const vestra = new Vestra(); // reads VESTRA_API_KEY and VESTRA_BASE_URL const traveller = vestra.user('traveller-42'); const run = traveller.ask('My 6am flight got cancelled. What should I do?');for await (const event of run) {  if (event.type === 'task.started') console.log(`Working on: ${event.data.title ?? 'a task'}`);}console.log(await run.text());

Ask a follow-up

It goes to the same conversation.

TypeScript
console.log(await run.ask('Can I get a refund for it?').text());

Stop a reply and its work

This stops the reply and every task the conversation started.

TypeScript
const hotel = traveller.ask('Find me a hotel near the airport.');await hotel.stop();

Ask for your company

Leave the user out, and the job goes to your company's own Bash.

TypeScript
console.log(await vestra.ask('Write a two-line notice for our travellers about storm delays at Mumbai airport today.').text());

Ask for many users at once

Each user gets their own conversation. If Bash asks one of them a question, that item waits for the answer, and so does wait().

TypeScript
const affected = [  { id: 'traveller-42', city: 'Mumbai' },  { id: 'traveller-77', city: 'Pune' },];const batch = vestra.ask(affected.map((t) => ({ user: t.id, message: `A storm may delay flights from ${t.city} today. What should I do before I leave for the airport? Two lines, in general: no need to look up my booking.` })));console.log((await batch.wait()).counts);

Ask for one of your users

Name them with your own id. Their Bash is created the first time you do.

Python
from vestra import Vestravestra = Vestra()  # reads VESTRA_API_KEY and VESTRA_BASE_URL traveller = vestra.user("traveller-42") run = traveller.ask("My 6am flight got cancelled. What should I do?")for event in run:    if event.type == "task.started":        print(f"Working on: {event.data.get('title') or 'a task'}")print(run.text())

Ask a follow-up

It goes to the same conversation.

Python
print(run.ask("Can I get a refund for it?").text())

Stop a reply and its work

This stops the reply and every task the conversation started.

Python
hotel = traveller.ask("Find me a hotel near the airport.")hotel.stop()

Ask for your company

Leave the user out, and the job goes to your company's own Bash.

Python
print(vestra.ask("Write a two-line notice for our travellers about storm delays at Mumbai airport today.").text())

Ask for many users at once

Each user gets their own conversation. If Bash asks one of them a question, that item waits for the answer, and so does wait().

Python
affected = [{"id": "traveller-42", "city": "Mumbai"}, {"id": "traveller-77", "city": "Pune"}]batch = vestra.ask([{"user": t["id"], "message": f"A storm may delay flights from {t['city']} today. What should I do before I leave for the airport? Two lines, in general: no need to look up my booking."} for t in affected])print(batch.wait().counts)

Ask for one of your users

Approvals and questions appear right here in the terminal. Ctrl-C stops it.

CLI
vestra ask "My 6am flight got cancelled. What should I do?" --user traveller-42

Ask a follow-up

It goes to the same conversation.

CLI
vestra ask "Can I get a refund for it?" --user traveller-42 --conversation conv_8f2k

Stop a reply and its work

This stops the reply and every task the conversation started.

CLI
vestra ask --stop conv_8f2k

Ask for your company

Leave the user out, and the job goes to your company's own Bash.

CLI
vestra ask "Write a two-line notice for our travellers about storm delays at Mumbai airport today."

Ask for many users at once

Pass a CSV with one user id per line, and each user gets their own conversation. If Bash asks one of them a question, that item waits for the answer.

CLI
vestra ask "A storm may delay flights from Mumbai today. What should I do before I leave for the airport? Two lines, in general: no need to look up my booking." --users travellers.csv

Ask for one of your users

Their Bash is created the first time you name them. With stream: true, the response streams this turn's events as they happen, and ends when the turn is over.

HTTP
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. What should I do?",    "stream": true  }'

Ask a follow-up

It goes to the same conversation.

HTTP
curl -N "$VESTRA_BASE_URL/v1/conversations/conv_8f2k/messages" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "message": "Can I get a refund for it?", "stream": true }'

Stop a reply and its work

This stops the reply and every task the conversation started.

HTTP
curl -X POST "$VESTRA_BASE_URL/v1/conversations/conv_8f2k/stop" \  -H "Authorization: Bearer $VESTRA_API_KEY"

Ask for your company

Leave user_id out, and the job goes to your company's own Bash.

HTTP
curl "$VESTRA_BASE_URL/v1/conversations" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "message": "Write a two-line notice for our travellers about storm delays at Mumbai airport today." }'

Ask for many users at once

Each user gets their own conversation. If Bash asks one of them a question, that item waits for the answer.

HTTP
curl "$VESTRA_BASE_URL/v1/batches" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "items": [      { "user_id": "traveller-42", "message": "A storm may delay flights from Mumbai today. What should I do before I leave for the airport? Two lines, in general: no need to look up my booking." },      { "user_id": "traveller-77", "message": "A storm may delay flights from Pune today. What should I do before I leave for the airport? Two lines, in general: no need to look up my booking." }    ]  }'
MCP
{  "method": "tools/call",  "params": {    "name": "ask",    "arguments": {      "user": "traveller-42",      "message": "My 6am flight got cancelled. Get me on the next one."    }  }}

Good to know

Who the job is for

vestra.user(id) acts for one of your users, named by your own id. The id is 1 to 128 characters and starts with a letter or digit. The user is created the first time you name them. Leave the user out, and the job goes to your company's own Bash, the one that works for your company rather than for a user. Over HTTP, the field is user_id.

When the reply has no text

A turn is your message and Bash's reply to it. When Bash only starts a task and says nothing, the turn can end with an empty message.completed.text. The task's name is in the task.started event, in its data.title, and its result arrives with task.completed.

When a job is stopped or fails

When a job is stopped or fails, the turn ends first, before the events that end its tasks arrive. A stream: true call closes when the turn ends. Then task.stopped or task.failed follow on the conversation's stream, on GET /v1/events and on your webhooks.

Conversation status

A conversation is idle when nothing is happening, replying while Bash writes its reply, working while tasks it started are running, and waiting while Bash waits for you: an approval, an answer, a hand-off (a step only a person can do) or one of your tools. Each change arrives as a conversation.status event, with the turn's turn_id and turn_status.

Asking many users at once

Pass a list of { user, message } to ask many users at once. This is a batch (POST /v1/batches), and each item gets its own conversation. Items with no user run on your company's own Bash, at most 5 at a time.

What a job cost

run.cost() returns the conversation's billed cost once the run ends. cost_complete stays false while charges from the tasks it started can still arrive. For the itemized receipt, read the line items on usage.

The routes behind ask14 routes, each with every field in the API reference
POST/v1/conversationsAsk. Start a conversation, optionally with its first message and that message's options (context, attachments, output_schema, success_criteria, tools, hand_off). user_id names whose Bash it is, created on first use; leave it out for the company's own Bash. stream: true answers the first turn's events as text/event-stream and ends when the turn settles.Key scope: runtime (or all)
GET/v1/conversationsConversations, newest first: one user's with user_id, or every one of the company's without it (the company's own Bash shows user_id null).Key scope: runtime (or all)
GET/v1/conversations/{conversation_id}One conversation: status (idle, replying, working, waiting), its open work, its open approvals.Key scope: runtime (or all)
POST/v1/conversations/{conversation_id}/messagesSend a message. Options: context (data for this turn only), attachments, output_schema, success_criteria, tools, hand_off (auto or always). Answers the turn at once; Prefer: wait=N holds for the final answer; stream: true answers this turn's events as text/event-stream and ends when the turn settles.Key scope: runtime (or all)
GET/v1/conversations/{conversation_id}/messagesThe conversation, paged: your messages, Bash's replies, and summaries of the work it did.Key scope: runtime (or all)
POST/v1/conversations/{conversation_id}/stopStop the reply and every piece of work the conversation started.Key scope: runtime (or all)
GET/v1/tasksAll work across your users, or one user's with user_id. Filter by status, deployment, conversation or date too.Key scope: runtime (or all)
GET/v1/tasks/{task_id}Status, title, the progress list, the result (text, and data when an output_schema was set), the outcome, cost so far.Key scope: runtime (or all)
DELETE/v1/tasks/{task_id}Delete it and its outputs.Key scope: runtime (or all)
POST/v1/tasks/{task_id}/stopStop this piece of work only.Key scope: runtime (or all)
POST/v1/batchesStart a batch: a deployment, a list of items (user_id optional, message, context, metadata) and a concurrency. Each item is one conversation message with hand_off: always.Key scope: runtime (or all)
GET/v1/batches/{batch_id}Progress: counts by status, spend so far.Key scope: runtime (or all)
GET/v1/batches/{batch_id}/itemsEach item with its task id and result.Key scope: runtime (or all)
POST/v1/batches/{batch_id}/cancelStop starting new items; running ones finish.Key scope: runtime (or all)