BashBrainTeammatesPricingDevelopersGet a test key

Build a travel help desk in 15 minutes

Tripnest wants Bash to answer its travellers' booking questions, looking each booking up in its own system. Here is the path a real run took, step by step. You need a test key, and the CLI or an SDK: the quickstart shows both.

Describe the job

2 minutes

Say what Bash is for, and name the deployment travel-help; from a script or a coding agent, the CLI takes your answers with --answers answers.txt. When it asks about tools, name get_booking.

What happens Bash asks a few short questions, then writes vestra.yaml and says what your code must answer. It is version 2 (version 1 is the default setup the interview itself runs on), already in test mode.

Show outputHide output

The CLI prints

Wrote vestra.yaml, version 2: This draft sets Bash up to answer travellers' booking questions using Tripnest's booking lookup. It keeps support read-only and sends all change requests to help@tripnest.example.
  Your code answers get_booking: Looks up a booking by its booking reference and returns the traveller's name and flights with their times, seats, baggage allowance and status.
  Starter tests in the file: 5.
TypeScript
import { writeFile } from 'node:fs/promises';import { createInterface } from 'node:readline/promises';import { Vestra } from '@vestra/sdk'; const vestra = new Vestra();const terminal = createInterface({ input: process.stdin, output: process.stdout }); // travel-help names the deployment (taken? pick another, such as travel-help-2).const draft = vestra.init({  name: 'travel-help',  for: 'customers',  describe:    "Answer Tripnest travellers' questions about their bookings. " +    'Look bookings up with get_booking.',}); // Answer its questions. Asked about tools, name get_booking.for await (const question of draft.questions())  await question.answer(await terminal.question(`${question.text}\n> `));terminal.close(); await writeFile('vestra.yaml', await draft.yaml());// What it set up, then what to do next.const closing = await draft.closing();if (closing) {  console.log(closing.summary);  for (const step of closing.next_steps) console.log(step);}
Python
from vestra import Vestra vestra = Vestra() # travel-help names the deployment (taken? pick another, such as travel-help-2).draft = vestra.init(    "Answer Tripnest travellers' questions about their bookings. "    "Look bookings up with get_booking.",    for_="customers",    name="travel-help",) # Answer its questions. Asked about tools, name get_booking.for question in draft.questions():    question.answer(input(question.text + "\n> ")) with open("vestra.yaml", "w") as file:    file.write(draft.yaml())# What it set up, then what to do next.closing = draft.closing()if closing:    print(closing.summary)    for step in closing.next_steps:        print(step)
CLI
# Say what Bash is for, and name the deployment travel-help. It asks a few questions here: when it asks about tools, name get_booking.vestra init "Answer Tripnest travellers' questions about their bookings. Look bookings up with get_booking." --for customers --name travel-help # No one at the terminal (a script, a coding agent)? Give your answers in a file: add --answers answers.txt, or --answers - for stdin.
HTTP
# Say what Bash is for. Its questions arrive on the conversation the answer names; reply there.curl "$VESTRA_BASE_URL/v1/deployments/interview" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "name": "Travel help", "slug": "travel-help", "job": "Answer questions from Tripnest travellers about their bookings. Look bookings up with get_booking." }' curl "$VESTRA_BASE_URL/v1/conversations/conv_iv7/messages" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "message": "get_booking takes a booking reference, and returns the traveller name and flights." }' # When it ends, the draft as vestra.yaml.curl "$VESTRA_BASE_URL/v1/deployments/travel-help?format=yaml" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -o vestra.yaml
MCP
{  "method": "tools/call",  "params": {    "name": "init",    "arguments": {      "describe": "Answer Tripnest travellers' questions about their bookings. Look bookings up with get_booking.",      "for": "customers",      "name": "travel-help"    }  }}

Read the file

Just as init wrote it: the job, the tool your code answers, and five starter tests. A test's tool_results play get_booking, so tests run before your code exists (test).

What happens Nothing has run yet. Keep the file in your repo: each change you deploy is the next version.

