> ## Documentation Index
> Fetch the complete documentation index at: https://docs.verahealth.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agentic tools

> Choose which tools Vera's agent may use, per request.

> [!WARNING]
>
> **Preview endpoint, integration testing only.** `POST /api/chat/v3` is enabled per API key under a
> written testing agreement. Request text is sent to the model provider and recorded in Vera's
> tracing system, and the endpoint carries no availability or stability commitment.

`POST /api/chat/v3` runs Vera's agentic engine: instead of a fixed pipeline, a model decides which
tools to call and how many times, then writes the answer. The `tools` request parameter decides
which switchable tools are on the table for that one request, so a single integration can offer a
different capability set at each step of a workflow.

Call it server to server. The endpoint sends no CORS headers, so a browser cannot read a
cross-origin response from it, and your API key must never reach a browser in the first place.

## Switchable tools

Tool ids are the engine's own tool names, so they are camelCase.

| `tools` id | What it enables | A question that exercises it |
| - | - | - |
| `drug` | Drug monograph lookup: dosing, interactions, labelling. Reports no payload; the drug appears as a `drug` reference | "Usual adult oral dose of amoxicillin for pneumonia?" |
| `medcalc` | Clinical calculators, resolved and computed from the question | "CHA2DS2-VASc for a 78yo woman with HTN and diabetes?" |
| `patientHandout` | Patient-facing care instructions | "Write a patient handout for newly diagnosed cellulitis." |
| `priorAuth` | Prior-authorization letter | "Draft a prior-auth letter for denosumab in osteoporosis." |

Requesting an id that is not in this table returns `400`. Requesting one that is in the table but is
not provisioned for your key returns `403` with code `tool_not_enabled`.

> [!NOTE]
>
> Figure retrieval, clinical-trial search, decision pathways and slide decks are not available on
> this endpoint yet. They exist in Vera's product but are not part of the partner API, and sending
> their ids is a `400` like any other unknown id.

## Tools that are always on

The agent cannot answer without these, so they are not in the `tools` vocabulary and cannot be
switched off:

| Tool | Why it is always on |
| - | - |
| `retrieve` | Literature search. Every evidence-based answer depends on it. |
| `stop` | How the agent declines a request that violates our terms. |

Sending one of these ids in `tools` is a `400`: they are not switches, and Vera does not accept a
request that implies they are.

