> ## 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.

# Knowledge providers: managed and external vector stores

> The vector stores ModuleX retrieves from for RAG: the managed modulexdb store versus bring-your-own Qdrant, Pinecone, MongoDB Atlas, and Weaviate, with the exact connection fields, the knowledge node contract, and how retrieval is billed.

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>;
};

A knowledge provider is the vector store ModuleX searches when a workflow or chat
needs retrieval. ModuleX supports two families: the **managed store**
(`modulexdb`), where ModuleX hosts your embeddings and chunks and bills retrieval
in credits, and **external (bring-your-own) stores** — Qdrant, Pinecone, MongoDB
Atlas, and Weaviate — that you connect with your own credentials and query
without ModuleX markup. This page is the exhaustive reference for both: how each
is identified, the exact fields you supply to connect, the knowledge node
contract that consumes them, and the retrieval billing model.

For the conceptual model of retrieval-augmented generation in ModuleX, see
[Knowledge & RAG](/concepts/knowledge-rag). To create and manage a managed
knowledge base in the app, see [Knowledge overview](/platform/knowledge/overview)
and [Managed knowledge (modulexdb)](/platform/knowledge/managed). To wire a
provider into a graph, see the [Knowledge node](/workflow-builder/nodes/knowledge).

<Note>
  Knowledge bases and their provider credentials are **organization-scoped**. Every
  knowledge endpoint and every credential operation requires the **owner** or
  **admin** role (`organization_admin_required`); the `member` role is retired. The
  organization is selected by the `X-Organization-ID` header on every request — see
  [Org context & X-Organization-ID](/security/org-context).
</Note>

## The two provider families

A provider is identified on the wire by a `provider_type` value. The backend
enumerates exactly five (`KnowledgeProviderType`): one managed and four external.

| `provider_type` | Family          | Display name                | How you connect                               | Retrieval billing |
| --------------- | --------------- | --------------------------- | --------------------------------------------- | ----------------- |
| `modulexdb`     | Managed         | modulexdb (managed)         | Auto-created when you create a knowledge base | Billed in credits |
| `qdrant`        | External (BYOK) | Qdrant                      | `custom` credential (URL + API key)           | Uncosted          |
| `pinecone`      | External (BYOK) | Pinecone                    | `custom` credential (API key + environment)   | Uncosted          |
| `mongodb_atlas` | External (BYOK) | MongoDB Atlas Vector Search | `custom` credential (connection string)       | Uncosted          |
| `weaviate`      | External (BYOK) | Weaviate                    | `custom` credential (URL + API key)           | Uncosted          |

<Card title="Per-provider connection guides" icon="database">
  This page is the cross-provider reference. For the field-by-field connect steps,
  open the page for your store: [modulexdb (managed)](/integrations/knowledge-providers/modulexdb),
  [Qdrant](/integrations/knowledge-providers/qdrant),
  [Pinecone](/integrations/knowledge-providers/pinecone),
  [MongoDB Atlas](/integrations/knowledge-providers/mongodb-atlas), and
  [Weaviate](/integrations/knowledge-providers/weaviate).
</Card>

### Managed: modulexdb