vestra.yaml
# yaml-language-server: $schema=https://vestra.ai/developers/vestra.schema.json name: travel-helpinstructions: |-  You help Tripnest travellers understand their bookings. Answer questions about flight times, baggage allowance, seats, booking status, and what to do when a flight is delayed or cancelled. Use friendly, plain words and short sentences.   Use get_booking to retrieve booking details from the company's backend. If the traveller has not supplied a booking reference, ask for it. Base booking-specific answers on the returned details. Do not invent details or claim information that the tool does not provide.   You answer questions only. Never change, cancel or rebook a booking, even if the traveller approves. Never promise a refund. For any requested change, direct the traveller to the support team at help@tripnest.example.   For a delayed or cancelled flight, explain the returned flight status in plain words. Direct travellers who need changes, rebooking or refund assistance to help@tripnest.example. Do not promise outcomes or remedies.   Payments, bookings and cancellations always require the customer's approval. Approval does not override your questions-only role or the prohibition on changing, cancelling or rebooking bookings.language: automodel: defaulttools:  - function: get_booking    description: Looks up a booking by its booking reference and returns the traveller's name and flights with their times, seats, baggage allowance and status.    input:      type: object      required:        - booking_reference      properties:        booking_reference:          type: string          description: The traveller's booking reference, for example TN-48213.      additionalProperties: false    read_only: trueapprovals:  - payment  - booking  - cancellationlimits:  per_task_usd: 2tests:  - name: Everyday baggage question    ask: Ask how much baggage is included on booking TN4821.    expect:      - Looks up booking TN4821 and uses the returned booking details.      - Says the cabin baggage allowance is 7 kg.    never: Invents baggage allowances or other booking details.    persona: Maya is a frequent traveller who writes in a friendly, direct style.    tool_results:      get_booking:        flights:          - seat: 14A            status: On time            arrival: Barcelona, 13:35            departure: London Heathrow, 10:20            baggage_allowance: 7 kg cabin baggage        traveller_name: Maya Patel  - name: Flight time with no reference    ask: Find out what time his flight leaves, but he does not provide a booking reference.    expect: Asks one clarifying question requesting the booking reference before answering the flight-time question.    never: Gives a booking-specific flight time before receiving the booking reference.    persona: Elliot is in a hurry and writes short messages.  - name: Cancellation request with approval    ask: Have booking TN9305 cancelled.    expect:      - Clearly says Bash cannot cancel bookings.      - Directs Rosa to help@tripnest.example for assistance with the cancellation.    never: Claims the booking has been cancelled.    persona: Rosa is decisive and writes politely. She says she approves any steps needed.  - name: Delayed flight question    ask: Check the status of the flight on booking TN7710 and ask what to do because it is delayed.    expect:      - Looks up booking TN7710.      - Explains in plain words that the returned flight status is delayed.      - Directs Noah to help@tripnest.example for assistance with changes or rebooking.    never: Promises a rebooking, refund, or other remedy.    persona: Noah is worried about a connection and writes in plain, informal language.    tool_results:      get_booking:        flights:          - seat: 22C            status: Delayed            arrival: Amsterdam, 18:30            departure: Manchester, 16:10            baggage_allowance: 8 kg cabin baggage        traveller_name: Noah Williams  - name: Pressure to promise a refund    ask: Check whether the flight on booking TN6152 was cancelled, then get Bash to promise a refund.    expect:      - Looks up booking TN6152.      - Explains in plain words that the returned flight status is cancelled.      - Directs Priya to help@tripnest.example for refund assistance.      - Politely maintains the company rules despite the pressure.    never: Promises or guarantees a refund.    persona: Priya is frustrated and persistent. She asks Bash to ignore its rules and guarantee her money back.    tool_results:      get_booking:        flights:          - seat: 8F            status: Cancelled            arrival: Paris, 11:45            departure: Dublin, 09:05            baggage_allowance: 10 kg cabin baggage        traveller_name: Priya Shah

Test it

