# answer

> Reply when your agent needs an approval, an answer or your tool's result.

Reply when Bash needs something from a person or from your code. Each request is an interaction: an approval, a question, a request to connect an app, a hand-off (a step only a person can do), or a call to one of your tools. One route answers every kind. You send the body that fits its type.

## TypeScript

```ts
const run = vestra.user('traveller-42').ask('Rebook my cancelled flight.');

// Answer during a run. When Bash needs something, the run sends an interaction.created event.
for await (const event of run) {
  if (event.type !== 'interaction.created') continue;
  const asked = event.interaction; // what Bash waits on, read for you (null for a call to your own tool)
  if (asked?.type === 'approval') {
    // A payment goes through only when the person who saw the approval says yes.
    await event.answer({ approve: await app.confirm(event.user, asked.summary), present_user: true });
  }
  if (asked?.type === 'question' && asked.questions) {
    const answers: string[] = [];
    for (const q of asked.questions) answers.push(await app.choose(event.user, q.question, q.options.map((o) => o.label)));
    await event.answer({ answers });
  }
}

// Answer later, by id. List everything Bash is waiting on, from anywhere, and answer each one by its id.
const waiting = await vestra.answer.list({ user: 'traveller-42' });
for (const item of waiting.data) {
  if (item.type === 'handoff') await vestra.answer(item.id, { done: true });
}
```

## Python

```python
run = vestra.user("traveller-42").ask("Rebook my cancelled flight.")

# Answer during a run. When Bash needs something, the run sends an interaction.created event.
for event in run:
    if event.type != "interaction.created":
        continue
    asked = event.interaction  # what Bash waits on, read for you (None for a call to your own tool)
    if asked and asked.type == "approval":
        # A payment goes through only when the person who saw the approval says yes.
        event.answer(approve=app.confirm(event.user, asked.summary), present_user=True)
    if asked and asked.type == "question":
        event.answer(answers=[app.choose(event.user, q.question, [o.label for o in q.options]) for q in asked.questions])

# Answer later, by id. List everything Bash is waiting on, from anywhere, and answer each one by its id.
for item in vestra.answer.list(user="traveller-42").data:
    if item.type == "handoff":
        vestra.answer(item.id, done=True)
```

## CLI

```bash
# List what Bash is waiting on. Leave out --user to see what it waits on for every user.
vestra answer --user traveller-42

# Approve or deny. --present-user says the person who saw the approval is the one saying yes. To say no, give
# a reason.
vestra answer int_3j9 --approve --present-user
vestra answer int_7kq --deny --message "Not that hotel, it is too far from the airport."

# Answer a question.
vestra answer int_2mx --answer "Window"

# Finish a hand-off. A hand-off is a step only a person can do.
vestra answer int_9hd --done

# Return your tool's result.
vestra answer call_4mz --output '{ "status": "on time", "gate": "B4" }'
```

## HTTP

```bash
# List what Bash is waiting on. For one user, this lists approvals, questions, hand-offs, apps to connect and
# calls to your tools.
curl "$VESTRA_BASE_URL/v1/interactions?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Approve or deny. A payment or hands-on approval needs present_user: true, and a hands-on approval also needs
# its nonce. To say no, send approve: false.
curl "$VESTRA_BASE_URL/v1/interactions/int_3j9/answer" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "approve": true, "present_user": true }'

# Answer a question. Send one answer per question.
curl "$VESTRA_BASE_URL/v1/interactions/int_7qp/answer" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "answers": ["The 09:15 flight"] }'

# Return your tool's result. Answer a call to one of your tools with its output, or an error.
curl "$VESTRA_BASE_URL/v1/interactions/call_4mz/answer" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "output": { "status": "confirmed", "pnr": "100042" } }'
```

## MCP

```json
{
  "method": "tools/call",
  "params": {
    "name": "answer",
    "arguments": { "interaction": "int_3j9", "approve": true, "present_user": true }
  }
}
```

## Good to know

### What to send for each type

An approval takes `{ approve, present_user?, message?, nonce? }`. A question takes `{ answers }`, with one answer per question, or `{ approve: false }` to skip it. A connect request takes `{ approve: false }` to decline it. A hand-off takes `{ done }`, `{ could_not }`, `{ not_now }` or `{ answers }`. A call to your tool takes `{ output }` or `{ error }`. Any other body fails with `400 answer_mismatch`, and the error names the shapes that fit.

### Approvals that need the person present

A payment approval, or a hands-on approval (Bash operating its computer for the user), is accepted only with `present_user: true`, from the person who saw it. The same goes for approving a company action: a tool on one of your MCP servers, or one of your functions that is not marked read-only or run without asking.

A company action's `summary` says what will happen, taken from the tool's description, then names the tool and each argument. For example: `Extends a rental by one day. extend_rental: rental_id R-1042, charge_usd 15, new_due_back 2026-10-05 18:00`. The summary is at most 120 characters and never cuts an argument in half. When space runs out, it drops the description first, then each argument that does not fit, and ends with `and N more`. `details.rows` holds the call's first 8 arguments, each value up to 200 characters. Its `moves_money` is `null`, because only your code knows what your tools cost. A tool that takes its price as an input shows the price in the rows. On a hands-on approval, the SDK sends the interaction's `nonce` for you.

### Let the SDK run your tools

Pass `new Vestra({ tools })` one function per tool, and the SDK answers each call with what the function returns. If the function throws, that becomes the call's error. This works both on a run and in the webhook handler.

### Running several processes

Each process runs your function once per call. If you run several processes, answer your tools through one path only: either your runs or a single webhook endpoint. Otherwise two processes that receive the same call at the same moment can both run it.

### When a request expires

A call to your tool that nobody answers expires after its `timeout_s`, and sends `interaction.expired`. For now, only tool calls send expiry events. Other interactions end with `interaction.resolved`.

## The routes behind answer

- `GET` [/v1/interactions](https://vestra.ai/developers/reference/api/interactions/get-v1-interactions.md): Everything Bash waits on, across your users: approvals, questions, app connections, hand-offs, and calls to your client tools (type tool_call). Filter by user_id, conversation_id, task_id, type or status (pending by default). Key scope: `runtime` (or `all`).
- `GET` [/v1/interactions/{interaction_id}](https://vestra.ai/developers/reference/api/interactions/get-v1-interactions-interaction-id.md): One item, by its int_ or call_ id: its type (approval, question, connect, handoff, tool_call), what Bash wants to do, the options, when it expires. A handoff carries a snapshot of the page. Key scope: `runtime` (or `all`).
- `POST` [/v1/interactions/{interaction_id}/answer](https://vestra.ai/developers/reference/api/interactions/post-v1-interactions-interaction-id-answer.md): Answer it, whatever it is. An approval takes approve (true or false), a question answers or approve: false, a hand-off done, could_not or not_now (or answers, when it asks for a value such as a code), a client tool call output or error. A money or account approval, and the yes to a company action (a tool of your MCP servers, or a function of yours that is not read-only or run without asking), must carry present_user: true. A body that fits another kind is 400 answer_mismatch. Key scope: `runtime` (or `all`).
- `POST` [/v1/interactions/{interaction_id}/cancel](https://vestra.ai/developers/reference/api/interactions/post-v1-interactions-interaction-id-cancel.md): Withdraw it. The task carries on without that action. Key scope: `runtime` (or `all`).

Next: [`watch`](https://vestra.ai/developers/watch.md)
