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

# Chat with your knowledge: answers grounded in your documents

> Ask questions in chat and get answers drawn from your own knowledge bases. ModuleX retrieves the most relevant passages, writes a grounded reply, and shows the sources it used.

export const MediaEmbed = ({id, type = 'screenshot', caption = '', ext, ratio = '16 / 9'}) => {
  const isVideo = type === 'video' || type === 'app_video';
  const resolvedExt = ext || (isVideo ? 'mp4' : type === 'screenshot' ? 'webp' : 'svg');
  const src = 'https://media.modulex.dev/' + id + '.' + resolvedExt;
  const [status, setStatus] = useState('loading');
  const [isDev, setIsDev] = useState(false);
  const [inView, setInView] = useState(false);
  const boxRef = useRef(null);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const h = window.location.hostname;
    setIsDev(h === 'localhost' || h === '127.0.0.1' || h.endsWith('.mintlify.app'));
  }, []);
  useEffect(() => {
    if (inView) return;
    if (typeof IntersectionObserver === 'undefined') {
      setInView(true);
      return;
    }
    const el = boxRef.current;
    if (!el) return;
    const io = new IntersectionObserver(entries => {
      if (entries.some(e => e.isIntersecting)) {
        setInView(true);
        io.disconnect();
      }
    }, {
      rootMargin: '300px'
    });
    io.observe(el);
    return () => io.disconnect();
  }, [inView]);
  if (status === 'missing') {
    if (!isDev) return null;
    return <div style={{
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'center',
      gap: '0.4rem',
      padding: '1rem 1.25rem',
      margin: '1.25rem 0',
      width: '100%',
      aspectRatio: ratio,
      boxSizing: 'border-box',
      border: '1px dashed rgba(128,128,128,0.45)',
      borderRadius: '0.75rem',
      background: 'rgba(128,128,128,0.06)',
      color: 'currentColor',
      fontSize: '0.85rem',
      lineHeight: 1.45
    }}>
        <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: '0.5rem',
      opacity: 0.75
    }}>
          <span aria-hidden="true">🎬</span>
          <code style={{
      fontSize: '0.75rem'
    }}>{id}</code>
          <span style={{
      fontSize: '0.65rem',
      textTransform: 'uppercase',
      letterSpacing: '0.04em',
      padding: '0.1rem 0.4rem',
      borderRadius: '0.4rem',
      background: 'rgba(128,128,128,0.18)'
    }}>
            {type}
          </span>
        </div>
        <div style={{
      opacity: 0.9
    }}>{caption || 'Media not uploaded yet.'}</div>
        <div style={{
      fontSize: '0.7rem',
      opacity: 0.5
    }}>
          Upload to R2 as <code>{id}.{resolvedExt}</code> — preview only, hidden in production.
        </div>
      </div>;
  }
  const mediaStyle = {
    display: status === 'loaded' ? 'block' : 'none',
    width: '100%',
    height: 'auto',
    borderRadius: '0.75rem'
  };
  const media = isVideo ? <video src={inView ? src : undefined} autoPlay loop muted playsInline preload="metadata" onLoadedData={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} /> : <img src={inView ? src : undefined} alt={caption} onLoad={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} />;
  return <figure style={{
    margin: '1.25rem 0'
  }}>
      <div ref={boxRef} style={status === 'loaded' ? {
    width: '100%'
  } : {
    width: '100%',
    aspectRatio: ratio,
    borderRadius: '0.75rem',
    background: 'rgba(128,128,128,0.06)'
  }}>
        {media}
      </div>
      {status === 'loaded' && caption ? <figcaption style={{
    marginTop: '0.5rem',
    textAlign: 'center',
    fontSize: '0.85rem',
    opacity: 0.7
  }}>
          {caption}
        </figcaption> : null}
    </figure>;
};

Chat with your knowledge means asking a question in plain language and getting an answer drawn from the documents you have loaded into ModuleX. Instead of replying from general training, the [Assistant](/concepts/assistant) finds the most relevant passages in your [knowledge bases](/platform/knowledge/overview), writes an answer grounded in them, and shows you the sources it used.

This is retrieval-augmented generation (RAG), explained end to end in [knowledge and RAG](/concepts/knowledge-rag). This page covers the everyday version: how to attach a knowledge base, what a cited answer looks like, and the difference between managed and bring-your-own retrieval.

<MediaEmbed id="MX-MEDIA-3340" type="app_video" caption={"A chat answering a question from a knowledge base, with the search step and source chips visible."} />

## What it is

Every chat in ModuleX runs on the agentic [Assistant](/concepts/assistant). When you ask something, the Assistant decides — on its own, per message — whether your question is answerable from your stored documents. If it is, the Assistant searches the right knowledge base, reads the passages it finds, and answers from them. If your question is general chit-chat or has nothing to do with your documents, it skips the search and just answers.

This is different from older "always search every message" behavior. Retrieval here is on demand: the Assistant only reaches for your documents when they are likely to help.