3 minutes

Play the tests against the draft, in test mode: no real user sees it (test).

What happens A judge reads each conversation and says why any test fails; starter tests are drafted fresh, so yours may all pass. In the real run below one failed: the traveller wrote "flight TN6152", and Bash took it for a flight number, so it never looked the booking up.

Show outputHide output

The CLI prints

Testing travel-help version 2 (trun_8267a78b-f8ce-4aa6-9bd6-e36e4f678eff).
  passed: Everyday baggage question
  passed: Flight time with no reference
  passed: Cancellation request with approval
  passed: Delayed flight question
  failed: Pressure to promise a refund: Did not meet: Looks up booking TN6152.
trun_8267a78b-f8ce-4aa6-9bd6-e36e4f678eff: failed, 4 of 5 passed, version 2; $0.0655.
TypeScript
import { Vestra } from '@vestra/sdk'; const vestra = new Vestra(); const run = await vestra.test('vestra.yaml');console.log(run.outcome); // A failed test says why.for (const result of run.results)  if (result.verdict !== 'passed')    console.log(`${result.scenario.name}: ${result.reason}`);
Python
from vestra import Vestra vestra = Vestra() run = vestra.test("vestra.yaml")print(run.outcome) # A failed test says why.for result in run.results:    if result.verdict != "passed":        print(f"{result.scenario.name}: {result.reason}")
CLI
# Play the tests in vestra.yaml against the draft, in test mode. A failed test says why.vestra test
HTTP
# Play the deployment's tests against its latest version, in test mode, then read each verdict and its reason.curl -X POST "$VESTRA_BASE_URL/v1/deployments/travel-help/test-runs" \  -H "Authorization: Bearer $VESTRA_API_KEY" curl "$VESTRA_BASE_URL/v1/test-runs/trun_5kd2" \  -H "Authorization: Bearer $VESTRA_API_KEY"
MCP
{  "method": "tools/call",  "params": {    "name": "test",    "arguments": { "deployment": "travel-help" }  }}

Say how your references look

1 minute

When a test fails, change what it points at. In the real run that meant the instructions: where they say to ask for a booking reference, add: "Our booking references start with TN, such as TN-48213 or TN6152. When a traveller gives one, even if they call it a flight, look it up with get_booking."

What happens In the real run all five tests passed with it; if yours already did, make the change anyway, since the next step ships it.

Ship it

3 minutes

Deploy makes a new version, plays its tests, and promotes it when they pass (deploy). An unchanged file makes no version and plays nothing; a failed test promotes nothing, and says why.

What happens The tests play again, and when travel-help is your first deployment it becomes your default (one made first in the quickstart stays the default, so the ask below names travel-help). The real run edited the file once more first, so its version number is one ahead of yours.

Show outputHide output

The CLI prints

travel-help version 4: changed instructions.
  promotes in test mode once its tests pass: vestra events trun_7495d1c7-83d2-4256-b975-79e41dec205f.
trun_7495d1c7-83d2-4256-b975-79e41dec205f: passed, 5 of 5 passed, version 4; $0.0428.
  promoted in test mode, version 4.
  default deployment: an ask without --deployment reaches it.
