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

# MongoDB Atlas Vector Search

> Connect MongoDB Atlas Vector Search to ModuleX as a bring-your-own-key knowledge provider: create the credential from a connection string, configure a workflow knowledge node against a database.collection, and run uncosted $vectorSearch retrieval. Every field, type, default, and error.

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

MongoDB Atlas Vector Search is a **bring-your-own-key (BYOK) knowledge provider** for
ModuleX. You connect your own Atlas cluster with a connection string, and ModuleX runs
semantic retrieval against an existing collection that already holds vector embeddings —
ModuleX does not host or ingest the vectors. The provider name on the wire is
`mongodb_atlas`.

It is one of the [knowledge providers](/integrations/knowledge-providers/overview) ModuleX
can retrieve from. Unlike the managed store
[modulexdb](/integrations/knowledge-providers/modulexdb) — where ModuleX hosts the vectors,
runs the embeddings, and meters the work in [credits](/billing/credits) — MongoDB Atlas is
**BYOK and uncosted**: retrieval runs against your cluster and is **never credited** by
ModuleX. Your upstream Atlas and embedding-provider usage is billed by those providers
directly. The other BYOK stores are
[Qdrant](/integrations/knowledge-providers/qdrant),
[Pinecone](/integrations/knowledge-providers/pinecone), and
[Weaviate](/integrations/knowledge-providers/weaviate).

<Note>
  This page is the **provider-catalog entry** for `mongodb_atlas`: how to connect your cluster,
  how to configure a [workflow knowledge node](/workflow-builder/nodes/knowledge) to retrieve
  from it, and the exact request and result shapes. For the broader managed-vs-BYOK model and
  the RAG concept, see [Knowledge & RAG](/concepts/knowledge-rag) and
  [External knowledge providers](/platform/knowledge/external-providers).
</Note>

## What Atlas does in ModuleX

MongoDB Atlas is a **retrieval-only** provider in ModuleX. ModuleX queries your cluster's
existing vector index; it does not upload documents, create indexes, or chunk and embed
content for you. You are responsible for populating the collection with documents that
contain an embedding field and for creating an Atlas Vector Search index over that field.

Because Atlas requires a precomputed query vector for similarity search, ModuleX embeds the
search text first (using an embedding credential you configure) and then runs Atlas's
`$vectorSearch` aggregation. Two surfaces use the provider:

<CardGroup cols={2}>
  <Card title="Workflow knowledge node" icon="diagram-project" href="/workflow-builder/nodes/knowledge">
    A [knowledge node](/workflow-builder/nodes/knowledge) with `provider_type` set to
    `mongodb_atlas` retrieves matching documents inside a running workflow and feeds them to
    a downstream LLM or agent node. This is the primary BYOK retrieval path.
  </Card>

  <Card title="Integration catalog entry" icon="book-open" href="/integrations/catalog">
    Atlas appears in the [integration catalog](/integrations/catalog) as a
    `knowledge_provider` with `query`, `list_databases`, and `list_collections` actions and
    a connection-string credential. The catalog is the browse and credential-setup surface.
  </Card>
</CardGroup>

<MediaEmbed id="MX-MEDIA-4150" type="image" caption={"BYOK retrieval flow from a ModuleX knowledge node to MongoDB Atlas Vector Search."} />

## Connect MongoDB Atlas

You connect Atlas by storing a credential that holds your cluster connection string. The
credential is created against the `mongodb_atlas` integration with `auth_type` set to
`custom`. You can create it in the app or over the API.

### Credential fields

The Atlas auth schema has a single field.

<ParamField path="connection_string" type="string" required>
  Your MongoDB Atlas connection string, in `mongodb+srv://...` form (for example
  `mongodb+srv://user:password@cluster.mongodb.net/`). This value is **sensitive** — it is
  encrypted at rest and never returned in full by the API. ModuleX uses it to open an async
  client (`motor`) against your cluster for every retrieval. The connecting database user
  needs read access to the database and collection you intend to query.
</ParamField>

<Warning>
  The connection string embeds your cluster credentials. Scope the Atlas database user to the
  data ModuleX needs to read, and rotate the credential if the string is exposed. ModuleX
  encrypts stored credential data; see [Data security & encryption](/security/data-encryption).
