# Build a travel help desk in 15 minutes

> Describe the job, test it, ship it and ask it, with your own booking lookup: the path a real run took.

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](https://vestra.ai/developers/quickstart.md) shows both.

About 15 minutes. It uses [`init`](https://vestra.ai/developers/init.md), [`test`](https://vestra.ai/developers/test.md), [`deploy`](https://vestra.ai/developers/deploy.md), [`ask`](https://vestra.ai/developers/ask.md).

## 1. Describe the job

Takes 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`.

**TypeScript**

```ts
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**

```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**

```bash
# 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**

```bash
# 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**

```json
{
  "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"
    }
  }
}
```

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

The CLI prints:

```text
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.
```

## 2. 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`](https://vestra.ai/developers/test.md)).

`vestra.yaml`:

```yaml
# yaml-language-server: $schema=https://vestra.ai/developers/vestra.schema.json

name: travel-help
instructions: |-
  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: auto
model: default
tools:
  - 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: true
approvals:
  - payment
  - booking
  - cancellation
limits:
  per_task_usd: 2
tests:
  - 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
```

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

## 3. Test it

Takes 3 minutes.

Play the tests against the draft, in test mode: no real user sees it ([`test`](https://vestra.ai/developers/test.md)).

**TypeScript**

```ts
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**

```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**

```bash
# Play the tests in vestra.yaml against the draft, in test mode. A failed test says why.
vestra test
```

**HTTP**

```bash
# 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**

```json
{
  "method": "tools/call",
  "params": {
    "name": "test",
    "arguments": { "deployment": "travel-help" }
  }
}
```

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

The CLI prints:

```text
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.
```

## 4. Say how your references look

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

## 5. Ship it

Takes 3 minutes.

Deploy makes a new version, plays its tests, and promotes it when they pass ([`deploy`](https://vestra.ai/developers/deploy.md)). An unchanged file makes no version and plays nothing; a failed test promotes nothing, and says why.

**TypeScript**

```ts
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**

```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**

```bash
# A new version from vestra.yaml. It names the test run that promotes it: follow that run to its verdict.
vestra deploy
vestra events trun_5kd2
```

**HTTP**

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

**MCP**

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

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

The CLI prints:

```text
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.
```

## 6. Answer get_booking

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

`tools.mjs`:

```ts
// 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',
      },
    ],
  };
}
```

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

## 7. Ask it

Takes 1 minute.

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

**TypeScript**

```ts
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**

```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**

```bash
# 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**

```bash
# 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" }] } }'
```

**MCP**

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.

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

The CLI prints:

```text
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
```

## 8. 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](https://vestra.ai/developers/guides/testing.md)).

**TypeScript**

```ts
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_booking
const vestra = new Vestra({
  tools: {
    get_booking: (input: { booking_reference: string }) =>
      backend.booking(input.booking_reference),
  },
});

// Describe the job
const 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 it
const 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 it
const 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 it
const 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**

```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_booking
def get_booking(booking_reference):
    return lookup_booking(booking_reference)


vestra = Vestra(tools={"get_booking": get_booking})

# Describe the job
name = "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 it
tested = vestra.test("vestra.yaml")
print(tested.outcome)
for result in tested.results:
    if result.verdict != "passed":
        print(f"{result.scenario.name}: {result.reason}")

# Ship it
applied = 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 it
traveller = vestra.user("traveller-42")
print(traveller.ask("Asha Rao here. When does my flight on TN-48213 leave?", deployment=name).text())
```

**CLI**

```bash
# 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 it
vestra test

# Ship it
vestra deploy

# Ask it: tools.mjs answers get_booking
vestra ask "Asha Rao here. When does my flight on TN-48213 leave?" --user traveller-42 --deployment travel-help --tools ./tools.mjs
```

**HTTP**

```bash
# 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 it
curl -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 for
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" }] } }
    }]
  }'

# Ask it, then answer get_booking
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 }'

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" }] } }'
```

**MCP**

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

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

Next: [Give Bash your tools](https://vestra.ai/developers/guides/your-tools.md)
