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

# Dokumente und Suche

> Dokumente in die Knowledge-Base hochladen, die Pipeline überwachen und per Hybrid Search semantisch durchsuchen.

Localmind trennt zwei Storage-Layer: **Raw Files** sind pfad-basierte Rohdateien in einem Space, **Documents** sind Dateien, die die Verarbeitungs-Pipeline durchlaufen haben und per Hybrid Search durchsuchbar sind. Diese Seite behandelt den **Documents-Layer** — die Knowledge-Base, aus der RAG-Retrieval gespeist wird. Den Raw-File-Layer und die Ordner-Verwaltung beschreibt [Dateien und Ordner](/api-reference/Dateien-und-Ordner).

<Note>
  Ein Upload über `POST /v1/data/upload` befüllt **beide** Layer gleichzeitig: Die Rohdatei wird gespeichert **und** ein durchsuchbares Document angelegt. Sie müssen nicht separat hochladen.
</Note>

Alle Beispiele nutzen die Base-URL `https://<deine-instanz>-api.localmind.ai/v1`. Ersetzen Sie `<deine-instanz>` durch den Host Ihrer Instanz (beachten Sie das `-api`-Suffix). Jeder Request trägt den Header `Authorization: Bearer sk-…`. Die geltenden Konventionen zu Base-URL, Paginierung und Statuscodes beschreibt [Konventionen und Fehler](/api-reference/Konventionen-und-Fehler).

## Documents verwalten

Die folgenden Endpunkte decken den Lebenszyklus eines Dokuments in der Knowledge-Base ab — vom Upload über die Inspektion bis zum Neuverarbeiten.

| Methode & Pfad                          | Zweck                                                                                                            |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `POST /v1/data/upload`                  | Datei (multipart) hochladen → `201` + `processing_state`; triggert die Pipeline automatisch.                     |
| `GET /v1/data/{document_id}`            | Metadaten und `processing_state` lesen. Unbekannte ID → `404`.                                                   |
| `PATCH /v1/data/{document_id}`          | Metadaten schreiben (`doc_metadata`) — **kein** Reprocess.                                                       |
| `DELETE /v1/data/{document_id}`         | `204` — entfernt den Knowledge-Base-Record samt Chunks; das Dokument verschwindet binnen Sekunden aus der Suche. |
| `GET /v1/data/{document_id}/text`       | Extrahierte Content-Units (markdown-aware) abrufen.                                                              |
| `GET /v1/data/{document_id}/chunks`     | Chunks samt Embedding-Metadaten abrufen.                                                                         |
| `GET /v1/data/{document_id}/versions`   | Reprocess-Historie eines Dokuments.                                                                              |
| `POST /v1/data/{document_id}/reprocess` | `202` + `pipeline_run_id`; verarbeitet die bestehende Originaldatei neu.                                         |
| `POST /v1/data/batch-reprocess`         | Mehrere Dokumente in einem Aufruf neu verarbeiten.                                                               |

### Dokument hochladen

Senden Sie die Datei und die Ziel-`space_id` als `multipart/form-data`. Die Pipeline (parse → chunk → embed) startet automatisch im Hintergrund.

<ParamField body="file" type="file" required>
  Die hochzuladende Datei. Das aktuelle Größenlimit pro Datei zeigt Ihnen das Upload-Feld in der Web-App; für Details kontaktieren Sie den Support. Dateien über dem Limit lehnt die API mit `400` ab.
</ParamField>

<ParamField body="space_id" type="string" required>
  UUID des Ziel-Space. Sie benötigen Schreibrechte in diesem Space — andernfalls antwortet die API mit `403`. Fehlt das Feld, ist die Antwort `422`.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/data/upload" \
    -H "Authorization: Bearer sk-…" \
    -F "space_id={space_id}" \
    -F "file=@reisekostenrichtlinie.md"
  ```

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

  resp = requests.post(
      "https://<deine-instanz>-api.localmind.ai/v1/data/upload",
      headers={"Authorization": "Bearer sk-…"},
      data={"space_id": "{space_id}"},
      files={"file": ("reisekostenrichtlinie.md", open("reisekostenrichtlinie.md", "rb"))},
  )
  document = resp.json()
  print(document["id"], document["processing_state"])
  ```
</RequestExample>