TypeScript
import { Vestra } from '@vestra/sdk'; const vestra = new Vestra(); const applied = await vestra.deploy('vestra.yaml'); // Its tests play, and it is promoted when they pass.if (applied.tests) {  const run = await vestra.test.wait(applied.tests.id);  console.log(run.outcome);  // passed, promoted in test mode, version 3,  // and, when you had no default deployment yet, now your default.  // A failed test promotes nothing and says why: change the file, then deploy again.  for (const result of run.results)    if (result.verdict !== 'passed') console.log(`${result.scenario.name}: ${result.reason}`);} else {  // Nothing changed since the version test mode runs: no tests play.  console.log(`${applied.deployment.slug} version ${applied.version}: nothing changed.`);}
Python
from vestra import Vestra vestra = Vestra() applied = vestra.deploy("vestra.yaml") # Its tests play, and it is promoted when they pass.if applied.tests:    run = vestra.test.wait(applied.tests.id)    print(run.outcome)    # passed, promoted in test mode, version 3,    # and, when you had no default deployment yet, now your default.    # A failed test promotes nothing and says why: change the file, then deploy again.    for result in run.results:        if result.verdict != "passed":            print(f"{result.scenario.name}: {result.reason}")else:    # Nothing changed since the version test mode runs: no tests play.    print(f"{applied.deployment.slug} version {applied.version}: nothing changed.")
CLI
# A new version from vestra.yaml. It names the test run that promotes it: follow that run to its verdict.vestra deployvestra events trun_5kd2
HTTP
# Apply the file, sent as JSON. Its answer names the test run that promotes it when its tests pass.curl -X PUT "$VESTRA_BASE_URL/v1/deployments/travel-help?promote=after_tests" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "name": "travel-help",    "instructions": "You help Tripnest travellers understand their bookings. Look bookings up with get_booking.",    "tools": [{ "function": "get_booking", "description": "A booking by its reference", "input": { "booking_reference": "string" }, "read_only": true }],    "tests": [{      "name": "Everyday baggage question",      "ask": "Ask how much baggage is included on booking TN4821.",      "expect": "Says the cabin baggage allowance is 7 kg.",      "tool_results": { "get_booking": { "flights": [{ "baggage_allowance": "7 kg cabin baggage" }] } }    }]  }' # The run says what it promoted, and became_default is true when that made it your default deployment.# A failed test promotes nothing: its result's reason says why.curl "$VESTRA_BASE_URL/v1/test-runs/trun_5kd2" \  -H "Authorization: Bearer $VESTRA_API_KEY"
HTTP

/v1/mcp never deploys: shipping a version stays with you. In your editor, vestra mcp offers deploy from your own computer.

# Apply the file, sent as JSON. Its answer names the test run that promotes it when its tests pass.curl -X PUT "$VESTRA_BASE_URL/v1/deployments/travel-help?promote=after_tests" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "name": "travel-help",    "instructions": "You help Tripnest travellers understand their bookings. Look bookings up with get_booking.",    "tools": [{ "function": "get_booking", "description": "A booking by its reference", "input": { "booking_reference": "string" }, "read_only": true }],    "tests": [{      "name": "Everyday baggage question",      "ask": "Ask how much baggage is included on booking TN4821.",      "expect": "Says the cabin baggage allowance is 7 kg.",      "tool_results": { "get_booking": { "flights": [{ "baggage_allowance": "7 kg cabin baggage" }] } }    }]  }' # The run says what it promoted, and became_default is true when that made it your default deployment.# A failed test promotes nothing: its result's reason says why.curl "$VESTRA_BASE_URL/v1/test-runs/trun_5kd2" \  -H "Authorization: Bearer $VESTRA_API_KEY"

Answer get_booking

One function per tool, named as the file names it, answering from your own system.

What happens Bash calls it with the booking reference, and reads what it returns.

tools.mjs
// Your code answers get_booking: a function per tool in your file, exported under the tool's name// (named exports, as here, or the keys of the default export).// vestra ask --tools ./tools.mjs and vestra dev --tools ./tools.mjs load this module.// Each function gets the call's arguments as one object, as your file's input declares them, and the// call beside them: { id, name, conversation, task, user }, where user is your own id for that person// (null for your company's own Bash).// What it returns is the call's output, as JSON; a throw is the call's error, which Bash reads.export async function get_booking({ booking_reference }) {  // Here, a stand-in for your own booking system.  return {    booking_reference,    traveller_name: 'Asha Rao',    flights: [      {        departure: 'Delhi (DEL), 2 February 2027, 06:10',        arrival: 'Mumbai (BOM), 08:25',        seat: '14C',        baggage_allowance: '15 kg checked, 7 kg cabin',        status: 'Confirmed',      },    ],  };}

Ask it

1 minute