</Warning>

### Connect in the app

Connecting a knowledge provider is an org-level write, so it requires the **owner** or
**admin** role (see [Roles & permissions](/security/roles-permissions); the `member` role is
retired). In the org settings, open the integration catalog, select **MongoDB Atlas Vector
Search**, choose **Add credential**, paste the connection string, and save. ModuleX validates
the credential by connecting and listing databases before it is stored.

<MediaEmbed id="MX-MEDIA-4151" type="screenshot" caption={"The Add-credential dialog for MongoDB Atlas Vector Search."} />

### Connect over the API

Every request authenticates with `Authorization: Bearer mx_live_…` plus the
`X-Organization-ID` header (the backend also accepts `X-API-KEY`); see
[Authentication](/api-reference/authentication). Creating a credential requires the
owner/admin role.

Send `auth_type: "custom"` and put the connection string in `auth_data` keyed by the field
name `connection_string`. A successful create returns `201` with a `CredentialResponse`; keep
the returned `credential_id` — the knowledge node references it.

<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": "mongodb_atlas",
      "auth_type": "custom",
      "auth_data": {
        "connection_string": "mongodb+srv://user:password@cluster.mongodb.net/"
      },
      "display_name": "Atlas production",
      "make_default": true
    }'
  ```

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

  mx = Modulex(api_key="mx_live_xxx", organization_id="org_123")

  async def main():
      credential = await mx.credentials.create(
          integration_name="mongodb_atlas",
          auth_type="custom",
          auth_data={
              "connection_string": "mongodb+srv://user:password@cluster.mongodb.net/",
          },
          display_name="Atlas production",
          make_default=True,
      )
      print(credential["credential_id"])

  asyncio.run(main())
  ```

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

  const mx = new Modulex({
    apiKey: "mx_live_xxx",
    organizationId: "org_123",
  });

  const credential = await mx.credentials.create({
    integrationName: "mongodb_atlas",
    authType: "custom",
    authData: {
      connection_string: "mongodb+srv://user:password@cluster.mongodb.net/",
    },
    displayName: "Atlas production",
    makeDefault: true,
  });
  console.log(credential.credential_id);
  ```
</CodeGroup>

<Note>
  The `POST /credentials` request body and the `custom` auth type are verified against the
  backend. The exact SDK method names and parameter casing for credential creation are not
  fully pinned in the research base; confirm them against the
  [JavaScript SDK](/sdks/javascript) and [Python SDK](/sdks/python) references before relying
  on the snippet shapes. {/* TODO: confirm credentials.create SDK method names/casing against modulex-js and modulex-python */}
</Note>

### Credential response

`POST /credentials` returns a `CredentialResponse`. The connection string is never echoed
back.

<ResponseField name="credential_id" type="string (UUID)">
  The credential identifier. Pass this as the knowledge node's `credential_id`.
</ResponseField>

<ResponseField name="integration_name" type="string">
  `mongodb_atlas`.
</ResponseField>

<ResponseField name="integration_type" type="string">
  `knowledge_provider`.
</ResponseField>

<ResponseField name="display_name" type="string">
  The display name you supplied, or a generated default.
</ResponseField>

<ResponseField name="auth_type" type="string">
  `custom`.
</ResponseField>

<ResponseField name="is_default" type="boolean">
  Whether this is the default credential for `mongodb_atlas` in the organization.
</ResponseField>

<ResponseField name="created_at" type="string (ISO-8601) | null">
  Creation timestamp.
</ResponseField>

<ResponseField name="updated_at" type="string (ISO-8601) | null">
  Last-update timestamp.
</ResponseField>

<ResponseField name="last_used_at" type="string (ISO-8601) | null">
  When the credential was last used for a retrieval. `null` until first use.
</ResponseField>

<ResponseField name="expires_at" type="string (ISO-8601) | null">
  Expiry, if any. Connection-string credentials do not expire on their own.
</ResponseField>

<ResponseField name="credentials_metadata" type="object | null">
  Any metadata you attached at create time.
</ResponseField>

### Test the connection before saving

To validate a connection string without persisting it, call `POST /credentials/test-temporary`
with the same `integration_name`, `auth_type`, and `auth_data`. For Atlas, the test connects
to the cluster, pings the server, and lists databases; it has no HTTP test endpoint.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.modulex.dev/credentials/test-temporary \
    -H "Authorization: Bearer mx_live_xxx" \
    -H "X-Organization-ID: org_123" \
    -H "Content-Type: application/json" \
    -d '{
      "integration_name": "mongodb_atlas",
      "auth_type": "custom",
      "auth_data": {
        "connection_string": "mongodb+srv://user:password@cluster.mongodb.net/"
      }
    }'
  ```
