# Answer

Generate a grounded AI answer to a user question. The endpoint is fully autonomous by default: it runs web search, fetch, reranking, and cited answer generation in a single call.

```
POST https://api.staan.ai/v2/answer
```

:::warning{title="Limited access"}

Answer is currently available by invitation only. [Contact us to be allowlisted →](mailto:support@staan.ai)

:::

:::tip

Answer is built on the same retrieval stack as [Web Search for AI](/docs/web-for-ai). You can also supply your own sources to skip web search entirely.

:::

## Quick start

```bash title="cURL"
curl https://api.staan.ai/v2/answer \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What are the latest advances in fusion energy?",
    "mode": "short",
    "language": "fr",
    "markdown": true
  }'
```

```python title="Python"
import requests

response = requests.post(
    "https://api.staan.ai/v2/answer",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "query": "What are the latest advances in fusion energy?",
        "mode": "short",
        "language": "fr",
        "markdown": True,
        "related_queries": True,
    },
)
data = response.json()
print(data["answer"])
```

```typescript title="TypeScript"
const res = await fetch('https://api.staan.ai/v2/answer', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'What are the latest advances in fusion energy?',
    mode: 'short',
    language: 'fr',
    markdown: true,
  }),
});
const { answer, sources, citations } = await res.json();
```

<OpenPlaygroundButton
  server="https://api.staan.ai/v2"
  url="/answer"
  method="POST"
  headers={[
    {
      name: 'Authorization',
      defaultValue: 'Bearer YOUR_API_KEY',
      defaultActive: true,
    },
    {
      name: 'Content-Type',
      defaultValue: 'application/json',
      defaultActive: true,
    },
  ]}
  defaultBody={JSON.stringify(
    {
      query:
        'Quelles sont les dernieres avancees en matiere de fusion nucleaire ?',
      mode: 'short',
      language: 'fr',
      markdown: true,
      related_queries: true,
    },
    null,
    2,
  )}
>
  Try — short answer
</OpenPlaygroundButton>

<OpenPlaygroundButton
  server="https://api.staan.ai/v2"
  url="/answer"
  method="POST"
  headers={[
    {
      name: 'Authorization',
      defaultValue: 'Bearer YOUR_API_KEY',
      defaultActive: true,
    },
    {
      name: 'Content-Type',
      defaultValue: 'application/json',
      defaultActive: true,
    },
  ]}
  defaultBody={JSON.stringify({
    query: 'Il vaut combien en ce moment ?',
    mode: 'long',
    language: 'fr',
    markdown: true,
    related_queries: true,
    filter: 'frandroid.com',
    history: [
      { role: 'user', content: 'Je cherche un iPhone 16 Pro.' },
      {
        role: 'assistant',
        content:
          'L iPhone 16 Pro est le flagship d Apple sorti en septembre 2024, disponible en titane et equipe de la puce A18 Pro. Vous voulez en savoir plus sur ses caracteristiques ou sur ou l acheter ?',
      },
    ],
  })}
>
  Try — long answer with history & domain filter
</OpenPlaygroundButton>

## How it works

When `sources` is not provided, the pipeline runs fully automatically:

1. **Query rewrite** — reformulates the query for better search recall (disable with `query_rewrite: false`)
2. **Web search** — retrieves relevant pages, with optional domain filter
3. **Fetch and chunk** — downloads and splits page content
4. **Rerank** — selects the most relevant passages
5. **Answer generation** — produces a cited answer

When you supply `sources`, web search is bypassed and the pipeline starts at step 3 (or skips it entirely with `sources_mode: "context"`).

## Request body