<ResponseExample>
  ```json 201 — direkt nach dem Upload theme={null}
  {
    "id": "{document_id}",
    "file_type": "MARKDOWN",
    "file_size": 412,
    "file_path": "spaces/{space_id}/documents/{document_id}/original/reisekostenrichtlinie.md",
    "doc_metadata": {
      "original_filename": "reisekostenrichtlinie.md",
      "content_type": "text/markdown"
    },
    "processing_state": { "parsed": false, "chunked": false, "embedded": false },
    "space_id": "{space_id}",
    "organization_id": "{org_id}",
    "created_by": "{user_id}"
  }
  ```
</ResponseExample>

Direkt nach dem Upload stehen alle drei `processing_state`-Flags auf `false` — die Pipeline läuft noch. Wie Sie auf den Abschluss warten, zeigt der nächste Abschnitt.

### Auf den Pipeline-Abschluss warten

Der Upload ist **asynchron**. Fragen Sie `GET /v1/data/{document_id}` ab, bis die Verarbeitung abgeschlossen ist. Maßgeblich ist das Feld `processing_state`:

<ResponseField name="processing_state" type="object">
  Status der Verarbeitungs-Pipeline.

  <Expandable title="Felder">
    <ResponseField name="parsed" type="boolean">
      `true`, sobald die Datei geparst (Text extrahiert) wurde.
    </ResponseField>

    <ResponseField name="chunked" type="boolean">
      `true`, sobald der extrahierte Text in Chunks zerlegt wurde.
    </ResponseField>

    <ResponseField name="embedded" type="boolean">
      `true`, sobald die Chunks eingebettet (Embeddings berechnet) wurden.
    </ResponseField>

    <ResponseField name="pipeline_status" type="string">
      Gesamtstatus. Bei Abschluss `"completed"` — dann ist das Dokument durchsuchbar.
    </ResponseField>

    <ResponseField name="chunk_count" type="integer">
      Anzahl der erzeugten Chunks.
    </ResponseField>

    <ResponseField name="embedding_model" type="string">
      Verwendetes Embedding-Modell, z. B. `localmind-embeddings`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseExample>
  ```json 200 — pipeline_status: completed theme={null}
  {
    "processing_state": {
      "parsed": true,
      "chunked": true,
      "embedded": true,
      "parser_name": "markdown",
      "chunker_name": "page_based",
      "chunk_count": 1,
      "embedding_model": "localmind-embeddings",
      "embedded_chunk_count": 1,
      "pipeline_status": "completed"
    }
  }
  ```
</ResponseExample>

<Tip>
  Pollen Sie `processing_state.pipeline_status` so lange, bis es `"completed"` ist, bevor Sie ein frisch hochgeladenes Dokument durchsuchen. Vorher liefert die Suche es nicht zurück.
</Tip>

### Dokumente neu verarbeiten

Wenn sich die Parser- oder Chunker-Konfiguration geändert hat, verarbeiten Sie ein bestehendes Dokument neu. Die Originaldatei bleibt dabei erhalten.

`POST /v1/data/{document_id}/reprocess` antwortet mit `202` und einer `pipeline_run_id`, über die Sie den neuen Lauf verfolgen können. Für mehrere Dokumente nutzen Sie `POST /v1/data/batch-reprocess` mit einem Objekt, das die IDs unter dem Schlüssel `document_ids` enthält:

<RequestExample>
  ```bash Einzelnes Dokument theme={null}
  curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/data/{document_id}/reprocess" \
    -H "Authorization: Bearer sk-…" \
    -H "Content-Type: application/json" \
    -d '{ "chunker_name": "page_based" }'
  ```

  ```bash Batch theme={null}
  curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/data/batch-reprocess" \
    -H "Authorization: Bearer sk-…" \
    -H "Content-Type: application/json" \
    -d '{ "document_ids": ["{document_id}", "{document_id}"] }'
  ```
</RequestExample>

<Warning>
  Der Body von `batch-reprocess` ist ein **Objekt** mit dem Schlüssel `document_ids` — kein rohes Array. Eine ungültige ID bricht den Batch nicht ab; die Antwort listet pro Dokument den Erfolg unter `results[]`.
</Warning>

## Semantische Suche

`POST /v1/data/hybrid-search` durchsucht die Documents eines Space mit **Hybrid Search** — einer Kombination aus dichter Vektor-Suche und lexikalischer BM25-Keyword-Suche (sparse). Das Ergebnis ist eine nach `score` sortierte Liste relevanter Chunks mit Quellenangabe. Dieselbe Suche nutzen Agenten intern für RAG-Retrieval.