</CodeGroup>

The response is a `TestTemporaryCredentialResponse` with `is_valid`, a human-readable
`message`, `tested_at`, `test_method`, `integration_name`, and `auth_type`. A failed
connection reports `is_valid: false` with the reason in `message`.

## Catalog actions

In the [integration catalog](/integrations/catalog), `mongodb_atlas` is a
`knowledge_provider` that advertises three actions. These describe the provider's capability
in the catalog; retrieval inside a workflow is driven by the
[knowledge node](/workflow-builder/nodes/knowledge) config in the next section, which maps
onto the `query` action.

<Expandable title="query — vector similarity search">
  Performs vector similarity search using MongoDB Atlas `$vectorSearch`.

  <ParamField path="database" type="string" required>
    Database name.
  </ParamField>

  <ParamField path="collection" type="string" required>
    Collection name.
  </ParamField>

  <ParamField path="index_name" type="string" required>
    The Atlas Vector Search index name.
  </ParamField>

  <ParamField path="query_vector" type="array" required>
    The query embedding vector.
  </ParamField>

  <ParamField path="path" type="string" required>
    The field path that holds the stored vectors.
  </ParamField>

  <ParamField path="num_candidates" type="integer" default="100">
    Number of candidates Atlas considers before returning results.
  </ParamField>

  <ParamField path="limit" type="integer" default="5">
    Maximum number of results to return.
  </ParamField>

  <ParamField path="filter" type="object" required={false}>
    Pre-filter conditions in MongoDB query-language (MQL) form.
  </ParamField>

  Returns an array of objects, each `{_id, score, document}`.
</Expandable>

<Expandable title="list_databases — list databases in the cluster">
  Takes no parameters. Returns an array of `{name, sizeOnDisk}` objects.
</Expandable>

<Expandable title="list_collections — list collections in a database">
  <ParamField path="database" type="string" required>
    Database name.
  </ParamField>

  Returns an array of collection-name strings.
</Expandable>

## Configure BYOK retrieval in a knowledge node

A [knowledge node](/workflow-builder/nodes/knowledge) retrieves from a provider inside a
running workflow. To retrieve from Atlas, set the node's `provider_type` to `mongodb_atlas`,
point `credential_id` at your Atlas credential, set `collection_name` to a
`database.collection` value, and supply an `embedding_config` so ModuleX can embed the query.

### How retrieval runs

When the node executes, ModuleX:

<Steps>
  <Step title="Resolves and decrypts the credential">
    It loads the org credential by `credential_id`, decrypts the stored connection string,
    and opens an async Atlas client.
  </Step>

  <Step title="Embeds the query">
    Atlas requires a precomputed query vector, so ModuleX embeds the resolved `query` text
    using `embedding_config`. If `embedding_config` is missing, the node fails with a
    validation error.
  </Step>

  <Step title="Runs $vectorSearch">
    It splits `collection_name` on the first `.` into database and collection, then runs an
    aggregation: a `$vectorSearch` stage followed by `$addFields` that exposes the
    `vectorSearchScore` as `score`. The vector field is excluded from results unless vectors
    are requested.
  </Step>

  <Step title="Filters and formats">
    Matches scoring below `min_score` are dropped. Each remaining document becomes a chunk
    with `content`, `score`, `metadata`, and `id`, then the node formats them per
    `output_format`.
  </Step>
</Steps>

