diff --git a/apps/next/package.json b/apps/next/package.json
index 6a37184fb19d87d1ac79042d15643c49f8485577..917c05f604f38395ddb38e822e648ca50bd53134 100644
--- a/apps/next/package.json
+++ b/apps/next/package.json
@@ -7,7 +7,7 @@
     "dev": "next dev",
     "start": "next start",
     "postdev": "fumadocs-mdx",
-    "postbuild": "fumadocs-mdx",
+    "postbuild": "fumadocs-mdx && tsx scripts/post-build.mts",
     "build:docs": "node ./scripts/generate-docs.mjs"
   },
   "dependencies": {
@@ -53,13 +53,21 @@
     "zod": "^3.23.8"
   },
   "devDependencies": {
+    "@next/env": "^15.0.1",
     "@types/mdx": "^2.0.13",
     "@types/node": "22.7.8",
     "@types/react": "^18.3.12",
     "@types/react-dom": "^18.3.1",
     "autoprefixer": "^10.4.20",
+    "fast-glob": "^3.3.2",
+    "gray-matter": "^4.0.3",
     "postcss": "^8.4.47",
+    "remark": "^15.0.1",
+    "remark-gfm": "^4.0.0",
+    "remark-mdx": "^3.1.0",
+    "remark-stringify": "^11.0.0",
     "tailwindcss": "^3.4.14",
+    "tsx": "^4.19.0",
     "typescript": "^5.6.3"
   }
 }
diff --git a/apps/next/scripts/post-build.mts b/apps/next/scripts/post-build.mts
new file mode 100644
index 0000000000000000000000000000000000000000..72367ee02b4d003523e4e43e366439d7437029ac
--- /dev/null
+++ b/apps/next/scripts/post-build.mts
@@ -0,0 +1,7 @@
+import env from "@next/env";
+
+import { updateLlamaCloud } from "./update-llamacloud.mjs";
+
+env.loadEnvConfig(process.cwd());
+
+await updateLlamaCloud();
diff --git a/apps/next/scripts/update-llamacloud.mts b/apps/next/scripts/update-llamacloud.mts
new file mode 100644
index 0000000000000000000000000000000000000000..9c995c32f9b3bfb6cf0cf13cbe0115d6193c9e1b
--- /dev/null
+++ b/apps/next/scripts/update-llamacloud.mts
@@ -0,0 +1,110 @@
+import { PipelinesService } from "@llamaindex/cloud/api";
+import fg from "fast-glob";
+import {
+  fileGenerator,
+  remarkDocGen,
+  remarkInstall,
+  typescriptGenerator,
+} from "fumadocs-docgen";
+import matter from "gray-matter";
+import * as fs from "node:fs/promises";
+import path, { relative } from "node:path";
+import { fileURLToPath } from "node:url";
+import { remark } from "remark";
+import remarkGfm from "remark-gfm";
+import remarkMdx from "remark-mdx";
+import remarkStringify from "remark-stringify";
+
+const baseDir = fileURLToPath(new URL("../src/content", import.meta.url));
+
+async function processContent(content: string): Promise<string> {
+  const file = await remark()
+    .use(remarkMdx)
+    .use(remarkGfm)
+    .use(remarkDocGen, { generators: [typescriptGenerator(), fileGenerator()] })
+    .use(remarkInstall, { persist: { id: "package-manager" } })
+    .use(remarkStringify)
+    .process(content);
+
+  return String(file);
+}
+
+export async function updateLlamaCloud(): Promise<void> {
+  // eslint-disable-next-line turbo/no-undeclared-env-vars
+  const apiKey = process.env.LLAMA_CLOUD_API_KEY;
+  // eslint-disable-next-line turbo/no-undeclared-env-vars
+  const index = process.env.LLAMA_CLOUD_PIPELINE_ID;
+
+  if (!apiKey || !index) {
+    console.log("no api key for LlamaCloud found, skipping");
+    return;
+  }
+
+  const files = await fg([
+    "./src/content/docs/**/*.mdx",
+    "!./src/content/docs/cloud/api/**/*",
+  ]);
+
+  const records: {
+    id: string;
+    title: string;
+    description: string;
+    content: string;
+    category: string | undefined;
+  }[] = [];
+
+  console.log("processing documents for AI");
+  const scan = files.map(async (file) => {
+    const fileContent = await fs.readFile(file);
+    const { content, data } = matter(fileContent.toString());
+
+    const dir = path.dirname(file).split(path.sep).at(3);
+    const category = {
+      cloud: "LlamaCloud",
+      llamaindex: "LlamaIndex.TS",
+    }[dir ?? ""];
+
+    if (data._mdx?.mirror) {
+      return;
+    }
+
+    const processed = await processContent(content);
+    const id = relative(baseDir, file);
+    records.push({
+      id,
+      title: data.title as string,
+      description: data.description as string,
+      content: processed,
+      category,
+    });
+  });
+
+  await Promise.all(scan);
+
+  console.log(`added ${records.length} records`);
+
+  await PipelinesService.upsertBatchPipelineDocumentsApiV1PipelinesPipelineIdDocumentsPut(
+    {
+      baseUrl: "https://api.cloud.llamaindex.ai/",
+      body: records.map((record) => ({
+        id: record.id,
+        metadata: {
+          title: record.title,
+          description: record.description,
+          documentUrl: record.id,
+          category: record.category,
+        },
+        text: record.content,
+      })),
+      path: {
+        pipeline_id: index,
+      },
+      throwOnError: true,
+      headers: {
+        Authorization: `Bearer ${apiKey}`,
+      },
+    },
+  );
+
+  console.log("done");
+}
diff --git a/apps/next/source.config.ts b/apps/next/source.config.ts
index 346eb3c7aee3dc4aad154691ec1e4cb1ea83e029..b03a6bac6ef3e80da7dcf001f2ed92f1e4fbbc7d 100644
--- a/apps/next/source.config.ts
+++ b/apps/next/source.config.ts
@@ -1,15 +1,10 @@
-import { PipelinesService } from "@llamaindex/cloud/api";
 import { rehypeCodeDefaultOptions } from "fumadocs-core/mdx-plugins";
 import { fileGenerator, remarkDocGen, remarkInstall } from "fumadocs-docgen";
 import { defineConfig, defineDocs } from "fumadocs-mdx/config";
 import { transformerTwoslash } from "fumadocs-twoslash";
