# Interactions API

> What Bash waits on (approvals, questions, apps to connect, hand-offs, calls to your tools) and the answers.

Send `Authorization: Bearer vk_test_...` on every call.

## `GET /v1/interactions`

Its own page: [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).

Your API key, scope `runtime` or `all`. On the card: [`answer`](https://vestra.ai/developers/answer.md). Every call here is a checked sample.

### Request

Query:

- `limit` (`integer`): How many items per page, 1 to 100. Default 20.
- `cursor` (`string`): Where to go on from: the `next_cursor` of the page before, unchanged. Absent: the first page.
- `user_id` (`string`): Only this user's, by your id for them.
- `conversation_id` (`string`): Only this conversation's (conv_...).
- `task_id` (`string`): Only this task's (task_...).
- `type` (`"approval" | "question" | "connect" | "handoff" | "tool_call"`): Only this type. Absent: every type, calls to your tools included. tool_call: those calls alone.
- `kind` (`"ask_question" | "composio_auth" | "tool_permission" | "credit_approval" | "exit_plan" | "custom" | "hands_approval" | "plan_limit"`): Only this kind.
- `status` (`"pending" | "resolved" | "cancelled" | "expired"`): Only this status. Default: pending, what Bash waits on now.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `object` (`"interaction"`): Always interaction.
  - `id` (`string`): The interaction, to answer by its id (int_...; call_... for a call to your tool). Example: `int_3j9`.
  - `user_id` (`string | null`): Your id for the user it is for; null on your company's own Bash.
  - `task_id` (`string | null`): The task that asked (task_...); null for a call to your tool that Bash's reply made.
  - `conversation_id` (`string | null`): Its conversation (conv_...); null when its task has none.
  - `kind` (`"ask_question" | "composio_auth" | "tool_permission" | "credit_approval" | "exit_plan" | "custom" | "hands_approval" | "plan_limit"`): What exactly Bash waits on. ask_question: a question, or a hand-off. composio_auth: connecting an app. tool_permission: a step with a tool. credit_approval: spending on a paid tool. exit_plan: a plan before it starts. hands_approval: an action on a computer, approved in person. plan_limit: a plan's limit ran out (read only). custom: anything else, named by details.renderer.
  - `type` (`"approval" | "question" | "connect" | "handoff" | "tool_call"`): What kind of answer it takes. approval: yes or no. question: an answer to each question. connect: your user connects an app. handoff: a person does one thing, then says so. tool_call: your tool returns an output or an error.
  - `status` (`"pending" | "resolved" | "cancelled" | "expired"`): pending: Bash waits on it. resolved: it was answered. cancelled: it was withdrawn before an answer. expired: nobody answered in time.
  - `summary` (`string`): What Bash wants, in a phrase to show your user. Example: `pay, amount $500.00`.
  - `moves_money` (`boolean | null`): Whether saying yes spends money, such as a payment. null on the card for one of your own tools (a function of yours, or a tool of your MCP servers): your app knows what your tools cost, and Vestra cannot.
  - `requires_present_user` (`boolean`): Whether a yes needs present_user: true, meaning the person who saw it said yes: true for a payment, and for an action approved in person.
  - `nonce` (`string`, may be absent): For an action approved in person (hands_approval) only: send it back with your yes.
  - `questions` (`object[]`, may be absent): For a question or a hand-off: what Bash asks, one or more questions.
    - `question` (`string`): The question, as Bash asks it. Example: `Which flight should I book?`.
    - `header` (`string`): A short label for it, for a heading or a chip; empty when Bash gave none.
    - `options` (`object[]`): Answers Bash suggests; your user may still answer in their own words. Empty when it suggests none.
    - `multi_select` (`boolean`): Whether more than one option may be picked.
  - `frame_url` (`string`, may be absent): For a hand-off, on a single read only (never in lists): the page Bash was on when it asked, as a JPEG data URI.
  - `browser` (`object`, may be absent): For a hand-off in the browser: open a session with a screen (POST /v1/sessions) so a person can do it.
    - `suggested_mode` (`"control"`): Open the screen with control, so the person can take the wheel.
    - `url` (`string | null`): The page Bash's browser is on; null when it did not say.
  - `details` (`map of any`): A few named facts for its kind, safe to show: such as tool_name and rows for a tool step (with server for a tool of an MCP server, and its arguments as rows), or app for a connection. Never the raw request.
  - `outcome` (`object | null`): How it ended; null while it is pending.
    - `decision` (`"approved" | "denied" | "answered" | "cancelled" | "expired"`): How it ended.
    - `answers` (`string[]`, may be absent): For an answered question: the answer to each question, in order.
    - `reason` (`string`, may be absent): For a denial: why, when one was recorded.
    - `message` (`string`, may be absent): For a denial: the note that was sent with the no, when there was one.
  - `created_at` (`string`): When Bash asked, as an ISO 8601 time.
  - `expires_at` (`string | null`): When it expires unanswered, as an ISO 8601 time; null when it never does.
  - `resolved_at` (`string | null`): When it was answered, withdrawn or expired, as an ISO 8601 time; null while pending.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**TypeScript**

```ts
const waiting = await vestra.answer.list();
```

**Python**

```python
waiting = vestra.answer.list()
```

**CLI**

```bash
vestra answer
```

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

## `GET /v1/interactions/{interaction_id}`

Its own page: [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.

Your API key, or a session's token, scope `runtime` or `all`. On the card: [`answer`](https://vestra.ai/developers/answer.md). Every call here is a checked sample.

### Request

Path:

- `interaction_id` (`string`, required): What Bash waits on, by its id: `int_...`, or `call_...` for a call to your tool.

### Answers 200

- `object` (`"interaction"`): Always interaction.
- `id` (`string`): The interaction, to answer by its id (int_...; call_... for a call to your tool). Example: `int_3j9`.
- `user_id` (`string | null`): Your id for the user it is for; null on your company's own Bash.
- `task_id` (`string | null`): The task that asked (task_...); null for a call to your tool that Bash's reply made.
- `conversation_id` (`string | null`): Its conversation (conv_...); null when its task has none.
- `kind` (`"ask_question" | "composio_auth" | "tool_permission" | "credit_approval" | "exit_plan" | "custom" | "hands_approval" | "plan_limit"`): What exactly Bash waits on. ask_question: a question, or a hand-off. composio_auth: connecting an app. tool_permission: a step with a tool. credit_approval: spending on a paid tool. exit_plan: a plan before it starts. hands_approval: an action on a computer, approved in person. plan_limit: a plan's limit ran out (read only). custom: anything else, named by details.renderer.
- `type` (`"approval" | "question" | "connect" | "handoff" | "tool_call"`): What kind of answer it takes. approval: yes or no. question: an answer to each question. connect: your user connects an app. handoff: a person does one thing, then says so. tool_call: your tool returns an output or an error.
- `status` (`"pending" | "resolved" | "cancelled" | "expired"`): pending: Bash waits on it. resolved: it was answered. cancelled: it was withdrawn before an answer. expired: nobody answered in time.
- `summary` (`string`): What Bash wants, in a phrase to show your user. Example: `pay, amount $500.00`.
- `moves_money` (`boolean | null`): Whether saying yes spends money, such as a payment. null on the card for one of your own tools (a function of yours, or a tool of your MCP servers): your app knows what your tools cost, and Vestra cannot.
- `requires_present_user` (`boolean`): Whether a yes needs present_user: true, meaning the person who saw it said yes: true for a payment, and for an action approved in person.
- `nonce` (`string`, may be absent): For an action approved in person (hands_approval) only: send it back with your yes.
- `questions` (`object[]`, may be absent): For a question or a hand-off: what Bash asks, one or more questions.
  - `question` (`string`): The question, as Bash asks it. Example: `Which flight should I book?`.
  - `header` (`string`): A short label for it, for a heading or a chip; empty when Bash gave none.
  - `options` (`object[]`): Answers Bash suggests; your user may still answer in their own words. Empty when it suggests none.
    - `label` (`string`): An answer your user can pick.
    - `description` (`string`): A line more about that answer; empty when there is none.
  - `multi_select` (`boolean`): Whether more than one option may be picked.
- `frame_url` (`string`, may be absent): For a hand-off, on a single read only (never in lists): the page Bash was on when it asked, as a JPEG data URI.
- `browser` (`object`, may be absent): For a hand-off in the browser: open a session with a screen (POST /v1/sessions) so a person can do it.
  - `suggested_mode` (`"control"`): Open the screen with control, so the person can take the wheel.
  - `url` (`string | null`): The page Bash's browser is on; null when it did not say.
- `details` (`map of any`): A few named facts for its kind, safe to show: such as tool_name and rows for a tool step (with server for a tool of an MCP server, and its arguments as rows), or app for a connection. Never the raw request.
- `outcome` (`object | null`): How it ended; null while it is pending.
  - `decision` (`"approved" | "denied" | "answered" | "cancelled" | "expired"`): How it ended.
  - `answers` (`string[]`, may be absent): For an answered question: the answer to each question, in order.
  - `reason` (`string`, may be absent): For a denial: why, when one was recorded.
  - `message` (`string`, may be absent): For a denial: the note that was sent with the no, when there was one.
- `created_at` (`string`): When Bash asked, as an ISO 8601 time.
- `expires_at` (`string | null`): When it expires unanswered, as an ISO 8601 time; null when it never does.
- `resolved_at` (`string | null`): When it was answered, withdrawn or expired, as an ISO 8601 time; null while pending.

### Code

**TypeScript**

```ts
const interaction = await vestra.answer.get('int_3j9');
```

**Python**

```python
interaction = vestra.answer.get("int_3j9")
```

**HTTP**

```bash
# interaction.created names what Bash waits on. Read it, show it to your user, then answer it.
curl "$VESTRA_BASE_URL/v1/interactions/int_3j9" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

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

**CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `POST /v1/interactions/{interaction_id}/answer`

Its own page: [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.

Your API key, or a session's token, scope `runtime` or `all`. On the card: [`answer`](https://vestra.ai/developers/answer.md). Every call here is a checked sample.

### Request

Path:

- `interaction_id` (`string`, required): What Bash waits on, by its id: `int_...`, or `call_...` for a call to your tool.

Headers:

- `Idempotency-Key` (`string`): Send the same key to retry this call safely for 24 hours.

Body:

Either `{ approve, present_user, message, nonce }`:

- `approve` (`boolean`, required): Yes or no to an approval. For a connection, only no: your user connects the app with POST /v1/connections, and the item settles itself. false also skips a question.
- `present_user` (`boolean`): true when the person who saw it said yes. A yes that moves money, or is approved in person, needs it.
- `message` (`string`): With a no: a note for Bash, in at most 500 characters. Limits: 1 to 500 characters.
- `nonce` (`string`): For an action approved in person: the interaction's nonce, sent back. Limits: 1 to 4096 characters.

Or `{ answers }`:

- `answers` (`string[]`, required): One answer per question, in order: at most 4, each at most 4,000 characters. Limits: 1 to 4 items.

Or `{ done }`:

- `done` (`true`, required): For a hand-off: the person did it.

Or `{ could_not }`:

- `could_not` (`true`, required): For a hand-off: the person tried and could not.

Or `{ not_now }`:

- `not_now` (`true`, required): For a hand-off: not now; Bash carries on without it.

Or `{ output }`:

- `output` (`any`, required): For a call to your tool: what it returned, as any JSON.

Or `{ error }`:

- `error` (`string`, required): For a call to your tool: why it failed, in at most 4,000 characters. Limits: 1 to 4000 characters.

### Answers 200

- `id` (`string`): What you answered (int_... or call_...).
- `object` (`"interaction"`): Always interaction.
- `type` (`"approval" | "question" | "connect" | "handoff" | "tool_call"`): What kind of answer it takes. approval: yes or no. question: an answer to each question. connect: your user connects an app. handoff: a person does one thing, then says so. tool_call: your tool returns an output or an error.
- `status` (`"resolved" | "completed" | "failed"`): resolved: Bash has your answer and carries on. For a call to your tool: completed with your output, or failed with your error.

### Code

**TypeScript**

```ts
await vestra.answer('int_3j9', { approve: true, present_user: true });
```

**Python**

```python
vestra.answer("int_3j9", approve=True, present_user=True)
```

**CLI**

```bash
vestra answer int_3j9 --approve --present-user
```

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

## `POST /v1/interactions/{interaction_id}/cancel`

Its own page: [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.

Your API key, scope `runtime` or `all`. On the card: [`answer`](https://vestra.ai/developers/answer.md). Over HTTP it is made from the contract: fill in each {…} and every value.

### Request

Path:

- `interaction_id` (`string`, required): What Bash waits on, by its id: `int_...`, or `call_...` for a call to your tool.

Headers:

- `Idempotency-Key` (`string`): Send the same key to retry this call safely for 24 hours.

Body, if you send one:

- `reason` (`string`): Why it was withdrawn, for Bash, in at most 500 characters. Default: Withdrawn by the company. Default: `Withdrawn by the company.`. Limits: 1 to 500 characters.

### Answers 200

- `object` (`"interaction"`): Always interaction.
- `id` (`string`): The interaction, to answer by its id (int_...; call_... for a call to your tool). Example: `int_3j9`.
- `user_id` (`string | null`): Your id for the user it is for; null on your company's own Bash.
- `task_id` (`string | null`): The task that asked (task_...); null for a call to your tool that Bash's reply made.
- `conversation_id` (`string | null`): Its conversation (conv_...); null when its task has none.
- `kind` (`"ask_question" | "composio_auth" | "tool_permission" | "credit_approval" | "exit_plan" | "custom" | "hands_approval" | "plan_limit"`): What exactly Bash waits on. ask_question: a question, or a hand-off. composio_auth: connecting an app. tool_permission: a step with a tool. credit_approval: spending on a paid tool. exit_plan: a plan before it starts. hands_approval: an action on a computer, approved in person. plan_limit: a plan's limit ran out (read only). custom: anything else, named by details.renderer.
- `type` (`"approval" | "question" | "connect" | "handoff" | "tool_call"`): What kind of answer it takes. approval: yes or no. question: an answer to each question. connect: your user connects an app. handoff: a person does one thing, then says so. tool_call: your tool returns an output or an error.
- `status` (`"pending" | "resolved" | "cancelled" | "expired"`): pending: Bash waits on it. resolved: it was answered. cancelled: it was withdrawn before an answer. expired: nobody answered in time.
- `summary` (`string`): What Bash wants, in a phrase to show your user. Example: `pay, amount $500.00`.
- `moves_money` (`boolean | null`): Whether saying yes spends money, such as a payment. null on the card for one of your own tools (a function of yours, or a tool of your MCP servers): your app knows what your tools cost, and Vestra cannot.
- `requires_present_user` (`boolean`): Whether a yes needs present_user: true, meaning the person who saw it said yes: true for a payment, and for an action approved in person.
- `nonce` (`string`, may be absent): For an action approved in person (hands_approval) only: send it back with your yes.
- `questions` (`object[]`, may be absent): For a question or a hand-off: what Bash asks, one or more questions.
  - `question` (`string`): The question, as Bash asks it. Example: `Which flight should I book?`.
  - `header` (`string`): A short label for it, for a heading or a chip; empty when Bash gave none.
  - `options` (`object[]`): Answers Bash suggests; your user may still answer in their own words. Empty when it suggests none.
    - `label` (`string`): An answer your user can pick.
    - `description` (`string`): A line more about that answer; empty when there is none.
  - `multi_select` (`boolean`): Whether more than one option may be picked.
- `frame_url` (`string`, may be absent): For a hand-off, on a single read only (never in lists): the page Bash was on when it asked, as a JPEG data URI.
- `browser` (`object`, may be absent): For a hand-off in the browser: open a session with a screen (POST /v1/sessions) so a person can do it.
  - `suggested_mode` (`"control"`): Open the screen with control, so the person can take the wheel.
  - `url` (`string | null`): The page Bash's browser is on; null when it did not say.
- `details` (`map of any`): A few named facts for its kind, safe to show: such as tool_name and rows for a tool step (with server for a tool of an MCP server, and its arguments as rows), or app for a connection. Never the raw request.
- `outcome` (`object | null`): How it ended; null while it is pending.
  - `decision` (`"approved" | "denied" | "answered" | "cancelled" | "expired"`): How it ended.
  - `answers` (`string[]`, may be absent): For an answered question: the answer to each question, in order.
  - `reason` (`string`, may be absent): For a denial: why, when one was recorded.
  - `message` (`string`, may be absent): For a denial: the note that was sent with the no, when there was one.
- `created_at` (`string`): When Bash asked, as an ISO 8601 time.
- `expires_at` (`string | null`): When it expires unanswered, as an ISO 8601 time; null when it never does.
- `resolved_at` (`string | null`): When it was answered, withdrawn or expired, as an ISO 8601 time; null while pending.

### Code

**TypeScript**

```ts
await vestra.answer.cancel('int_3j9');
```

**Python**

```python
vestra.answer.cancel("int_3j9")
```

**HTTP**

```bash
curl -X POST "$VESTRA_BASE_URL/v1/interactions/{interaction_id}/cancel" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**CLI, MCP**: No sample in your language for this route, so here it is over HTTP.