<Warning>
  **BYOK retrieval is uncosted.** Unlike [modulexdb](/integrations/knowledge-providers/modulexdb),
  an Atlas knowledge node consumes **no ModuleX credits** — there is no billing gate on the
  retrieval. Your Atlas query cost and your embedding-provider token cost are billed by those
  providers directly. See [Credits & metering](/billing/credits).
</Warning>

### Knowledge-node fields

These are the `KnowledgeNodeConfig` fields that apply when `provider_type` is `mongodb_atlas`.

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

<ParamField path="credential_id" type="string" required>
  The Atlas credential ID from the credential you created. ModuleX decrypts its connection
  string to reach your cluster.
</ParamField>

<ParamField path="collection_name" type="string" required>
  The target collection in **`database.collection`** format (for example
  `support.kb_chunks`). It must contain the first `.` separator; a value with no `.` fails
  with a `database.collection format` error. Required for Atlas — a node with no
  `collection_name` fails before it queries.
</ParamField>

<ParamField path="query" type="string" required>
  The search text. Supports `{{nodeId.path}}` references so the query can come from an
  upstream node's output. ModuleX embeds this text to build the Atlas query vector.
</ParamField>

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

<ParamField path="embedding_config" type="object" required>
  **Required** for Atlas. The embedding model ModuleX uses to embed the query text before the
  vector search. The embedding model and dimension you configure here must match the model
  and dimension used to build the vectors stored in your Atlas collection, or scores will be
  meaningless. See the sub-fields below.
</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. Range `0.0`–`1.0`. Matches from Atlas scoring below
  this value are dropped.
</ParamField>

<ParamField path="max_tokens" type="integer" default="2000">
  Maximum tokens in the formatted context string, when `output_format` returns a context.
  Range `100`–`10000`.
</ParamField>

<ParamField path="filters" type="object" required={false}>
  Provider-specific pre-filter passed through to Atlas as the `$vectorSearch` filter (MQL
  form). `null` by default.
</ParamField>

<ParamField path="namespace" type="string" required={false}>
  **Not used** for Atlas (it applies to Pinecone-style providers). Leave unset.
</ParamField>

<ParamField path="document_ids" type="array" required={false}>
  Filter to specific document IDs. **Native (modulexdb) knowledge bases only** — has no
  effect on Atlas. Use `filters` for Atlas pre-filtering.
</ParamField>

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

<ParamField path="include_metadata" type="boolean" default="true">
  Include each document's metadata (every field except `_id`, `score`, and the vector field)
  in the results.
</ParamField>

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

<Expandable title="embedding_config sub-fields">
  <ParamField path="embedding_config.integration_name" type="string" required>
    The integration that owns the embedding provider, for example `openai` or `cohere`.
  </ParamField>

  <ParamField path="embedding_config.provider_id" type="string" required>
    The provider identifier, for example `openai`.
  </ParamField>

  <ParamField path="embedding_config.model_id" type="string" required>
    The embedding model, for example `text-embedding-3-small`. Match this to the model that
    produced the vectors stored in your Atlas collection.
  </ParamField>

  <ParamField path="embedding_config.credential_id" type="string" required={false}>
    The credential for the embedding provider. If omitted, ModuleX uses the organization's
    default credential for `integration_name`.
  </ParamField>
</Expandable>

### Atlas index defaults

ModuleX builds the `$vectorSearch` stage with these defaults. Make sure your Atlas Vector
Search index and stored documents match them, or override the index in your collection
setup accordingly.

| Setting                  | Default ModuleX uses | Notes                                                                             |
| ------------------------ | -------------------- | --------------------------------------------------------------------------------- |
| Vector Search index name | `vector_index`       | The index name passed to `$vectorSearch`. Create your Atlas index with this name. |
| Vector field path        | `embedding`          | The document field that holds the stored vectors.                                 |
| `numCandidates`          | `top_k × 10`         | Candidate pool size; scales with `top_k`.                                         |
| `limit`                  | `top_k`              | Results returned by the aggregation.                                              |

<Note>
  ModuleX queries Atlas with a fixed index name of `vector_index` and a fixed vector field of
  `embedding` for the knowledge-node retrieval path. Your collection must have an Atlas Vector
  Search index named `vector_index` over a field named `embedding`. {/* TODO: confirm whether index_name/vector_field are surfaced as overridable node-config fields in the UI; the workflow-engine call uses the adapter defaults */}