<ParamField body="query" type="string" required>
  Die Suchanfrage in natürlicher Sprache.
</ParamField>

<ParamField body="space_id" type="string" required>
  UUID des zu durchsuchenden Space. Die Suche ist **space-scoped**.
</ParamField>

<ParamField body="document_ids" type="array">
  Optionale Liste von Document-UUIDs, auf die der Suchraum eingegrenzt wird.
</ParamField>

Die Antwort enthält das Feld `results` mit den Treffern:

<ResponseField name="results" type="array">
  Nach `score` absteigend sortierte Treffer.

  <Expandable title="Felder pro Treffer">
    <ResponseField name="chunk_id" type="string">
      UUID des Chunks.
    </ResponseField>

    <ResponseField name="document_id" type="string">
      UUID des Quell-Dokuments.
    </ResponseField>

    <ResponseField name="document_name" type="string">
      Dateiname des Quell-Dokuments.
    </ResponseField>

    <ResponseField name="score" type="number">
      Kombinierter Relevanz-Score (Hybrid). Höher = relevanter.
    </ResponseField>

    <ResponseField name="dense_score" type="number | null">
      Anteil der Vektor-Suche am Score.
    </ResponseField>

    <ResponseField name="sparse_score" type="number | null">
      Anteil der BM25-Keyword-Suche am Score.
    </ResponseField>

    <ResponseField name="text" type="string">
      Der Chunk-Text — die eigentliche Beleg-Passage.
    </ResponseField>

    <ResponseField name="metadata" type="object">
      Chunk-Metadaten wie `heading_text`, `format` und `word_count`.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/data/hybrid-search" \
    -H "Authorization: Bearer sk-…" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "Hotelübernachtung Erstattung pro Nacht",
      "space_id": "{space_id}"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "query": "Hotelübernachtung Erstattung pro Nacht",
    "space_id": "{space_id}",
    "total_results": 10,
    "results": [
      {
        "chunk_id": "{chunk_id}",
        "document_id": "{document_id}",
        "document_name": "reisekostenrichtlinie.md",
        "file_type": "MARKDOWN",
        "folder_path": null,
        "chunk_index": 0,
        "score": 0.913,
        "dense_score": null,
        "sparse_score": null,
        "text": "## Übernachtung\nHotelübernachtungen werden bis 120 EUR pro Nacht erstattet. …",
        "metadata": { "heading_text": "Übernachtung", "format": "markdown", "word_count": 11 }
      }
    ]
  }
  ```
</ResponseExample>

<Note>
  Ein in `document_ids` oder über Folder-Pfade gesetzter Filter wird mit der für Ihren Key **erreichbaren Menge geschnitten**, nie erweitert. Ein Filter auf ein Dokument außerhalb Ihres Zugriffs liefert keine Treffer statt eines Fehlers — siehe [Authentifizierung und Rollen](/api-reference/Authentifizierung-und-Rollen).
</Note>

## End-to-End: eigenes Dokument hochladen und abfragen

Das folgende Beispiel ist end-to-end verifiziert. Es zeigt den vollständigen RAG-Ablauf: vom Auffinden des eigenen Private Space über den Upload bis zur semantischen Suche.

<Steps>
  <Step title="Private Space finden">
    Jeder Nutzer ist Owner seines [Privaten Space](/api-reference/Authentifizierung-und-Rollen) und darf dort per Key hochladen. Listen Sie Ihre Spaces auf und wählen Sie den Eintrag mit `"is_private": true`.

    ```bash theme={null}
    curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/spaces/search" \
      -H "Authorization: Bearer sk-…" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    ```json Antwort (gekürzt) theme={null}
    {
      "items": [
        { "name": "Private Space", "id": "{space_id}", "is_private": true, "owner_id": "{user_id}" }
      ],
      "total_items": 2, "total_pages": 1, "page": 1, "page_size": 10
    }
    ```

    <Check>
      Sie haben die `id` des Private Space — das ist Ihr `{space_id}` für die nächsten Schritte.
    </Check>
  </Step>

  <Step title="Dokument hochladen">
    Laden Sie die Datei per multipart hoch. Die Antwort ist `201`; die Pipeline läuft asynchron an.

    ```bash theme={null}
    curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/data/upload" \
      -H "Authorization: Bearer sk-…" \
      -F "space_id={space_id}" \
      -F "file=@reisekostenrichtlinie.md"
    ```

    Notieren Sie die `id` aus der Antwort — das ist Ihr `{document_id}`. Alle `processing_state`-Flags stehen zunächst auf `false`.
  </Step>

  <Step title="Auf den Abschluss pollen">
    Fragen Sie das Dokument ab, bis `pipeline_status` den Wert `"completed"` hat.

    ```bash theme={null}
    curl "https://<deine-instanz>-api.localmind.ai/v1/data/{document_id}" \
      -H "Authorization: Bearer sk-…"
    ```

    <Check>
      `processing_state.parsed`, `chunked` und `embedded` sind alle `true`, `pipeline_status` ist `"completed"` — das Dokument ist durchsuchbar.
    </Check>
  </Step>

  <Step title="Semantisch abfragen">
    Stellen Sie die Suchanfrage an `hybrid-search`. Der passende Chunk steht mit dem höchsten `score` oben.

    ```bash theme={null}
    curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/data/hybrid-search" \
      -H "Authorization: Bearer sk-…" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "Hotelübernachtung Erstattung pro Nacht",
        "space_id": "{space_id}"
      }'
    ```

    Im verifizierten Beispiel lieferte diese präzise Frage den Ziel-Chunk auf Platz 1 mit `score` 0.91.
  </Step>
</Steps>

<Tip>
  **Retrieval scopen:** In einem Space mit vielen oder großen Dokumenten kann ein großes Dokument die Top-Treffer dominieren. Beobachtet: Dieselbe Frage präzise gestellt → Ziel-Chunk auf Platz 1; umgangssprachlich gestellt → nur auf Platz 4. Verwenden Sie präzise Begriffe und/oder grenzen Sie den Suchraum per `document_ids` ein:

  ```json theme={null}
  { "query": "Hotel Erstattung", "space_id": "{space_id}", "document_ids": ["{document_id}"] }
  ```
</Tip>

<Note>
  Als Alternative zum direkten `hybrid-search`-Aufruf können Sie einen [Agent](/api-reference/Agenten) mit Daten-Tool ansprechen — dann genügt `POST /v1/chat/completions`, und der Agent zieht die Belege selbst aus seinem Space. Der direkte `hybrid-search`-Aufruf oben ist der explizit verifizierte Weg.
</Note>

## IDs finden (Discovery)

IDs ermitteln Sie ausschließlich über diese Such-Endpunkte — die Web-App zeigt sie nicht an. Alle drei Endpunkte sind paginiert und **auto-narrowing**: Sie liefern nur, was Ihr Key erreicht.

| Methode & Pfad           | Zweck                                                                                             |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| `POST /v1/spaces/search` | Spaces auflisten (Body `{}` für alle erreichbaren). `is_private` markiert den Private Space.      |
| `POST /v1/data/search`   | Documents eines Space auflisten — mit `filters` und `order_by` im Body, `page`/`limit` als Query. |
| `POST /v1/agents/search` | Agenten samt vollständiger Konfiguration auflisten — siehe [Agenten](/api-reference/Agenten).     |

```bash theme={null}
curl -X POST "https://<deine-instanz>-api.localmind.ai/v1/data/search?page=1&limit=20" \
  -H "Authorization: Bearer sk-…" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": { "space_id__exact": "{space_id}", "file_type__exact": "PDF" },
    "order_by": ["-created_at"]
  }'
```

Die Antwort ist ein Pagination-Envelope (`items`, `total_items`, `total_pages`, `page`, `page_size`); jedes Element enthält die Document-Metadaten samt `processing_state`. Das Envelope-Format und die Filter-DSL beschreibt [Konventionen und Fehler](/api-reference/Konventionen-und-Fehler).

## Verwandte Seiten

<CardGroup cols={2}>
  <Card title="Dateien und Ordner" icon="folder-tree" href="/api-reference/Dateien-und-Ordner">
    Der Raw-File-Layer und die Resource-Ordner unter `/v1/folders`.
  </Card>

  <Card title="Use Cases" icon="lightbulb" href="/api-reference/Use-Cases">
    RAG, Chatbot und n8n als fertige Rezepte.
  </Card>

  <Card title="Konventionen und Fehler" icon="list-checks" href="/api-reference/Konventionen-und-Fehler">
    Base-URL, Paginierung, Filter-DSL und Statuscodes.
  </Card>

  <Card title="Dokumente (Plattform)" icon="file-text" href="/core-functions/Dokumente">
    Dieselbe Knowledge-Base aus der UI-Perspektive.
  </Card>
</CardGroup>
