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

# How to Use API Results

> Turn a recall response into a grounded LLM answer.

A recall response is structured JSON. Your model wants prose. This page shows how to bridge that gap - turning a `RetrievalResult` into a clean, grounded prompt for an LLM.

The same pattern works for [`full_recall`](/api-reference/endpoint/full-recall), [`recall_preferences`](/api-reference/endpoint/recall-preferences), and combinations of the two.

***

## 1. What you get back

The retrieval endpoints (`full_recall` and `recall_preferences`) return the same core response shape:

```json theme={"dark"}
{
  "chunks": [
    {
      "chunk_uuid": "doc-001_chunk_0",
      "source_title": "my_document.pdf",
      "chunk_content": "Text content of the retrieved chunk...",
      "relevancy_score": 1.09,
      "extra_context_ids": ["ctx-id-1"],
      "additional_metadata": { ... }
    }
  ],
  "graph_context": {
    "query_paths": [
      {
        "triplets": [
          {
            "source": { "name": "EntityA" },
            "target": { "name": "EntityB" },
            "relation": {
              "canonical_predicate": "DEPENDS_ON",
              "context": "reason text",
              "temporal_details": "2024-01"
            }
          }
        ]
      }
    ],
    "chunk_id_to_group_ids": {
      "doc-001_chunk_0": ["group-1"]
    },
    "chunk_relations": [
      {
        "group_id": "group-1",
        "triplets": [ ... ]
      }
    ]
  },
  "additional_context": {
    "ctx-id-1": {
      "source_title": "related_doc.pdf",
      "chunk_content": "Extra related content..."
    }
  }
}
```

Four things matter for prompt construction:

* **`chunks`** - the primary retrieval output. Ranked by relevance; preserve the order HydraDB returns.
* **`graph_context.query_paths`** - entity traversal paths derived from your query. Useful for relational reasoning. See [Context Graphs](/essentials/context-graphs).
* **`graph_context.chunk_relations`** + **`chunk_id_to_group_ids`** - per-chunk graph relations grouped by `group_id`, so you can attach the right triplets to each chunk.
* **`additional_context`** - a map keyed by `chunk_uuid`. When a chunk includes `extra_context_ids`, use those IDs to look up related chunks here.

Don't pass the raw object to the LLM. Convert it into a structured context string first.

***

## 2. Transforming the response into LLM context

The helper below takes a `RetrievalResult` and produces a single string with:

1. **Entity paths** from `graph_context.query_paths` (when present)
2. **Ranked chunks** in server order, each preceded by `Source: <title>`
3. **Graph relations per chunk**, matched via `chunk_id_to_group_ids` → `chunk_relations`
4. **Extra context** for any `extra_context_ids` attached to a chunk

The output is delimited by `=== ENTITY PATHS ===` and `=== CONTEXT ===` blocks so the LLM can read it cleanly.

First, call the recall endpoint with the parameters you need:

<Tabs>
  <Tab title="Python">
    ```python theme={"dark"}
    import httpx

    response = httpx.post(
        "https://api.hydradb.com/recall/full_recall",
        headers={"Authorization": "Bearer YOUR_HYDRA_DB_API_KEY"},
        json={
            "tenant_id": "your-tenant",
            "sub_tenant_id": "your-sub-tenant",
            "query": "How does authentication work?",
            "max_results": 5,
            "mode": "fast",
            "alpha": 0.8,
            "recency_bias": 0,
            "graph_context": True,
            "search_forceful_relations": True,
            "metadata_filters": {
                "category": "engineering",
            },
        }
    )

    result = response.json()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"dark"}
    const res = await fetch("https://api.hydradb.com/recall/full_recall", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer YOUR_HYDRA_DB_API_KEY",
      },
      body: JSON.stringify({
        tenant_id: "your-tenant",
        sub_tenant_id: "your-sub-tenant",
        query: "How does authentication work?",
        max_results: 5,
        mode: "fast",
        alpha: 0.8,
        recency_bias: 0,
        graph_context: true,
        search_forceful_relations: true,
        metadata_filters: {
          category: "engineering",
        },
      }),
    });

    const result = await res.json();
    ```
  </Tab>
</Tabs>

Then pass the response into the helper below to build a context string:

<CodeGroup>
  ```bash cURL theme={"dark"}
  # build_context_string is a client-side utility that converts the data
  # returned by /recall/full_recall or /recall/recall_preferences into a
  # formatted context string.
  #
  # The logic remains the same, except that recall objects are now dictionaries
  # instead of JSON objects.
  #
  # SDKs access values using dot notation (result.xyz), whereas API responses
  # use dictionary access (result.get("xyz")).
  ```

  ```typescript TypeScript SDK theme={"dark"}
  export interface VectorStoreChunk {
    chunk_uuid: string;
    source_id: string;
    chunk_content: string;
    source_type: string;
    source_upload_time: string;
    source_title: string;
    source_last_updated_time: string;
    layout: string | null;
    relevancy_score: number | null;
    additional_metadata: Record<string, unknown> | null;
    metadata: Record<string, unknown> | null;
    extra_context_ids: string[] | null;
  }

  export interface Entity {
    name: string;
    type: string;
    namespace: string;
    entity_id: string;
    identifier: string | null;
  }

  export interface RelationEvidence {
    canonical_predicate: string;
    raw_predicate: string;
    context: string;
    confidence: number;
    temporal_details: string | null;
    timestamp: string;
    relationship_id: string;
    chunk_id: string | null;
    source_entity_id: string | null;
    target_entity_id: string | null;
  }

  export interface PathTriplet {
    source: Entity;
    relation: RelationEvidence;
    target: Entity;
  }

  export interface ScoredPathResponse {
    triplets: PathTriplet[];
    relevancy_score: number;
    combined_context: string | null;
    group_id: string | null;
    source_chunk_ids: string[] | null;
  }

  export interface GraphContext {
    query_paths: ScoredPathResponse[];
    chunk_relations: ScoredPathResponse[];
    chunk_id_to_group_ids: Record<string, string[]>;
  }

  export interface SourceInfo {
    id: string;
    title: string;
    type: string;
    description: string;
    url: string;
    timestamp: string;
    metadata: Record<string, unknown>;
    additional_metadata: Record<string, unknown>;
  }

  export interface RetrievalResult {
    chunks: VectorStoreChunk[];
    sources: SourceInfo[];
    graph_context: GraphContext;
    additional_context: Record<string, VectorStoreChunk>;
  }

  function formatPathChain(path: ScoredPathResponse): string {
    return path.triplets
      .map((t) => {
        let str = `[${t.source.name}] -> ${t.relation.canonical_predicate} -> [${t.target.name}]`;
        if (t.relation.context) str += `: ${t.relation.context}`;
        if (t.relation.temporal_details) str += ` [Time: ${t.relation.temporal_details}]`;
        return str;
      })
      .join("\n  ↳ ");
  }

  export function buildContextString(result: RetrievalResult): string {
    const lines: string[] = [];
    const gc = result.graph_context;

    // --- Entity paths -----------------------------------------------------------
    if (gc?.query_paths?.length) {
      lines.push("=== ENTITY PATHS ===");
      for (const path of gc.query_paths) {
        lines.push(formatPathChain(path));
      }
      lines.push("");
    }

    // --- Chunks -----------------------------------------------------------------
    if (result.chunks?.length) {
      lines.push("=== CONTEXT ===");
      for (let i = 0; i < result.chunks.length; i++) {
        const chunk = result.chunks[i];
        lines.push(`Chunk ${i + 1}`);
        lines.push(`Source: ${chunk.source_title}`);
        lines.push(chunk.chunk_content);

        // Graph relations for this chunk
        if (gc?.chunk_id_to_group_ids && gc.chunk_relations) {
          const groupIds = gc.chunk_id_to_group_ids[chunk.chunk_uuid] || [];
          const relations = gc.chunk_relations.filter(
            (r) => r.group_id && groupIds.includes(r.group_id)
          );
          if (relations.length) {
            lines.push("Graph Relations:");
            for (const rel of relations) {
              for (const t of rel.triplets) {
                let line = `  [${t.source.name}] -> ${t.relation.canonical_predicate} -> [${t.target.name}]: ${t.relation.context}`;
                if (t.relation.temporal_details) line += ` [Time: ${t.relation.temporal_details}]`;
                lines.push(line);
              }
            }
          }
        }

        // Extra context
        if (chunk.extra_context_ids?.length && result.additional_context) {
          const extras = chunk.extra_context_ids
            .map((id) => result.additional_context[id])
            .filter(Boolean);
          if (extras.length) {
            lines.push("Extra Context:");
            for (const extra of extras) {
              lines.push(`  Related Context (${extra.source_title}): ${extra.chunk_content}`);
            }
          }
        }

        lines.push("---");
        lines.push("");
      }
    }

    return lines.join("\n");
  }
  ```

  ```python Python SDK theme={"dark"}
  from __future__ import annotations

  from typing import Any


  def _format_path_chain(path: Any) -> str:
      """Format a ScoredPathResponse into a readable chain string.

      Each triplet becomes: [src] -> predicate -> [tgt]: context [Time: ...]
      Multiple triplets are joined with ↳ to show traversal direction.
      """
      triplets = getattr(path, "triplets", None) or []
      parts = []
      for t in triplets:
          src  = t.source.name if t.source else ""
          tgt  = t.target.name if t.target else ""
          rel  = t.relation
          pred = rel.canonical_predicate if rel else ""
          line = f"[{src}] -> {pred} -> [{tgt}]"
          ctx  = rel.context if rel else None
          if ctx:
              line += f": {ctx}"
          temporal = rel.temporal_details if rel else None
          if temporal:
              line += f" [Time: {temporal}]"
          parts.append(line)

      return "\n  ↳ ".join(parts)


  def build_context_string(result) -> str:
      """
      Build a formatted context string from a HydraDB full_recall response.

      Combines entity paths, chunk content, graph relations, and extra context
      into a single readable string ready to pass into an LLM prompt.
      """
      lines: list[str] = []
      gc = result.graph_context

      # --- Entity paths -----------------------------------------------------------
      query_paths = gc.query_paths if gc else []
      if query_paths:
          lines.append("=== ENTITY PATHS ===")
          for path in query_paths:
              lines.append(_format_path_chain(path))
          lines.append("")

      # --- Chunks -----------------------------------------------------------------
      chunks = result.chunks or []
      additional_context = result.additional_context or {}
      chunk_id_to_group_ids = gc.chunk_id_to_group_ids if gc else {}
      chunk_relations = gc.chunk_relations if gc else []

      if chunks:
          lines.append("=== CONTEXT ===")
          for i, chunk in enumerate(chunks):
              lines.append(f"Chunk {i + 1}")
              source = chunk.source_title or ""
              if source:
                  lines.append(f"Source: {source}")
              lines.append(chunk.chunk_content or "")

              # Graph relations for this chunk
              chunk_uuid = chunk.chunk_uuid or ""
              if chunk_uuid and chunk_id_to_group_ids and chunk_relations:
                  group_ids = chunk_id_to_group_ids.get(chunk_uuid, [])
                  relevant_relations = [
                      r for r in chunk_relations
                      if r.group_id in group_ids
                  ]
                  if relevant_relations:
                      lines.append("Graph Relations:")
                      for rel in relevant_relations:
                          for triplet in (rel.triplets or []):
                              src  = triplet.source.name if triplet.source else ""
                              tgt  = triplet.target.name if triplet.target else ""
                              pred = triplet.relation.canonical_predicate if triplet.relation else ""
                              ctx  = triplet.relation.context if triplet.relation else ""
                              line = f"  [{src}] -> {pred} -> [{tgt}]: {ctx}"
                              temporal = triplet.relation.temporal_details if triplet.relation else None
                              if temporal:
                                  line += f" [Time: {temporal}]"
                              lines.append(line)

              # Extra context
              extra_ids = getattr(chunk, "extra_context_ids", None) or []
              if extra_ids and additional_context:
                  extras = [
                      additional_context[eid]
                      for eid in extra_ids
                      if eid in additional_context
                  ]
                  if extras:
                      lines.append("Extra Context:")
                      for extra in extras:
                          extra_source = extra.source_title or ""
                          extra_content = extra.chunk_content or ""
                          lines.append(f"  Related Context ({extra_source}): {extra_content}")

              lines.append("---")
              lines.append("")

      return "\n".join(lines)
  ```