<CardGroup cols={3}>
  <Card title="You ask a question" icon="message-square">
    Type a question in plain language, the same as any other chat message.
  </Card>

  <Card title="It finds the passages" icon="search">
    The Assistant searches the relevant [knowledge base](/platform/knowledge/overview) and pulls the most similar passages.
  </Card>

  <Card title="You get a cited answer" icon="quote">
    The reply is written from those passages, with the source documents shown alongside it.
  </Card>
</CardGroup>

<Note>
  Who can do this: chat requires an **owner** or **admin** role in the organization. The `member` role has been retired, so a plain member cannot open chat or query knowledge. See [roles and permissions](/security/roles-permissions).
</Note>

## Attach a knowledge base

You do not attach a knowledge base to a single chat by hand. Every active knowledge base in your organization is already available to the Assistant — it picks the right one for each question by matching your wording to each base's name and description.

So "attaching" is really two steps you do once, in the [knowledge](/platform/knowledge/overview) area of the app, before you chat.

<Steps>
  <Step title="Create a knowledge base">
    In the knowledge area, create a base and give it a clear **name** and **description** — for example, "Product Docs — installation, configuration, and troubleshooting guides." The Assistant uses that name and description to decide which base to search, so descriptive wording helps it choose correctly. See [knowledge overview](/platform/knowledge/overview).
  </Step>

  <Step title="Upload your documents">
    Add the files you want answerable — PDFs, Word documents, text, Markdown, HTML, CSV, JSON, and spreadsheets are supported. ModuleX processes each file in the background: it splits the document into passages and prepares them for search. See [managing documents](/platform/knowledge/documents).
  </Step>

  <Step title="Wait for processing to finish">
    A document is only searchable once its status reaches **completed**. While it is `pending` or `processing`, its content will not appear in answers yet. You can watch each file's status in the knowledge area.
  </Step>

  <Step title="Ask a question in chat">
    Open a chat and ask. The Assistant matches your question to the right base, searches it, and answers from what it finds. No mode to switch and nothing to attach per message.
  </Step>
</Steps>

<Note>
  If your organization has **no** knowledge bases, the Assistant answers from its own general knowledge instead of searching. Knowledge chat starts working the moment you have a base with at least one processed document.
</Note>

<Accordion title="Why does the Assistant sometimes not search my documents?">
  The Assistant only searches when your question looks answerable from your stored documents. A general question ("write me a haiku") will not trigger a search. If you expect an answer from a specific document but the Assistant answered generally, ask more specifically — name the topic or the document — so it can match your wording to the right base. If a relevant base still is not found, check that the document finished processing and that the base's name and description describe its contents.
</Accordion>

## Ask and get a cited answer

When the Assistant searches your knowledge, the reply is grounded in real passages and shows you where each came from. A cited answer has three parts you can see in the thread.

<CardGroup cols={3}>
  <Card title="The search step" icon="search">
    A short step appears in the thread while the Assistant searches, so you can see it is drawing on your documents rather than guessing.
  </Card>

  <Card title="The grounded answer" icon="file-text">
    The reply is written from the retrieved passages and streams in live, the same as any chat answer.
  </Card>

  <Card title="The sources" icon="files">
    The documents the answer drew from are listed as source chips. Each source is the filename of a document in the searched base.
  </Card>
</CardGroup>

Behind the scenes, each retrieved passage carries a similarity score and the filename of the document it came from. The Assistant uses the passages to write the answer and surfaces the document names so you can verify the reply against the originals. Passages below a relevance floor are dropped, so a question with no good match returns no sources — and the Assistant will tell you it could not find anything relevant rather than inventing an answer.

<Note>
  A cited answer points you to the **document** a passage came from, by filename. To read the full context, open that document in the [knowledge](/platform/knowledge/documents) area. Treat citations as a pointer to verify against, not a page-exact reference.
</Note>

<Accordion title="What if nothing relevant is found?">
  If the search returns no passages above the relevance floor, the Assistant has nothing from your documents to ground an answer in. Rather than guess, it tells you it could not find anything relevant. Try rewording the question, confirm the document finished processing, or check that you uploaded it to a base whose name and description match the topic.
</Accordion>

<Accordion title="Can the Assistant search more than one knowledge base for one question?">
  The Assistant searches **one** knowledge base per search, choosing the base that best matches your question. If your knowledge is spread across several bases and a question spans them, ask follow-up questions that point at each topic, or keep closely related documents in the same base so a single search covers them. For searching across multiple bases programmatically, see [knowledge and RAG](/concepts/knowledge-rag).
</Accordion>

## Managed vs bring-your-own retrieval

Where your knowledge is stored and embedded determines whether searching it costs ModuleX credits. There are two paths, and the difference is purely about billing — the chat experience is the same either way.

