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

# POST /v1/knowledge/search

> Search the knowledge base by full-text, vector, or hybrid.

Runs a query over the indexed chunks of the knowledge base. Supports three search modes — pick the one that best fits the user's query:

| Mode     | When to use it                                                     | How it works internally                                                                               |
| -------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| `full`   | Exact terms, keywords, numbers, IDs.                               | MongoDB text index over `page_content` with Spanish stemming.                                         |
| `vector` | Natural language questions, synonyms, paraphrasing.                | MongoDB Atlas Vector Search with `text-embedding-3-small` embeddings.                                 |
| `hybrid` | Sensible default when you don't know what kind of query is coming. | Runs both in parallel, normalizes scores, and combines them with a 0.6 vector / 0.4 full-text weight. |

Results are grouped by `node_id` (the same document appears only once even if several chunks match) and sorted by descending score.

## Endpoint

```
POST https://api.keebai.com/v1/knowledge/search
```

## Required scope

`knowledge:query`

## Headers

| Header          | Required | Value                    |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer kbai_pk_<token>` |
| `Content-Type`  | Yes      | `application/json`       |

## Body

| Field      | Type       | Required | Description                                                                                                                     |
| ---------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `query`    | `string`   | Yes      | Search text.                                                                                                                    |
| `mode`     | `string`   | Yes      | `full`, `vector`, or `hybrid`.                                                                                                  |
| `top_k`    | `number`   | No       | Max number of results, between 1 and 25. Default `5`.                                                                           |
| `node_ids` | `string[]` | No       | Restrict the search to these `node_id` values (folders or documents). Useful to scope to a domain within the tree. Max 100 ids. |

## Example request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.keebai.com/v1/knowledge/search \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "¿cómo cancelar mi suscripción?",
      "mode": "hybrid",
      "top_k": 3
    }'
  ```

  ```js JavaScript theme={null}
  const resp = await fetch("https://api.keebai.com/v1/knowledge/search", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      query: "¿cómo cancelar mi suscripción?",
      mode: "hybrid",
      top_k: 3,
    }),
  });
  const { results } = await resp.json();
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.post(
      "https://api.keebai.com/v1/knowledge/search",
      headers={
          "Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}",
          "Content-Type": "application/json",
      },
      json={
          "query": "¿cómo cancelar mi suscripción?",
          "mode": "hybrid",
          "top_k": 3,
      },
      timeout=15,
  )
  resp.raise_for_status()
  results = resp.json()["results"]
  ```
</CodeGroup>

## Response

### 200 OK

```json theme={null}
{
  "mode": "hybrid",
  "results": [
    {
      "node_id": "65f3a1b2c3d4e5f6a7b8c9d3",
      "title": "Política de devolución",
      "snippet": "Las devoluciones se aceptan dentro de los 10 días posteriores a la compra...",
      "score": 0.873,
      "source": "vector"
    },
    {
      "node_id": "65f3a1b2c3d4e5f6a7b8c9d4",
      "title": "Cancelación de suscripciones",
      "snippet": "Para cancelar tu suscripción, ingresá a Mi cuenta > Suscripciones...",
      "score": 0.612,
      "source": "full"
    }
  ]
}
```

| Field               | Type     | Description                                                                                                                                                                             |
| ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`              | `string` | Mode applied (same as in the request).                                                                                                                                                  |
| `results[].node_id` | `string` | `ObjectId` of the document in the knowledge base. Useful for cross-referencing with [`GET /v1/knowledge/tree`](/dev/endpoints/knowledge-tree).                                          |
| `results[].title`   | `string` | Document title (no full content).                                                                                                                                                       |
| `results[].snippet` | `string` | Fragment of the highest-scoring chunk, truncated to \~320 characters.                                                                                                                   |
| `results[].score`   | `number` | Normalized score. The scale depends on the mode: in `vector` and `hybrid` it's in \[0,1]; in `full` it's the raw Mongo `textScore`. Only comparable between items in the same response. |
| `results[].source`  | `string` | `vector` or `full`, depending on which engine produced the top-scoring match (in `hybrid` it indicates which one won after combination).                                                |

### 400 / 401 / 403 / 429

* `400 BAD_REQUEST`: `query` empty, `mode` not supported, or `node_ids` with invalid format.
* `403 FORBIDDEN` with `code: INSUFFICIENT_SCOPE`: the PAT doesn't have `knowledge:query`.

<Tip>
  If you're going to feed the results to an external LLM (RAG), prefer `hybrid` with `top_k=5` as a starting point. It's the setup that best balances recall (vector) with precision on exact terms (full-text).
</Tip>


## OpenAPI

````yaml POST /v1/knowledge/search
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/knowledge/search:
    post:
      tags:
        - knowledge
      summary: Buscar en la base de conocimientos por full-text, vector o híbrido.
      operationId: PublicKnowledgeController_search
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchKnowledgeDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchKnowledgeResponseDto'
      security:
        - PAT: []
components:
  schemas:
    SearchKnowledgeDto:
      type: object
      properties:
        query:
          type: string
          description: Texto de búsqueda.
          example: ¿cómo cancelar suscripción?
        mode:
          type: string
          description: >-
            Modo de búsqueda: full (full-text), vector (semántica) o hybrid
            (combinación con re-ranking).
          enum:
            - full
            - hybrid
            - vector
          example: hybrid
        top_k:
          type: number
          description: Cantidad máxima de resultados (1-25).
          example: 5
          default: 5
        node_ids:
          description: Limita la búsqueda a estos node ids (carpetas o documentos).
          type: array
          items:
            type: string
      required:
        - query
        - mode
    SearchKnowledgeResponseDto:
      type: object
      properties:
        mode:
          type: string
          enum:
            - full
            - hybrid
            - vector
        results:
          type: array
          items:
            $ref: '#/components/schemas/KnowledgeSearchResultItemDto'
      required:
        - mode
        - results
    KnowledgeSearchResultItemDto:
      type: object
      properties:
        node_id:
          type: string
        title:
          type: string
        snippet:
          type: string
        score:
          type: number
        source:
          type: string
          enum:
            - full
            - vector
      required:
        - node_id
        - score
        - source
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````