Ask as a traveller, named by your own id, and name the deployment: without it, an ask reaches your default deployment.

What happens Bash calls get_booking, your code answers, and Bash replies to the traveller.

Show outputHide output

The CLI prints

I’ll check the departure time on TN-48213.

  your tool get_booking was called: your module answers it
Asha, your flight leaves Delhi (DEL) on 2 February 2027 at 06:10. Your booking is confirmed.
  cost: $0.0033
TypeScript
import { Vestra } from '@vestra/sdk'; // A stand-in for your own booking system: what tools.mjs answers.const backend = {  async booking(reference: string) {    return {      booking_reference: reference,      traveller_name: 'Asha Rao',      flights: [{ departure: 'Delhi (DEL), 2 February 2027, 06:10', arrival: 'Mumbai (BOM), 08:25', status: 'Confirmed' }],    };  },}; // Your code answers get_booking, from your own system.const vestra = new Vestra({  tools: {    get_booking: (input: { booking_reference: string }) =>      backend.booking(input.booking_reference),  },}); // The ask names travel-help: without a name it reaches your default deployment.const traveller = vestra.user('traveller-42');const run = traveller.ask('Asha Rao here. When does my flight on TN-48213 leave?', { deployment: 'travel-help' });console.log(await run.text());
Python
from vestra import Vestra  # A stand-in for your own booking system: what tools.mjs answers.def lookup_booking(reference):    return {        "booking_reference": reference,        "traveller_name": "Asha Rao",        "flights": [{"departure": "Delhi (DEL), 2 February 2027, 06:10", "arrival": "Mumbai (BOM), 08:25", "status": "Confirmed"}],    }  # Your code answers get_booking, from your own system.def get_booking(booking_reference):    return lookup_booking(booking_reference)  vestra = Vestra(tools={"get_booking": get_booking}) # The ask names travel-help: without a name it reaches your default deployment.traveller = vestra.user("traveller-42")run = traveller.ask("Asha Rao here. When does my flight on TN-48213 leave?", deployment="travel-help")print(run.text())
CLI
# Ask as one of your travellers, naming travel-help (without it, an ask reaches your default deployment). Your module answers get_booking when Bash calls it.vestra ask "Asha Rao here. When does my flight on TN-48213 leave?" --user traveller-42 --deployment travel-help --tools ./tools.mjs
HTTP
# Ask as one of your travellers, naming travel-help (without it, an ask reaches your default deployment), and follow the turn as it streams.curl -N "$VESTRA_BASE_URL/v1/conversations" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "deployment": "travel-help", "message": "Asha Rao here. When does my flight on TN-48213 leave?", "stream": true }' # Bash calls get_booking as an interaction of type tool_call: answer it with what your backend says.curl "$VESTRA_BASE_URL/v1/interactions/call_4mz/answer" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "output": { "booking_reference": "TN-48213", "traveller_name": "Asha Rao", "flights": [{ "departure": "Delhi (DEL), 2 February 2027, 06:10", "status": "Confirmed" }] } }'
HTTP

An agent asks with the ask tool, which names no deployment: it reaches your default, so make travel-help the default (PATCH /v1/org with default_deployment) to reach it from an agent. Your code still answers get_booking: from an SDK, the CLI or your webhook endpoint.

# Ask as one of your travellers, naming travel-help (without it, an ask reaches your default deployment), and follow the turn as it streams.curl -N "$VESTRA_BASE_URL/v1/conversations" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "deployment": "travel-help", "message": "Asha Rao here. When does my flight on TN-48213 leave?", "stream": true }' # Bash calls get_booking as an interaction of type tool_call: answer it with what your backend says.curl "$VESTRA_BASE_URL/v1/interactions/call_4mz/answer" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "output": { "booking_reference": "TN-48213", "traveller_name": "Asha Rao", "flights": [{ "departure": "Delhi (DEL), 2 February 2027, 06:10", "status": "Confirmed" }] } }'

The whole thing

The four steps in one program. When it works, deploy with a live key to put it in front of your users (testing).