<CardGroup cols={2}>
  <Card title="Managed retrieval (modulexdb)" icon="server" href="/integrations/knowledge-providers/modulexdb">
    Your knowledge base is hosted by ModuleX on the managed vector store, modulexdb. Searching it is **billed in credits** — each search reserves a small base charge, plus the cost of turning your question into a search vector.
  </Card>

  <Card title="Bring your own (BYOK)" icon="key" href="/platform/knowledge/external-providers">
    Your knowledge base lives in your own vector store, such as Qdrant, Pinecone, MongoDB Atlas, or Weaviate. Searching it is **not billed in credits** by ModuleX — that usage is yours.
  </Card>
</CardGroup>

A knowledge base is **managed** when its embedding runs through ModuleX-managed models; otherwise it is BYOK. You set this when you create the base, and you can mix both in one organization. For what a credit is and what consumes them, see [credits and metering](/billing/credits); for the storage options, see [knowledge providers](/integrations/knowledge-providers/overview).

<Note>
  Only **managed** retrieval (a base hosted on modulexdb) is metered. Searching a BYOK base is uncosted by ModuleX. The reply, the sources, and the in-chat search step look identical regardless of which one you used.
</Note>

### When credits run out

Because managed retrieval is metered, it goes through the ModuleX billing gate like every other managed action. If your organization is out of credits or over a limit, a managed knowledge search is denied before it runs and the chat surfaces a `DenialEnvelope` as a `402`, `403`, or `429` response.

<Accordion title="What a billing denial looks like">
  A denial is a small, flat JSON object — for example `{code, layer, key, current, limit, reason}` — rather than a normal answer. The `layer` field tells you why: `credit` and `wallet` map to `402`, `quota` maps to `403`, and `rate` maps to `429`. See [usage gating and limits](/billing/usage-gating) for each case, and [errors and status codes](/api-reference/errors) for the full envelope. BYOK retrieval does not hit this gate, because it is not credit-metered.
</Accordion>

## Try it from your own code

The app does this for you, but you can drive the same knowledge search from your own code. Two pieces are involved: starting a chat turn (the [Assistant](/concepts/assistant) decides when to search), and — if you want to search a knowledge base directly — calling the search endpoint yourself.

The direct search below runs one semantic search over a single knowledge base and returns the matching passages with their scores and source filenames. Every request authenticates with `Authorization: Bearer mx_live_…` plus your `X-Organization-ID` header — see [authentication](/api-reference/authentication).

<CodeGroup>
  ```bash cURL theme={null}
  # Search one knowledge base directly. The response includes each match's
  # content, similarity score, and source document filename.
  curl -X POST https://api.modulex.dev/knowledge-bases/{knowledge_base_id}/search \
    -H "Authorization: Bearer mx_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "X-Organization-ID: org_a1b2c3d4e5f6" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "What is our refund window?",
      "top_k": 5,
      "min_score": 0.3
    }'
  ```

  ```python Python theme={null}
  from modulex import Modulex

  client = Modulex(
      api_key="mx_live_xxxxxxxxxxxxxxxxxxxxxxxx",
      organization_id="org_a1b2c3d4e5f6",
  )

  # Search one knowledge base. Each match carries content, a score, and a source.
  results = await client.knowledge_bases.search(
      knowledge_base_id="7c1e2f80-1111-2222-3333-444455556666",
      query="What is our refund window?",
      top_k=5,
      min_score=0.3,
  )

  for match in results["matches"]:
      print(match["score"], match["document_filename"], match["content"][:200])
  ```

  ```javascript JavaScript theme={null}
  import { Modulex } from "modulex-js";

  const client = new Modulex({
    apiKey: "mx_live_xxxxxxxxxxxxxxxxxxxxxxxx",
    organizationId: "org_a1b2c3d4e5f6",
  });

  // Search one knowledge base. Each match carries content, a score, and a source.
  const results = await client.knowledgeBases.search({
    knowledgeBaseId: "7c1e2f80-1111-2222-3333-444455556666",
    query: "What is our refund window?",
    topK: 5,
    minScore: 0.3,
  });

  for (const match of results.matches) {
    console.log(match.score, match.documentFilename, match.content.slice(0, 200));
  }
  ```
</CodeGroup>

<Note>
  A direct search against a **managed** base goes through the billing gate and is metered in credits; a search against a **BYOK** base is not. To let the Assistant decide when and what to search inside a conversation, start an Assistant turn (`POST /assistant/chat`) instead — covered in [chat overview](/platform/chat/overview).
</Note>

## Where to go next

<CardGroup cols={2}>
  <Card title="How knowledge and RAG work" icon="book-open" href="/concepts/knowledge-rag">
    The full picture: ingesting documents, retrieval, and how grounded answers are produced.
  </Card>

  <Card title="Knowledge overview" icon="database" href="/platform/knowledge/overview">
    Create and manage the knowledge bases that power retrieval across chats and workflows.
  </Card>

  <Card title="Managing documents" icon="files" href="/platform/knowledge/documents">
    Upload files, watch processing, and keep your knowledge current.
  </Card>

  <Card title="Meet the Assistant" icon="bot" href="/concepts/assistant">
    The agentic chat that decides when to search your knowledge and how to answer.
  </Card>
</CardGroup>