The managed store is the default. When you create a knowledge base without
choosing an external provider, ModuleX provisions a `modulexdb` knowledge base: it
parses, chunks, and embeds your documents, stores the chunk vectors in its own
managed vector store, and serves vector, hybrid, and RAG-context search over
them. You do not connect anything — the store is created for you, and an internal
credential is linked to the knowledge base automatically (the "native knowledge
base = credential" pattern).

A knowledge base counts as **managed** when its `embedding_config.integration_name`
is `modulexai`. Managed retrieval and managed ingest are metered in credits (see
[Retrieval billing](#retrieval-billing) below). Choose modulexdb when you want
ModuleX to own ingestion and storage end to end and you do not already run a vector
database.

### External: bring your own vector store

An external provider connects ModuleX to a vector database you already operate.
ModuleX does **not** ingest or store documents for an external provider — you
populate and maintain your own collection/index, and ModuleX only **queries** it
at run time. Because ModuleX provides no storage and no embeddings for these,
external retrieval is **uncosted** (your vector-database vendor and your embedding
provider bill you directly — this is bring-your-own-key usage; see
[BYOK in the glossary](/reference/glossary)).

All four external providers connect with a `custom` auth credential. Each declares
its own connection fields and exposes catalog actions (`query`, plus a list/describe
pair) so the catalog UI can show what the provider can do. Choose an external
provider when you already have vectors in Qdrant, Pinecone, MongoDB Atlas, or
Weaviate and want to retrieve over them without re-ingesting into ModuleX.

<Note>
  **Important capability boundary.** External providers query an existing
  collection; they have no ModuleX-side ingestion pipeline. The document
  upload, chunking, embedding, and processing flows described in
  [Managing documents](/platform/knowledge/documents) apply to **managed** knowledge
  bases only. You are responsible for keeping an external collection populated and
  its embedding model consistent with the one you configure on the ModuleX side.
</Note>

## How a provider is connected

The connection mechanism differs by family.

<Steps>
  <Step title="Managed (modulexdb): create a knowledge base">
    Create a knowledge base — in the app under Knowledge, or with `POST /knowledge-bases`.
    ModuleX provisions the managed store, auto-discovers an embedding-capable
    credential if you do not supply one, and links an internal `modulexdb` credential
    to the new knowledge base. There is nothing else to connect. See
    [Build a RAG knowledge base](/guides/build-a-knowledge-base).
  </Step>

  <Step title="External: create a credential for the provider">
    Create a `custom` credential for the provider with its connection fields (URL,
    API key, connection string — see the per-provider tables below), in the app under
    Settings → Credentials, or with `POST /credentials`. ModuleX encrypts the secret
    at rest and tests the connection before saving where a test endpoint is declared.
    See [Managing credentials](/integrations/managing-credentials).
  </Step>

  <Step title="Reference the provider from a knowledge node">
    In a workflow, add a [Knowledge node](/workflow-builder/nodes/knowledge), set its
    `provider_type`, and point `credential_id` at the credential (or, for managed, the
    auto-linked knowledge-base credential). For external providers, also set
    `collection_name` and an `embedding_config`. The node configuration is documented
    in [The knowledge node contract](#the-knowledge-node-contract) below.
  </Step>
</Steps>

### Creating an external-provider credential

External providers use the `custom` auth schema. Create the credential with the
provider's `name` and an `auth_data` object holding its fields. Auth in every
request follows the standard model: `Authorization: Bearer mx_live_…` plus
`X-Organization-ID`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.modulex.dev/credentials \
    -H "Authorization: Bearer mx_live_xxx" \
    -H "X-Organization-ID: org_123" \
    -H "Content-Type: application/json" \
    -d '{
      "integration_name": "qdrant",
      "auth_type": "custom",
      "display_name": "Prod Qdrant",
      "make_default": true,
      "auth_data": {
        "url": "https://xyz-abc.aws.cloud.qdrant.io:6333",
        "api_key": "qdrant-api-key-..."
      }
    }'
  ```

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

  client = ModuleX(api_key="mx_live_xxx", organization_id="org_123")

  credential = client.credentials.create(
      integration_name="qdrant",
      auth_type="custom",
      display_name="Prod Qdrant",
      make_default=True,
      auth_data={
          "url": "https://xyz-abc.aws.cloud.qdrant.io:6333",
          "api_key": "qdrant-api-key-...",
      },
  )
  ```

  ```javascript JavaScript theme={null}
  import { ModuleX } from "modulex";

  const client = new ModuleX({
    apiKey: "mx_live_xxx",
    organizationId: "org_123",
  });

  const credential = await client.credentials.create({
    integration_name: "qdrant",
    auth_type: "custom",
    display_name: "Prod Qdrant",
    make_default: true,
    auth_data: {
      url: "https://xyz-abc.aws.cloud.qdrant.io:6333",
      api_key: "qdrant-api-key-...",
    },
  });
  ```
</CodeGroup>

<Note>
  The exact SDK method surface for credential creation may vary between the
  JavaScript and Python SDKs; the credential resource is not guaranteed to be at
  parity across both. Confirm against the [SDK ⇄ API parity matrix](/sdks/parity)
  and fall back to the REST call above when a method is absent. The cURL request is
  the authoritative shape.
</Note>

## Connection fields by provider

Each external provider declares its connection fields in its manifest under a
`custom` auth schema. The fields below are the exact `auth_data` keys you supply.

<Tabs>
  <Tab title="Qdrant">
    <ParamField path="url" type="string" required>
      URL of your Qdrant instance, including the port. Not treated as a secret.
      Example: `https://xyz-abc.aws.cloud.qdrant.io:6333`.
    </ParamField>

    <ParamField path="api_key" type="string">
      API key for Qdrant Cloud. Optional for local/unauthenticated instances. Stored
      encrypted. Example format: `qdrant-api-key-...`.
    </ParamField>

    ModuleX validates the connection by listing collections
    (`GET {url}/collections`, expecting a `result` field in the response). Catalog
    actions: `query`, `list_collections`, `get_collection_info`.
  </Tab>

  <Tab title="Pinecone">
    <ParamField path="api_key" type="string" required>
      Your Pinecone API key. Stored encrypted. Example format: `pcsk_...`.
    </ParamField>

    <ParamField path="environment" type="string" required>
      Pinecone environment. Not treated as a secret. Examples: `us-east-1-aws`,
      `gcp-starter`.
    </ParamField>

    ModuleX validates the connection by listing indexes
    (`GET https://api.pinecone.io/indexes`, expecting an `indexes` field). Catalog
    actions: `query`, `list_indexes`, `describe_index`. Pinecone queries accept an
    optional `namespace`.
  </Tab>

  <Tab title="MongoDB Atlas">
    <ParamField path="connection_string" type="string" required>
      MongoDB Atlas connection string. Stored encrypted (it embeds your credentials).
      Format: `mongodb+srv://user:password@cluster.mongodb.net/`.
    </ParamField>

    ModuleX validates by listing databases. Catalog actions: `query`,
    `list_databases`, `list_collections`. A query targets a `database`, `collection`,
    vector-search `index_name`, and the `path` field that holds the vectors.
  </Tab>

  <Tab title="Weaviate">
    <ParamField path="url" type="string" required>
      URL of your Weaviate instance. Not treated as a secret. Example:
      `https://your-cluster.weaviate.cloud`.
    </ParamField>

    <ParamField path="api_key" type="string">
      API key for Weaviate Cloud. Optional for local/unauthenticated instances. Stored
      encrypted. Example format: `weaviate-api-key-...`.
    </ParamField>

    ModuleX validates by checking cluster status. Catalog actions: `query`,
    `list_classes`, `get_class_stats`. Weaviate is the one provider that can search
    from text directly (`nearText`) when a `text2vec` module is enabled, so a query
    embedding may be optional; otherwise supply a `query_vector`.
  </Tab>
</Tabs>

<Warning>
  The connection fields above arrive in each manifest under a `fields` array, not the
  `setup_environment_variables` array used by tool and LLM-provider auth schemas. If
  you read the raw manifest from the catalog API, expect `fields` for the four
  knowledge providers. The manifest's `provider_type` value (`external`) is dropped
  during catalog sync and never appears in API responses — do not rely on it.
</Warning>

## Browsing providers in the catalog

The catalog exposes knowledge providers as read-only metadata for discovery; it is
not the execution surface. List providers with `GET /integrations/knowledge-providers`
or fetch one with `GET /integrations/knowledge-providers/{provider_name}`. Both
require the owner or admin role and `X-Organization-ID`. The detail endpoint
returns the provider's `actions` and its `auth_schemas` (with OAuth flags enriched
where applicable — not relevant to these `custom`-auth providers).

<CodeGroup>
  ```bash cURL theme={null}
  curl 'https://api.modulex.dev/integrations/knowledge-providers' \
    -H 'Authorization: Bearer mx_live_xxx' \
    -H 'X-Organization-ID: org_123'
  ```

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

  client = ModuleX(api_key="mx_live_xxx", organization_id="org_123")
  providers = client.integrations.list(type="knowledge_provider")
  ```

  ```javascript JavaScript theme={null}
  import { ModuleX } from "modulex";

  const client = new ModuleX({ apiKey: "mx_live_xxx", organizationId: "org_123" });
  const providers = await client.integrations.list({ type: "knowledge_provider" });
  ```
</CodeGroup>

<ResponseField name="name" type="string">
  Unique provider id, e.g. `qdrant`, `pinecone`, `mongodb_atlas`, `weaviate`.
</ResponseField>

<ResponseField name="display_name" type="string">
  Human-readable name, e.g. `MongoDB Atlas Vector Search`.
</ResponseField>

<ResponseField name="description" type="string">
  Short summary of the provider.
</ResponseField>

<ResponseField name="integration_type" type="string">
  Always `knowledge_provider` for these entries.
</ResponseField>

<ResponseField name="categories" type="string[]">
  Tags such as `Vector Database`, `semantic-search`.
</ResponseField>

<ResponseField name="actions" type="object[]">
  The provider's catalog actions (returned by the detail endpoint), each with
  `name`, `description`, `parameters`, and a derived `output_schema`.

  <Expandable title="action object">
    <ResponseField name="name" type="string">
      Action id, e.g. `query`, `list_indexes`.
    </ResponseField>

    <ResponseField name="description" type="string">
      What the action does.
    </ResponseField>

    <ResponseField name="parameters" type="object">
      Map of parameter name to `{type, description, required, default}`.
    </ResponseField>

    <ResponseField name="output_schema" type="object">
      JSON-schema description of the action's result shape.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="auth_schemas" type="object[]">
  The auth variants the provider supports. For all four external providers this is a
  single `custom` schema whose connection fields live under `fields` (see the
  warning above).
</ResponseField>

<Note>
  The catalog SDK method names and exact filter arguments may differ between SDKs and
  are not guaranteed to be at parity. Treat the cURL request as authoritative; verify
  SDK signatures against the [SDK ⇄ API parity matrix](/sdks/parity).
</Note>

## The knowledge node contract

A [Knowledge node](/workflow-builder/nodes/knowledge) is how a provider is consumed
inside a workflow. The node configuration (`KnowledgeNodeConfig`) is shared across
families; a handful of fields apply only to external providers.

<ParamField path="credential_id" type="string" required>
  Credential for the knowledge base. For managed, this is the internal credential
  auto-linked to the knowledge base; for external, the `custom` credential you
  created for the provider.
</ParamField>

<ParamField path="provider_type" type="string" default="modulexdb">
  One of `modulexdb`, `qdrant`, `pinecone`, `weaviate`, `mongodb_atlas`.
</ParamField>

<ParamField path="query" type="string" required>
  The search query. Supports `{{nodeId.path}}` references to upstream node outputs
  for dynamic queries.
</ParamField>

<ParamField path="query_from_input" type="boolean" default="false">
  When `true`, use the workflow input as the query instead of the `query` field.
</ParamField>

<ParamField path="collection_name" type="string">
  Collection/index/class name on the external store. **Required for external
  providers**; ignored by modulexdb.
</ParamField>

<ParamField path="namespace" type="string">
  Namespace for Pinecone (or similar). External only.
</ParamField>

<ParamField path="top_k" type="integer" default="5">
  Number of results to retrieve. Range 1–50.
</ParamField>

<ParamField path="min_score" type="number" default="0.3">
  Minimum similarity-score threshold, 0.0–1.0. Score scales are normalized per
  provider, so the effective floor differs by vector store.
</ParamField>

<ParamField path="max_tokens" type="integer" default="2000">
  Maximum tokens in the formatted context string (for the `context` output format).
  Range 100–10000.
</ParamField>

<ParamField path="filters" type="object">
  Provider-specific filter conditions applied to the search.
</ParamField>

<ParamField path="document_ids" type="string[]">
  Restrict retrieval to specific document ids. **Managed knowledge bases only.**
</ParamField>

<ParamField path="output_format" type="string" default="context">
  One of `chunks` (individual chunks with metadata), `context` (a single formatted
  RAG context string), or `both`.
</ParamField>

<ParamField path="include_metadata" type="boolean" default="true">
  Include chunk metadata in results.
</ParamField>

<ParamField path="include_source" type="boolean" default="true">
  Include source-document info in the formatted context.
</ParamField>

<ParamField path="embedding_config" type="object">
  Embedding settings for providers that do not embed internally. **Required for
  external vector databases** (Qdrant, Pinecone, MongoDB Atlas, and Weaviate when not
  using a text vectorizer), because ModuleX must turn the query text into a vector
  before searching your store.

  <Expandable title="embedding_config object">
    <ParamField path="integration_name" type="string" required>
      Embedding provider integration, e.g. `openai`, `cohere`.
    </ParamField>

    <ParamField path="provider_id" type="string" required>
      Provider identifier, e.g. `openai`.
    </ParamField>

    <ParamField path="model_id" type="string" required>
      Embedding model id, e.g. `text-embedding-3-small`.
    </ParamField>

    <ParamField path="credential_id" type="string">
      Credential for the embedding provider. If omitted, the organization's default
      credential for that integration is used.
    </ParamField>
  </Expandable>
</ParamField>

<Warning>
  Match the embedding model in `embedding_config` to the model your external
  collection was indexed with. ModuleX embeds the query with the model you configure;
  if it differs from the one used to build the collection (or the dimension does not
  match), similarity scores are meaningless and retrieval returns poor or empty
  results. For managed (modulexdb) knowledge bases this is handled for you — ingest
  and query use the same configured embedding model.
</Warning>

<Note>
  **Known limitation.** External-provider retrieval is implemented in the workflow
  engine's knowledge node, which embeds the query (per `embedding_config`) and calls
  the provider adapter. If an external-provider knowledge node returns "External
  provider not yet supported", retry it through the standard knowledge node path,
  which fully supports external providers. Managed (modulexdb) retrieval is supported
  on every path.
</Note>

## Retrieval billing

Whether a provider is metered depends entirely on the family.

| Operation                                      | Managed (modulexdb)                                                | External (BYOK)                                 |
| ---------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------- |
| Document ingest (parse → chunk → embed)        | `FILE_INGEST_BASE = 1` credit, plus per-chunk embedding token cost | Not applicable — you ingest into your own store |
| Retrieval (search / hybrid / retrieve-context) | `RETRIEVAL_BASE = 1` credit, plus query-embedding token cost       | Uncosted by ModuleX                             |

A knowledge base is billed only when it is managed — that is, when
`embedding_config.integration_name == "modulexai"`. One credit equals \$0.01.
External (BYOK) retrieval is not credited; your vector-database vendor and your
embedding provider bill you directly. For the full credit model see
[Credits & metering](/billing/credits).

<Note>
  On the interactive knowledge API, a managed-store credit or rate denial returns the
  flat billing `DenialEnvelope` as **402 / 403 / 429** (reject-before-write — no
  search runs). Inside a workflow run, the managed-retrieval gate is best-effort so a
  billing hiccup never crashes an in-flight run. The error envelope shapes are
  documented on [Errors & status codes](/api-reference/errors); the gate itself on
  [Usage gating & limits](/billing/usage-gating).
</Note>

<MediaEmbed id="MX-MEDIA-4110" type="image" caption={"Decision diagram contrasting the managed modulexdb path with the external BYOK path."} />

<MediaEmbed id="MX-MEDIA-4111" type="screenshot" caption={"The credential-add dialog connecting an external knowledge provider."} />

## Next steps

<CardGroup cols={2}>
  <Card title="Managed knowledge (modulexdb)" icon="server" href="/platform/knowledge/managed">
    Set up ModuleX-hosted storage and retrieval, billed in credits.
  </Card>

  <Card title="Connect an external store" icon="database" href="/integrations/knowledge-providers/qdrant">
    Bring your own Qdrant, Pinecone, MongoDB Atlas, or Weaviate.
  </Card>

  <Card title="Knowledge node" icon="diagram-project" href="/workflow-builder/nodes/knowledge">
    Wire a provider into a workflow and shape its output.
  </Card>

  <Card title="Build a RAG knowledge base" icon="book-open" href="/guides/build-a-knowledge-base">
    End to end: create a knowledge base, ingest documents, and query it.
  </Card>
</CardGroup>