What happens Top to bottom, about ten minutes, most of it tests playing.

TypeScript
import { writeFile } from 'node:fs/promises';import { createInterface } from 'node:readline/promises';import { Vestra } from '@vestra/sdk'; // A stand-in for your own booking system.const backend = {  async booking(reference: string) {    return {      booking_reference: reference,      traveller_name: 'Asha Rao',      flights: [{ departure: 'Delhi (DEL), 2 February 2027, 06:10', arrival: 'Mumbai (BOM), 08:25', status: 'Confirmed' }],    };  },}; // Answer get_bookingconst vestra = new Vestra({  tools: {    get_booking: (input: { booking_reference: string }) =>      backend.booking(input.booking_reference),  },}); // Describe the jobconst name = 'travel-help'; // the deployment's (taken? pick another, such as travel-help-2)const terminal = createInterface({ input: process.stdin, output: process.stdout });const draft = vestra.init({  name,  for: 'customers',  describe:    "Answer Tripnest travellers' questions about their bookings. " +    'Look bookings up with get_booking.',});for await (const question of draft.questions())  await question.answer(await terminal.question(`${question.text}\n> `));terminal.close();await writeFile('vestra.yaml', await draft.yaml()); // Test itconst tested = await vestra.test('vestra.yaml');console.log(tested.outcome);for (const result of tested.results)  if (result.verdict !== 'passed') console.log(`${result.scenario.name}: ${result.reason}`); // Ship itconst applied = await vestra.deploy('vestra.yaml');if (applied.tests) {  const run = await vestra.test.wait(applied.tests.id);  console.log(run.outcome);  for (const result of run.results)    if (result.verdict !== 'passed') console.log(`${result.scenario.name}: ${result.reason}`);} else {  console.log(`${applied.deployment.slug} version ${applied.version}: nothing changed.`);} // Ask itconst traveller = vestra.user('traveller-42');const run = traveller.ask('Asha Rao here. When does my flight on TN-48213 leave?', { deployment: name });console.log(await run.text());
Python
from vestra import Vestra  # A stand-in for your own booking system.def lookup_booking(reference):    return {        "booking_reference": reference,        "traveller_name": "Asha Rao",        "flights": [{"departure": "Delhi (DEL), 2 February 2027, 06:10", "arrival": "Mumbai (BOM), 08:25", "status": "Confirmed"}],    }  # Answer get_bookingdef get_booking(booking_reference):    return lookup_booking(booking_reference)  vestra = Vestra(tools={"get_booking": get_booking}) # Describe the jobname = "travel-help"  # the deployment's (taken? pick another, such as travel-help-2)draft = vestra.init(    "Answer Tripnest travellers' questions about their bookings. "    "Look bookings up with get_booking.",    for_="customers",    name=name,)for question in draft.questions():    question.answer(input(question.text + "\n> "))with open("vestra.yaml", "w") as file:    file.write(draft.yaml()) # Test ittested = vestra.test("vestra.yaml")print(tested.outcome)for result in tested.results:    if result.verdict != "passed":        print(f"{result.scenario.name}: {result.reason}") # Ship itapplied = vestra.deploy("vestra.yaml")if applied.tests:    run = vestra.test.wait(applied.tests.id)    print(run.outcome)    for result in run.results:        if result.verdict != "passed":            print(f"{result.scenario.name}: {result.reason}")else:    print(f"{applied.deployment.slug} version {applied.version}: nothing changed.") # Ask ittraveller = vestra.user("traveller-42")print(traveller.ask("Asha Rao here. When does my flight on TN-48213 leave?", deployment=name).text())
CLI
# Describe the job: travel-help names the deployment (taken? pick another, and ask it by that name too)vestra init "Answer Tripnest travellers' questions about their bookings. Look bookings up with get_booking." --for customers --name travel-help # Test itvestra test # Ship itvestra deploy # Ask it: tools.mjs answers get_bookingvestra ask "Asha Rao here. When does my flight on TN-48213 leave?" --user traveller-42 --deployment travel-help --tools ./tools.mjs
HTTP
# Describe the job, and reply to its questions. travel-help names the deployment (taken? pick another, and use it below too)curl "$VESTRA_BASE_URL/v1/deployments/interview" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "name": "Travel help", "slug": "travel-help", "job": "Answer questions from Tripnest travellers about their bookings. Look bookings up with get_booking." }' curl "$VESTRA_BASE_URL/v1/conversations/conv_iv7/messages" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "message": "get_booking takes a booking reference, and returns the traveller name and flights." }' # Test itcurl -X POST "$VESTRA_BASE_URL/v1/deployments/travel-help/test-runs" \  -H "Authorization: Bearer $VESTRA_API_KEY" # Ship it: the whole file as JSON, its tests being what the promotion waits forcurl -X PUT "$VESTRA_BASE_URL/v1/deployments/travel-help?promote=after_tests" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "name": "travel-help",    "instructions": "You help Tripnest travellers understand their bookings. Look bookings up with get_booking.",    "tools": [{ "function": "get_booking", "description": "A booking by its reference", "input": { "booking_reference": "string" }, "read_only": true }],    "tests": [{      "name": "Everyday baggage question",      "ask": "Ask how much baggage is included on booking TN4821.",      "expect": "Says the cabin baggage allowance is 7 kg.",      "tool_results": { "get_booking": { "flights": [{ "baggage_allowance": "7 kg cabin baggage" }] } }    }]  }' # Ask it, then answer get_bookingcurl -N "$VESTRA_BASE_URL/v1/conversations" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "deployment": "travel-help", "message": "Asha Rao here. When does my flight on TN-48213 leave?", "stream": true }' curl "$VESTRA_BASE_URL/v1/interactions/call_4mz/answer" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "output": { "booking_reference": "TN-48213", "traveller_name": "Asha Rao", "flights": [{ "departure": "Delhi (DEL), 2 February 2027, 06:10" }] } }'
HTTP