</Note>

### Example node config

```json theme={null}
{
  "provider_type": "mongodb_atlas",
  "credential_id": "939f74dc-7b2f-473c-b87d-b95c30c32fd3",
  "collection_name": "support.kb_chunks",
  "query": "{{trigger.question}}",
  "top_k": 5,
  "min_score": 0.3,
  "output_format": "context",
  "embedding_config": {
    "integration_name": "openai",
    "provider_id": "openai",
    "model_id": "text-embedding-3-small"
  }
}
```

## Retrieval results

Each Atlas match is normalized into a chunk before the node formats it.

<ResponseField name="id" type="string">
  The matched document's `_id`, stringified.
</ResponseField>

<ResponseField name="score" type="number">
  The Atlas `vectorSearchScore` for the match. Matches below `min_score` are excluded.
</ResponseField>

<ResponseField name="content" type="string | null">
  The text content extracted from the document. ModuleX looks for a text value in common
  fields — `content`, `text`, `chunk_text`, `page_content`, `data`, `body`, `summary`,
  `document` — and returns the first match, or `null` if none is present.
</ResponseField>

<ResponseField name="metadata" type="object | null">
  Every document field except `_id`, `score`, and the vector field. Present only when
  `include_metadata` is `true`.
</ResponseField>

The node then returns `chunks`, a formatted `context` string, or `both`, per
`output_format`. The downstream LLM or agent node consumes this as RAG context.

## Errors

Atlas retrieval runs inside the workflow executor, so failures surface as the node failing
the run rather than as an HTTP envelope. Because BYOK retrieval is uncosted, there is **no**
`DenialEnvelope` (`{code, layer, …}`) on this path — that envelope only appears on managed
(`modulexdb`) knowledge operations. The credential-management endpoints return the standard
`{detail}` `HTTPException` shape. See [Errors & status codes](/api-reference/errors).

| Condition                                                           | What you see                                                             |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `collection_name` missing                                           | The node fails with `collection_name is required for external provider`. |
| `collection_name` has no `.`                                        | `Collection name must be in 'database.collection' format`.               |
| `embedding_config` missing                                          | `embedding_config required for providers that don't handle embeddings`.  |
| No query vector reached Atlas                                       | `MongoDB Atlas requires query_vector for search`.                        |
| Bad or unauthorized connection string                               | `MongoDB authentication failed: …` (an authentication error).            |
| Cluster unreachable / `motor` not installed                         | `Failed to connect to MongoDB: …` (a connection error).                  |
| Aggregation failure (for example a missing or misnamed Atlas index) | `MongoDB query failed: …` (a query error).                               |
| Credential not found in the org                                     | `Credential not found: <credential_id>`.                                 |

<Note>
  The `motor` driver must be available in the runtime for Atlas connections. If it is missing,
  connections fail with an instruction to install it. This is an environment dependency of the
  ModuleX backend, not something you configure per credential.
</Note>

## Related pages

<CardGroup cols={2}>
  <Card title="Knowledge providers" icon="layer-group" href="/integrations/knowledge-providers/overview">
    Compare MongoDB Atlas with modulexdb and the other BYOK vector stores.
  </Card>

  <Card title="modulexdb (managed)" icon="database" href="/integrations/knowledge-providers/modulexdb">
    The managed alternative: ModuleX hosts the vectors and meters usage in credits.
  </Card>

  <Card title="Knowledge node" icon="diagram-project" href="/workflow-builder/nodes/knowledge">
    Retrieve from Atlas inside a workflow.
  </Card>

  <Card title="External knowledge providers" icon="cloud" href="/platform/knowledge/external-providers">
    The product overview of bringing your own vector store.
  </Card>

  <Card title="Authentication & credentials" icon="key" href="/integrations/authentication">
    How ModuleX stores and resolves integration credentials.
  </Card>

  <Card title="Knowledge & RAG" icon="brain" href="/concepts/knowledge-rag">
    The retrieval-augmented-generation model behind knowledge nodes.
  </Card>
</CardGroup>