| Parameter         | Type                      | Default    | Description                                                                              |
| ----------------- | ------------------------- | ---------- | ---------------------------------------------------------------------------------------- |
| `query`           | `string`                  | —          | **Required.** User question (1–2,000 characters).                                        |
| `stream`          | `boolean`                 | `false`    | Stream the response as Server-Sent Events.                                               |
| `mode`            | `"short"` \| `"long"`     | `"short"`  | Response length. Use `"long"` for detailed answers.                                      |
| `language`        | `"fr"` \| `"en"`          | `"fr"`     | Answer language.                                                                         |
| `markdown`        | `boolean`                 | `false`    | Format the answer with Markdown.                                                         |
| `query_rewrite`   | `boolean`                 | `true`     | Rewrite the query before search. Ignored when `sources` is set.                          |
| `filter`          | `string`                  | —          | Restrict search to a domain (e.g. `"lemonde.fr"`). Ignored when `sources` is set.        |
| `related_queries` | `boolean`                 | `false`    | Return four follow-up question suggestions.                                              |
| `sources`         | `SourceItem[]`            | —          | Caller-supplied sources (max 10). Bypasses web search.                                   |
| `sources_mode`    | `"search"` \| `"context"` | `"search"` | How to handle supplied sources. See [Caller-supplied sources](#caller-supplied-sources). |
| `history`         | `HistoryMessage[]`        | —          | Conversation history for multi-turn. Max 20 messages (10 turns).                         |

## Response

### Non-streaming (`stream: false`)

```json title="Example response"
{
  "request_id": "01938fc2-1b3a-7e4d-9c12-a4f8e3c20d91",
  "answer": "Les dernières avancées en fusion nucléaire incluent...",
  "citations": [
    { "position": 142, "source_id": "1" },
    { "position": 310, "source_id": "3" }
  ],
  "sources": [
    {
      "id": "1",
      "url": "https://www.cea.fr/...",
      "title": "CEA — Fusion nucléaire"
    },
    {
      "id": "2",
      "url": "https://www.nature.com/...",
      "title": "Nature — Fusion milestone"
    },
    {
      "id": "3",
      "url": "https://www.iter.org/...",
      "title": "ITER — Latest news"
    }
  ],
  "related_queries": [
    "How does ITER work?",
    "What is the difference between fission and fusion?"
  ],
  "usages": [
    { "step": "query_rewrite", "input_tokens": 95, "output_tokens": 12 },
    { "step": "answer", "input_tokens": 2840, "output_tokens": 194 }
  ]
}
```

### Response fields

| Field             | Type     | Description                                                             |
| ----------------- | -------- | ----------------------------------------------------------------------- |
| `request_id`      | `string` | Request identifier.                                                     |
| `answer`          | `string` | Generated answer text.                                                  |
| `citations`       | `array`  | Inline citation markers: `{ position, source_id }`.                     |
| `sources`         | `array`  | Sources used: `{ id, url, title }`.                                     |
| `related_queries` | `array`  | Follow-up suggestions (only if `related_queries: true`).                |
| `usages`          | `array`  | Token usage per pipeline step: `{ step, input_tokens, output_tokens }`. |

## Streaming

Set `stream: true` to receive a `text/event-stream` response. Events arrive in this order:

| Event       | Payload                                        | Description                                                    |
| ----------- | ---------------------------------------------- | -------------------------------------------------------------- |
| `sources`   | `Array<{ id, url, title }>`                    | Emitted first — sources used for the answer.                   |
| `assistant` | `string`                                       | Answer text chunk. Concatenate all chunks for the full answer. |
| `citation`  | `{ reference_ids: number[] }`                  | Source references at the current position.                     |
| `related`   | `{ related_queries: string[] }`                | Follow-up suggestions (if `related_queries: true`).            |
| `usage`     | `{ step, input_tokens, output_tokens }`        | Token usage for one pipeline step.                             |
| `done`      | `{ finish_reason: string }`                    | End of generation.                                             |
| `usages`    | `Array<{ step, input_tokens, output_tokens }>` | Aggregated usage, emitted after `done`.                        |

```text title="Streaming response example"
event: sources
data: [{"id":"1","url":"https://www.cea.fr/...","title":"CEA — Fusion"}]

event: assistant
data: "Les dernières avancées en fusion nucléaire"

event: citation
data: {"reference_ids":[1]}

event: assistant
data: " incluent la réalisation d'un gain énergétique net..."

event: done
data: {"finish_reason":"stop"}

event: usages
data: [{"step":"answer","input_tokens":2840,"output_tokens":194}]
```

## Caller-supplied sources

Provide `sources` when you already have a document set or SERP. Web search, `query_rewrite`, and `filter` are all ignored.

### Source object

| Field     | Type     | Required | Description                                                     |
| --------- | -------- | -------- | --------------------------------------------------------------- |
| `url`     | `string` | No       | Page URL. If set without `content`, the API may fetch the page. |
| `title`   | `string` | No       | Document title.                                                 |
| `content` | `string` | No       | Pre-extracted text (max 50,000 characters).                     |

### `sources_mode`

| Value       | Behaviour                                                                                                                                                                                     |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"search"`  | Fetch URLs, chunk, and rerank (default). Use when you have URLs or snippets and want Staan to retrieve and rank passages.                                                                     |
| `"context"` | Inject content directly — no fetch, chunk, or rerank. Use for short, pre-processed text where you control the context. In this mode, only the first 5,000 characters per source are injected. |

```json title="Example with caller-supplied sources"
{
  "query": "What is the return policy?",
  "sources": [
    {
      "url": "https://www.example.com/returns",
      "title": "Return Policy",
      "content": "Our return policy allows customers to return any item within 30 days..."
    }
  ],
  "sources_mode": "context",
  "language": "fr"
}
```

## Multi-turn conversations

Pass prior turns in `history` to maintain conversation context.

**Rules:**

- Strict alternation `user` → `assistant`, starting with `user`
- Max 20 messages (10 turns)
- `user` content: max 2,000 characters; `assistant`: max 10,000

```json title="Multi-turn example"
{
  "query": "And what about its applications in medicine?",
  "history": [
    { "role": "user", "content": "What is nuclear fusion?" },
    {
      "role": "assistant",
      "content": "Nuclear fusion is the process by which two light atomic nuclei combine..."
    }
  ],
  "language": "fr"
}
```

## Error codes

| HTTP status | Meaning                          |
| ----------- | -------------------------------- |
| `401`       | Missing or invalid Bearer token. |
| `500`       | Internal server error.           |