-import { relative } from "node:path";
-import { fileURLToPath } from "node:url";
 import rehypeKatex from "rehype-katex";
 import remarkMath from "remark-math";
 
-const baseDir = fileURLToPath(new URL("../src/content", import.meta.url));
-
 export const { docs, meta } = defineDocs({
   dir: "./src/content/docs",
 });
@@ -49,68 +44,6 @@ export default defineConfig({
       remarkMath,
       [remarkInstall, { persist: { id: "package-manager" } }],
       [remarkDocGen, { generators: [fileGenerator()] }],
-      () => {
-        return (_, file, next) => {
-          const metadata = file.data.frontmatter as Record<string, unknown>;
-          const title = metadata.title as string;
-          const description = metadata.description as string;
-          let content: string;
-          if (file.value instanceof Uint8Array) {
-            content = file.value.toString();
-          } else {
-            content = file.value;
-          }
-          if (file.path.includes("content/docs/cloud/api")) {
-            // skip cloud api docs
-            return next();
-          }
-          // eslint-disable-next-line turbo/no-undeclared-env-vars
-          if (process.env.NODE_ENV === "development") {
-            // skip development
-            return next();
-          }
-          if (!title || !description) {
-            throw new Error(`Missing title or description in ${file.path}`);
-          }
-          const id = relative(baseDir, file.path);
-
-          if (
-            // eslint-disable-next-line turbo/no-undeclared-env-vars
-            process.env.LLAMA_CLOUD_UPSERT_PIPELINE_DOCUMENTS === "true" &&
-            // eslint-disable-next-line turbo/no-undeclared-env-vars
-            process.env.LLAMA_CLOUD_PIPELINE_ID !== undefined
-          ) {
-            PipelinesService.upsertBatchPipelineDocumentsApiV1PipelinesPipelineIdDocumentsPut(
-              {
-                baseUrl: "https://api.cloud.llamaindex.ai/",
-                body: [
-                  {
-                    metadata: {
-                      title,
-                      description,
-                      documentUrl: id,
-                    },
-                    text: content,
-                    id,
-                  },
-                ],
-                path: {
-                  // eslint-disable-next-line turbo/no-undeclared-env-vars
-                  pipeline_id: process.env.LLAMA_CLOUD_PIPELINE_ID,
-                },
-                throwOnError: true,
-                headers: {
-                  // eslint-disable-next-line turbo/no-undeclared-env-vars
-                  Authorization: `Bearer ${process.env.LLAMA_CLOUD_API_KEY}`,
-                },
-              },
-            ).catch((error) => {
-              console.error(error);
-            });
-          }
-          return next();
-        };
-      },
     ],
     rehypePlugins: (v) => [rehypeKatex, ...v],
   },