</CodeGroup>

***

## 3. Feeding the context into your LLM

Once you have a context string, call your model with a grounded system prompt:

<CodeGroup>
  ```bash cURL theme={"dark"}
  # HydraDB does not wrap LLM calls. Pass your context string to any LLM provider.
  # Example using OpenAI's chat completions endpoint directly:
  curl -X POST 'https://api.openai.com/v1/chat/completions' \
    -H "Authorization: Bearer $OPENAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "gpt-4o",
      "messages": [
        {
          "role": "system",
          "content": "Answer the user'\''s question using only the context below. If the answer is not in the context, say you don'\''t know."
        },
        {
          "role": "user",
          "content": "Context:\n<your_context_string>\n\nQuestion: <your_question>"
        }
      ]
    }'
  ```

  ```typescript TypeScript SDK theme={"dark"}
  import OpenAI from "openai";

  const client = new OpenAI({ apiKey: "YOUR_OPENAI_API_KEY" });
  const question = "How does authentication work?";

  const completion = await client.chat.completions.create({
    model: "gpt-4o",
    messages: [
      {
        role: "system",
        content:
          "You are a helpful assistant. Answer the user's question using only the context provided. If the answer is not in the context, say you don't know.",
      },
      {
        role: "user",
        content: `Context:\n${context}\n\nQuestion: ${question}`,
      },
    ],
  });

  console.log(completion.choices[0].message.content);
  ```

  ```python Python SDK theme={"dark"}
  from openai import OpenAI

  client = OpenAI(api_key="YOUR_OPENAI_API_KEY")
  question = "How does authentication work?"

  completion = client.chat.completions.create(
      model="gpt-4o",
      messages=[
          {
              "role": "system",
              "content": "You are a helpful assistant. Answer the user's question using only the context provided. If the answer is not in the context, say you don't know.",
          },
          {
              "role": "user",
              "content": f"Context:\n{context}\n\nQuestion: {question}",
          },
      ],
  )

  print(completion.choices[0].message.content)
  ```