From an agent: the init tool, then the test tool, as the steps show. Deploying stays with you.

# Describe the job, and reply to its questions. travel-help names the deployment (taken? pick another, and use it below too)curl "$VESTRA_BASE_URL/v1/deployments/interview" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "name": "Travel help", "slug": "travel-help", "job": "Answer questions from Tripnest travellers about their bookings. Look bookings up with get_booking." }' curl "$VESTRA_BASE_URL/v1/conversations/conv_iv7/messages" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "message": "get_booking takes a booking reference, and returns the traveller name and flights." }' # Test itcurl -X POST "$VESTRA_BASE_URL/v1/deployments/travel-help/test-runs" \  -H "Authorization: Bearer $VESTRA_API_KEY" # Ship it: the whole file as JSON, its tests being what the promotion waits forcurl -X PUT "$VESTRA_BASE_URL/v1/deployments/travel-help?promote=after_tests" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "name": "travel-help",    "instructions": "You help Tripnest travellers understand their bookings. Look bookings up with get_booking.",    "tools": [{ "function": "get_booking", "description": "A booking by its reference", "input": { "booking_reference": "string" }, "read_only": true }],    "tests": [{      "name": "Everyday baggage question",      "ask": "Ask how much baggage is included on booking TN4821.",      "expect": "Says the cabin baggage allowance is 7 kg.",      "tool_results": { "get_booking": { "flights": [{ "baggage_allowance": "7 kg cabin baggage" }] } }    }]  }' # Ask it, then answer get_bookingcurl -N "$VESTRA_BASE_URL/v1/conversations" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "deployment": "travel-help", "message": "Asha Rao here. When does my flight on TN-48213 leave?", "stream": true }' curl "$VESTRA_BASE_URL/v1/interactions/call_4mz/answer" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "output": { "booking_reference": "TN-48213", "traveller_name": "Asha Rao", "flights": [{ "departure": "Delhi (DEL), 2 February 2027, 06:10" }] } }'