> [!NOTE]
>
> `patientHandout` and `priorAuth` deliver their whole result as a **tool payload**, not as answer
> text. On those turns `answer` is empty by design. See
> [Reading the deliverable](#reading-the-deliverable) below.

## The three ways to send `tools`

### Omitted, or an empty array: retrieval only

```json theme={null}
{"question": "Usual adult oral dose of amoxicillin for pneumonia?"}
```

```json theme={null}
{"question": "Usual adult oral dose of amoxicillin for pneumonia?", "tools": []}
```

These two are equivalent. Every switchable tool is off. The agent can still search the literature
and cite it, because retrieval is always on, but it will not reach for a drug monograph or a
calculator. There is no per-key default set: if you want a tool, name it.

### An explicit list

```json theme={null}
{"question": "Usual adult oral dose of amoxicillin for pneumonia?", "tools": ["drug", "medcalc"]}
```

The agent may call these and no others. This is the parameter to drive from your workflow step.
Duplicates are collapsed.

### An id you are not provisioned for

```json theme={null}
{"question": "...", "tools": ["medcalc"]}
```

```json theme={null}
{"error": {"code": "tool_not_enabled", "message": "tool \"medcalc\" is not enabled for this API key"}}
```

Returned as `403`. Contact [support@verahealth.ai](mailto:support@verahealth.ai) to change what
your key is provisioned for. An id outside the table above is a `400` instead: that one is a typo in
your request, not a missing entitlement.

## Driving tools from a workflow step

Each step sends the same endpoint a different `tools` value.

```js theme={null}
const TOOLS_BY_STEP = {
  chart_review: [],
  medication_review: ["drug"],
  risk_scoring: ["medcalc"],
  patient_discharge: ["patientHandout"],
  coverage_request: ["priorAuth"],
};

async function askVera(step, question) {
  const response = await fetch("https://www.verahealth.ai/api/chat/v3", {
    method: "POST",
    headers: {"x-api-key": process.env.VERA_API_KEY, "Content-Type": "application/json"},
    body: JSON.stringify({question, tools: TOOLS_BY_STEP[step], stream: false}),
  });

  return response.json();
}
```

## Verifying which tools ran

A tool whose result you render appears in the non-streaming response under `tool_outputs`, and on
the stream as a frame carrying its payload: `tool-input-available` for `patientHandout` and
`priorAuth`, whose input is the deliverable, and `tool-output-available` for `medcalc`. Each payload
is trimmed to the fields documented for that tool in the
[API reference](https://docs.verahealth.ai/api-reference/v3/ask-the-agent-with-a-chosen-tool-set.md), at every depth.

Retrieval is not reported: it produces no payload of its own, and what it found reaches you as
references and answer text. `drug` is not reported either, for the same reason: the monograph stays
on the model channel and the drug reaches you as a `drug` reference whose `title` is its name. A
**switchable** tool appearing that you did not enable is a bug worth reporting.

## Reading the deliverable

For most tools the answer is in `answer` and the sources behind it in `references`. Every
reference carries its `sourceId` (what a `cite` or `calculator` index in the answer resolves
against) and its `kind`. A `paper` adds `title`, `firstAuthor`, `journal`, `year`, its
`doi` when it has one, which is the only identifier to link out with, `isGuideline`, and
`evidenceStrength` when it has a graded level of evidence. A `drug` and a `calculator` carry only
their name as `title`; the id a card renders from arrives on that tool's payload, not here.

```json theme={null}
{
  "answer": "Amoxicillin 1 g orally three times daily is the usual adult dose.",
  "references": [
    {
      "sourceId": 5,
      "kind": "paper",
      "title": "Community-Acquired Pneumonia Treatment Guidelines",
      "firstAuthor": "Metlay JP",
      "journal": "JAMA",
      "year": "2019",
      "doi": "10.1001/jama.2023.0001",
      "isGuideline": true,
      "evidenceStrength": "High"
    },
    {"sourceId": 7, "kind": "drug", "title": "Amoxicillin"}
  ],
  "tool_outputs": []
}
```

For `patientHandout` and `priorAuth` the model is instructed to let the
artifact speak for itself, so `answer` is empty and the artifact is the tool's payload:

```json theme={null}
{
  "answer": "",
  "references": [
    {
      "sourceId": 1,
      "kind": "paper",
      "title": "Practice Guidelines for the Diagnosis and Management of Skin and Soft Tissue Infections",
      "firstAuthor": "Stevens DL",
      "journal": "Clinical Infectious Diseases",
      "year": "2014",
      "isGuideline": true
    }
  ],
  "tool_outputs": [
    {"tool": "patientHandout", "output": {"title": "Your Care Guide for Cellulitis", "content": "..."}}
  ]
}
```

`references` is not empty on these turns: the agent still searches the literature to write the
artifact, so the sources behind it are reported even though `answer` is.

```js theme={null}
const handout = body.tool_outputs.find((call) => call.tool === "patientHandout")?.output;
```

On the streaming path the same payload is the `input` field of the `tool-input-available` frame for
`patientHandout` and `priorAuth`, and the `output` field of `tool-output-available` for `medcalc`.

## Follow-up questions

A request without `thread_id` starts a new thread, which Vera saves under your partner account. To
ask a follow-up, send the thread id from the earlier answer (the `x-thread-id` header, or
`thread_id` in a non-streaming body) back as `thread_id`:

```json theme={null}
{"question": "And in children?", "thread_id": "3f2b8c1e-6a4d-4e2f-9b7a-1c5d8e9f0a2b", "tools": ["drug"]}
```

Vera reads the earlier turns of that thread from its own copy, so you send only the new question.
Both streaming and non-streaming answers return the same `x-thread-id`; a non-streaming answer
also returns it as `thread_id` in the body.
`tools` and `country` apply to the request they are sent with: send them again on every follow-up
that needs them.

| Status | Code | Meaning |
| - | - | - |
| `404` | `thread_not_found` | No such thread, or it belongs to another partner. |
| `409` | `answer_in_progress` | The previous answer is still being generated. Wait for it, then resend. |
| `413` | `conversation_too_long` | The thread has reached its maximum length. Start a new thread without `thread_id`. |

## Guideline jurisdiction

Pass `country` when you know where the clinician practises:

```json theme={null}
{"question": "...", "tools": ["drug"], "country": "FR"}
```

Only `FR` and `IT` are accepted, and anything else is a `400`. Those are the jurisdictions whose
national authority publishes in its own language, so they are the only ones that change an answer:
a `country` we hold no localized authority for would weight nothing and quietly read as supported.
Case does not matter. Omit it and no jurisdiction weighting is applied.

Vera deliberately does not infer this from your server's location, which would be wrong for most
integrations.

## Errors

Error bodies are **not** all the same shape, because they come from different layers.

| Status | Body | Meaning |
| - | - | - |
| 400 | `{"error": "The payload is not valid", "message": "The payload is not valid", "code": "VALIDATION_ERROR"}` | The body failed validation, or `tools` carries an unknown id. The message is deliberately generic and does not name the offending field. |
| 401 | `Unauthorized` (plain text) | An `x-api-key` header was sent and the key is invalid, disabled or expired. |
| 401 | `{"error": "unauthorized"}` | No `x-api-key` header was sent at all. |
| 403 | `{"error": {"code": "access_not_enabled", ...}}` | This key is not enabled for `/api/chat/v3`. |
| 403 | `{"error": {"code": "tool_not_enabled", ...}}` | A requested tool is not provisioned for this key. |
| 413 | `{"error": {"code": "question_too_long", ...}}` | The question is longer than the endpoint accepts. |
| 422 | `{"error": {"code": "content_policy_stop", ...}}` | Refused on content-policy grounds (`stream: false` only). |
| 429 | `{"error": {"code": "rate_limited", ...}}` | See [rate limits and quotas](https://docs.verahealth.ai/docs/guides/rate-limits-and-quotas.md). |
| 500 | `{"error": {"code": "internal_error", ...}}` | Unexpected server error. |

> [!NOTE]
>
> A content-policy refusal only surfaces as `422` when `stream` is `false`. On the streaming path
> the run has already begun, so the refusal arrives as a `tool-input-available` frame with
> `toolName: "stop"` and no answer text follows.

Successful responses carry an `x-vera-request-id` header. Quote it when you contact support. Error
responses do not carry it, so log the status and body instead.

> [card] [Agentic streaming](https://docs.verahealth.ai/docs/guides/agentic-streaming.md)
>
> The event format, which is not the same as `/api/chat/v2`.

> [card] [API reference](https://docs.verahealth.ai/api-reference/v3/ask-the-agent-with-a-chosen-tool-set.md)
>
> Full request and response schemas.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.