</CodeGroup>

***

## 4. Combining Knowledge and Memories

For personalised answers, run `full_recall` and `recall_preferences` in parallel, format each result, and combine them into one prompt. There's no required ordering - preferences before docs is purely for readability.

<CodeGroup>
  ```bash cURL theme={"dark"}
  # Combine both recall results in your application layer.
  # 1. Run full_recall for Knowledge:
  curl -X POST 'https://api.hydradb.com/recall/full_recall' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"tenant_id": "acme_corp", "query": "refund policy", "mode": "thinking"}'

  # 2. Run recall_preferences for Memories:
  curl -X POST 'https://api.hydradb.com/recall/recall_preferences' \
    -H "Authorization: Bearer $HYDRA_DB_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"tenant_id": "acme_corp", "sub_tenant_id": "user_123", "query": "answer style preferences"}'

  # 3. Merge both context strings in your application before prompting the LLM.
  ```

  ```typescript TypeScript SDK theme={"dark"}
  const memoryCtx = buildContextString(memoryResult);
  const knowledgeCtx = buildContextString(knowledgeResult);

  const prompt =
    `User preferences:\n${memoryCtx}\n\n` +
    `Relevant docs:\n${knowledgeCtx}\n\n` +
    `Question: ${question}`;
  ```

  ```python Python SDK theme={"dark"}
  memory_ctx = build_context_string(memory_result)
  knowledge_ctx = build_context_string(knowledge_result)

  prompt = (
      f"User preferences:\n{memory_ctx}\n\n"
      f"Relevant docs:\n{knowledge_ctx}\n\n"
      f"Question: {question}"
  )
  ```
</CodeGroup>

If memory recall fails or times out, fall back to the Knowledge-only prompt - personalisation is an enhancement, not a hard dependency.

***

## 5. Practical guidance

* **Preserve server order.** Don't re-sort chunks client-side.
* **Start small on chunks.** `max_results: 10` is a reasonable default. Drop to 5 if you hit token limits, raise to 20 if you rerank downstream.
* **Use graph context selectively.** It improves relational queries and bloats simple lookups. See [Context Graphs](/essentials/context-graphs).
* **Always ground the model.** A system prompt like "answer only from the provided context" prevents the model from inventing answers when retrieval is thin.
* **Format consistently.** Whatever section delimiters you choose (`=== CONTEXT ===`, `Chunk N`, `Source:`), keep them stable across calls so the model learns the structure.

***

## 6. Common mistakes

| Mistake                                                      | What goes wrong                                                            | Fix                                                                                                     |
| ------------------------------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Passing the raw result object to the LLM                     | Wastes tokens on IDs and metadata; the model struggles to read the content | Format into a context string first.                                                                     |
| Ignoring Memories when personalisation matters               | Answers feel generic                                                       | Call `recall_preferences` and include it in the prompt.                                                 |
| Including too many chunks                                    | Token overflow or answer quality drops                                     | Start at `max_results: 10`; reduce if needed.                                                           |
| Re-sorting chunks client-side                                | Overrides HydraDB's ranking                                                | Preserve the server-returned order.                                                                     |
| Missing a grounding instruction                              | The model invents answers when context is thin                             | System prompt: answer only from the provided context.                                                   |
| Expecting graph fields without requesting them               | Graph context will not be populated                                        | Set `graph_context: true` on the recall call.                                                           |
| Looking up `chunk_relations` without `chunk_id_to_group_ids` | The right relations don't attach to the right chunk                        | Use `chunk_id_to_group_ids[chunk_uuid]` to find the `group_id`s, then filter `chunk_relations` by them. |

***

## Related

* [Recall](/essentials/recall) - request parameters and response shape
* [Memories](/essentials/memories) - what `recall_preferences` searches
* [Knowledge](/essentials/knowledge) - what `full_recall` searches
* [Context Graphs](/essentials/context-graphs) - what `graph_context` fields contain