diff --git a/apps/next/tsconfig.json b/apps/next/tsconfig.json
index 4b48dac84795dad219da6474ddc49fd8c818d4ed..2ba48c5759c413a96f4bd8d24fd4add5751c28d9 100644
--- a/apps/next/tsconfig.json
+++ b/apps/next/tsconfig.json
@@ -24,6 +24,12 @@
       }
     ]
   },
-  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+  "include": [
+    "next-env.d.ts",
+    "**/*.ts",
+    "**/*.tsx",
+    ".next/types/**/*.ts",
+    "**/*.mts"
+  ],
   "exclude": ["node_modules"]
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index af5535d9b987796f72eb3cbe8e1d40efc095d0f0..a2dcb075081977f0c088756f586375f27c328b0d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -245,6 +245,9 @@ importers:
         specifier: ^3.23.8
         version: 3.23.8
     devDependencies:
+      '@next/env':
+        specifier: ^15.0.1
+        version: 15.0.1
       '@types/mdx':
         specifier: ^2.0.13
         version: 2.0.13
@@ -260,12 +263,33 @@ importers:
       autoprefixer:
         specifier: ^10.4.20
         version: 10.4.20(postcss@8.4.47)
+      fast-glob:
+        specifier: ^3.3.2
+        version: 3.3.2
+      gray-matter:
+        specifier: ^4.0.3
+        version: 4.0.3
       postcss:
         specifier: ^8.4.47
         version: 8.4.47
+      remark:
+        specifier: ^15.0.1
+        version: 15.0.1
+      remark-gfm:
+        specifier: ^4.0.0
+        version: 4.0.0
+      remark-mdx:
+        specifier: ^3.1.0
+        version: 3.1.0
+      remark-stringify:
+        specifier: ^11.0.0
+        version: 11.0.0
       tailwindcss:
         specifier: ^3.4.14
         version: 3.4.14
+      tsx:
+        specifier: ^4.19.0
+        version: 4.19.0
       typescript:
         specifier: ^5.6.3
         version: 5.6.3
@@ -11489,8 +11513,8 @@ packages:
   remark-math@6.0.0:
     resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==}
 
-  remark-mdx@3.0.1:
-    resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==}
+  remark-mdx@3.1.0:
+    resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==}
 
   remark-parse@11.0.0:
     resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
@@ -17878,7 +17902,7 @@ snapshots:
       recma-jsx: 1.0.0(acorn@8.13.0)
       recma-stringify: 1.0.0
       rehype-recma: 1.0.0
-      remark-mdx: 3.0.1
+      remark-mdx: 3.1.0
       remark-parse: 11.0.0
       remark-rehype: 11.1.1
       source-map: 0.7.4
@@ -22111,7 +22135,7 @@ snapshots:
       '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.3)
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint@8.57.0))(eslint@8.57.0)
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0)
       eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0)
       eslint-plugin-react: 7.35.0(eslint@8.57.0)
@@ -22140,13 +22164,13 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint@8.57.0))(eslint@8.57.0):
+  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
     dependencies:
       '@nolyfill/is-core-module': 1.0.39
       debug: 4.3.7
       enhanced-resolve: 5.17.1
       eslint: 8.57.0
-      eslint-module-utils: 2.8.2(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
+      eslint-module-utils: 2.8.2(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
       fast-glob: 3.3.2
       get-tsconfig: 4.8.1
       is-bun-module: 1.1.0
@@ -22159,14 +22183,14 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-module-utils@2.8.2(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
+  eslint-module-utils@2.8.2(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
       '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.6.3)
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint@8.57.0))(eslint@8.57.0)
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -22180,7 +22204,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.2(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
+      eslint-module-utils: 2.8.2(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
       hasown: 2.0.2
       is-core-module: 2.15.1
       is-glob: 4.0.3
@@ -26986,7 +27010,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  remark-mdx@3.0.1:
+  remark-mdx@3.1.0:
     dependencies:
       mdast-util-mdx: 3.0.0
       micromark-extension-mdxjs: 3.0.0