init
Start a new agent by describing its job in plain words. Bash, the AI agent Vestra runs for you, asks you a few short questions. Then it writes the first draft of your deployment, with starter tests. A deployment is your agent's setup: its instructions, the tools it may use and its limits. The questions arrive in a conversation, like any other reply from Bash.
Describe the job
Say in plain words what the agent should do, and for whom.
const draft = vestra.init({ for: 'customers', describe: 'Help Tripnest travellers fix broken trips: cancellations, delays, missed connections. Look bookings up with get_booking.',});Answer its questions
Bash asks a few short questions, then writes vestra.yaml with starter tests.
for await (const question of draft.questions()) await question.answer(await prompt(question.text));Save the draft to your repo
Publish it with deploy when you are ready.
await writeFile('vestra.yaml', await draft.yaml());Read the closing words
They say what the draft sets up, the tools your code must answer, the tests it wrote, and the next steps.
const closing = await draft.closing();for (const tool of closing?.tools ?? []) console.log(`Your code answers ${tool.name}: ${tool.description}`);closing?.next_steps.forEach((step, n) => console.log(`${n + 1}. ${step}`));Describe the job
Say in plain words what the agent should do, and for whom.
draft = vestra.init( "Help Tripnest travellers fix broken trips: cancellations, delays, missed connections. Look bookings up with get_booking.", for_="customers",)Answer its questions
Bash asks a few short questions, then writes vestra.yaml with starter tests.
for question in draft.questions(): question.answer(input(question.text + " "))Save the draft to your repo
Publish it with deploy when you are ready.
with open("vestra.yaml", "w") as file: file.write(draft.yaml())Read the closing words
They say what the draft sets up, the tools your code must answer, the tests it wrote, and the next steps.
closing = draft.closing()for tool in closing.tools if closing else []: print(f"Your code answers {tool.name}: {tool.description}")for n, step in enumerate(closing.next_steps if closing else [], 1): print(f"{n}. {step}")Describe the job
Bash asks a few short questions here in the terminal, then writes vestra.yaml with starter tests. It ends with its closing words: the tools your code must answer, and the next steps.
vestra init "Help Tripnest travellers fix broken trips: cancellations, delays, missed connections. Look bookings up with get_booking." --for customersRun init from a script or CI
Pass your answers as text, from a file or from stdin (-), so nobody needs to be at the terminal. --json prints the file's path and the closing words as JSON.
vestra init "Help Tripnest travellers fix broken trips: cancellations, delays, missed connections." \ --answers answers.txt --force --jsonDescribe the job
The interview runs as a conversation, so its questions arrive like any other reply.
curl "$VESTRA_BASE_URL/v1/deployments/interview" \ -H "Authorization: Bearer $VESTRA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Trip rescue", "slug": "trip-rescue", "job": "Help Tripnest travellers fix broken trips: cancellations, delays, missed connections. Look bookings up with get_booking." }'Answer its questions
Follow the conversation the response names, and reply in it.
curl -N "$VESTRA_BASE_URL/v1/conversations/conv_iv7/events" \ -H "Authorization: Bearer $VESTRA_API_KEY" curl "$VESTRA_BASE_URL/v1/conversations/conv_iv7/messages" \ -H "Authorization: Bearer $VESTRA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Ask first before paying a fare difference." }'Save the draft to your repo
When the interview ends, download the draft as vestra.yaml, starter tests included.
curl "$VESTRA_BASE_URL/v1/deployments/trip-rescue?format=yaml" \ -H "Authorization: Bearer $VESTRA_API_KEY" \ -o vestra.yamlRead the closing words
The draft version (the deployment's draft_version) has them in its interview field: what it set up, the tools your code must answer, the spending cap per task, the tests, and the next steps.
curl "$VESTRA_BASE_URL/v1/deployments/trip-rescue/versions/2" \ -H "Authorization: Bearer $VESTRA_API_KEY"{ "method": "tools/call", "params": { "name": "init", "arguments": { "describe": "Help Tripnest travellers fix broken trips: cancellations, delays, missed connections. Look bookings up with get_booking.", "for": "customers" } }}Good to know
What you get at the end
The draft becomes the deployment's next version, with its starter tests. The interview ends with its closing words: what the draft sets Bash up to do, the tools your code must answer, the spending cap per task, how many tests it wrote, and the next steps. The CLI prints them. You can also read them with draft.closing() in an SDK, or in the version's interview field over HTTP and MCP.
GET /v1/deployments/{name}?format=yaml returns the draft as vestra.yaml, ready to keep in your repo and publish with deploy. After you edit the file, vestra test saves your edit as a new version and tests it, without promoting it. vestra deploy then promotes it, which makes it the version your agent runs, without running the tests again.
Your own tools
When the job mentions one of your own tools, the file declares it: its name, what it does, its input and what it returns. Vestra never writes the tool's code. Your code runs the tool, with vestra dev --tools ./tools.mjs or with the SDK's tools option.
The interview asks what each tool returns. Answer with one example result that uses your code's own field names and value formats, with made-up values. Never paste a real record. For example: {customer: 'Maya Chen', due_back: '2026-10-04 18:00'}. The tool's description keeps this example. Each starter test that calls the tool gets tool_results copied from it, with no field, unit or time zone the example lacks. That way test can play the tool before your code exists, and answer the way your code will.
Some tools need your user's approval before they run. Such a tool also takes, as inputs, each value the user agrees to, such as charge_usd and new_due_back on extend_rental. The instructions say where each value comes from, such as daily_price_usd from get_rental. The approval shows every input, so the user always sees the price before saying yes. If none of your lookups returns such a value (because your code works out the price only as it acts), the interview asks for a tool that does, such as a quote, and marks it read_only. Bash never guesses a value for an approval. The closing words remind you that your code must check these values against its own before it acts.
Running init from a script or CI
With --answers, vestra init reads your answers as text instead of asking at the terminal. Use it from a coding agent or CI: vestra init "<the job>" --answers answers.txt, or --answers - to read them from stdin. If the interview asks a question after your answers run out, it is told to use the defaults. The draft holds only the rules your answers give, so include every rule the job needs. Init adds one rule without asking, which you can edit: how Bash checks who is asking before it shares a user's record.
Who the agent works for
Say in the job description whether this agent works for your users, inside your product, or for your own company. The interview runs in a conversation of its own, kept apart from your company's own Bash and its memory.
The routes behind init1 route, each with every field in the API reference
setup (or all)