From 31e171b2844054d9263effa4cb736cd14792230d Mon Sep 17 00:00:00 2001
From: Kapil Gowru <kapil-fern@Kapils-Fern-Mac.local>
Date: Wed, 30 Jul 2025 17:22:00 -0400
Subject: [PATCH 1/7] feat: added a url mapper

---
 .github/scripts/fern-scribe.js     | 353 ++++++++++++-------------
 .github/scripts/fern-url-mapper.js | 408 +++++++++++++++++++++++++++++
 .github/workflows/preview-docs.yml |   1 +
 fern/docs.yml                      |   6 +-
 4 files changed, 573 insertions(+), 195 deletions(-)
 create mode 100644 .github/scripts/fern-url-mapper.js

diff --git a/.github/scripts/fern-scribe.js b/.github/scripts/fern-scribe.js
index 0c11a336..845038fd 100644
--- a/.github/scripts/fern-scribe.js
+++ b/.github/scripts/fern-scribe.js
@@ -3,6 +3,7 @@ const Turbopuffer = require('@turbopuffer/turbopuffer').default;
 const fs = require('fs').promises;
 const path = require('path');
 const yaml = require('js-yaml');
+const FernUrlMapper = require('./fern-url-mapper');
 
 class FernScribeGitHub {
   constructor() {
@@ -22,9 +23,8 @@ class FernScribeGitHub {
     
     this.systemPrompt = null;
     
-    // Initialize dynamic path mapping
-    this.dynamicPathMapping = new Map();
-    this.isPathMappingLoaded = false;
+    // Use centralized URL mapper
+    this.urlMapper = new FernUrlMapper(process.env.GITHUB_TOKEN, process.env.REPOSITORY);
   }
 
   async init() {
@@ -502,6 +502,144 @@ Complete updated file content:`;
     }
   }
 
+  async analyzeDocumentationNeeds(context) {
+    if (!this.anthropicApiKey) {
+      console.log('⚠️ No Anthropic API key provided - skipping documentation analysis');
+      return { recommendations: [], reasoning: '' };
+    }
+
+    const prompt = `You are a documentation expert analyzing a GitHub issue and Slack discussion to identify exactly which documentation sections need updates.
+
+## Issue Context
+Title: ${this.issueTitle || 'No title'}
+Description: ${context.requestDescription || 'No description'}
+Additional Context: ${context.additionalContext || 'None'}
+
+## Slack Discussion
+${context.slackThreadContent || 'No Slack discussion provided'}
+
+## Your Task
+Analyze this issue and discussion to:
+1. Identify the core problem or missing documentation
+2. Determine which specific documentation sections/pages should be updated
+3. Suggest additional search terms that would find the right pages
+
+Be specific about page paths. For example:
+- If it's about images, suggest "/learn/docs/writing-content/markdown" (which covers images)
+- If it's about API configuration, suggest specific product pages like "/learn/sdks/generators/[language]/configuration"
+- If it's about navigation features, suggest "/learn/docs/navigation/*" pages
+
+Output your response as JSON:
+{
+  "coreIssue": "Brief description of what's missing or broken",
+  "suggestedPages": [
+    {
+      "path": "/learn/docs/path/to/page",
+      "reason": "Why this page should be updated",
+      "priority": "high|medium|low"
+    }
+  ],
+  "additionalSearchTerms": ["term1", "term2", "term3"],
+  "reasoning": "Your detailed analysis of why these pages were chosen"
+}`;
+
+    try {
+      const response = await fetch('https://api.anthropic.com/v1/messages', {
+        method: 'POST',
+        headers: {
+          'x-api-key': this.anthropicApiKey,
+          'Content-Type': 'application/json',
+          'anthropic-version': '2023-06-01'
+        },
+        body: JSON.stringify({
+          model: 'claude-3-5-sonnet-20241022',
+          max_tokens: 2000,
+          messages: [{
+            role: 'user',
+            content: prompt
+          }]
+        })
+      });
+
+      if (!response.ok) {
+        throw new Error(`Anthropic API error: ${response.status}`);
+      }
+
+      const data = await response.json();
+      const analysisText = data.content[0]?.text || '{}';
+      
+      try {
+        const analysis = JSON.parse(analysisText);
+        console.log(`💡 Core Issue: ${analysis.coreIssue}`);
+        console.log(`🎯 Suggested ${analysis.suggestedPages?.length || 0} specific pages for updates`);
+        return analysis;
+      } catch (parseError) {
+        console.log('⚠️ Could not parse analysis response as JSON, using fallback');
+        return { 
+          coreIssue: analysisText.slice(0, 200) + '...',
+          suggestedPages: [],
+          additionalSearchTerms: [],
+          reasoning: analysisText 
+        };
+      }
+    } catch (error) {
+      console.error('❌ Documentation analysis failed:', error);
+      return { recommendations: [], reasoning: '' };
+    }
+  }
+
+  async enhanceResultsWithAnalysis(turbopufferResults, analysis) {
+    if (!analysis.suggestedPages || analysis.suggestedPages.length === 0) {
+      return turbopufferResults;
+    }
+
+    console.log('🔍 Searching for AI-suggested documentation pages...');
+    const enhancedResults = [...turbopufferResults];
+    const existingPaths = new Set(turbopufferResults.map(r => r.pathname || r.url));
+
+    // Search for each suggested page
+    for (const suggestion of analysis.suggestedPages) {
+      if (existingPaths.has(suggestion.path)) {
+        console.log(`   ✅ Already found: ${suggestion.path}`);
+        continue;
+      }
+
+      // Try to find the suggested page using targeted search
+      console.log(`   🔍 Searching for suggested page: ${suggestion.path}`);
+      
+      // Extract search terms from the path and reason
+      const searchTerms = [
+        suggestion.path.split('/').filter(Boolean).join(' '),
+        suggestion.reason,
+        ...suggestion.path.split('/').slice(-2) // Last two path segments
+      ].join(' ');
+
+      const targetedResults = await this.queryTurbopuffer(searchTerms, {
+        namespace: process.env.TURBOPUFFER_NAMESPACE || 'default',
+        topK: 3
+      });
+
+      // Look for exact or close matches to the suggested path
+      for (const result of targetedResults) {
+        const resultPath = result.pathname || result.url || '';
+        if (resultPath.includes(suggestion.path) || 
+            suggestion.path.includes(resultPath.replace('/learn', ''))) {
+          console.log(`   ✅ Found suggested page: ${resultPath}`);
+          enhancedResults.push({
+            ...result,
+            aiSuggested: true,
+            priority: suggestion.priority,
+            reason: suggestion.reason
+          });
+          existingPaths.add(resultPath);
+          break;
+        }
+      }
+    }
+
+    return enhancedResults;
+  }
+
   async generateChangelogEntry(context) {
     const prompt = `Generate a changelog entry for the following documentation update:
 
@@ -547,191 +685,9 @@ Changelog entry:`;
     }
   }
 
-  // Load dynamic path mapping from Fern docs structure
-  async loadDynamicPathMapping() {
-    if (this.isPathMappingLoaded) return;
-    
-    try {
-      console.log('Loading Fern docs structure for dynamic path mapping...');
-      
-      // Load main docs.yml
-      const docsContent = await this.fetchFileContent('fern/docs.yml');
-      if (!docsContent) return;
-      
-      const docsConfig = yaml.load(docsContent);
-      if (!docsConfig.products) return;
-      
-      // Process each product
-      for (const product of docsConfig.products) {
-        if (!product.path) continue;
-        
-        // Resolve relative path
-        let resolvedPath = product.path;
-        if (resolvedPath.startsWith('./')) {
-          resolvedPath = `fern/${resolvedPath.substring(2)}`;
-        } else if (!resolvedPath.startsWith('fern/')) {
-          resolvedPath = `fern/${resolvedPath}`;
-        }
-        
-        // Determine the effective slug for URL generation
-        let effectiveSlug = null;
-        if (product['skip-slug'] === true) {
-          // Skip slug entirely - pages will be at root level
-          effectiveSlug = '';
-        } else if (product.slug) {
-          // Use explicit slug
-          effectiveSlug = product.slug;
-        } else {
-          // Derive slug from path or display-name
-          const pathBasename = product.path.split('/').pop().replace(/\.yml$/, '');
-          effectiveSlug = pathBasename;
-        }
-        
-        // Load product config
-        const productContent = await this.fetchFileContent(resolvedPath);
-        if (!productContent) continue;
-        
-        const productConfig = yaml.load(productContent);
-        if (!productConfig.navigation) continue;
-        
-        // Process navigation structure
-        await this.processNavigation(productConfig, effectiveSlug, resolvedPath);
-      }
-      
-      this.isPathMappingLoaded = true;
-      console.log(`Loaded ${this.dynamicPathMapping.size} dynamic path mappings`);
-    } catch (error) {
-      console.error('Failed to load dynamic path mapping:', error);
-    }
-  }
-  
-  async processNavigation(config, productSlug, configPath, parentSections = []) {
-    if (!config.navigation) return;
-
-    // Extract the directory path from the config file path
-    // e.g., "fern/products/fern-def.yml" -> "products/fern-def"
-    const basePath = configPath.replace(/\.yml$/, '').replace('fern/', '');
-    
-    for (const navItem of config.navigation) {
-      if (navItem.page) {
-        // It's a page - check if it has a custom path
-        let pageFilePath;
-        if (navItem.path) {
-          // Use custom path
-          pageFilePath = `fern/${basePath}/pages/${navItem.path}`;
-        } else {
-          // Default path based on page name
-          pageFilePath = `fern/${basePath}/pages/${navItem.page}`;
-        }
-        
-        // Build URL with parent sections
-        const urlParts = [productSlug, ...parentSections, navItem.page].filter(Boolean);
-        const pageUrl = `/${urlParts.join('/')}`;
-        this.dynamicPathMapping.set(pageUrl, pageFilePath);
-      } else if (navItem.section) {
-        // It's a section with pages - create section-based URLs
-        const sectionSlug = navItem.section.toLowerCase().replace(/\s+/g, '-');
-        const newParentSections = [...parentSections, sectionSlug];
-        
-        for (const pageItem of navItem.contents || []) {
-          if (pageItem.page) {
-            let pageFilePath;
-            if (pageItem.path) {
-              // Use custom path
-              pageFilePath = `fern/${basePath}/pages/${pageItem.path}`;
-            } else {
-              // Default path based on page name
-              pageFilePath = `fern/${basePath}/pages/${pageItem.page}`;
-            }
-            
-            // Build URL with all parent sections
-            const urlParts = [productSlug, ...newParentSections, pageItem.page].filter(Boolean);
-            const sectionUrl = `/${urlParts.join('/')}`;
-            this.dynamicPathMapping.set(sectionUrl, pageFilePath);
-            
-            // Also create direct URL for backwards compatibility (without parent sections)
-            const directUrlParts = [productSlug, pageItem.page].filter(Boolean);
-            const directUrl = `/${directUrlParts.join('/')}`;
-            if (!this.dynamicPathMapping.has(directUrl)) {
-              this.dynamicPathMapping.set(directUrl, pageFilePath);
-            }
-          } else if (pageItem.section) {
-            // Handle nested sections recursively
-            const nestedSectionSlug = pageItem.section.toLowerCase().replace(/\s+/g, '-');
-            const nestedParentSections = [...newParentSections, nestedSectionSlug];
-            
-            // Create a temporary config object for recursive processing
-            const nestedConfig = { navigation: pageItem.contents || [] };
-            await this.processNavigation(nestedConfig, productSlug, configPath, nestedParentSections);
-          }
-        }
-      }
-    }
-  }
-  
-  // Transform Turbopuffer URLs to actual GitHub file paths
-  transformTurbopufferUrlToPath(turbopufferUrl) {
-    // Remove /learn prefix and clean up trailing slashes
-    let urlPath = turbopufferUrl.replace('/learn', '').replace(/\/$/, '');
-    
-    // First try to use dynamic mapping
-    if (this.dynamicPathMapping.has(urlPath)) {
-      const mappedPath = this.dynamicPathMapping.get(urlPath);
-      // Add .mdx extension if not present and not already a complete path
-      if (!mappedPath.endsWith('.mdx') && !mappedPath.endsWith('/')) {
-        // Check if it's a known directory case (like changelog)
-        if (mappedPath.endsWith('/changelog') || (mappedPath.includes('/changelog') && !mappedPath.includes('.'))) {
-          return mappedPath; // Return as directory
-        }
-        return `${mappedPath}.mdx`;
-      }
-      return mappedPath;
-    }
-    
-    // Fallback to hardcoded logic for backwards compatibility
-    const pathParts = urlPath.split('/').filter(p => p);
-    if (pathParts.length === 0) return null;
-    
-    const product = pathParts[0]; // docs, sdks, etc.
-    const remainingPath = pathParts.slice(1).join('/');
-    
-    // Build the file path
-    let basePath = `fern/products/${product}`;
-    
-    // Handle special cases and path mapping
-    if (product === 'docs') {
-      if (remainingPath === 'changelog') {
-        // Special case: changelog is a folder
-        return `${basePath}/pages/changelog`;
-      } else if (remainingPath.startsWith('navigation/')) {
-        // navigation/* maps directly
-        const navPath = remainingPath.replace('navigation/', '');
-        return `${basePath}/pages/navigation/${navPath}.mdx`;
-      } else {
-        // Other docs paths
-        return `${basePath}/pages/${remainingPath}.mdx`;
-      }
-    } else if (product === 'sdks') {
-      if (remainingPath.startsWith('generators/')) {
-        // sdks/generators/* maps to overview/
-        const generatorPath = remainingPath.replace('generators/', '');
-        return `${basePath}/overview/${generatorPath}.mdx`;
-      } else {
-        return `${basePath}/pages/${remainingPath}.mdx`;
-      }
-    } else {
-      // Default mapping for other products
-      return `${basePath}/pages/${remainingPath}.mdx`;
-    }
-  }
-
-  // Map Turbopuffer URLs to actual GitHub file paths (now using dynamic mapping)
+  // Map Turbopuffer URLs to actual GitHub file paths (using centralized mapper)
   async mapTurbopufferPathToGitHub(turbopufferPath) {
-    // Ensure dynamic mapping is loaded
-    await this.loadDynamicPathMapping();
-    
-    // Use the improved transformation logic that prioritizes dynamic mapping
-    return this.transformTurbopufferUrlToPath(turbopufferPath) || turbopufferPath;
+    return await this.urlMapper.mapTurbopufferPathToGitHub(turbopufferPath);
   }
 
   // Simple file content fetcher for dynamic mapping (without path transformation)
@@ -891,12 +847,18 @@ ${context.additionalContext ? `**Additional Context:** ${context.additionalConte
       if (context.slackThread) {
         slackThreadContent = await this.fetchSlackThread(context.slackThread);
       }
+      context.slackThreadContent = slackThreadContent;
 
+      // Analyze the issue and Slack discussion to determine documentation gaps
+      console.log('🧠 Analyzing issue and discussion to identify documentation gaps...');
+      const documentationAnalysis = await this.analyzeDocumentationNeeds(context);
+      
       // Create enhanced query text that includes both request description and Slack context
       const enhancedQuery = [
         context.requestDescription,
         slackThreadContent ? `\n\nSlack Discussion Context:\n${slackThreadContent}` : '',
-        context.additionalContext ? `\n\nAdditional Context:\n${context.additionalContext}` : ''
+        context.additionalContext ? `\n\nAdditional Context:\n${context.additionalContext}` : '',
+        documentationAnalysis.additionalSearchTerms ? `\n\nAI-suggested terms: ${documentationAnalysis.additionalSearchTerms.join(', ')}` : ''
       ].filter(Boolean).join('\n');
 
       // Query TurboBuffer for relevant files
@@ -909,23 +871,30 @@ ${context.additionalContext ? `**Additional Context:** ${context.additionalConte
         topK: 3
       });
 
-      console.log(`\n📁 Found ${turbopufferResults.length} relevant files from Turbopuffer:`);
+      // Enhance results with AI-identified sections
+      const enhancedResults = await this.enhanceResultsWithAnalysis(turbopufferResults, documentationAnalysis);
+
+      console.log(`\n📁 Found ${enhancedResults.length} relevant files (${turbopufferResults.length} from Turbopuffer + ${enhancedResults.length - turbopufferResults.length} AI-suggested):`);
       
-      turbopufferResults.forEach((result, index) => {
+      enhancedResults.forEach((result, index) => {
         const path = result.pathname || result.url || 'Unknown path';
         const title = result.title || 'Untitled';
         const url = result.url || `https://${result.domain || ''}${result.pathname || ''}`;
         const relevance = result.$dist !== undefined ? (1 - result.$dist).toFixed(3) : 'N/A';
+        const aiSuggested = result.aiSuggested ? ' 🤖 AI-suggested' : '';
         
-        console.log(`${index + 1}. ${path}`);
+        console.log(`${index + 1}. ${path}${aiSuggested}`);
         console.log(`   Title: ${title}`);
         console.log(`   URL: ${url}`);
         console.log(`   Relevance Score: ${relevance}`);
+        if (result.reason) {
+          console.log(`   AI Reason: ${result.reason}`);
+        }
       });
       console.log('');
 
       // Deduplicate results by URL
-      for (const result of turbopufferResults) {
+      for (const result of enhancedResults) {
         const url = result.url || `https://${result.domain}${result.pathname}${result.hash || ''}`;
         
         if (result.url) {
diff --git a/.github/scripts/fern-url-mapper.js b/.github/scripts/fern-url-mapper.js
new file mode 100644
index 00000000..9f995063
--- /dev/null
+++ b/.github/scripts/fern-url-mapper.js
@@ -0,0 +1,408 @@
+require('dotenv').config();
+const { Octokit } = require('@octokit/rest');
+const yaml = require('js-yaml');
+const fs = require('fs').promises;
+
+class FernUrlMapper {
+  constructor(githubToken = null, repository = null) {
+    this.dynamicPathMapping = new Map();
+    this.isPathMappingLoaded = false;
+    
+    // Initialize GitHub client if credentials provided
+    if (githubToken && repository) {
+      this.octokit = new Octokit({ auth: githubToken });
+      this.owner = repository.split('/')[0];
+      this.repo = repository.split('/')[1];
+    } else if (process.env.GITHUB_TOKEN && process.env.REPOSITORY) {
+      this.octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
+      this.owner = process.env.REPOSITORY.split('/')[0];
+      this.repo = process.env.REPOSITORY.split('/')[1];
+    } else {
+      throw new Error('GitHub credentials not provided. Either pass them as parameters or set GITHUB_TOKEN and REPOSITORY environment variables.');
+    }
+  }
+
+  // Fetch file content from GitHub API
+  async fetchFileContent(filePath) {
+    try {
+      const { data } = await this.octokit.rest.repos.getContent({
+        owner: this.owner,
+        repo: this.repo,
+        path: filePath
+      });
+      
+      if (data.content) {
+        return Buffer.from(data.content, 'base64').toString('utf-8');
+      }
+      return null;
+    } catch (error) {
+      console.log(`Could not fetch ${filePath}: ${error.message}`);
+      return null;
+    }
+  }
+
+  // Load dynamic path mapping from Fern docs structure
+  async loadDynamicPathMapping() {
+    if (this.isPathMappingLoaded) return;
+    
+    try {
+      console.log('Loading Fern docs structure for dynamic path mapping...');
+      
+      // Read the root docs.yml file
+      const rootDocsContent = await this.fetchFileContent('fern/docs.yml');
+      if (!rootDocsContent) {
+        console.log('Could not fetch fern/docs.yml');
+        return;
+      }
+
+      const rootConfig = yaml.load(rootDocsContent);
+      
+      // Process root navigation if it exists
+      if (rootConfig.navigation) {
+        await this.processNavigation(rootConfig.navigation, '', 'fern', []);
+      }
+
+      // Process products if they exist
+      if (rootConfig.products && Array.isArray(rootConfig.products)) {
+        for (const product of rootConfig.products) {
+          await this.processProduct(product);
+        }
+      }
+      
+      this.isPathMappingLoaded = true;
+      console.log(`Loaded ${this.dynamicPathMapping.size} dynamic path mappings`);
+    } catch (error) {
+      console.error('Failed to load dynamic path mapping:', error);
+    }
+  }
+
+  async processProduct(product) {
+    // Determine the product slug
+    let productSlug = '';
+    if (product['skip-slug'] === true) {
+      productSlug = '';
+    } else if (product.slug) {
+      productSlug = product.slug;
+    } else {
+      // Derive slug from path or display-name
+      const pathBasename = product.path.split('/').pop().replace(/\.yml$/, '');
+      productSlug = pathBasename;
+    }
+
+    // Resolve the product config path
+    let configPath = product.path;
+    // Remove leading ./ if present
+    if (configPath.startsWith('./')) {
+      configPath = configPath.substring(2);
+    }
+    // Add fern/ prefix if not present
+    if (!configPath.startsWith('fern/')) {
+      configPath = `fern/${configPath}`;
+    }
+
+    console.log(`Loading product config: ${configPath} (slug: ${productSlug})`);
+
+    // Load the product configuration
+    const productConfigContent = await this.fetchFileContent(configPath);
+    if (!productConfigContent) {
+      return;
+    }
+
+    const productConfig = yaml.load(productConfigContent);
+    
+    // Process the product's navigation
+    if (productConfig.navigation) {
+      // Extract just the directory path from the config file
+      const productBasePath = configPath.replace(/\.yml$/, '');
+      await this.processNavigation(productConfig.navigation, productSlug, productBasePath, []);
+    }
+  }
+  
+  async processNavigation(navigation, productSlug, basePath, parentSections = []) {
+    for (const navItem of navigation) {
+      if (navItem.page) {
+        // It's a direct page
+        await this.processPage(navItem, productSlug, basePath, parentSections);
+      } else if (navItem.section) {
+        // It's a section with contents
+        await this.processSection(navItem, productSlug, basePath, parentSections);
+      }
+    }
+  }
+
+  async processPage(pageItem, productSlug, basePath, parentSections) {
+    const pageName = pageItem.page;
+    let pageFilePath;
+
+    // Build file path based on whether there's a custom path
+    if (pageItem.path) {
+      // Custom path - handle relative paths
+      let customPath = pageItem.path;
+      if (customPath.startsWith('./')) {
+        customPath = customPath.substring(2);
+      }
+      // Don't add pages/ when there's an explicit path - it's relative to basePath
+      pageFilePath = `${basePath}/${customPath}`;
+    } else {
+      // Default path based on page name - use pages/ subdirectory
+      pageFilePath = `${basePath}/pages/${pageName}`;
+    }
+
+    // Add .mdx extension if not present
+    if (!pageFilePath.endsWith('.mdx') && !pageFilePath.includes('.')) {
+      pageFilePath += '.mdx';
+    }
+
+    // Normalize the file path to remove duplications and fix structure
+    pageFilePath = this.normalizeFilePath(pageFilePath);
+
+    // Convert pageName to URL slug (lowercase, hyphenated)
+    const pageSlug = this.toUrlSlug(pageName);
+
+    // Build the URL
+    const urlParts = [productSlug, ...parentSections, pageSlug].filter(Boolean);
+    const pageUrl = `/learn/${urlParts.join('/')}`;
+
+    this.dynamicPathMapping.set(pageUrl, pageFilePath);
+
+    // Also create direct mapping (without sections) for backward compatibility
+    if (parentSections.length > 0) {
+      const directUrlParts = [productSlug, pageSlug].filter(Boolean);
+      const directUrl = `/learn/${directUrlParts.join('/')}`;
+      if (!this.dynamicPathMapping.has(directUrl)) {
+        this.dynamicPathMapping.set(directUrl, pageFilePath);
+      }
+    }
+  }
+
+  async processSection(sectionItem, productSlug, basePath, parentSections) {
+    const sectionName = sectionItem.section;
+    
+    // Convert section name to URL slug (lowercase, hyphenated)
+    const sectionSlug = this.toUrlSlug(sectionName);
+    const newParentSections = [...parentSections, sectionSlug];
+
+    // Process contents of this section
+    if (sectionItem.contents && Array.isArray(sectionItem.contents)) {
+      for (const contentItem of sectionItem.contents) {
+        if (contentItem.page) {
+          await this.processPage(contentItem, productSlug, basePath, newParentSections);
+        } else if (contentItem.section) {
+          // Nested section - recursive processing
+          await this.processSection(contentItem, productSlug, basePath, newParentSections);
+        }
+      }
+    }
+  }
+
+  normalizeFilePath(filePath) {
+    // Remove relative path markers and normalize
+    let normalized = filePath.replace(/\/\.\//g, '/');
+    
+    // Fix duplicated directory names (e.g., docs/docs -> docs)
+    const parts = normalized.split('/');
+    const cleanParts = [];
+    let previousPart = '';
+    
+    for (const part of parts) {
+      if (part && part !== previousPart) {
+        cleanParts.push(part);
+      }
+      previousPart = part;
+    }
+    
+    return cleanParts.join('/');
+  }
+
+  toUrlSlug(text) {
+    return text
+      .toLowerCase()
+      .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with hyphens
+      .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens
+  }
+  
+  // Transform Turbopuffer URLs to actual GitHub file paths
+  transformTurbopufferUrlToPath(turbopufferUrl) {
+    // Clean up trailing slashes but keep the /learn prefix for dynamic mapping lookup
+    let cleanUrl = turbopufferUrl.replace(/\/$/, '');
+    
+    // First try to use dynamic mapping with full URL (including /learn)
+    if (this.dynamicPathMapping.has(cleanUrl)) {
+      const mappedPath = this.dynamicPathMapping.get(cleanUrl);
+      // Add .mdx extension if not present and not already a complete path
+      if (!mappedPath.endsWith('.mdx') && !mappedPath.endsWith('/')) {
+        // Check if it's a known directory case (like changelog)
+        if (mappedPath.endsWith('/changelog') || (mappedPath.includes('/changelog') && !mappedPath.includes('.'))) {
+          return mappedPath; // Return as directory
+        }
+        return `${mappedPath}.mdx`;
+      }
+      return mappedPath;
+    }
+    
+    // Remove /learn prefix for fallback logic
+    let urlPath = turbopufferUrl.replace('/learn', '').replace(/\/$/, '');
+    
+    // Fallback to hardcoded logic for backwards compatibility
+    const pathParts = urlPath.split('/').filter(p => p);
+    if (pathParts.length === 0) return null;
+    
+    const product = pathParts[0]; // docs, sdks, etc.
+    const remainingPath = pathParts.slice(1).join('/');
+    
+    // Build the file path
+    let basePath = `fern/products/${product}`;
+    
+    // Handle special cases and path mapping
+    if (product === 'docs') {
+      if (remainingPath === 'changelog') {
+        // Special case: changelog is a folder
+        return `${basePath}/pages/changelog`;
+      } else if (remainingPath.startsWith('navigation/')) {
+        // navigation/* maps directly
+        const navPath = remainingPath.replace('navigation/', '');
+        return `${basePath}/pages/navigation/${navPath}.mdx`;
+      } else {
+        // Other docs paths
+        return `${basePath}/pages/${remainingPath}.mdx`;
+      }
+    } else if (product === 'sdks') {
+      if (remainingPath.startsWith('generators/')) {
+        // sdks/generators/* maps to overview/
+        const generatorPath = remainingPath.replace('generators/', '');
+        return `${basePath}/overview/${generatorPath}.mdx`;
+      } else {
+        return `${basePath}/pages/${remainingPath}.mdx`;
+      }
+    } else {
+      // Default mapping for other products
+      return `${basePath}/pages/${remainingPath}.mdx`;
+    }
+  }
+
+  // Map Turbopuffer URLs to actual GitHub file paths (now using dynamic mapping)
+  async mapTurbopufferPathToGitHub(turbopufferPath) {
+    // Ensure dynamic mapping is loaded
+    await this.loadDynamicPathMapping();
+    
+    // Use the improved transformation logic that prioritizes dynamic mapping
+    return this.transformTurbopufferUrlToPath(turbopufferPath) || turbopufferPath;
+  }
+
+  // Get all mappings as an object for external use
+  async getAllMappings() {
+    await this.loadDynamicPathMapping();
+    const mappings = {};
+    for (const [url, path] of this.dynamicPathMapping) {
+      mappings[url] = path;
+    }
+    return mappings;
+  }
+
+  // Output all mappings to a markdown file
+  async outputMappingsToMarkdown(filename = 'fern-url-mappings.md') {
+    await this.loadDynamicPathMapping();
+    
+    const timestamp = new Date().toISOString();
+    let content = `# Fern URL Mappings\n\n`;
+    content += `Generated on: ${timestamp}\n`;
+    content += `Total mappings: ${this.dynamicPathMapping.size}\n\n`;
+    
+    // Group by product for better organization
+    const productGroups = {};
+    for (const [url, path] of this.dynamicPathMapping) {
+      const product = url.split('/')[2] || 'root'; // Extract product from URL
+      if (!productGroups[product]) {
+        productGroups[product] = [];
+      }
+      productGroups[product].push({ url, path });
+    }
+    
+    // Sort products alphabetically
+    const sortedProducts = Object.keys(productGroups).sort();
+    
+    for (const product of sortedProducts) {
+      content += `## ${product.charAt(0).toUpperCase() + product.slice(1)}\n\n`;
+      
+      // Sort URLs within each product
+      productGroups[product].sort((a, b) => a.url.localeCompare(b.url));
+      
+      for (const { url, path } of productGroups[product]) {
+        content += `- \`${url}\` → \`${path}\`\n`;
+      }
+      content += '\n';
+    }
+    
+    // Write to file
+    await fs.writeFile(filename, content, 'utf-8');
+    console.log(`✅ Mappings written to ${filename}`);
+    
+    return filename;
+  }
+
+  // Test specific URL mappings
+  async testMappings(testUrls = []) {
+    await this.loadDynamicPathMapping();
+    
+    console.log('\n=== TESTING URL MAPPINGS ===');
+    
+    if (testUrls.length === 0) {
+      // Default test URLs
+      testUrls = [
+        '/learn/docs/writing-content/markdown',
+        '/learn/sdks/generators/python/configuration',
+        '/learn/openapi-definition/overlay-customizations'
+      ];
+    }
+    
+    for (const testUrl of testUrls) {
+      const mappedPath = this.transformTurbopufferUrlToPath(testUrl);
+      console.log(`${testUrl} → ${mappedPath || 'NOT FOUND'}`);
+    }
+    
+    return testUrls.map(url => ({
+      url,
+      mappedPath: this.transformTurbopufferUrlToPath(url)
+    }));
+  }
+}
+
+// CLI interface when run directly
+async function main() {
+  const args = process.argv.slice(2);
+  const shouldOutputToFile = args.includes('--output') || args.includes('-o');
+  const outputFile = args.find((arg, index) => 
+    (args[index - 1] === '--output' || args[index - 1] === '-o') && !arg.startsWith('-')
+  ) || 'fern-url-mappings.md';
+  
+  const shouldTest = args.includes('--test') || args.includes('-t');
+  const testUrls = args.filter(arg => arg.startsWith('/learn/'));
+  
+  try {
+    const mapper = new FernUrlMapper();
+    
+    if (shouldTest) {
+      await mapper.testMappings(testUrls.length > 0 ? testUrls : undefined);
+    }
+    
+    if (shouldOutputToFile) {
+      await mapper.outputMappingsToMarkdown(outputFile);
+    } else {
+      // Just load and show stats
+      await mapper.loadDynamicPathMapping();
+      console.log(`✅ Loaded ${mapper.dynamicPathMapping.size} URL mappings`);
+    }
+    
+  } catch (error) {
+    console.error('❌ Error:', error.message);
+    process.exit(1);
+  }
+}
+
+// Export the class for use in other modules
+module.exports = FernUrlMapper;
+
+// Run CLI if this file is executed directly
+if (require.main === module) {
+  main().catch(console.error);
+} 
\ No newline at end of file
diff --git a/.github/workflows/preview-docs.yml b/.github/workflows/preview-docs.yml
index bc3a67e6..5e60d764 100644
--- a/.github/workflows/preview-docs.yml
+++ b/.github/workflows/preview-docs.yml
@@ -2,6 +2,7 @@ name: Preview Docs
 
 on:
   pull_request_target:
+    types: [opened, synchronize, ready_for_review]
     branches:
       - main
 
diff --git a/fern/docs.yml b/fern/docs.yml
index 6a155dbb..a0347004 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -74,21 +74,21 @@ products:
     path: ./products/asyncapi-def/asyncapi-def.yml
     icon: fa-regular fa-bolt
     image: ./images/product-switcher/asyncapi-light.png
-    slug: asyncapi
+    slug: asyncapi-definition
   
   - display-name: OpenRPC
     subtitle: Docs coming soon
     path: ./products/openrpc-def/openrpc-def.yml
     icon: fa-regular fa-code
     image: ./images/product-switcher/openrpc-light.png
-    slug: openrpc
+    slug: openrpc-definition
   
   - display-name: gRPC
     subtitle: Docs coming soon
     path: ./products/grpc-def/grpc-def.yml
     icon: fa-regular fa-plug
     image: ./images/product-switcher/grpc-light.png
-    slug: grpc
+    slug: grpc-definition
 
 
 css: ./assets/styles.css

From 0bc1d28650a92e708bd22022a70cd78b6da64aa1 Mon Sep 17 00:00:00 2001
From: Kapil Gowru <kapil-fern@mac.mynetworksettings.com>
Date: Wed, 30 Jul 2025 23:40:13 -0400
Subject: [PATCH 2/7] feat: added a url mapper

---
 .github/scripts/fern-url-mapper.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.github/scripts/fern-url-mapper.js b/.github/scripts/fern-url-mapper.js
index 9f995063..e3aa7e7d 100644
--- a/.github/scripts/fern-url-mapper.js
+++ b/.github/scripts/fern-url-mapper.js
@@ -1,4 +1,3 @@
-require('dotenv').config();
 const { Octokit } = require('@octokit/rest');
 const yaml = require('js-yaml');
 const fs = require('fs').promises;

From a759744a3bc764f93f8ab799f64c6139732941c2 Mon Sep 17 00:00:00 2001
From: Kapil Gowru <kapil-fern@Kapils-Fern-Mac.local>
Date: Thu, 31 Jul 2025 12:51:43 -0400
Subject: [PATCH 3/7] feat: reduced context window for turbopuffer

---
 .github/scripts/fern-scribe.js | 190 +++++++++++++++++++++++++++++----
 .github/scripts/package.json   |   1 -
 2 files changed, 167 insertions(+), 24 deletions(-)

diff --git a/.github/scripts/fern-scribe.js b/.github/scripts/fern-scribe.js
index 845038fd..bb7065a3 100644
--- a/.github/scripts/fern-scribe.js
+++ b/.github/scripts/fern-scribe.js
@@ -3,8 +3,60 @@ const Turbopuffer = require('@turbopuffer/turbopuffer').default;
 const fs = require('fs').promises;
 const path = require('path');
 const yaml = require('js-yaml');
+const https = require('https');
+const http = require('http');
 const FernUrlMapper = require('./fern-url-mapper');
 
+// Helper function to replace fetch with Node.js built-in modules
+function httpRequest(url, options = {}) {
+  return new Promise((resolve, reject) => {
+    const urlObj = new URL(url);
+    const isHttps = urlObj.protocol === 'https:';
+    const lib = isHttps ? https : http;
+    
+    const requestOptions = {
+      hostname: urlObj.hostname,
+      port: urlObj.port || (isHttps ? 443 : 80),
+      path: urlObj.pathname + urlObj.search,
+      method: options.method || 'GET',
+      headers: options.headers || {},
+    };
+
+    const req = lib.request(requestOptions, (res) => {
+      const chunks = [];
+      
+      res.on('data', chunk => {
+        chunks.push(chunk);
+      });
+      
+      res.on('end', () => {
+        const buffer = Buffer.concat(chunks);
+        const data = buffer.toString('utf8');
+        
+        const response = {
+          ok: res.statusCode >= 200 && res.statusCode < 300,
+          status: res.statusCode,
+          headers: {
+            get: (name) => res.headers[name.toLowerCase()]
+          },
+          json: () => Promise.resolve(JSON.parse(data)),
+          text: () => Promise.resolve(data),
+          arrayBuffer: () => Promise.resolve(buffer)
+        };
+        resolve(response);
+      });
+    });
+
+    req.on('error', reject);
+    
+    if (options.body) {
+      req.write(options.body);
+    }
+    
+    req.end();
+  });
+}
+
 class FernScribeGitHub {
   constructor() {
     this.octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
@@ -106,7 +158,7 @@ class FernScribeGitHub {
 
     try {
       // Download the file content
-      const response = await fetch(file.url_private, {
+      const response = await httpRequest(file.url_private, {
         headers: {
           'Authorization': `Bearer ${this.slackToken}`
         }
@@ -151,7 +203,7 @@ class FernScribeGitHub {
 
     try {
       // Download image and convert to base64
-      const response = await fetch(imageUrl, {
+      const response = await httpRequest(imageUrl, {
         headers: {
           'Authorization': `Bearer ${this.slackToken}`
         }
@@ -164,7 +216,7 @@ class FernScribeGitHub {
       const mimeType = response.headers.get('content-type') || 'image/jpeg';
 
       // Use Claude to describe the image
-      const claudeResponse = await fetch('https://api.anthropic.com/v1/messages', {
+      const claudeResponse = await httpRequest('https://api.anthropic.com/v1/messages', {
         method: 'POST',
         headers: {
           'x-api-key': this.anthropicApiKey,
@@ -215,7 +267,7 @@ class FernScribeGitHub {
 
     try {
       // Fetch the thread replies
-      const response = await fetch(`https://slack.com/api/conversations.replies?${new URLSearchParams({
+      const response = await httpRequest(`https://slack.com/api/conversations.replies?${new URLSearchParams({
         channel: parsedUrl.channelId,
         ts: parsedUrl.threadTs,
         inclusive: 'true'
@@ -315,25 +367,117 @@ class FernScribeGitHub {
     }
   }
 
-  async createEmbedding(text) {
-    const response = await fetch('https://api.openai.com/v1/embeddings', {
-      method: 'POST',
-      headers: {
-        'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
-        'Content-Type': 'application/json',
-      },
-      body: JSON.stringify({
-        input: text,
-        model: 'text-embedding-3-large',
-      }),
-    });
+  // Estimate tokens (rough approximation: ~4 chars per token for English)
+  estimateTokens(text) {
+    return Math.ceil(text.length / 4);
+  }
 
-    if (!response.ok) {
-      throw new Error(`OpenAI API error: ${response.status}`);
+  // Truncate query intelligently to fit within token limits
+  truncateQuery(text, maxTokens = 8000) { // Leave some buffer below 8192
+    const estimatedTokens = this.estimateTokens(text);
+    
+    if (estimatedTokens <= maxTokens) {
+      return text;
     }
 
-    const data = await response.json();
-    return data.data[0].embedding;
+    console.log(`⚠️  Query too long (${estimatedTokens} tokens), truncating to fit within ${maxTokens} tokens...`);
+    
+    // Try to parse the enhanced query structure
+    const sections = text.split('\n\n');
+    let truncatedText = '';
+    let currentTokens = 0;
+    
+    // Prioritize sections: core request first, Slack discussion last
+    const prioritizedSections = [];
+    
+    for (const section of sections) {
+      if (section.includes('Add a comprehensive list') || section.startsWith('Issue:') || section.startsWith('Request:')) {
+        prioritizedSections.unshift(section); // High priority - add to beginning
+      } else if (section.includes('AI-suggested terms:')) {
+        prioritizedSections.splice(1, 0, section); // Medium-high priority
+      } else if (section.includes('Additional Context:')) {
+        prioritizedSections.splice(-1, 0, section); // Medium priority
+      } else if (section.includes('Slack Discussion Context:')) {
+        prioritizedSections.push(section); // Low priority - add to end
+      } else {
+        prioritizedSections.push(section); // Default priority
+      }
+    }
+    
+    // Build truncated query by adding sections until we hit the limit
+    for (const section of prioritizedSections) {
+      const sectionTokens = this.estimateTokens(section);
+      
+      if (currentTokens + sectionTokens <= maxTokens) {
+        if (truncatedText) truncatedText += '\n\n';
+        truncatedText += section;
+        currentTokens += sectionTokens;
+      } else {
+        // If this is a Slack discussion, try to include a truncated version
+        if (section.includes('Slack Discussion Context:')) {
+          const remainingTokens = maxTokens - currentTokens;
+          const remainingChars = remainingTokens * 4;
+          
+          if (remainingChars > 200) { // Only add if we have meaningful space
+            const truncatedSection = section.slice(0, remainingChars - 50) + '\n\n[... Slack discussion truncated for token limit ...]';
+            if (truncatedText) truncatedText += '\n\n';
+            truncatedText += truncatedSection;
+          }
+        }
+        break;
+      }
+    }
+    
+    const finalTokens = this.estimateTokens(truncatedText);
+    console.log(`✂️  Truncated query: ${text.length} → ${truncatedText.length} chars (${estimatedTokens} → ${finalTokens} tokens)`);
+    
+    return truncatedText;
+  }
+
+  async createEmbedding(text) {
+    console.log(`🔍 Creating embedding for text (${text.length} chars)...`);
+    
+    try {
+      // Truncate if necessary to fit within token limits
+      const truncatedText = this.truncateQuery(text);
+      
+      const response = await httpRequest('https://api.openai.com/v1/embeddings', {
+        method: 'POST',
+        headers: {
+          'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          input: truncatedText,
+          model: 'text-embedding-3-large',
+        }),
+      });
+
+      if (!response.ok) {
+        const errorText = await response.text();
+        console.error('❌ OpenAI API error details:', errorText);
+        
+        // Try to parse the error as JSON for better understanding
+        try {
+          const errorData = JSON.parse(errorText);
+          console.error('📋 Parsed error data:', JSON.stringify(errorData, null, 2));
+        } catch (e) {
+          console.error('📋 Raw error text:', errorText);
+        }
+        
+        throw new Error(`OpenAI API error: ${response.status} - ${errorText}`);
+      }
+
+      const data = await response.json();
+      console.log(`✅ Embedding created successfully (${data.data[0]?.embedding?.length} dimensions)`);
+      return data.data[0].embedding;
+      
+    } catch (error) {
+      console.error('❌ createEmbedding failed:', error.message);
+      console.error('📝 Query preview (first 200 chars):', text.slice(0, 200));
+      console.error('🔑 API Key present:', process.env.OPENAI_API_KEY ? `Yes (${process.env.OPENAI_API_KEY.length} chars)` : 'No');
+      throw error;
+    }
   }
 
   reciprocalRankFusion(semanticResults, bm25Results, k = 60) {
@@ -473,7 +617,7 @@ IMPORTANT: Return ONLY the clean file content. Do not include any explanatory te
 Complete updated file content:`;
 
     try {
-      const response = await fetch('https://api.anthropic.com/v1/messages', {
+      const response = await httpRequest('https://api.anthropic.com/v1/messages', {
         method: 'POST',
         headers: {
           'x-api-key': this.anthropicApiKey,
@@ -544,7 +688,7 @@ Output your response as JSON:
 }`;
 
     try {
-      const response = await fetch('https://api.anthropic.com/v1/messages', {
+      const response = await httpRequest('https://api.anthropic.com/v1/messages', {
         method: 'POST',
         headers: {
           'x-api-key': this.anthropicApiKey,
@@ -654,7 +798,7 @@ Example format:
 Changelog entry:`;
 
     try {
-      const response = await fetch('https://api.anthropic.com/v1/messages', {
+      const response = await httpRequest('https://api.anthropic.com/v1/messages', {
         method: 'POST',
         headers: {
           'x-api-key': this.anthropicApiKey,
diff --git a/.github/scripts/package.json b/.github/scripts/package.json
index bf1c049e..55ecc159 100644
--- a/.github/scripts/package.json
+++ b/.github/scripts/package.json
@@ -6,7 +6,6 @@
   "dependencies": {
     "@octokit/rest": "^20.0.2",
     "@turbopuffer/turbopuffer": "^0.10.14",
-    "node-fetch": "^3.3.2",
     "js-yaml": "^4.1.0"
   },
   "engines": {

From ef8c087d7662ef4f9578d54bd6cb37217b233910 Mon Sep 17 00:00:00 2001
From: Kapil Gowru <kapil-fern@mac.mynetworksettings.com>
Date: Fri, 1 Aug 2025 01:44:10 -0400
Subject: [PATCH 4/7] feat: added scribe updates and removed api-definition
 directory

---
 .github/scripts/fern-scribe.js                | 376 ++++++++++++-
 .github/scripts/fern-url-mapper.js            |  58 +-
 .github/scripts/generate-mappings.js          | 229 ++++++++
 .github/workflows/fern-scribe.yml             |   7 +
 fern/docs.yml                                 |   7 -
 .../product-switcher-askfern-dark.png         | Bin 8390 -> 7727 bytes
 .../product-switcher-askfern-light.png        | Bin 6957 -> 6881 bytes
 .../api-definition/api-definition.yml         | 153 ------
 .../fern-definition/api-yml/environments.mdx  |  87 ---
 .../pages/fern-definition/api-yml/errors.mdx  |  54 --
 .../api-yml/global-configuration.mdx          |  53 --
 .../fern-definition/api-yml/overview.mdx      |  57 --
 .../pages/fern-definition/audiences.mdx       | 113 ----
 .../pages/fern-definition/auth.mdx            | 224 --------
 .../pages/fern-definition/availability.mdx    |  96 ----
 .../depending-on-other-apis.mdx               |  67 ---
 .../pages/fern-definition/endpoints.mdx       | 519 ------------------
 .../pages/fern-definition/endpoints/bytes.mdx |  60 --
 .../fern-definition/endpoints/multipart.mdx   |  46 --
 .../pages/fern-definition/endpoints/rest.mdx  |  45 --
 .../pages/fern-definition/endpoints/sse.mdx   | 101 ----
 .../pages/fern-definition/errors.mdx          |  25 -
 .../pages/fern-definition/examples.mdx        | 339 ------------
 .../pages/fern-definition/export-openapi.mdx  |  23 -
 .../pages/fern-definition/imports.mdx         |  22 -
 .../pages/fern-definition/overview.mdx        | 104 ----
 .../pages/fern-definition/packages.mdx        | 153 ------
 .../pages/fern-definition/types.mdx           | 279 ----------
 .../pages/fern-definition/webhooks.mdx        |  65 ---
 .../pages/fern-definition/websocket.png       | Bin 792611 -> 0 bytes
 .../pages/fern-definition/websockets.mdx      |  96 ----
 .../pages/fern-definition/wss-reference.png   | Bin 456280 -> 0 bytes
 .../what-is-an-api-definition.mdx             | 299 ----------
 .../introduction/what-is-the-fern-folder.mdx  | 127 -----
 .../api-definition/pages/openapi/auth.mdx     | 196 -------
 .../pages/openapi/automation.mdx              |  54 --
 .../pages/openapi/endpoints/multipart.mdx     |  75 ---
 .../pages/openapi/endpoints/rest.mdx          |  66 ---
 .../pages/openapi/endpoints/sse.mdx           | 109 ----
 .../api-definition/pages/openapi/examples.mdx |  93 ----
 .../pages/openapi/extensions/audiences.mdx    | 118 ----
 .../pages/openapi/extensions/method-names.mdx |  58 --
 .../pages/openapi/extensions/others.mdx       | 416 --------------
 .../openapi/extensions/parameter-names.mdx    |  65 ---
 .../pages/openapi/overrides.mdx               |  74 ---
 .../api-definition/pages/openapi/overview.mdx | 128 -----
 .../openapi/server-frameworks/fastapi.mdx     |  92 ----
 .../api-definition/pages/openapi/servers.mdx  |  84 ---
 .../api-definition/pages/openapi/webhooks.mdx |  33 --
 49 files changed, 657 insertions(+), 4888 deletions(-)
 create mode 100644 .github/scripts/generate-mappings.js
 delete mode 100644 fern/products/api-definition/api-definition.yml
 delete mode 100644 fern/products/api-definition/pages/fern-definition/api-yml/environments.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/api-yml/errors.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/api-yml/global-configuration.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/api-yml/overview.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/audiences.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/auth.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/availability.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/depending-on-other-apis.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/endpoints.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/endpoints/bytes.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/endpoints/multipart.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/endpoints/rest.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/endpoints/sse.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/errors.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/examples.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/export-openapi.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/imports.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/overview.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/packages.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/types.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/webhooks.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/websocket.png
 delete mode 100644 fern/products/api-definition/pages/fern-definition/websockets.mdx
 delete mode 100644 fern/products/api-definition/pages/fern-definition/wss-reference.png
 delete mode 100644 fern/products/api-definition/pages/introduction/what-is-an-api-definition.mdx
 delete mode 100644 fern/products/api-definition/pages/introduction/what-is-the-fern-folder.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/auth.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/automation.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/endpoints/multipart.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/endpoints/rest.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/endpoints/sse.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/examples.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/extensions/audiences.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/extensions/method-names.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/extensions/others.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/extensions/parameter-names.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/overrides.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/overview.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/server-frameworks/fastapi.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/servers.mdx
 delete mode 100644 fern/products/api-definition/pages/openapi/webhooks.mdx

diff --git a/.github/scripts/fern-scribe.js b/.github/scripts/fern-scribe.js
index bb7065a3..f21b2be7 100644
--- a/.github/scripts/fern-scribe.js
+++ b/.github/scripts/fern-scribe.js
@@ -6,6 +6,53 @@ const yaml = require('js-yaml');
 const https = require('https');
 const http = require('http');
 const FernUrlMapper = require('./fern-url-mapper');
+const fsSync = require('fs'); // For synchronous read
+
+// Parse the Product Root Directories section from my-mappings.md
+function parseProductRootMapping(mappingsPath = path.join(__dirname, 'my-mappings.md')) {
+  const slugToDir = {};
+  if (!fsSync.existsSync(mappingsPath)) return slugToDir;
+  const content = fsSync.readFileSync(mappingsPath, 'utf-8');
+  const rootSection = content.split('## Product Root Directories')[1]?.split('##')[0] || '';
+  rootSection.split('\n').forEach(line => {
+    const match = line.match(/^([\w-]+):\s*([\w-]+)/);
+    if (match) {
+      slugToDir[match[1].trim()] = match[2].trim();
+    }
+  });
+  return slugToDir;
+}
+
+// Helper to parse my-mappings.md and build slug->dir mapping
+function buildProductSlugToDirMap(mappingsPath = path.join(__dirname, 'my-mappings.md')) {
+  const slugToDir = {};
+  if (!fsSync.existsSync(mappingsPath)) return slugToDir;
+  const content = fsSync.readFileSync(mappingsPath, 'utf-8');
+  // Regex: /learn/<slug>... → fern/products/<dir>/pages
+  const regex = /\/learn\/([\w-]+)[^`]*?→\s*fern\/products\/([\w-]+)\/pages/g;
+  let match;
+  while ((match = regex.exec(content)) !== null) {
+    const slug = match[1];
+    const dir = match[2];
+    slugToDir[slug] = dir;
+  }
+  return slugToDir;
+}
+
+// Parse all /learn/... → file path mappings from my-mappings.md
+function parseLearnToFileMapping(mappingsPath = path.join(__dirname, 'my-mappings.md')) {
+  const learnToFile = {};
+  if (!fsSync.existsSync(mappingsPath)) return learnToFile;
+  const content = fsSync.readFileSync(mappingsPath, 'utf-8');
+  const mappingLines = content.split('\n').filter(line => line.trim().startsWith('- `'));
+  for (const line of mappingLines) {
+    const match = line.match(/- `([^`]+)` → `([^`]+)`/);
+    if (match) {
+      learnToFile[match[1].trim()] = match[2].trim();
+    }
+  }
+  return learnToFile;
+}
 
 // Helper function to replace fetch with Node.js built-in modules
 function httpRequest(url, options = {}) {
@@ -77,6 +124,8 @@ class FernScribeGitHub {
     
     // Use centralized URL mapper
     this.urlMapper = new FernUrlMapper(process.env.GITHUB_TOKEN, process.env.REPOSITORY);
+    this.productSlugToDir = parseProductRootMapping();
+    this.learnToFile = parseLearnToFileMapping();
   }
 
   async init() {
@@ -612,6 +661,14 @@ ${existingContent}
 ## Instructions
 Update this file to address the documentation request. Use the Slack discussion context to understand the specific pain points and requirements mentioned by users. Follow Fern documentation best practices and maintain consistency with the existing structure.
 
+CRITICAL MDX SYNTAX REQUIREMENTS:
+- ALL opening tags MUST have corresponding closing tags (e.g., <ParamField> must have </ParamField>)
+- Self-closing tags must use proper syntax (e.g., <ParamField param="value" />)
+- Preserve existing MDX component structure exactly
+- When adding new ParamField, CodeBlock, or other components, ensure they are properly closed
+- Check that every < has a matching >
+- Validate that nested components are properly structured
+
 IMPORTANT: Return ONLY the clean file content. Do not include any explanatory text, meta-commentary, or descriptions about what you're doing. Start directly with the frontmatter (---) or first line of the file content.
 
 Complete updated file content:`;
@@ -635,17 +692,61 @@ Complete updated file content:`;
       });
 
       if (!response.ok) {
-        throw new Error(`Anthropic API error: ${response.status}`);
+        const errorText = await response.text();
+        console.error('❌ Anthropic API error details (generateContent):', errorText);
+        throw new Error(`Anthropic API error: ${response.status} - ${errorText}`);
       }
 
       const data = await response.json();
-      return data.content[0]?.text || '';
+      const generatedContent = data.content[0]?.text || '';
+      
+      // Basic MDX validation
+      const validationResult = this.validateMDXContent(generatedContent);
+      if (!validationResult.isValid) {
+        console.warn(`⚠️  MDX validation warnings for ${filePath}:`, validationResult.warnings);
+      }
+      
+      return generatedContent;
     } catch (error) {
       console.error('Claude API error:', error);
       return existingContent; // Return original if AI fails
     }
   }
 
+  // Basic MDX validation to catch common issues
+  validateMDXContent(content) {
+    const warnings = [];
+    
+    // Check for unclosed ParamField tags
+    const paramFieldMatches = content.match(/<ParamField[^>]*>/g) || [];
+    const paramFieldCloses = content.match(/<\/ParamField>/g) || [];
+    if (paramFieldMatches.length !== paramFieldCloses.length) {
+      warnings.push(`Mismatched ParamField tags: ${paramFieldMatches.length} opening, ${paramFieldCloses.length} closing`);
+    }
+    
+    // Check for unclosed CodeBlock tags
+    const codeBlockMatches = content.match(/<CodeBlock[^>]*>/g) || [];
+    const codeBlockCloses = content.match(/<\/CodeBlock>/g) || [];
+    if (codeBlockMatches.length !== codeBlockCloses.length) {
+      warnings.push(`Mismatched CodeBlock tags: ${codeBlockMatches.length} opening, ${codeBlockCloses.length} closing`);
+    }
+    
+    // Check for other common unclosed tags
+    const commonTags = ['Accordion', 'AccordionItem', 'Tab', 'Tabs', 'Card'];
+    for (const tag of commonTags) {
+      const openTags = content.match(new RegExp(`<${tag}[^>]*>`, 'g')) || [];
+      const closeTags = content.match(new RegExp(`<\/${tag}>`, 'g')) || [];
+      if (openTags.length !== closeTags.length) {
+        warnings.push(`Mismatched ${tag} tags: ${openTags.length} opening, ${closeTags.length} closing`);
+      }
+    }
+    
+    return {
+      isValid: warnings.length === 0,
+      warnings
+    };
+  }
+
   async analyzeDocumentationNeeds(context) {
     if (!this.anthropicApiKey) {
       console.log('⚠️ No Anthropic API key provided - skipping documentation analysis');
@@ -706,7 +807,9 @@ Output your response as JSON:
       });
 
       if (!response.ok) {
-        throw new Error(`Anthropic API error: ${response.status}`);
+        const errorText = await response.text();
+        console.error('❌ Anthropic API error details (analyzeDocumentationNeeds):', errorText);
+        throw new Error(`Anthropic API error: ${response.status} - ${errorText}`);
       }
 
       const data = await response.json();
@@ -818,7 +921,9 @@ Changelog entry:`;
       });
 
       if (!response.ok) {
-        throw new Error(`Anthropic API error: ${response.status}`);
+        const errorText = await response.text();
+        console.error('❌ Anthropic API error details (generateChangelogEntry):', errorText);
+        throw new Error(`Anthropic API error: ${response.status} - ${errorText}`);
       }
 
       const data = await response.json();
@@ -834,6 +939,186 @@ Changelog entry:`;
     return await this.urlMapper.mapTurbopufferPathToGitHub(turbopufferPath);
   }
 
+  // Returns the canonical product file path for a new file, using the mapping from my-mappings.md
+  getCanonicalProductFilePath(slugOrUrl, relPath) {
+    // Try to construct the /learn/... URL
+    let slug = null;
+    if (slugOrUrl) {
+      const match = /learn\/([\w-]+)/.exec(slugOrUrl);
+      if (match) slug = match[1];
+    }
+    if (!slug) slug = slugOrUrl;
+    // Build the canonical /learn/... URL
+    let learnUrl = `/learn/${slug}/${relPath.replace(/\.mdx$/, '').replace(/\/+/, '/')}`;
+    // Remove any double slashes
+    learnUrl = learnUrl.replace(/\/+/g, '/');
+    // Remove trailing .mdx if present
+    learnUrl = learnUrl.replace(/\.mdx$/, '');
+    // Look up the mapping
+    const mappedPath = this.learnToFile[learnUrl];
+    if (mappedPath) {
+      console.log(`[DEBUG] Using mapping: ${learnUrl} → ${mappedPath}`);
+      return mappedPath;
+    } else {
+      console.warn(`[DEBUG] No mapping found for ${learnUrl}, skipping file creation.`);
+      return null;
+    }
+  }
+
+  // Find the appropriate product YAML file based on the file path
+  getProductYamlPath(filePath) {
+    if (filePath.includes('openapi-def') || filePath.includes('openapi-definition')) {
+      return 'fern/products/openapi-def/openapi-def.yml';
+    } else if (filePath.includes('fern-def') || filePath.includes('fern-definition')) {
+      return 'fern/products/fern-def/fern-def.yml';
+    } else if (filePath.includes('sdks')) {
+      return 'fern/products/sdks/sdks.yml';
+    } else if (filePath.includes('docs')) {
+      return 'fern/products/docs/docs.yml';
+    } else if (filePath.includes('ask-fern')) {
+      return 'fern/products/ask-fern/ask-fern.yml';
+    } else if (filePath.includes('cli-api-reference')) {
+      return 'fern/products/cli-api-reference/cli-api-reference.yml';
+    } else if (filePath.includes('asyncapi-def')) {
+      return 'fern/products/asyncapi-def/asyncapi-def.yml';
+    } else if (filePath.includes('openrpc-def')) {
+      return 'fern/products/openrpc-def/openrpc-def.yml';
+    } else if (filePath.includes('grpc-def')) {
+      return 'fern/products/grpc-def/grpc-def.yml';
+    }
+    return null;
+  }
+
+  // Extract the page information from a file path for YAML navigation
+  extractPageInfo(filePath, title) {
+    const pathParts = filePath.split('/');
+    const fileName = pathParts[pathParts.length - 1].replace('.mdx', '');
+    
+    // Create a slug from the file name
+    const slug = fileName;
+    
+    // Extract the relative path after 'fern/products/[product]/'
+    let relativePath = null;
+    const fernProductsIndex = pathParts.indexOf('products');
+    if (fernProductsIndex >= 0 && fernProductsIndex + 2 < pathParts.length) {
+      // Get the path after 'fern/products/[product-name]/'
+      const pathAfterProduct = pathParts.slice(fernProductsIndex + 2).join('/');
+      relativePath = './' + pathAfterProduct;
+    }
+    
+    // Try to find the appropriate section based on path
+    let section = null;
+    if (filePath.includes('extensions')) {
+      section = 'extensions';
+    } else if (filePath.includes('configuration')) {
+      section = 'configuration';
+    } else if (filePath.includes('generators')) {
+      section = 'generators';
+    } else if (filePath.includes('overview')) {
+      section = 'overview';
+    }
+    
+    return {
+      slug,
+      title: title || fileName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
+      section,
+      path: relativePath
+    };
+  }
+
+  // Update product YAML file to include new page
+  async updateProductYaml(filePath, pageTitle, newPageCreated = false) {
+    if (!newPageCreated) {
+      return; // Only update YAML for new pages
+    }
+
+    const yamlPath = this.getProductYamlPath(filePath);
+    if (!yamlPath) {
+      console.log(`   ⚠️  Could not determine product YAML for ${filePath}`);
+      return;
+    }
+
+    try {
+      console.log(`   📝 Updating product navigation: ${yamlPath}`);
+      
+      // Fetch current YAML content
+      const currentYaml = await this.fetchFileContent(yamlPath);
+      if (!currentYaml) {
+        console.log(`   ⚠️  Could not fetch YAML file: ${yamlPath}`);
+        return;
+      }
+
+      // Parse YAML
+      const yamlData = yaml.load(currentYaml);
+      const pageInfo = this.extractPageInfo(filePath, pageTitle);
+      
+      // Find the appropriate section to add the new page
+      let targetSection = null;
+      if (yamlData.navigation) {
+        // Look for existing section
+        for (const item of yamlData.navigation) {
+          if (item.section === pageInfo.section) {
+            targetSection = item;
+            break;
+          }
+        }
+        
+        // If no specific section found, add to the end
+        if (!targetSection && yamlData.navigation.length > 0) {
+          // Find a good parent section or create one
+          if (pageInfo.section === 'extensions') {
+            targetSection = yamlData.navigation.find(item => 
+              item.section === 'extensions' || 
+              item.title?.toLowerCase().includes('extension')
+            );
+          }
+          
+          if (!targetSection) {
+            // Add to the last section that has children
+            targetSection = yamlData.navigation.find(item => item.contents);
+          }
+        }
+      }
+
+      // Add the new page
+      const newPageEntry = {
+        page: pageInfo.slug,
+        title: pageInfo.title,
+        path: pageInfo.path
+      };
+
+      if (targetSection && targetSection.contents) {
+        targetSection.contents.push(newPageEntry);
+      } else if (yamlData.navigation) {
+        // Create a new section if needed
+        yamlData.navigation.push({
+          section: pageInfo.section || 'other',
+          contents: [newPageEntry]
+        });
+      } else {
+        // Fallback: create basic navigation structure
+        yamlData.navigation = [{
+          section: pageInfo.section || 'main',
+          contents: [newPageEntry]
+        }];
+      }
+
+      // Convert back to YAML
+      const updatedYaml = yaml.dump(yamlData, { 
+        indent: 2,
+        lineWidth: -1,
+        noRefs: true
+      });
+
+      console.log(`   ✅ Added page "${pageInfo.title}" to ${yamlPath}`);
+      return { yamlPath, updatedYaml };
+
+    } catch (error) {
+      console.error(`   ❌ Error updating YAML for ${filePath}:`, error.message);
+      return null;
+    }
+  }
+
   // Simple file content fetcher for dynamic mapping (without path transformation)
   async fetchFileContent(filePath) {
     try {
@@ -1097,7 +1382,36 @@ ${context.additionalContext ? `**Additional Context:** ${context.additionalConte
           };
           
           console.log(`   🤖 Generating AI suggestions based on context...`);
-          const suggestedContent = await this.generateContent(filePath, currentContent, contextWithDocument, fernStructure);
+          let suggestedContent = null;
+          let valid = false;
+          let attempts = 0;
+          while (attempts < 3 && !valid) {
+            suggestedContent = await this.generateContent(filePath, currentContent, contextWithDocument, fernStructure);
+            const validationResult = this.validateMDXContent(suggestedContent);
+            if (validationResult.isValid) {
+              valid = true;
+            } else {
+              attempts++;
+              console.warn(`⚠️  MDX validation failed for ${filePath} (attempt ${attempts}):`, validationResult.warnings);
+              // Optionally: try to auto-fix here (not implemented yet)
+            }
+          }
+          if (!valid) {
+            const msg = `❌ Skipping file due to invalid MDX after 3 attempts: ${filePath}\nWarnings: ${JSON.stringify(this.validateMDXContent(suggestedContent).warnings)}`;
+            console.warn(msg);
+            // If running in GitHub Actions, comment on the issue
+            if (process.env.GITHUB_TOKEN && process.env.REPOSITORY && process.env.ISSUE_NUMBER) {
+              const [owner, repo] = process.env.REPOSITORY.split('/');
+              const octokit = this.octokit;
+              await octokit.rest.issues.createComment({
+                owner,
+                repo,
+                issue_number: this.issueNumber,
+                body: msg
+              });
+            }
+            continue; // Skip this file
+          }
           
           if (suggestedContent && suggestedContent !== currentContent) {
             analysisResults.push({
@@ -1199,17 +1513,63 @@ ${context.additionalContext ? `**Additional Context:** ${context.additionalConte
           // Update files with suggested content
           for (const result of analysisResults) {
             try {
-              const actualPath = await this.mapTurbopufferPathToGitHub(result.filePath);
+              let actualPath;
+              const isNewFile = result.currentContent.length === 0;
+              if (isNewFile) {
+                // Use mapping to get correct product directory for new files
+                let slug = null;
+                if (result.url) {
+                  const match = /learn\/([\w-]+)/.exec(result.url);
+                  if (match) slug = match[1];
+                }
+                if (!slug && result.filePath) {
+                  const match = /learn\/([\w-]+)/.exec(result.filePath);
+                  if (match) slug = match[1];
+                }
+                // Extract relative path after /learn/<slug>/
+                let relPath = '';
+                if (result.url) {
+                  const relMatch = result.url.match(/learn\/[\w-]+\/(.*)/);
+                  if (relMatch) relPath = relMatch[1];
+                }
+                if (!relPath && result.filePath) {
+                  const relMatch = result.filePath.match(/learn\/[\w-]+\/(.*)/);
+                  if (relMatch) relPath = relMatch[1];
+                }
+                relPath = relPath.replace(/\.mdx$/, '') + '.mdx';
+                actualPath = this.getCanonicalProductFilePath(slug, relPath);
+                if (!actualPath) {
+                  console.warn(`[DEBUG] Skipping file creation for ${result.url || result.filePath} (no mapping found)`);
+                  continue;
+                }
+              } else {
+                actualPath = await this.mapTurbopufferPathToGitHub(result.filePath);
+              }
               
-              console.log(`   📝 Updating file: ${actualPath}`);
+              console.log(`   📝 Updating file: ${actualPath}${isNewFile ? ' (new file)' : ''}`);
               await this.updateFile(
                 actualPath,
                 result.suggestedContent,
                 branchName,
-                `Update ${path.basename(actualPath)} based on issue #${this.issueNumber}`
+                `${isNewFile ? 'Create' : 'Update'} ${path.basename(actualPath)} based on issue #${this.issueNumber}`
               );
               
               filesUpdated.push(actualPath);
+              
+              // Update product YAML if this is a new file
+              if (isNewFile) {
+                const yamlUpdate = await this.updateProductYaml(actualPath, result.title, true);
+                if (yamlUpdate) {
+                  console.log(`   📝 Updating navigation: ${yamlUpdate.yamlPath}`);
+                  await this.updateFile(
+                    yamlUpdate.yamlPath,
+                    yamlUpdate.updatedYaml,
+                    branchName,
+                    `Add ${result.title} page to navigation`
+                  );
+                  filesUpdated.push(yamlUpdate.yamlPath);
+                }
+              }
             } catch (error) {
               console.error(`   ⚠️  Could not update ${result.filePath}: ${error.message}`);
             }
diff --git a/.github/scripts/fern-url-mapper.js b/.github/scripts/fern-url-mapper.js
index e3aa7e7d..772ffae9 100644
--- a/.github/scripts/fern-url-mapper.js
+++ b/.github/scripts/fern-url-mapper.js
@@ -5,7 +5,9 @@ const fs = require('fs').promises;
 class FernUrlMapper {
   constructor(githubToken = null, repository = null) {
     this.dynamicPathMapping = new Map();
+    this.staticPathMapping = new Map();
     this.isPathMappingLoaded = false;
+    this.isStaticMappingLoaded = false;
     
     // Initialize GitHub client if credentials provided
     if (githubToken && repository) {
@@ -40,6 +42,35 @@ class FernUrlMapper {
     }
   }
 
+  // Load static path mapping from my-mappings.md
+  async loadStaticPathMapping() {
+    if (this.isStaticMappingLoaded) return;
+    
+    try {
+      const mappingsContent = await fs.readFile('my-mappings.md', 'utf-8');
+      console.log('Loading static path mappings from my-mappings.md...');
+      
+      // Parse the markdown file for URL mappings
+      const lines = mappingsContent.split('\n');
+      let mappingCount = 0;
+      
+      for (const line of lines) {
+        // Look for lines that match the mapping pattern: - `/learn/...` → `fern/...`
+        const match = line.match(/^-\s+`([^`]+)`\s+→\s+`([^`]+)`/);
+        if (match) {
+          const [, url, path] = match;
+          this.staticPathMapping.set(url, path);
+          mappingCount++;
+        }
+      }
+      
+      this.isStaticMappingLoaded = true;
+      console.log(`Loaded ${mappingCount} static path mappings from my-mappings.md`);
+    } catch (error) {
+      console.error('Failed to load static path mapping:', error.message);
+    }
+  }
+
   // Load dynamic path mapping from Fern docs structure
   async loadDynamicPathMapping() {
     if (this.isPathMappingLoaded) return;
@@ -222,10 +253,15 @@ class FernUrlMapper {
   
   // Transform Turbopuffer URLs to actual GitHub file paths
   transformTurbopufferUrlToPath(turbopufferUrl) {
-    // Clean up trailing slashes but keep the /learn prefix for dynamic mapping lookup
+    // Clean up trailing slashes but keep the /learn prefix for mapping lookup
     let cleanUrl = turbopufferUrl.replace(/\/$/, '');
     
-    // First try to use dynamic mapping with full URL (including /learn)
+    // First try to use static mapping from my-mappings.md
+    if (this.staticPathMapping.has(cleanUrl)) {
+      return this.staticPathMapping.get(cleanUrl);
+    }
+    
+    // Second try to use dynamic mapping with full URL (including /learn)
     if (this.dynamicPathMapping.has(cleanUrl)) {
       const mappedPath = this.dynamicPathMapping.get(cleanUrl);
       // Add .mdx extension if not present and not already a complete path
@@ -279,22 +315,33 @@ class FernUrlMapper {
     }
   }
 
-  // Map Turbopuffer URLs to actual GitHub file paths (now using dynamic mapping)
+  // Map Turbopuffer URLs to actual GitHub file paths (now using static mapping first, then dynamic)
   async mapTurbopufferPathToGitHub(turbopufferPath) {
-    // Ensure dynamic mapping is loaded
+    // Ensure static mapping is loaded first
+    await this.loadStaticPathMapping();
+    // Ensure dynamic mapping is loaded as fallback
     await this.loadDynamicPathMapping();
     
-    // Use the improved transformation logic that prioritizes dynamic mapping
+    // Use the improved transformation logic that prioritizes static mapping, then dynamic mapping
     return this.transformTurbopufferUrlToPath(turbopufferPath) || turbopufferPath;
   }
 
   // Get all mappings as an object for external use
   async getAllMappings() {
+    await this.loadStaticPathMapping();
     await this.loadDynamicPathMapping();
     const mappings = {};
+    
+    // Add dynamic mappings first
     for (const [url, path] of this.dynamicPathMapping) {
       mappings[url] = path;
     }
+    
+    // Override with static mappings (they take priority)
+    for (const [url, path] of this.staticPathMapping) {
+      mappings[url] = path;
+    }
+    
     return mappings;
   }
 
@@ -341,6 +388,7 @@ class FernUrlMapper {
 
   // Test specific URL mappings
   async testMappings(testUrls = []) {
+    await this.loadStaticPathMapping();
     await this.loadDynamicPathMapping();
     
     console.log('\n=== TESTING URL MAPPINGS ===');
diff --git a/.github/scripts/generate-mappings.js b/.github/scripts/generate-mappings.js
new file mode 100644
index 00000000..6fa6c140
--- /dev/null
+++ b/.github/scripts/generate-mappings.js
@@ -0,0 +1,229 @@
+const fs = require('fs');
+const path = require('path');
+const yaml = require('js-yaml');
+const { Octokit } = require('@octokit/rest');
+
+const OUTPUT_FILE = path.join(__dirname, 'my-mappings.md');
+const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
+const REPOSITORY = process.env.REPOSITORY;
+const BRANCH = 'main';
+
+if (!GITHUB_TOKEN || !REPOSITORY) {
+  console.error('GITHUB_TOKEN and REPOSITORY env vars are required.');
+  process.exit(1);
+}
+
+const [owner, repo] = REPOSITORY.split('/');
+const octokit = new Octokit({ auth: GITHUB_TOKEN });
+
+async function listDir(pathInRepo) {
+  try {
+    const res = await Promise.race([
+      octokit.repos.getContent({
+        owner,
+        repo,
+        path: pathInRepo,
+        ref: BRANCH
+      }),
+      new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 10000))
+    ]);
+    return Array.isArray(res.data) ? res.data : [];
+  } catch (e) {
+    return [];
+  }
+}
+
+async function getFileContent(pathInRepo) {
+  try {
+    const res = await Promise.race([
+      octokit.repos.getContent({
+        owner,
+        repo,
+        path: pathInRepo,
+        ref: BRANCH
+      }),
+      new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 10000))
+    ]);
+    if (res.data && res.data.content) {
+      return Buffer.from(res.data.content, 'base64').toString('utf-8');
+    }
+    return null;
+  } catch (e) {
+    return null;
+  }
+}
+
+function slugify(str) {
+  return String(str)
+    .replace(/(-def|-reference|-docs|-api)$/i, '') // Only remove at the end
+    .replace(/_/g, '-')
+    .replace(/\s+/g, '-')
+    .toLowerCase();
+}
+
+async function findDocsYml(productDir) {
+  // Try <dir>.yml, docs.yml, or any .yml in the product dir
+  const files = await listDir(`fern/products/${productDir}`);
+  const candidates = [
+    `${productDir}.yml`,
+    `docs.yml`
+  ];
+  for (const candidate of candidates) {
+    if (files.find(f => f.name === candidate)) {
+      return candidate;
+    }
+  }
+  // fallback: first .yml file
+  const yml = files.find(f => f.name.endsWith('.yml'));
+  return yml ? yml.name : null;
+}
+
+async function findPageFile(productDir, page) {
+  // Try to find the .mdx file in pages/ recursively using the API
+  async function walk(dir) {
+    console.log(`[DEBUG] Listing directory: ${dir}`);
+    const items = await listDir(dir);
+    for (const item of items) {
+      if (item.type === 'dir') {
+        const found = await walk(item.path);
+        if (found) return found;
+      } else if (item.name.replace(/\.mdx$/, '') === page) {
+        console.log(`[DEBUG] Found page file: ${item.path} for page: ${page}`);
+        return item.path;
+      }
+    }
+    return null;
+  }
+  return await walk(`fern/products/${productDir}/pages`);
+}
+
+async function walkNav(nav, parentSlugs, pages, productDir, canonicalSlug, depth = 0) {
+  for (const item of nav) {
+    let sectionSlug = '';
+    if (item['skip-slug']) {
+      sectionSlug = '';
+      console.log(`[DEBUG] [${'  '.repeat(depth)}] Skipping slug for section: ${item.section || ''}`);
+    } else if (item.slug === true && item.section) {
+      sectionSlug = slugify(item.section);
+      console.log(`[DEBUG] [${'  '.repeat(depth)}] Section with slug:true: ${sectionSlug}`);
+    } else if (typeof item.slug === 'string') {
+      sectionSlug = slugify(item.slug);
+      console.log(`[DEBUG] [${'  '.repeat(depth)}] Section with explicit slug: ${sectionSlug}`);
+    } else if (item.section) {
+      sectionSlug = slugify(item.section);
+      console.log(`[DEBUG] [${'  '.repeat(depth)}] Section with name: ${sectionSlug}`);
+    }
+    const newSlugs = sectionSlug ? [...parentSlugs, sectionSlug] : parentSlugs;
+    if (item.contents) {
+      console.log(`[DEBUG] [${'  '.repeat(depth)}] Entering section: ${sectionSlug || '(no slug)'} with path: /learn/${[canonicalSlug, ...newSlugs].join('/')}`);
+      await walkNav(item.contents, newSlugs, pages, productDir, canonicalSlug, depth + 1);
+      console.log(`[DEBUG] [${'  '.repeat(depth)}] Exiting section: ${sectionSlug || '(no slug)'}`);
+    }
+    if (item.page) {
+      let pageSlug = typeof item.slug === 'string' ? slugify(item.slug) : slugify(item.page);
+      // Only add pageSlug if it's not the same as the last section slug
+      let urlSegments = ['/learn', canonicalSlug, ...newSlugs];
+      if (newSlugs[newSlugs.length - 1] !== pageSlug) {
+        urlSegments.push(pageSlug);
+      }
+      const learnUrl = urlSegments.filter(Boolean).join('/');
+      if (item.path) {
+        // Remove leading './' if present
+        let repoPath = item.path.replace(/^\.\//, '');
+        // Always make it relative to fern/products/<productDir>
+        if (!repoPath.startsWith('fern/products/')) {
+          repoPath = `fern/products/${productDir}/${repoPath}`;
+        }
+        pages.push({ learnUrl, repoPath });
+        console.log(`[DEBUG] [${'  '.repeat(depth)}] Mapping: ${learnUrl} → ${repoPath}`);
+      } else {
+        console.warn(`[DEBUG] [${'  '.repeat(depth)}] Skipping page: ${item.page} in product: ${productDir} (missing path)`);
+      }
+    }
+  }
+}
+
+async function main() {
+  // Step 1: Find and parse the root docs.yml
+  const rootDocsYmlContent = await getFileContent('fern/docs.yml');
+  if (!rootDocsYmlContent) {
+    console.error('Could not find fern/docs.yml in the repo.');
+    process.exit(1);
+  }
+  const rootDocsYml = yaml.load(rootDocsYmlContent);
+
+  // Step 2: Get the root URL subpath (e.g., /learn)
+  let rootUrlSubpath = '';
+  if (rootDocsYml.url) {
+    const url = rootDocsYml.url;
+    const match = url.match(/https?:\/\/[^/]+(\/.*)/);
+    rootUrlSubpath = match ? match[1].replace(/\/$/, '') : '';
+    console.log(`[DEBUG] Root URL subpath: ${rootUrlSubpath}`);
+  }
+  if (!rootUrlSubpath) rootUrlSubpath = '/learn';
+  console.log(`[DEBUG] rootUrlSubpath: "${rootUrlSubpath}"`);
+
+  // Step 3: Parse products from root docs.yml
+  const products = rootDocsYml.products || [];
+  let rootMappingLines = ['## Product Root Directories', ''];
+  let slugToDir = {};
+  let allPages = [];
+  for (const product of products) {
+    if (!product.path || !product.slug) {
+      console.warn(`[DEBUG] Skipping product with missing path or slug: ${JSON.stringify(product)}`);
+      continue;
+    }
+    // product.path is like ./products/openapi-def/openapi-def.yml
+    const productDir = product.path.split('/')[2];
+    const productYmlPath = product.path.replace('./', 'fern/');
+    const ymlContent = await getFileContent(productYmlPath);
+    if (!ymlContent) {
+      console.warn(`[DEBUG] Could not fetch product YAML: ${productYmlPath}`);
+      continue;
+    }
+    const productYml = yaml.load(ymlContent);
+    const canonicalSlug = slugify(product.slug);
+    slugToDir[canonicalSlug] = productDir;
+    rootMappingLines.push(`${canonicalSlug}: ${productDir}`);
+    console.log(`[DEBUG] Product: ${productDir}, Slug: ${canonicalSlug}, YAML: ${productYmlPath}`);
+    if (productYml && productYml.navigation) {
+      await walkNav(productYml.navigation, [], allPages, productDir, canonicalSlug);
+    } else {
+      console.warn(`[DEBUG] No navigation found in ${productYmlPath}`);
+    }
+  }
+  rootMappingLines.push('');
+
+  let lines = [
+    '# Fern URL Mappings',
+    '',
+    `Generated on: ${new Date().toISOString()}`,
+    '',
+    ...rootMappingLines,
+    '## Products',
+    ''
+  ];
+  let total = 0;
+  for (const slug in slugToDir) {
+    lines.push(`## ${slug.charAt(0).toUpperCase() + slug.slice(1)}`);
+    lines.push('');
+    const pages = allPages.filter(p => p.learnUrl.startsWith(`/learn/${slug}/`));
+    if (pages.length === 0) {
+      lines.push('_No .mdx files found for this product._');
+    }
+    for (const { learnUrl, repoPath } of pages) {
+      lines.push(`- \`${learnUrl}\` → \`${repoPath}\``);
+      console.log(`[DEBUG] Mapping: ${learnUrl} → ${repoPath}`);
+      total++;
+    }
+    lines.push('');
+  }
+  lines[3] = `Total mappings: ${total}`;
+  fs.writeFileSync(OUTPUT_FILE, lines.join('\n'), 'utf-8');
+  console.log(`Wrote ${total} mappings to ${OUTPUT_FILE}`);
+  if (total === 0) {
+    console.warn('Warning: No mappings were generated. Check your repo structure and permissions.');
+  }
+}
+
+main();
\ No newline at end of file
diff --git a/.github/workflows/fern-scribe.yml b/.github/workflows/fern-scribe.yml
index a41498cd..99ebdb93 100644
--- a/.github/workflows/fern-scribe.yml
+++ b/.github/workflows/fern-scribe.yml
@@ -32,6 +32,13 @@ jobs:
           cd .github/scripts
           npm install
 
+      # --- NEW STEP: Generate my-mappings.md ---
+      - name: Generate Fern URL Mappings
+        run: |
+          cd .github/scripts
+          node generate-mappings.js
+      # -----------------------------------------
+
       - name: Run Fern Scribe
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/fern/docs.yml b/fern/docs.yml
index a0347004..d0944cc9 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -42,13 +42,6 @@ products:
     image: ./images/product-switcher/product-switcher-askfern-light.png
     slug: ask-fern
     subtitle: Let users find answers in your documentation instantly
-  
-
-  # - display-name: API Definition
-  #   path: ./products/api-definition/api-definition.yml
-  #   icon: fa-regular fa-book
-  #   image: ./images/product-switcher/api-definitions-light.png
-  #   slug: api-definition
 
   - display-name: OpenAPI
     path: ./products/openapi-def/openapi-def.yml
diff --git a/fern/images/product-switcher/product-switcher-askfern-dark.png b/fern/images/product-switcher/product-switcher-askfern-dark.png
index 9092dcea1720eb9378f9797732b46f65eef390fa..d0c29de538bfad2971e7b939123caa7b564c91b2 100644
GIT binary patch
diff --git a/fern/images/product-switcher/product-switcher-askfern-light.png b/fern/images/product-switcher/product-switcher-askfern-light.png
index 3332c135117b97ef3de368cc9de5526020f647d6..200235328d4fc9310ffd83d1595710a1b80a5220 100644
GIT binary patch
diff --git a/fern/products/api-definition/api-definition.yml b/fern/products/api-definition/api-definition.yml
deleted file mode 100644
index ec4fbbe6..00000000
--- a/fern/products/api-definition/api-definition.yml
+++ /dev/null
@@ -1,153 +0,0 @@
-navigation:
-  - section: Introduction
-    contents:
-      - page: What is an API Definition?
-        icon: fa-regular fa-question-circle
-        path: ./pages/introduction/what-is-an-api-definition.mdx
-      - page: What is the Fern Folder?
-        icon: fa-regular fa-folder
-        path: ./pages/introduction/what-is-the-fern-folder.mdx
-  - section: OpenAPI Specification
-    slug: openapi
-    contents:
-      - page: Overview
-        icon: fa-regular fa-brackets-curly
-        path: ./pages/openapi/overview.mdx
-      - page: Authentication
-        icon: fa-regular fa-lock-keyhole
-        path: ./pages/openapi/auth.mdx
-      - page: Servers
-        icon: fa-regular fa-globe
-        path: ./pages/openapi/servers.mdx            
-      - section: Endpoints
-        icon: fa-regular fa-object-intersect
-        slug: endpoints
-        contents:
-          - page: HTTP JSON Endpoints
-            icon: fa-regular fa-display-code
-            path: ./pages/openapi/endpoints/rest.mdx
-            slug: http
-          - page: Multipart Form Uploads
-            icon: fa-regular fa-file
-            path: ./pages/openapi/endpoints/multipart.mdx
-            slug: multipart
-          - page: Server-Sent Events
-            path: ./pages/openapi/endpoints/sse.mdx
-            icon: fa-regular fa-signal-stream
-            slug: sse
-      - page: Webhooks
-        path: ./pages/openapi/webhooks.mdx
-        icon: fa-regular fa-webhook
-      - page: Audiences
-        icon: fa-duotone fa-users
-        path: ./pages/openapi/extensions/audiences.mdx
-        slug: audiences
-      - section: Extensions
-        icon: fa-regular fa-object-intersect
-        slug: extensions
-        contents:
-          - page: SDK Method Names
-            icon: fa-regular fa-display-code
-            path: ./pages/openapi/extensions/method-names.mdx
-            slug: method-names
-          - page: Parameter Names
-            icon: fa-regular fa-input-text
-            path: ./pages/openapi/extensions/parameter-names.mdx
-          - page: Other
-            icon: fa-regular fa-ellipsis-h
-            path: ./pages/openapi/extensions/others.mdx
-            slug: others
-      - page: Overlay Customizations
-        icon: fa-regular fa-shuffle
-        path: ./pages/openapi/overrides.mdx
-      - page: Sync your OpenAPI Specification
-        icon: fa-regular fa-arrows-rotate
-        path: ./pages/openapi/automation.mdx            
-      - section: Integrate your Server Framework
-        icon: fa-regular fa-server
-        slug: frameworks
-        contents:
-          - page: FastAPI
-            icon: fa-regular fa-circle-bolt
-            path: ./pages/openapi/server-frameworks/fastapi.mdx
-            slug: fastapi
-  - section: Fern Definition
-    slug: fern
-    contents:
-      - page: Overview
-        icon: fa-regular fa-seedling
-        path: ./pages/fern-definition/overview.mdx
-      - page: Authentication
-        icon: fa-regular fa-lock-keyhole
-        path: ./pages/fern-definition/auth.mdx
-      - page: Types
-        icon: fa-regular fa-shapes
-        path: ./pages/fern-definition/types.mdx
-      - section: Endpoints
-        icon: fa-regular fa-plug
-        path: ./pages/fern-definition/endpoints.mdx
-        contents: 
-          - page: HTTP JSON Endpoints
-            icon: fa-regular fa-display-code
-            path: ./pages/fern-definition/endpoints/rest.mdx
-            slug: http
-          - page: Multipart Form Uploads
-            icon: fa-regular fa-file
-            path: ./pages/fern-definition/endpoints/multipart.mdx
-            slug: multipart
-          - page: Bytes
-            path: ./pages/fern-definition/endpoints/bytes.mdx
-            icon: fa-regular fa-server
-            slug: bytes                
-          - page: Server-Sent Events
-            icon: fa-regular fa-signal-stream
-            path: ./pages/fern-definition/endpoints/sse.mdx
-            slug: sse
-      - page: Webhooks
-        icon: fa-regular fa-webhook
-        path: ./pages/fern-definition/webhooks.mdx
-      - page: WebSockets
-        icon: fa-regular fa-globe
-        path: ./pages/fern-definition/websockets.mdx
-        slug: websockets
-      - page: Errors
-        icon: fa-regular fa-exclamation-triangle
-        path: ./pages/fern-definition/errors.mdx
-      - page: Imports
-        icon: fa-regular fa-download
-        path: ./pages/fern-definition/imports.mdx
-      - page: Examples
-        icon: fa-regular fa-square-terminal
-        path: ./pages/fern-definition/examples.mdx
-      - page: Audiences
-        icon: fa-duotone fa-users
-        path: ./pages/fern-definition/audiences.mdx
-      - page: Availability
-        icon: fa-regular fa-clock-rotate-left
-        path: ./pages/fern-definition/availability.mdx
-      - section: api.yml Reference
-        icon: fa-regular fa-books
-        slug: api-yml
-        contents:
-          - page: Overview
-            icon: fa-regular fa-book
-            path: ./pages/fern-definition/api-yml/overview.mdx
-          - page: Environments
-            icon: fa-regular fa-circle-wifi
-            path: ./pages/fern-definition/api-yml/environments.mdx
-          - page: Global Headers
-            icon: fa-regular fa-globe
-            path: ./pages/fern-definition/api-yml/global-configuration.mdx
-          - page: Errors
-            icon: fa-regular fa-exclamation-triangle
-            path: ./pages/fern-definition/api-yml/errors.mdx
-      - page: Packages
-        icon: fa-regular fa-box-open
-        path: ./pages/fern-definition/packages.mdx
-      - page: Depending on Other APIs
-        icon: fa-regular fa-link
-        path: ./pages/fern-definition/depending-on-other-apis.mdx
-      - page: Export to OpenAPI
-        icon: fa-regular fa-file-export
-        slug: export-openapi
-        path: ./pages/fern-definition/export-openapi.mdx
diff --git a/fern/products/api-definition/pages/fern-definition/api-yml/environments.mdx b/fern/products/api-definition/pages/fern-definition/api-yml/environments.mdx
deleted file mode 100644
index bdb9cc69..00000000
--- a/fern/products/api-definition/pages/fern-definition/api-yml/environments.mdx
+++ /dev/null
@@ -1,87 +0,0 @@
----
-title: Environments
-description: List environments like production, staging, and development. 
----
-
-You can specify the environments where your server is deployed. 
-
-## Single URL environments
-
-```yaml title="api.yml"
-name: api
-environments:
-  Production: https://www.yoursite.com
-  Staging:
-    docs: This staging environment is helpful for testing!
-    url: https://www.staging.yoursite.com
-```
-
-## Multiple URLs per environment
-
-You can specify multiple URLs per environment. This is helpful if you have a
-microservice architecture, and you want a single SDK to interact with multiple
-servers.
-
-```yaml title="api.yml"
-environments:
-  Production:
-    urls:
-      Auth: https://auth.yoursite.com
-      Plants: https://plants.yoursite.com
-  Staging:
-    urls:
-      Auth: https://auth.staging.yoursite.com
-      Plants: https://plants.staging.yoursite.com
-```
-
-If you choose to use this feature, you must specify a `url` for each service you define:
-
-```yaml title="auth.yml"
-service:
-  url: Auth
-  base-path: /auth
-  ...
-```
-
-## Default environment
-
-You can also provide a default environment:
-
-```yaml title="api.yml"
-name: api
-environments:
-  Production: https://www.yoursite.com
-  Staging:
-    docs: This staging environment is helpful for testing!
-    url: https://www.staging.yoursite.com
-default-environment: Production
-```
-
-<Note> By providing a default environment, the generated SDK will be setup to hit that URL out-of-the-box. </Note>
-
-## Base path
-If you would like all of your endpoints to be prefixed with a path, use `base-path`. 
-
-In the example below, every endpoint is prefixed with a `/v1`: 
-```yaml title="api.yml"
-name: api
-base-path: /v1
-```
-
-## Audiences
-
-If you have listed environments that you want to filter, you can leverage audiences. 
-
-```yaml title="api.yml"
-audiences:
-  - public
-
-environments:
-  Dev: 
-    url: https://api.dev.buildwithfern.com
-  Prod: 
-    url: https://api.buildwithfern.com
-    audiences:
-      - external
-```
-
diff --git a/fern/products/api-definition/pages/fern-definition/api-yml/errors.mdx b/fern/products/api-definition/pages/fern-definition/api-yml/errors.mdx
deleted file mode 100644
index 06ce9ef3..00000000
--- a/fern/products/api-definition/pages/fern-definition/api-yml/errors.mdx
+++ /dev/null
@@ -1,54 +0,0 @@
----
-title: Errors
-description: Specify error types and schemas
----
-
-In order to generate SDKs idiomatically, Fern needs to know how to differentiate
-between different errors when parsing an endpoint response.
-
-### Discriminate by status code
-
-You can specify Fern to discriminate by status code. This means on each
-endpoint, every error that's listed must have a different HTTP status code.
-
-<CodeBlock title="api.yml">
-```yaml
-name: api
-error-discrimination:
-  strategy: status-code
-```
-</CodeBlock>
-
-### Discriminate by error name
-
-You can specify Fern to discriminate by error name. If you select this strategy,
-then Fern will assume that every error response has an extra property denoting
-the error name.
-
-If you use Fern to generate server-side code, then this option provides
-the most flexibility. Otherwise, you'll probably want to use the status code
-discrimination strategy.
-
-<CodeBlock title="api.yml">
-```yaml
-name: api
-error-discrimination:
-  strategy: property
-  property-name: errorName
-```
-</CodeBlock>
-
-### Global errors
-
-You can import and list errors that will be thrown by every endpoint.
-
-<CodeBlock title="api.yml">
-```yaml
-imports:
-  commons: commons.yml
-
-errors:
-  - commons.NotFoundError
-  - commons.BadRequestError
-```
-</CodeBlock>
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/api-yml/global-configuration.mdx b/fern/products/api-definition/pages/fern-definition/api-yml/global-configuration.mdx
deleted file mode 100644
index 0f3cc3b7..00000000
--- a/fern/products/api-definition/pages/fern-definition/api-yml/global-configuration.mdx
+++ /dev/null
@@ -1,53 +0,0 @@
----
-title: Global Configuration
-description: Specify global headers, path parameters or query parameters meant to be included on every request.
----
-
-The `api.yml` configuration supports global configuration like headers and path parameters. 
-
-## Global headers
-
-You can specify headers that are meant to be included on every request:
-
-<CodeBlock title="api.yml">
-```yaml
-name: api
-headers:
-  X-App-Id: string
-```
-</CodeBlock>
-
-## Global path parameters
-
-You can specify path parameters that are meant to be included on every request:
-
-<CodeBlock title="api.yml">
-```yaml
-name: api
-base-path: /{userId}/{orgId}
-path-parameters:
-  userId: string
-  orgId: string
-```
-</CodeBlock>
-
-### Overriding the base path
-
-If you have certain endpoints that do not live at the configured `base-path`, you can 
-override the `base-path` at the endpoint level. 
-
-```yml imdb.yml {5}
-service: 
-  endpoints: 
-    getMovie: 
-      method: POST
-      base-path: "override/{arg}"
-      path: "movies/{movieId}"
-      path-parameters: 
-        arg: string
-```
-
-## Global query parameters
-
-You cannot yet specify query parameters that are meant to be included on every request.
-If you'd like to see this feature, please upvote [this issue](https://github.com/fern-api/fern/issues/2930).
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/api-yml/overview.mdx b/fern/products/api-definition/pages/fern-definition/api-yml/overview.mdx
deleted file mode 100644
index d27912ad..00000000
--- a/fern/products/api-definition/pages/fern-definition/api-yml/overview.mdx
+++ /dev/null
@@ -1,57 +0,0 @@
----
-title: The api.yml configuration file
-description: The api.yml file contains general API configuration when using the Fern Definition format.
----
-
-A `fern/` folder has a special file called `api.yml`, which includes all the API-wide configuration.
-
-```bash {5}
-fern/
-├─ fern.config.json
-├─ generators.yml
-└─ definition/
-  ├─ api.yml
-  ├─ pet.yml
-  ├─ store.yml
-  └─ user.yml
-```
-
-## API name
-
-This name is used to uniquely identify your API in your organization. If you just have one API, then `api` is a sufficient name.
-
-<CodeBlock title="api.yml">
-```yaml
-name: api
-```
-</CodeBlock>
-
-## API description
-
-You can define a top level API description. This description will come through in the OpenAPI Specification and Postman collection.
-
-<CodeBlock title="api.yml">
-```yaml {2-4}
-name: api
-docs: | 
-  ## Header
-  This API provides access to...
-```
-</CodeBlock>
-
-## API version
-
-You can define your header-based API versioning scheme, such as an `X-API-Version`. The supported versions 
-and default value are specified like so:
-
-<CodeBlock title="api.yml">
-```yaml
-version:
-  header: X-API-Version
-  default: "2.0.0"
-  values:
-    - "1.0.0"
-    - "2.0.0"
-    - "latest"
-```
-</CodeBlock>
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/audiences.mdx b/fern/products/api-definition/pages/fern-definition/audiences.mdx
deleted file mode 100644
index 20f2876a..00000000
--- a/fern/products/api-definition/pages/fern-definition/audiences.mdx
+++ /dev/null
@@ -1,113 +0,0 @@
----
-title: Audiences in Fern Definition 
-subtitle: Use audiences in your Fern Definition to segment your API for different groups of consumers.
----
-
-Audiences are a useful tool for segmenting your API for different consumers. You can configure your Fern Docs to publish documentation specific to an `Audience`. You can use [audiences in your OpenAPI Specification](/learn/api-definition/openapi/audiences), too. 
-
-Common examples of audiences include:
-
-- Internal consumers (e.g., frontend developers who use the API)
-- Beta testers
-- Customers
-
-By default, if no audience is specified, it will be accessible to all consumers. 
-
-## Configuration
-
-The Fern Definition has a first-class concept for marking different endpoints, types, and properties for different audiences. 
-
-To use audiences in your Fern Definition, add them to `api.yml`. 
-
-In the example below, we have created audiences for `internal`, `beta`, and `customer` groups:
-
-```yaml title='api.yml' {2-5}
-name: api 
-audiences: 
-  - internal 
-  - beta 
-  - customers
-```
-
-## Audiences for endpoints
-
-To mark an endpoint for a particular consumer, add an `audience` with the relevant groups. 
-
-In this example, the `sendEmail` endpoint is only available to internal consumers:
-
-```yaml title='user.yml' {6-7}
-service:
-  base-path: /users
-  auth: true
-  endpoints:
-    sendEmail:
-      audiences:
-        - internal
-      path: /send-email
-      ...
-```
-
-## Audiences for types
-
-Types can also be marked for different audiences. 
-
-In this example, the `Email` type is available to internal and beta consumers:
-
-```yaml title='user.yml' {5-7}
-Email: 
-  properties:
-    subject: string
-    body: optional<string>
-  audiences: 
-    - internal
-    - beta
-```
-
-## Audiences for properties
-
-Properties of a type can also be marked for different audiences. 
-
-In this example, the `to` property is available to beta consumers only:
-
-```yaml title='user.yml' {8-9}
-Email: 
-  properties:
-    subject: string
-    body: optional<string>
-    to: 
-      type: string
-      docs: The recipient of the email
-      audiences: 
-        - beta
-```
-
-## Audiences for SDKs
-
-In `generators.yml`, you can apply audience filters so that only certain
-endpoints are passed to the generators. 
-
-The following example configures the SDKs to filter for `customers`:
-
-```yaml title='generators.yml' {3-4}
-groups:
-  external:
-    audiences:
-      - customers
-    generators: 
-    ...
-```
-
-## Audiences with docs 
-
-If generating Fern Docs, update your `docs.yml` configuration to include your audiences. 
-
-The following example shows how to configure your `docs.yml` to publish documentation for the `customers` audience:
-
-<CodeBlock title='docs.yml'>
-```yaml {3-4}
-navigation:
-  - api: API Reference
-    audiences:
-      - customers
-```
-</CodeBlock>
diff --git a/fern/products/api-definition/pages/fern-definition/auth.mdx b/fern/products/api-definition/pages/fern-definition/auth.mdx
deleted file mode 100644
index e75ebbbd..00000000
--- a/fern/products/api-definition/pages/fern-definition/auth.mdx
+++ /dev/null
@@ -1,224 +0,0 @@
----
-title: Authentication
-subtitle: Model auth schemes such as bearer, basic, custom headers, and oauth. 
----
-
-Configuring authentication schemes happens in the `api.yml` file. 
-
-```bash {5}
-fern/
-├─ fern.config.json # root-level configuration
-├─ generators.yml # generators you're using
-└─ definition/
-  ├─ api.yml  # API-level configuration
-  └─ imdb.yml # endpoints, types, and errors
-```
-
-To add an authentication scheme, specify the authentication method under the `auth-schemes` section. 
-
-```yaml api.yml {1-2}
-auth-schemes:
-  AuthScheme:                     
-    ...
-```
-
-<Note>
-To apply an authentication scheme across all endpoints, reference the `auth-scheme` within the `auth` section of your `api.yml` file. 
-```yaml api.yml {1}
-auth: AuthScheme                  
-auth-schemes:
-  AuthScheme:                     
-    ...
-```
-</Note>
-
-## Bearer authentication
-
-Start by defining a `Bearer` authentication scheme in `api.yml`: 
-
-```yaml api.yml
-auth: Bearer                  
-auth-schemes:
-  Bearer:                     
-    scheme: bearer
-```
-
-This will generate an SDK where the user would have to provide 
-a mandatory argument called `token`. 
-
-```ts index.ts
-const client = new Client({
-  token: "ey34..."
-})
-```
-
-If you want to control variable naming and the environment variable to scan, 
-use the configuration below: 
-
-```yaml title="api.yml" {5-7}
-auth: Bearer                  
-auth-schemes:
-  Bearer:                     
-    scheme: bearer
-    token:
-      name: apiKey 
-      env: PLANTSTORE_API_KEY
-```
-
-The generated SDK would look like: 
-
-```ts index.ts
-
-// Uses process.env.PLANTSTORE_API_KEY
-let client = new Client(); 
-
-// token has been renamed to apiKey
-client = new Client({
-  apiKey: "ey34..."
-})
-```
-
-## Basic authentication
-
-Start by defining a `Basic` authentication scheme in `api.yml`: 
-
-```yaml api.yml
-auth: Basic                  
-auth-schemes:
-  Basic:                     
-    scheme: basic
-```
-
-This will generate an SDK where the user would have to provide 
-a mandatory arguments called `username` and `password`. 
-
-```ts index.ts
-const client = new Client({
-  username: "joeschmoe"
-  password: "ey34..."
-})
-```
-
-If you want to control variable naming and environment variables to scan, 
-use the configuration below: 
-
-```yaml title="api.yml" {5-11}
-auth: Basic                  
-auth-schemes:
-  Basic:                     
-    scheme: basic
-    username:
-      name: clientId
-      env: PLANTSTORE_CLIENT_ID
-    password:
-      name: clientSecret
-      env: PLANTSTORE_CLIENT_SECRET
-```
-
-The generated SDK would look like: 
-
-```ts index.ts
-
-// Uses process.env.PLANTSTORE_CLIENT_ID and process.env.PLANTSTORE_CLIENT_SECRET
-let client = new Client(); 
-
-// parameters have been renamed
-client = new Client({
-  clientId: "joeschmoe", 
-  clientSecret: "ey34..."
-})
-```
-
-## Custom header (e.g. API key)
-
-You can also create your own authentication scheme with customized headers. 
-
-```yaml title="api.yml" {3-5}
-auth: ApiKeyAuthScheme
-auth-schemes:
-  ApiKeyAuthScheme:
-    header: X-API-Key
-    type: string
-```
-
-This will generate an SDK where the user would have to provide 
-a mandatory argument called `apiKey`. 
-
-```ts index.ts
-const client = new Client({
-  xApiKey: "ey34..."
-})
-```
-
-If you want to control variable naming and environment variables to scan, 
-use the configuration below: 
-
-```yaml title="api.yml" {7-8}
-auth: ApiKeyAuthScheme
-auth-schemes:
-  ApiKeyAuthScheme:
-    header: X-API-Key
-    type: string
-    name: apiKey
-    env: PLANTSTORE_API_KEY
-```
-
-The generated SDK would look like: 
-
-```ts index.ts
-
-// Uses process.env.PLANTSTORE_API_KEY
-let client = new Client(); 
-
-// parameters have been renamed
-client = new Client({
-  apiKey: "ey34..."
-})
-```
-
-## OAuth client credentials
-
-If your API uses OAuth, you can specify an oauth scheme. Note that you'll need to define a token retrieval endpoint. 
-
-```yaml api.yml
-name: api
-
-imports:
-  auth: auth.yml
-
-auth: OAuthScheme
-auth-schemes:
-  OAuthScheme:
-    scheme: oauth
-    type: client-credentials
-    client-id-env: YOUR_CLIENT_ID
-    client-secret-env: YOUR_CLIENT_SECRET
-    get-token:
-      endpoint: auth.getToken               
-      response-properties:
-        access-token: $response.access_token 
-        expires-in: $response.expires_in    
-
-```
-
-If the `expires-in` property is set, the generated OAuth token provider will automatically refresh the token when it expires.
-Otherwise, it's assumed that the access token is valid indefinitely.
-
-With this, all of the OAuth logic happens automatically in the generated SDKs. As long as you configure these settings, your
-client will automatically retrieve an access token and refresh it as needed.
-
-When using the docs playground, `token-header` and `token-prefix` can optionally be set to customize the header key name and 
-header value prefix, to match the expected format of the API auth scheme.
-
-For example, the following would produce a header `Fern-Authorization: Fern-Bearer <token>`:
-
-```yaml api.yml {5-6}
-auth-schemes:
-  OAuthScheme:
-    scheme: oauth
-    type: client-credentials
-    token-header: Fern-Authorization
-    token-prefix: Fern-Bearer
-    get-token:
-      ...   
-```
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/availability.mdx b/fern/products/api-definition/pages/fern-definition/availability.mdx
deleted file mode 100644
index 9e335ec1..00000000
--- a/fern/products/api-definition/pages/fern-definition/availability.mdx
+++ /dev/null
@@ -1,96 +0,0 @@
----
-title: Availability in Fern Definition 
-description: Add availability to Fern Definition API services, endpoints, types, or properties to indicate their release status.
----
-
-You can add `availability` to an endpoint, type, or property within your Fern Definition.
-
-Availability can be:
-- `in-development` which means it is being worked on; will show a `Beta` tag
-- `pre-release` which means it is available; will show a `Beta` tag
-- `deprecated` which means it will be removed in the future; will show a `Deprecated` tag
-- `generally-available` which means it is stable and available for use; will show a `GA` tag
-
-## Endpoint 
-
-<CodeBlock title="pet.yml">
-```yaml {6}
-service:
-  base-path: /pet
-  auth: true
-  endpoints:
-    add:
-      availability: deprecated
-      display-name: Add pet
-      docs: Add a new Pet to the store
-      method: POST
-      path: ""
-      request: AddPetRequest
-      response: Pet
-```
-</CodeBlock>
-
-In Fern Docs, this will look like: 
-
-<Frame>
-![Screenshot showing a deprecated tag next to an endpoint in API Reference docs](https://fern-image-hosting.s3.amazonaws.com/endpoint-deprecated.png)
-</Frame>
-
-## Type
-
-<CodeBlock title="pet.yml">
-```yaml {15}
-  Pet:
-    properties:
-      id: 
-        type: integer
-        docs: A unique ID for the Pet
-      name: 
-        type: string
-        docs: The first name of the Pet
-      photoUrls: 
-        type: list<string>
-        docs: A list of publicly available URLs featuring the Pet
-        availability: generally-available
-      category: 
-        type: optional<Category>
-        availability: pre-release
-
-  Category:
-    properties:
-      id: optional<integer>
-      name: optional<string>
-```
-</CodeBlock>
-
-In Fern Docs, this will look like: 
-
-<Frame>
-![Screenshot showing a beta tag next to a type in API Reference docs](https://fern-image-hosting.s3.amazonaws.com/type-beta.png)
-</Frame>
-
-## Property 
-
-<CodeBlock title="pet.yml">
-```yaml {12}
-  Pet:
-    properties:
-      id: 
-        type: integer
-        docs: A unique ID for the Pet
-      name: 
-        type: string
-        docs: The first name of the Pet
-      photoUrls: 
-        type: list<string>
-        docs: A list of publicly available URLs featuring the Pet
-        availability: deprecated
-      category: optional<Category>
-```
-</CodeBlock>
-
-In Fern Docs, this will look like: 
-
-<Frame>
-![Screenshot showing a deprecated tag next to a type's property in API Reference docs](https://fern-image-hosting.s3.amazonaws.com/property-deprecated.png)
-</Frame>
diff --git a/fern/products/api-definition/pages/fern-definition/depending-on-other-apis.mdx b/fern/products/api-definition/pages/fern-definition/depending-on-other-apis.mdx
deleted file mode 100644
index 5a327fea..00000000
--- a/fern/products/api-definition/pages/fern-definition/depending-on-other-apis.mdx
+++ /dev/null
@@ -1,67 +0,0 @@
----
-title: "Depending on other APIs"
-subtitle: Import API Definitions to generate unified SDKs
----
-
-Fern allows you to import other APIs into your API.
-
-This is often useful if:
-
-- you want to reuse another API's types in your API
-- you want to combine multiple microservices' APIs into one SDK (similar to the AWS SDK)
-
-## Registering the dependency API
-
-The first step is to **register** the API you want to depend on. To do this, use
-the `register` command:
-
-```
-$ fern register
-[some-dependency]: Uploading definition...
-[some-dependency]: Registered @fern/some-dependency:0.0.1
-```
-
-## Depending on the registered API
-
-To add a dependency on another API, you must add a `dependencies.yml` to declare which 
-APIs you wish to depend on. 
-
-```bash {4}
-fern/
-├─ fern.config.json
-├─ generators.yml
-├─ dependencies.yml
-└─ definition/
-  ├─ api.yml
-  ├─ imdb.yml
-```
-
-Your `dependencies.yml` has a list of all the APIs you wish to depend: 
-
-```yaml dependencies.yml 
-dependencies:
-   "@fern/some-dependency": "0.0.1"
-```
-
-Next, you need create a folder in your Fern Definition to house the dependency. Inside the folder, create a special file
-`__package__.yml` which specifies the dependency and version you want to add.
-
-```bash {8-9}
-fern/
-├─ fern.config.json
-├─ generators.yml
-├─ dependencies.yml
-└─ definition/
-  ├─ api.yml
-  ├─ imdb.yml
-  └─ my-folder
-    └─ __package__.yml  
-```
-
-```yaml __package__.yml
-export:
-  dependency: "@fern/some-dependency"
-```
-
-When you generate the SDK with `fern generate`, the `__package__.yml` file will
-effectively be replaced with the API you're depending on.
diff --git a/fern/products/api-definition/pages/fern-definition/endpoints.mdx b/fern/products/api-definition/pages/fern-definition/endpoints.mdx
deleted file mode 100644
index ad91ab18..00000000
--- a/fern/products/api-definition/pages/fern-definition/endpoints.mdx
+++ /dev/null
@@ -1,519 +0,0 @@
----
-title: Endpoints in Fern Definition 
-description: Organize related API endpoints into a service in Fern Definition and define each endpoint's URL, HTTP method, request, response, errors, and more.
----
-
-In Fern, you organize related endpoints into a **Service**. This grouping
-improves clarity and makes the generated SDKs more idiomatic.
-
-## Service definition
-
-Each service defines:
-
-1. A **base-path**: A common prefix for all the endpoints' HTTP paths
-2. Whether the service requires [authentication](/learn/api-definition/fern/authentication)
-3. **Endpoints**
-
-<CodeBlock title='user.yml'>
-  ```yaml 
-    service: 
-      base-path: /users 
-      auth: false 
-      endpoints: {}
-  ```
-</CodeBlock>
-
-<Info>
-  To define a service with an empty base path use the empty string: `base-path: ""`
-</Info>
-
-## Endpoints
-
-An endpoint includes:
-
-- A **URL path** _(Optionally including path parameters)_
-- A **Display Name** _(Optional)_
-- An **HTTP Method** 
-- **Request information** _(Optional)_
-  - **Query-parameters**
-  - **Headers**
-  - **Request body**
-- **Successful (200) response** information _(Optional)_
-- **Error (non-200) responses** that this endpoint might return _(Optional)_
-
-## URL path
-
-Each endpoint has a URL path.
-
-<CodeBlock title="user.yml">
-```yaml {6}
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    getAllUsers:
-      path: /all
-      method: GET
-```
-</CodeBlock>
-
-The full path for the endpoint is the concatenation of:
-
-- The [environment](/learn/api-definition/fern/api-yml/environments) URL
-- The service `base-path`
-- The endpoint `path`
-
-## Display name
-
-The display name will appear as the title of an endpoint. By default, the display name is equal to the 'Title Case' of the endpoint name. If you would like to customize the endpoint name, you can **set the display name**. 
-
-In the example below, ["Add a new plant to the store"](https://plantstore.dev/api-reference/plant/add-plant) displays as the title of the endpoint page within the API Reference.
-
-<CodeBlock title="user.yml">
-```yaml {7}
-service:
-  base-path: /v3
-  auth: false
-  endpoints:
-    addPlant:
-      path: /plant
-      display-name: Add a new plant to the store
-      method: POST
-```
-</CodeBlock>
-
-## Path parameters
-
-Supply path parameters for your endpoints to create dynamic URLs.
-
-<CodeBlock title="user.yml">
-```yaml {6-8}
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    getUser:
-      path: /{userId} 
-      path-parameters: 
-        userId: string
-      method: GET
-```
-</CodeBlock>
-
-Services can also have path-parameters:
-
-<CodeBlock title='project.yml'>
-  ```yaml {2-4}
-  service: 
-    base-path: /projects/{projectId}
-    path-parameters: 
-      projectId: string 
-    auth: false 
-    endpoints: 
-      ...
-  ```
-</CodeBlock>
-
-## Query parameters
-
-Each endpoint can specify query parameters:
-
-<CodeBlock title="user.yml">
-```yaml
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    getAllUsers:
-      path: /all
-      method: GET
-      request:
-        # this name is required for idiomatic SDKs
-        name: GetAllUsersRequest
-        query-parameters:
-          limit: optional<integer>
-```
-</CodeBlock>
-
-### `allow-multiple`
-
-Use `allow-multiple` to specify that a query parameter is allowed
-multiple times in the URL, as in `?filter=jane&filter=smith`. This will alter
-the generated SDKs so that consumers can provide multiple values for the query
-parameter.
-
-<CodeBlock title="user.yml">
-```yaml {5}
-  ...
-  query-parameters:
-    filter:
-      type: string
-      allow-multiple: true
-```
-</CodeBlock>
-
-## Auth
-
-Each endpoint can override the auth behavior specified in the service.
-
-<CodeBlock title='user.yml'>
-  ```yaml 
-  service: 
-    base-path: /users 
-    auth: false 
-    endpoints: 
-      getMe: 
-        path: "" 
-        method: GET 
-        # This endpoint will be authed 
-        auth: true 
-        docs: Return the current user based on Authorization header. 
-  ```
-</CodeBlock>
-
-## Headers
-
-Each endpoint can specify request headers:
-
-<CodeBlock title='user.yml'>
-  ```yaml 
-  service: 
-    base-path: /users 
-    auth: false 
-    endpoints: 
-      getAllUsers: 
-        path: /all
-        method: GET 
-        request: 
-          # this name is required for idiomatic SDKs name:
-          name: GetAllUsersRequest 
-          headers: 
-            X-Endpoint-Header: string
-  ```
-</CodeBlock>
-
-Services can also specify request headers. These headers will cascade to the service's endpoints.
-
-<CodeBlock title='user.yml'>
-  ```yaml {4-5} 
-  service: 
-    base-path: /users 
-    auth: false 
-    headers: 
-      X-Service-Header: string 
-    endpoints: 
-      getAllUsers: 
-        path: /all 
-        method: GET 
-        request: 
-          # this name is required for idiomatic SDKs 
-          name: GetAllUsersRequest 
-          headers: 
-            X-Endpoint-Header: string
-  ```
-</CodeBlock>
-
-## Request body
-
-Endpoints can specify a request body type.
-
-<CodeBlock title="user.yml">
-```yaml {10}
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    setUserName:
-      path: /{userId}/set-name
-      path-parameters:
-        userId: string
-      method: POST
-      request: string
-```
-</CodeBlock>
-
-### Inlining a request body
-
-If the request body is an object, you can **inline the type declaration**. This
-makes the generated SDKs a bit more idiomatic.
-
-<CodeBlock title='user.yml'>
-  ```yaml 
-  service: 
-    base-path: /users 
-    auth: false 
-    endpoints: 
-      createUser: 
-        path: /create 
-        method: POST 
-        request: 
-          # this name is required for idiomatic SDKs 
-          name: CreateUserRequest 
-          body: 
-            properties: 
-              userName: string
-  ```
-</CodeBlock>
-
-## Success response
-
-Endpoints can specify a `response`, which is the type of the body that will be
-returned on a successful (200) call.
-
-<CodeBlock title="user.yml">
-```yaml
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    getAllUsers:
-      path: /all
-      method: GET
-      response: list<User>
-
-types:
-  User:
-    properties:
-      userId: string
-      name: string
-```
-</CodeBlock>
-
-## Response status codes 
-
-You can also use the `status-code` field to specify a custom status code 
-for a success response. 
-
-<CodeBlock title="user.yml">
-```yaml {11}
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    create: :
-      path: ""
-      method: POST
-      request: CreateUserRequest
-      response: 
-        type: User
-        status-code: 201
-
-types:
-  User:
-    properties:
-      userId: string
-      name: string
-```
-</CodeBlock>
-
-## Error responses
-
-Endpoints can specify error responses, which detail the non-200 responses that
-the endpoint might return.
-
-<CodeBlock title="user.yml">
-```yaml
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    getUser:
-      path: /{userId}
-      path-parameters:
-        userId: string
-      method: GET
-      response: User
-      errors:
-        - UserNotFoundError
-
-types:
-  User:
-    properties:
-      userId: string
-      name: string
-
-errors:
-  UserNotFoundError:
-    status-code: 404
-```
-</CodeBlock>
-
-You can learn more about how to define errors on the [Errors](/learn/api-definition/fern/errors) page.
-
-## Specifying examples
-
-When you declare an example, you can also specify some examples of how that
-endpoint might be used. These are used by the compiler to enhance the generated
-outputs. Examples will show up as comments in your SDKs, API documentation, and Postman collection.
-
-You may add examples for endpoints, types, and errors.
-
-<CodeBlock title="user.yml">
-```yaml {13-19}
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    getUser:
-      path: /{userId}
-      path-parameters:
-        userId: string
-      method: GET
-      response: User
-      errors:
-        - UserNotFoundError
-      examples:
-        - path-parameters:
-            userId: alice-user-id
-          response:
-            body:
-              userId: alice-user-id
-              name: Alice
-
-types:
-  User:
-    properties:
-      userId: string
-      name: string
-
-errors:
-  UserNotFoundError:
-    status-code: 404
-```
-</CodeBlock>
-
-If you're adding an example to an endpoint and the type already has an example, you can reference it using `$`.
-```yaml
-service:
-  auth: true
-  base-path: /address
-  endpoints:
-    create:
-      method: POST
-      path: ""
-      request: CreateAddress
-      response: Address
-      examples:
-        - request: $CreateAddress.WhiteHouse
-          response:
-            body: $Address.WhiteHouseWithID
-
-  CreateAddress:
-    properties:
-      street1: string
-      street2: optional<string>
-      city: string
-      state: string
-      postalCode: string
-      country: string
-      isResidential: boolean
-    examples:
-      - name: WhiteHouse
-        value:
-          street1: 1600 Pennsylvania Avenue NW
-          city: Washington DC
-          state: Washington DC
-          postalCode: "20500"
-          country: US
-          isResidential: true
-
-  Address:
-    extends: CreateAddress
-    properties:
-      id:
-        type: uuid
-        docs: The unique identifier for the address.
-    examples:
-      - name: WhiteHouseWithID
-        value:
-          id: 65ce514c-41e3-11ee-be56-0242ac120002
-          street1: 1600 Pennsylvania Avenue NW
-          city: Washington DC
-          state: Washington DC
-          postalCode: "20500"
-          country: US
-          isResidential: true
-````
-
-Examples contain all the information about the endpoint call, including
-the request body, path parameters, query parameters, headers, and response body.
-
-<CodeBlock title='user.yml'>
-  ```yaml
-  examples: 
-    - path-parameters: 
-        userId: some-user-id 
-      query-parameters:
-        limit: 50 
-      headers: 
-        X-My-Header: some-value 
-      response: 
-        body: 
-          response-field: hello
-  ```
-</CodeBlock>
-
-### Failed examples
-
-You can also specify examples of failed endpoints calls. Add the `error`
-property to a response example to designate which failure you're demonstrating.
-
-<CodeBlock title="user.yml">
-```yaml {5}
-examples:
-  - path-parameters:
-      userId: missing-user-id
-    response:
-      error: UserNotFoundError
-
-errors:
-  UserNotFoundError:
-    status-code: 404
-```
-</CodeBlock>
-
-If the error has a body, then you must include the body in the example.
-
-<CodeBlock title="user.yml">
-```yaml {6, 11}
-examples:
-  - path-parameters:
-      userId: missing-user-id
-    response:
-      error: UserNotFoundError
-      body: "User with id `missing-user-id` was not found"
-
-errors:
-  UserNotFoundError:
-    status-code: 404
-    type: string
-```
-</CodeBlock>
-
-### Referencing examples from types
-
-To avoid duplication, you can reference examples from types using `$`.
-
-<CodeBlock title="user.yml">
-```yaml {12}
-service:
-  base-path: /users
-  auth: true
-  endpoints:
-    getUser:
-      method: GET
-      path: /{userId}
-      path-parameters:
-        userId: UserId
-      examples:
-        - path-parameters:
-            userId: $UserId.Example1
-
-types:
-  UserId:
-  type: integer
-  examples: 
-    - name: Example1
-      value: user-id-123
-```
-</CodeBlock>
diff --git a/fern/products/api-definition/pages/fern-definition/endpoints/bytes.mdx b/fern/products/api-definition/pages/fern-definition/endpoints/bytes.mdx
deleted file mode 100644
index 43a1926d..00000000
--- a/fern/products/api-definition/pages/fern-definition/endpoints/bytes.mdx
+++ /dev/null
@@ -1,60 +0,0 @@
----
-title: Binary Data and Files
-subtitle: Use the `bytes` type to handle binary data in your API
----
-
-<Note>
-  The `bytes` type allows you to handle binary data in both requests and responses.
-</Note>
-
-## Sending bytes
-
-If your API needs to send a stream of bytes (i.e. typical for assets like audio, images and other files) then
-you can use the `bytes` type in the Fern Definition to model this.
-
-```yml audio.yml
-service:
-  base-path: /audio
-  endpoints:
-    upload:
-      display-name: Upload audio
-      method: POST
-      path: /upload
-      content-type: application/octet-stream
-      request:
-        type: bytes
-        docs: The bytes of the MP3 file that you would like to upload
-```
-
-## Receiving bytes
-
-<Note>
-  When handling binary data in responses, use `type: file` instead of `type: bytes`.
-</Note>
-
-On the other hand, if your API is returning a stream of bytes, then you can leverage the `bytes` type as a response.
-
-```yml textToSpeech.yml
-service:
-  base-path: /tts
-  endpoints:
-    upload:
-      display-name: Upload audio
-      method: POST
-      path: ""
-      request:
-        name: TTSRequest
-        body:
-          properties:
-            text:
-              type: string
-              docs: The text that you want converted to speech.
-      response:
-        type: file
-        docs: The bytes of the audio file.
-```
-
-
-
-
-
diff --git a/fern/products/api-definition/pages/fern-definition/endpoints/multipart.mdx b/fern/products/api-definition/pages/fern-definition/endpoints/multipart.mdx
deleted file mode 100644
index f5270d6c..00000000
--- a/fern/products/api-definition/pages/fern-definition/endpoints/multipart.mdx
+++ /dev/null
@@ -1,46 +0,0 @@
----
-title: Multipart File Upload
-description: Document endpoints with the `multiform` content type.
----
-
-Endpoints in Fern are defined underneath the `endpoints` key. If your endpoint request includes file uploads, you can use the `file` type to indicate the request is of a `multiform` content type. The example below demonstrates an endpoint which includes a file in the request body. 
-
-<CodeBlock title="document.yml">
-```yaml {12}
-service:
-  base-path: /documents
-  auth: false
-  endpoints:
-    uploadDocument:
-      path: /upload
-      method: POST
-      request:
-        name: UploadDocumentRequest
-        body:
-          properties:
-            file: file
-```
-</CodeBlock>
-
-Within a given multipart request, a string parameter with `format:binary` will represent an arbitrary file.
-
-## List of Files
-
-If your endpoint supports a list of files, then your request body must indicate such.
-
-<CodeBlock title="document.yml">
-```yaml {12}
-service:
-  base-path: /documents
-  auth: false
-  endpoints:
-    uploadDocuments:
-      path: /upload
-      method: POST
-      request:
-        name: UploadDocumentsRequest
-        body:
-          properties:
-            files: list<file>
-```
-</CodeBlock>
diff --git a/fern/products/api-definition/pages/fern-definition/endpoints/rest.mdx b/fern/products/api-definition/pages/fern-definition/endpoints/rest.mdx
deleted file mode 100644
index 3a4459ba..00000000
--- a/fern/products/api-definition/pages/fern-definition/endpoints/rest.mdx
+++ /dev/null
@@ -1,45 +0,0 @@
----
-title: HTTP JSON Endpoints
----
-
-Endpoints in Fern are defined underneath the `endpoints` key. Below is an example of defining 
-a single REST endpoint: 
-
-```yml title="users.yml" maxLines=0
-service: 
-  base-path: /users 
-  auth: false 
-  endpoints: 
-    createUser: 
-      path: /create 
-      method: POST 
-      request: 
-        body: 
-          properties: 
-            userName: string
-```
-
-## Examples
-
-You can provide examples of requests and responses by using the `examples` key.   
-
-```yaml {11-17}
-service:
-  base-path: /users
-  auth: false
-  endpoints:
-    getUser:
-      path: /{userId}
-      path-parameters:
-        userId: string
-      method: GET
-      response: User
-      examples:
-        - path-parameters:
-            userId: alice-user-id
-          response:
-            body:
-              userId: alice-user-id
-              name: Alice
-```
-
diff --git a/fern/products/api-definition/pages/fern-definition/endpoints/sse.mdx b/fern/products/api-definition/pages/fern-definition/endpoints/sse.mdx
deleted file mode 100644
index c1ab5377..00000000
--- a/fern/products/api-definition/pages/fern-definition/endpoints/sse.mdx
+++ /dev/null
@@ -1,101 +0,0 @@
----
-title: Server-Sent Events and Streaming APIs
-subtitle: Use the `response-stream` key to model streaming endpoints
----
-
-<Note>
-  Specifying `response-stream` on an endpoints allows you to represent endpoint responses that are streaming. 
-</Note>
-
-
-## JSON streaming
-
-If your API returns a series of `JSON` chunks as seen below
-
-```json
-{ "text": "Hi, I am a" }
-{ "text": "chatbot. Do you have any"}
-{ "text": "questions for me"}
-```
-
-then simply specify the response under `response-stream` for your endpoint. 
-
-```yaml title="chat.yml" {4}
-service:
-  base-path: /chat
-  endpoints:
-    stream:
-      method: POST
-      path: ""
-      response-stream: Chat
-
-types:
-  Chat:
-    properties:
-      text: string
-```
-
-## Server-sent events 
-
-If your API returns server-sent-events, with the `data` and `event` keys as seen below
-
-```json
-data: { "text": "Hi, I am a" }
-data: { "text": "chatbot. Do you have any"}
-data: { "text": "questions for me"}
-```
-
-then make sure to include `format: sse`. 
-
-```yaml title="chat.yml" {9}
-service:
-  base-path: /chat
-  endpoints:
-    stream:
-      method: POST
-      path: ""
-      response-stream:
-        type: Chat
-        format: sse
-
-types:
-  Chat:
-    properties:
-      text: string        
-```
-
-## `Stream` parameter
-
-It has become common practice for endpoints to have a `stream` parameter that 
-controls whether the response is streamed or not. Fern supports this pattern in a first 
-class way. 
-
-Simply specify the `stream-condition` as well as the ordinary response and the streaming response: 
-
-```yaml title="chat.yml" {7}
-service:
-  base-path: /chat
-  endpoints:
-    stream:
-      method: POST
-      path: ""
-      stream-condition: $request.stream
-      request:
-        name: StreamChatRequest
-        body: 
-          properties: 
-            stream: boolean
-      response: Chat
-      response-stream:
-        type: ChatChunk
-        format: sse
-
-types:
-  Chat:
-    properties:
-      text: string
-      tokens: integer
-  ChatChunk:
-    properties:
-      text: string
-```
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/errors.mdx b/fern/products/api-definition/pages/fern-definition/errors.mdx
deleted file mode 100644
index 60fa6e63..00000000
--- a/fern/products/api-definition/pages/fern-definition/errors.mdx
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: Errors in Fern Definition 
-description: Add errors representing failed responses from API endpoints in Fern Definition.
----
-
-Errors represent failed (non-200) responses from endpoints.
-
-An error has:
-
-- An HTTP status code
-- A body type _(Optional)_
-
-<CodeBlock title="user.yml">
-```yaml
-errors:
-  UserNotFoundError:
-    status-code: 404
-    type: UserNotFoundErrorBody
-
-types:
-  UserNotFoundErrorBody:
-    properties:
-      requestedUserId: string
-```
-</CodeBlock>
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/examples.mdx b/fern/products/api-definition/pages/fern-definition/examples.mdx
deleted file mode 100644
index b16ab493..00000000
--- a/fern/products/api-definition/pages/fern-definition/examples.mdx
+++ /dev/null
@@ -1,339 +0,0 @@
----
-title: Examples in Fern Definition 
-subtitle: Use Fern Definition to add API examples that are shown in comments of SDKs, API Reference documentation, and a Postman collection.
----
-
-You can add examples for types and endpoints. Examples are shown as
-comments in your SDKs, in the request & response of your documentation,
-and in a Postman Collection.
-
-## Validation
-
-The Fern CLI validates that your examples match the expected types. The following won't compile:
-
-```yaml
-types:
-  UserId:
-    type: integer
-    examples:
-      - value: hello # not an integer
-```
-
-```bash CLI Error Message
-[api]: example.yml -> types -> UserId -> examples[0]
-       Expected example to be an integer. Example is: "hello"
-```
-
-## Referencing examples
-
-You can reference an example from another type, endpoint, or error.
-
-Just like types, you can compose examples. To reference an example from another
-type, use `$`.
-
-```yaml {14}
-types:
-  UserId:
-    type: integer
-    examples:
-      - name: Example1
-        value: user-id-123
-
-  User:
-    properties:
-      id: UserId
-      name: string
-    examples:
-      - value:
-          id: $UserId.Example1
-          name: Jane Smith
-```
-
-## Examples for types
-
-### Objects
-
-```yml
-types:
-  ShipTo:
-    properties:
-      street1: string
-      street2: optional<string>
-      city: string
-      state: string
-      postalCode: string
-      country: Country
-      isResidential: boolean
-    examples:
-      - name: WhiteHouse
-        value:
-          street1: 1600 Pennsylvania Avenue NW
-          city: Washington DC
-          state: Washington DC
-          postalCode: "20500"
-          country: US
-          isResidential: true
-      - name: EmpireStateBuilding
-        value:
-          street1: 350 5th Ave
-          street2: Attn: Maintenance Department
-          city: New York
-          state: NY
-          postalCode: "10118"
-          country: US
-          isResidential: false
-```
-
-<CodeBlock title = "Generated TypeScript SDK">
-```typescript
-/**
- * Represents a shipping address.
- * 
- * The White House address
- * @example {
- *  street1: "1600 Pennsylvania Avenue NW",
- *  city: "Washington DC",
- *  state: "Washington DC",
- *  postalCode: "20500",
- *  country: "US",
- *  isResidential: true
- * }
- * 
- * * The Empire State Building address
- * @example {
- *  street1: "350 5th Ave",
- *  street2: "Attn: Maintenance Department",
- *  city: "New York",
- *  state: "NY",
- *  postalCode: "10118",
- *  country: "US",
- *  isResidential: false
- * }
- */
-type ShipTo = {
-  street1: string;
-  street2?: string;
-  city: string;
-  state: string;
-  postalCode: string;
-  country: Country;
-  isResidential: boolean;
-};
-```
-</CodeBlock>
-
-### Lists
-
-```yml
- Shipments:
-    type: list<ShipmentStatus>
-    examples:
-      - name: Default
-        value:
-          - status: "InTransit"
-            estimatedDeliveryDate: "2024-01-11"
-          - status: "Delivered"
-            estimatedDeliveryDate: "2024-01-13"
-```
-
-### Unions
-
-#### Discriminated union
-
-```yml
-types:
-  Animal:
-    union:
-      dog: Dog
-      cat: Cat
-    examples:
-      - value:
-          type: dog
-          likesToWoof: true
-  Dog:
-    properties:
-      likesToWoof: boolean
-  Cat:
-    properties:
-      likesToMeow: boolean
-```
-
-<CodeBlock title = "Generated TypeScript SDK">
-```typescript
-/**
- * Represents an animal, which can be either a Dog or a Cat.
- *
- * Example of a Dog:
- * @example {
- *  type: "dog",
- *  likesToWoof: true
- * }
- */
-type Animal = Dog | Cat;
-```
-</CodeBlock>
-
-#### Undiscriminated union
-
-```yml
-types:
-  Animal:
-    discriminated: false
-    union:
-      - Dog
-      - Cat
-    examples:
-      - value:
-          likesToMeow: true
-  Dog:
-    properties:
-      likesToWoof: boolean
-  Cat:
-    properties:
-      likesToMeow: boolean
-```
-
-<CodeBlock title = "Generated TypeScript SDK">
-```typescript
-/**
- * Represents an Animal, which can be either a Dog or a Cat.
- *
- * Example of an Animal as a Cat:
- * @example {
- *  likesToMeow: true
- * }
- */
-type Animal = Dog | Cat;
-```
-</CodeBlock>
-
-### Aliases
-
-```yml
-types:
-  UserId:
-    docs: A unique identifier for a user
-    type: string
-    examples:
-      - value: user-id-123
-```
-
-<CodeBlock title="Generated TypeScript SDK">
-  ```typescript 
-  /** 
-  * A unique identifier for a user * 
-  * @example "user-id-123" 
-  */ 
-  type UserId = string; 
-  ```
-</CodeBlock>
-
-## Examples for endpoints
-
-You can add examples of successful and error responses for your endpoints. 
-Examples can reference the examples of types to avoid duplication.
-
-```yml
-service:
-  auth: true
-  base-path: ""
-  endpoints:
-    CreateShippingLabel:
-      docs: Create a new shipping label.
-      method: POST
-      path: /shipping
-      request: CreateShippingLabelRequest
-      response: ShippingLabel
-      errors:
-        - NotAuthorized
-        - InsufficientFunds
-      examples:
-        # A successful response that doesn't reference other examples.
-        - request:
-            orderId: "online_789"
-            weightInOunces: 5
-          response:
-            body:
-              orderId: "online_789"
-              weightInOunces: 5
-              trackingNumber: "1Z26W8370303469306"
-              price: 2.50
-
-        # A successful response that uses references.
-        - request: $CreateShippingLabelRequest.SuccessfulRequest
-          response:
-            body: $ShippingLabel.Default
-
-        # An error response.
-        - request: $CreateShippingLabelRequest.InsufficientFundsRequest
-          response:
-            error: InsufficientFunds
-            body: $InsufficientFundsBody.Default
-
-types:
-  CreateShippingLabelRequest:
-    properties:
-      orderId: string
-      weightInOunces: integer
-    examples:
-      - name: SuccessfulRequest
-        value:
-          orderId: "online_123"
-          weightInOunces: 13
-      - name: InsufficientFundsRequest
-        value:
-          orderId: "online_456"
-          weightInOunces: 2000
-
-  ShippingLabel:
-    properties:
-      orderId: string
-      weightInOunces: integer
-      trackingNumber: string
-      price: double
-    examples:
-      - name: Default
-        value:
-          orderId: "online_123"
-          weightInOunces: 13
-          trackingNumber: "1Z12345E0205271688"
-          price: 12.35
-
-  InsufficientFundsBody:
-    properties:
-      message: string
-    examples:
-      - name: Default
-        value:
-          message: "Insufficient funds to create shipping label."
-
-errors:
-  NotAuthorized:
-    status-code: 401
-  InsufficientFunds:
-    status-code: 422
-    type: InsufficientFundsBody
-```
-
-## Examples for path parameters
-
-```yml
-service:
-  auth: true
-  base-path: ""
-  endpoints:
-    TrackShipment:
-      docs: Track the status of a shipment.
-      method: GET
-      path: /shipping/{trackingNumber}
-      path-parameters: 
-        trackingNumber: string
-      response: ShipmentStatus
-      examples:
-        - path-parameters: 
-            trackingNumber: "1Z26W8370303469306"
-          response:
-            body:
-              status: "InTransit"
-              estimatedDeliveryDate: "2024-01-11"
-```
diff --git a/fern/products/api-definition/pages/fern-definition/export-openapi.mdx b/fern/products/api-definition/pages/fern-definition/export-openapi.mdx
deleted file mode 100644
index cf5b99cd..00000000
--- a/fern/products/api-definition/pages/fern-definition/export-openapi.mdx
+++ /dev/null
@@ -1,23 +0,0 @@
----
-title: Export from Fern Definition to OpenAPI
-description: Export your Fern Definition files to OpenAPI using Fern's OpenAPI generator.
----
-
-To prevent lock-in to the Fern Definition format, we provide a generator that will export your Fern Def files to OpenAPI 3.1. 
-This lets you switch to using OpenAPI at any time, or use your API definition with OpenAPI tools.
-To convert your Fern Definition to OpenAPI, use the `fern-openapi` generator. 
-
-Update your `generators.yml` file:
-
-<CodeBlock title="generators.yml">
-```yaml
-- name: fernapi/fern-openapi
-  version: 0.0.31
-  config:
-    format: yaml # options are yaml or json
-  output:
-    location: local-file-system
-    path: ../openapi # relative path to output location
-```
-</CodeBlock>
-
diff --git a/fern/products/api-definition/pages/fern-definition/imports.mdx b/fern/products/api-definition/pages/fern-definition/imports.mdx
deleted file mode 100644
index d52749f8..00000000
--- a/fern/products/api-definition/pages/fern-definition/imports.mdx
+++ /dev/null
@@ -1,22 +0,0 @@
----
-title: Imports in Fern Definition 
-description: Use imports to reference API types and errors from other Fern Definition files.
----
-
-Imports allow you to reference types and errors from other files.
-
-```yaml title="person.yml"
-types:
-  Person: ...
-```
-
-```yaml title="family.yml"
-imports:
-  person: ./path/to/person.yml
-types:
-  Family:
-    properties:
-      people: list<person.Person> # use an imported type
-```
-
-Note that you can only import files that exist in your Fern Definition (i.e., in the same `definition/` folder).
diff --git a/fern/products/api-definition/pages/fern-definition/overview.mdx b/fern/products/api-definition/pages/fern-definition/overview.mdx
deleted file mode 100644
index 25b04a2d..00000000
--- a/fern/products/api-definition/pages/fern-definition/overview.mdx
+++ /dev/null
@@ -1,104 +0,0 @@
----
-title: What is a Fern Definition? 
-subtitle: "A Fern Definition is a set of YAML files that describe your API."
----
-
-A Fern Definition is a set of YAML files that are the single source of truth for your API. You check your Fern Definition into your repo,
-inside of which describes your API requests, responses, models, paths, methods, errors, and authentication scheme.
-
-<Note>
-  Want to use OpenAPI instead? No worries, we support that [as well](/learn/api-definition/introduction/what-is-an-api-definition#openapi-swagger)
-</Note>
-
-## Fern Definition structure
-
-To initialize a Fern Definition, simply run:
-
-```sh
-npm install -g fern-api
-fern init
-```
-
-This will create the following folder structure in your project:
-
-```bash
-fern/
-├─ fern.config.json # root-level configuration
-├─ generators.yml # generators you're using
-└─ definition/
-  ├─ api.yml  # API-level configuration
-  └─ imdb.yml # endpoints, types, and errors
-```
-
-## Definition file
-
-Each **Fern Definition** file may define:
-
-- **[Custom types](/learn/api-definition/fern/types)**. Use **custom types** to build your data model.
-- **[Endpoints](/learn/api-definition/fern/endpoints)**. A **service** is a set of related REST endpoints.
-- **[Errors](/learn/api-definition/fern/errors)**. An **error** represents a failed (non-200) response from an endpoint.
-- **[Imports](/learn/api-definition/fern/imports)**. Use **imports** to share types across files.
-
-```yml imdb.yml maxLines=0
-service:
-  auth: false
-  base-path: /movies
-  endpoints:
-    createMovie:
-      docs: Add a movie to the database
-      method: POST
-      path: /create-movie
-      request: CreateMovieRequest
-      response: MovieId
-
-    getMovie:
-      method: GET
-      path: /{movieId}
-      path-parameters:
-        movieId: MovieId
-      response: Movie
-      errors:
-        - NotFoundError
-        - UnauthorizedError
-
-types:
-  Movie:
-    properties:
-      title: string
-      rating:
-        type: double
-        docs: The rating scale from one to five stars
-      id:
-        type: MovieId
-        docs: The unique identifier for a movie
-
-  CreateMovieRequest:
-    properties:
-      title: string
-      rating: double
-
-errors:
-  NotFoundError:
-    http:
-      statusCode: 404
-    type:
-      properties:
-        id: MovieId
-
-  UnauthorizedError:
-    http:
-      statusCode: 401
-```
-
-## Why another format?
-
-Google built gRPC. Amazon built Smithy. Facebook built GraphQL. Palantir built
-Conjure. These companies rejected OpenAPI in favor of a more concise API Definition Language.
-
-We built Fern to productize this design and make it accessible to all
-software companies.
-
-<Info>
-  Despite being a different format for describing APIs, **you are never locked in to Fern.** It's easy to convert your
-  [Fern Definition to OpenAPI](/learn/api-definition/fern/export-openapi).
-</Info>
diff --git a/fern/products/api-definition/pages/fern-definition/packages.mdx b/fern/products/api-definition/pages/fern-definition/packages.mdx
deleted file mode 100644
index d6656456..00000000
--- a/fern/products/api-definition/pages/fern-definition/packages.mdx
+++ /dev/null
@@ -1,153 +0,0 @@
----
-title: Packages in Fern Definition 
-description: Fern Definition enables the reuse of API type and error names across packages, and can configure the structure of your API documentation.
----
-
-## What is a package?
-
-Every folder in your API definition is a package.
-
-<CodeBlock title='Example of package and nested package'>
-```bash
-fern/
-├─ fern.config.json
-├─ generators.yml
-└─ definition/ # <--- root package
-  ├─ api.yml
-  ├─ projects.yml
-  └─ roles/ # <--- nested package
-    └─ admin.yml
-```
-</CodeBlock>
-
-The generated SDK will match the hierarchy of your API definition.
-
-<CodeBlock title='Generated SDK'>
-```ts
-const client = new Client();
-
-// calling endpoint defined in projects.yml
-client.projects.get();
-
-// calling endpoint defined in roles/admin.yml
-client.roles.admin.get();
-```
-</CodeBlock>
-
-## Package configuration
-
-Each package can have a special definition file called `__package__.yml`. Like any
-other definition file, it can contain [imports](/learn/api-definition/fern/imports),
-[types](/learn/api-definition/fern/types), [endpoints](/learn/api-definition/fern/endpoints),
-and [errors](/learn/api-definition/fern/errors).
-
-Endpoints in `__package__.yml` will appear at the root of the package.
-For example, the following generated SDK:
-
-<CodeBlock title='Generated SDK'>
-```ts
-const client = new Client();
-
-client.getProjects();
-```
-</CodeBlock>
-
-would have a `fern/` folder:
-
-<CodeBlock title='fern/'>
-```bash {5}
-fern/
-├─ fern.config.json
-├─ generators.yml
-└─ definition/
-  ├─ __package__.yml
-  └─ roles.yml
-```
-</CodeBlock>
-
-that contains the following `__package__.yml`:
-
-<CodeBlock title="__package__.yml">
-```yaml 
-service:
-  base-path: ""
-  auth: false
-  endpoints:
-    getProjects:
-      method: GET
-      path: ""
-      response: list<Project>
-```
-</CodeBlock>
-
-## Namespacing
-
-Each package has its own namespace. This means you can reuse type names and
-error names across packages.
-
-This is useful when versioning your APIs. For example, when you want to
-increment your API version, you can copy the existing API
-to a new package and start making changes. If the new API version reuses
-certain types or errors, that's okay because the two APIs live in different
-packages.
-
-<CodeBlock title="Versioning example">
-```bash
-fern/
-├─ fern.config.json
-├─ generators.yml
-└─ definition/
-  ├─ api.yml
-  └─ roles/
-      └─ v1/
-        └─ admin.yml # type names can overlap with v2/admin.yml
-      └─ v2/
-        └─ admin.yml
-```
-</CodeBlock>
-
-## Navigation
-
-`__package__.yml` also allows you to configure the navigation order
-of your services. This is relevant when you want to control the display
-of your documentation.
-
-For example, let's say you have the following `fern/` folder:
-
-<CodeBlock title='fern/'>
-```bash
-fern/
-├─ fern.config.json
-├─ generators.yml
-└─ definition/
-  ├─ projects.yml
-  ├─ roles.yml
-  └─ users.yml
-```
-</CodeBlock>
-
-Your API will be sorted alphabetically: projects, roles, then users. If you
-want to control the navigation, you can add a `__package__.yml` file
-and configure the order:
-
-<CodeBlock title='fern/'>
-```bash
-fern/
-├─ fern.config.json
-├─ generators.yml
-└─ definition/
-  ├─ __package__.yml # <--- New File
-  ├─ projects.yml
-  ├─ roles.yml
-  └─ users.yml
-```
-</CodeBlock>
-
-<CodeBlock title='__package__.yml'>
-```yaml
-navigation: 
-  - users.yml
-  - roles.yml
-  - projects.yml
-```
-</CodeBlock>
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/types.mdx b/fern/products/api-definition/pages/fern-definition/types.mdx
deleted file mode 100644
index 894f06b5..00000000
--- a/fern/products/api-definition/pages/fern-definition/types.mdx
+++ /dev/null
@@ -1,279 +0,0 @@
----
-title: Types in Fern Definition 
-description: Types describe the data model of your API. Fern has many built-in types and supports custom types, as well as extending and aliasing objects, and unions.
----
-
-Types describe the data model of your API.
-
-## Built-in types
-
-- `string`
-- `integer`
-- `long`
-- `double`
-- `boolean`
-- `datetime` _An [RFC 3339, section 5.6 datetime](https://ijmacd.github.io/rfc3339-iso8601/). For example, `2017-07-21T17:32:28Z`._
-- `date` _An RFC 3339, section 5.6 date (YYYY-MM-DD). For example, `2017-07-21`._
-- `uuid`
-- `base64`
-- `list` _e.g., list\<string\>_
-- `set` _e.g., set\<string\>_
-- `map` _e.g., map\<string, integer\>_
-- `optional` _e.g., optional\<string\>_
-- `literal` _e.g., literal\<"Plants"\>_
-- `file` _e.g., [file uploads](/learn/api-definition/fern/endpoints/multipart)_
-- `unknown` _Represents arbitrary JSON._
-
-## Custom types
-
-Creating your own types is easy in Fern!
-
-### Objects
-
-The most common custom types are **objects**.
-
-In Fern, you use the `"properties"` key to create an object:
-
-```yaml {3,8}
-types:
-  Person:
-    properties:
-      name: string
-      address: Address
-
-  Address:
-    properties:
-      line1: string
-      line2: optional<string>
-      city: string
-      state: string
-      zip: string
-      country: literal<"USA">
-```
-
-These represent JSON objects:
-
-```json
-{
-  "name": "Alice",
-  "address": {
-    "line1": "123 Happy Lane",
-    "city": "New York",
-    "state": "NY",
-    "zip": "10001",
-    "country": "USA"
-  }
-}
-```
-
-You can also use **extends** to compose objects:
-
-```yaml {6}
-types:
-  Pet:
-    properties:
-      name: string
-  Dog:
-    extends: Pet
-    properties:
-      breed: string
-```
-
-You can extend multiple objects:
-
-```yaml {3-5}
-types:
-  GoldenRetriever:
-    extends:
-      - Dog
-      - Pet
-    properties:
-      isGoodBoy: boolean
-```
-
-### Aliases
-
-An Alias type is a renaming of an existing type. This is usually done for clarity.
-
-```yaml
-types:
-  # UserId is an alias of string
-  UserId: string
-
-  User:
-    properties:
-      id: UserId
-      name: string
-```
-
-### Enums
-
-An enum represents a string with a set of allowed values.
-
-In Fern, you use the `"enum"` key to create an enum:
-
-```yaml {3}
-types:
-  WeatherReport:
-    enum:
-      - SUNNY
-      - CLOUDY
-      - RAINING
-      - SNOWING
-```
-
-Enum names are restricted to `A-Z`, `a-z`, `0-9`, and `_` to ensure that generated code can compile across all of the languages that Fern can output. If you have an enum that doesn't follow this convention, you can use the `"name"` key to specify a custom name:
-
-```yaml
-types:
-  Operator:
-    enum:
-      - name: LESS_THAN # <--- the name that will be used in SDKs
-        value: '<' # <--- the value that will be serialized
-      - name: GREATER_THAN
-        value: '>'
-      - name: NOT_EQUAL
-        value: '!='
-```
-
-### Discriminated Unions
-
-Fern supports tagged unions (a.k.a. discriminated unions). Unions are useful for
-polymorphism. This is similar to the `oneOf` concept in OpenAPI.
-
-In Fern, you use the `"union"` key to create an union:
-
-```yaml {3-5}
-types:
-  Animal:
-    union:
-      dog: Dog
-      cat: Cat
-  Dog:
-    properties:
-      likesToWoof: boolean
-  Cat:
-    properties:
-      likesToMeow: boolean
-```
-
-In JSON, unions have a **discriminant property** to differentiate between
-different members of the union. By default, Fern uses `"type"` as the
-discriminant property:
-
-```json
-{
-  "type": "dog",
-  "likesToWoof": true
-}
-```
-
-You can customize the discriminant property using the "discriminant" key:
-
-```yaml {3}
- types:
-   Animal:
-     discriminant: animalType
-     union:
-       dog: Dog
-       cat: Cat
-   Dog:
-     properties:
-       likesToWoof: boolean
-   Cat:
-     properties:
-       likesToMeow: boolean
-```
-
-This corresponds to a JSON object like this:
-
-```json
-{
-  "animalType": "dog",
-  "likesToWoof": true
-}
-```
-
-### Undiscriminated Unions
-
-Undiscriminated unions are similar to discriminated unions, however you don't 
-need to define an explicit discriminant property. 
-
-```yaml 
-MyUnion:
-  discriminated: false
-  union:
-    - string
-    - integer
-```
- 
-### Generics
-
-Fern supports shallow generic objects, to minimize code duplication. You can 
-define a generic for reuse like so:
-
-```yaml
-MySpecialMapItem<Key, Value>:
-  properties:
-    key: Key, 
-    value: Value,
-    diagnostics: string
-```
-
-Now, you can instantiate generic types as a type alias:
-
-```yml 
-StringIntegerMapItem:
-  type: Response<string, number>
-
-StringStringMapItem:
-  type: Response<string, string>
-```
-
-You can now freely use this type as if it were any other type! Note, generated
-code will not use generics. The above example will be generated in typescript as:
-
-```typescript
-type StringIntegerMapItem = {
-  key: string,
-  value: number,
-  diagnostics: string
-}   
- 
-type StringStringMapItem = {
-  key: string,
-  value: string,
-  diagnostics: string
-}
-```
-
-### Documenting types
-
-You can add documentation for types. These docs are passed into the compiler,
-and are incredibly useful in the generated outputs (e.g., docstrings in SDKs).
-
-<CodeBlock title="Fern Definition">
-```yaml
-types:
-  Person:
-    docs: A person represents a human being
-    properties:
-      name: string
-      age:
-        docs: age in years
-        type: integer
-```
-</CodeBlock>
-
-<CodeBlock title="Generated TypeScript SDK from Fern Definition">
-```typescript 
-/**
- * A person represents a human being
- */
-interface Person {
-  name: string;
-  // age in years
-  age: number;
-}
-```
-</CodeBlock>
diff --git a/fern/products/api-definition/pages/fern-definition/webhooks.mdx b/fern/products/api-definition/pages/fern-definition/webhooks.mdx
deleted file mode 100644
index 399c3c2a..00000000
--- a/fern/products/api-definition/pages/fern-definition/webhooks.mdx
+++ /dev/null
@@ -1,65 +0,0 @@
----
-title: Webhooks in the Fern Definition 
-description: Learn how to define webhooks in the Fern Definition
----
-
-In Fern, you can specify webhooks in your API definition. The webhooks will be included 
-in both the generated SDKs and the API documentation.
-
-## Webhook definition
-
-Each webhook defines:
-
-1. **Method**: The HTTP Method that the webhook will use (either `GET` or `POST`)
-2. **Headers**: The headers that the webhook will send
-3. **Payload**: The schema of the webhook payload
-
-<CodeBlock title='webhooks.yml'>
-  ```yaml {2-10}
-  webhooks: 
-    paymentNotification: 
-      display-name: Payment Notification
-      docs: Receive a notification when a payment changes status
-      method: POST 
-      headers: 
-        X-Signature-Primary: 
-          type: string 
-          docs: An HMAC signature of the payload
-      payload: PaymentNotificationPayload
-  
-  types: 
-    PaymentNotificationPayload: 
-      discriminant: notificationType
-      union: 
-        queued: QueuedPaymentNotification
-        processing: ProcessingPaymentNotification
-        completed: CompletedPaymentNotification
-  ```
-</CodeBlock>
-
-### Inlined payloads
-
-You can inline the schema of the payload by doing the following: 
-
-<CodeBlock title='webhooks.yml'>
-  ```yaml
-  webhooks: 
-    paymentNotification: 
-      display-name: Payment Notification
-      docs: Receive a notification when a payment changes status
-      method: POST 
-      headers: 
-        X-Signature-Primary: 
-          type: string 
-          docs: An HMAC signature of the payload
-      payload:
-        name: PaymentNotificationPayload
-        properties: 
-          id:
-            type: string
-            docs: The notification id
-          amount: double
-          currency: Currency
-  ```
-</CodeBlock>
-
diff --git a/fern/products/api-definition/pages/fern-definition/websocket.png b/fern/products/api-definition/pages/fern-definition/websocket.png
deleted file mode 100644
index e7f3cc355ee7c8539a483c175d5b5c5e4c051d20..0000000000000000000000000000000000000000
GIT binary patch
diff --git a/fern/products/api-definition/pages/fern-definition/websockets.mdx b/fern/products/api-definition/pages/fern-definition/websockets.mdx
deleted file mode 100644
index efac1a6e..00000000
--- a/fern/products/api-definition/pages/fern-definition/websockets.mdx
+++ /dev/null
@@ -1,96 +0,0 @@
----
-title: WebSockets in the Fern Definition 
-description: Learn how to define WebSockets in the Fern Definition
----
-
-WebSockets enable a user to create a connection with a server, over which bidirectional communication can be sent. 
-
-In Fern, you can specify WebSockets in your API definition. The WebSockets will be included in both the generated SDKs and the API documentation. 
-
-## WebSocket definition
-Each WebSocket is defined in its own file, where it is described by the `channel` object. 
-
-### The channel object
-
-A `channel` is defined by the following fields:
-
-- `auth`: The authentication scheme for the WebSocket
-- `path`: The path of the WebSocket 
-- `headers` _(Optional)_: Any headers the WebSocket will send
-- `path-parameters` _(Optional)_: Any path parameters in the WebSocket path
-- `query-parameters` _(Optional)_: Any query parameters used in the initial request of the WebSocket
-- `messages` _(Optional)_: The schemas of the messages the WebSocket can send and receive once connected
-    - `origin`: The entity that sent the message (e.g. `client` or `server`)
-    - `body`: The schema of the message
-- `examples`: Example WebSocket connection _(Optional)_
-
-### WebSocket example 
-
-<CodeBlock title='chat.yml'>
-  ```yaml
-  channel:
-    path: /chat
-    auth: false
-    query-parameters:
-      model_id:
-        type: optional<string>
-        docs: The unique identifier of the model.
-      model_version:
-        type: optional<integer>
-        docs: The version number of the model.
-    messages:
-      publish:
-        origin: client
-        body: PublishEvent
-      subscribe:
-        origin: server
-        body: SubscribeEvent
-    examples: 
-      - query-parameters: 
-          model_id: "123"
-        messages: 
-          - type: publish
-            body:
-              text: "Hello, world."
-          - type: subscribe
-            body: 
-              id: "23823049"
-              message: "Hello there, how are you?"
-  types:
-    PublishEvent:
-      docs: The input from the user to send through the WebSocket. 
-      properties: 
-        text: 
-          type: string
-          docs: The user text to send into the conversation. 
-    SubscribeEvent:
-      docs: The response from the server sent through the WebSocket.
-      properties: 
-        id: 
-          type: string
-          docs: The id of the message. 
-        message:
-          type: string
-          docs: The message sent through the socket. 
-  ```
-</CodeBlock>
-
-## WebSocket API Reference
-
-### WebSocket Reference
-
-Fern renders a unique reference page for WebSockets. The **Handshake** section outlines the protocol for connecting with the server, while the **Send** and **Receive** sections outline the message schemas that can be sent between the client and server. 
-
-<Frame caption="Cartesia's WebSocket Reference page">
-<img src="./wss-reference.png" alt="The WebSocket Reference" />
-</Frame>
-
-### WebSocket Playground
-
-<Markdown src="/snippets/pro-callout.mdx" />
-
-Users can connect to and use WebSockets from right within the API Reference (check one of Hume's WebSockets [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat)). 
-
-<Frame caption="Click 'Play' to open the WebSocket Playground">
-<img src="./websocket.png" alt="WebSocket Playground"/>
-</Frame>
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/fern-definition/wss-reference.png b/fern/products/api-definition/pages/fern-definition/wss-reference.png
deleted file mode 100644
index cfef31111b480b5f53f2c7c960a1144c260f0bdf..0000000000000000000000000000000000000000
GIT binary patch
diff --git a/fern/products/api-definition/pages/introduction/what-is-an-api-definition.mdx b/fern/products/api-definition/pages/introduction/what-is-an-api-definition.mdx
deleted file mode 100644
index 1c0d973c..00000000
--- a/fern/products/api-definition/pages/introduction/what-is-an-api-definition.mdx
+++ /dev/null
@@ -1,299 +0,0 @@
----
-title: What is an API Definition?
-description: Describes the contract between the API provider and API consumer
----
-
-<Info>
-An API Definition is a document that defines the structure of the API. It includes the **endpoints**, 
-**request and response schemas**, and **authentication** requirements.
-</Info>
-
-Fern integrates with several API definition formats: 
-
-<AccordionGroup>
-  <Accordion
-    title="OpenAPI (REST & Webhook APIs)"
-  >
-    Formerly known as Swagger, [OpenAPI](https://swagger.io/specification/) is the most popular API definition format.
-    OpenAPI can be used to document RESTful APIs and is defined in a YAML or JSON file.
-
-    Check out an example OpenAPI Specification for the Petstore API [here](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
-  
-    ```yaml maxLines={0}
-    openapi: 3.0.2
-    tags:
-      - name: pet
-        description: Everything about your Pets
-    paths:
-      /pet:
-        post:
-          tags:
-            - pet
-          summary: Add a new pet to the store
-          description: Add a new pet to the store
-          operationId: addPet
-          requestBody:
-            description: Create a new pet in the store
-            required: true
-            content:
-              application/json:
-                schema:
-                  $ref: '#/components/schemas/Pet'
-              application/xml:
-                schema:
-                  $ref: '#/components/schemas/Pet'
-              application/x-www-form-urlencoded:
-                schema:
-                  $ref: '#/components/schemas/Pet'
-          responses:
-            '200':
-              description: Successful operation
-              content:
-                application/xml:
-                  schema:
-                    $ref: '#/components/schemas/Pet'
-                application/json:
-                  schema:
-                    $ref: '#/components/schemas/Pet'
-            '405':
-              description: Invalid input
-        components:
-          schemas:
-            Pet:
-              required:
-                - name
-                - photoUrls
-              properties:
-                id:
-                  type: integer
-                  format: int64
-                  example: 10
-                name:
-                  type: string
-                  example: doggie
-                category:
-                  $ref: '#/components/schemas/Category'
-                photoUrls:
-                  type: array
-                  xml:
-                    wrapped: true
-                  items:
-                    type: string
-                    xml:
-                      name: photoUrl
-                tags:
-                  type: array
-                  xml:
-                    wrapped: true
-                  items:
-                    $ref: '#/components/schemas/Tag'
-                    xml:
-                      name: tag
-                status:
-                  type: string
-                  description: pet status in the store
-                  enum:
-                    - available
-                    - pending
-                    - sold
-              xml:
-                name: pet
-              type: object
-    ```
-  </Accordion>
-  <Accordion
-    title="AsyncAPI (WebSocket APIs)"
-  >
-    [AsyncAPI](https://v2.asyncapi.com/docs) is a specification for defining event-driven APIs. It is used to document APIs that use 
-    WebSockets, MQTT, and other messaging protocols.
-
-    Check out an example AsyncAPI spec for a chat application below: 
-
-    ```yaml maxLines={0}
-    asyncapi: 2.0.0
-    info:
-      title: Chat server
-      version: 1.0.0
-
-    servers:
-      Production:
-        url: chat.com
-        protocol: ws
-
-    channels:
-      "/application":
-        bindings:
-          ws:
-            query:
-              type: object
-              properties:
-                apiKey:
-                  type: string
-                  description: The API key for the client
-                  minimum: 1
-            bindingVersion: 0.1.0
-        subscribe:
-          operationId: sendMessage
-          message:
-            $ref: '#/components/messages/SendMessage'
-        publish:
-          operationId: receiveMessage
-          message:
-            $ref: '#/components/messages/ReceiveMessage'
-
-    components:
-      messages:
-        SendMessage:
-          payload:
-            message: string
-        ReceiveMessage:
-          payload:
-            message: string
-            from: 
-              type: string
-              description: The userId for the sender of the message
-  ```
-    
-  </Accordion>
-  <Accordion
-    title="Fern Definition (REST, Webhook, & WebSocket APIs)"
-  >
-    The Fern Definition is our take on a simpler API definition format. It is designed with **best-practices**, 
-    supports **both RESTful and event-driven APIs**, and is optimized for **SDK generation**. 
-
-    <Note>
-      The Fern Definition is inspired from internal API Definition formats built at companies like 
-      [Amazon](https://smithy.io/2.0/index.html), [Google](https://grpc.io/), [Palantir](https://blog.palantir.com/introducing-conjure-palantirs-toolchain-for-http-json-apis-2175ec172d32), 
-      Twilio and Stripe. These companies **rejected** OpenAPI and built their own version. 
-    </Note>
-
-    Check out an example Fern Definition below: 
-
-    ```yaml maxLines={0}
-    types:
-      MovieId: string
-
-      Movie:
-        properties:
-          id: MovieId
-          title: string
-          rating:
-            type: double
-            docs: The rating scale is one to five stars
-
-      CreateMovieRequest:
-        properties:
-          title: string
-          rating: double
-
-    service:
-      auth: false
-      base-path: /movies
-      endpoints:
-        createMovie:
-          docs: Add a movie to the database
-          method: POST
-          path: /create-movie
-          request: CreateMovieRequest
-          response: MovieId
-
-        getMovie:
-          method: GET
-          path: /{movieId}
-          path-parameters:
-            movieId: MovieId
-          response: Movie
-          errors:
-            - MovieDoesNotExistError
-
-    errors:
-      MovieDoesNotExistError:
-        status-code: 404
-        type: MovieId
-    ```
-
-  </Accordion>
-  <Accordion
-    title="OpenRPC (JSON-RPC APIs)"
-  >
-    [OpenRPC](https://open-rpc.org/) is a spec for describing JSON-RPC 2.0 APIs. It enables interactive docs, code generation, and tooling—bringing OpenAPI-style benefits to the JSON-RPC ecosystem.
-
-    Check out an example OpenRPC Specification for a crypto wallet service below:
-
-    ```json maxLines={0}
-    {
-      "openrpc": "1.2.6",
-      "info": {
-        "title": "Crypto Wallet Service",
-        "version": "1.0.0",
-        "description": "A simple JSON-RPC API for managing a crypto wallet."
-      },
-      "methods": [
-        {
-          "name": "getBalance",
-          "summary": "Get the balance of a wallet address.",
-          "params": [
-            {
-              "name": "address",
-              "schema": { "type": "string" },
-              "description": "The wallet address."
-            }
-          ],
-          "result": {
-            "name": "balance",
-            "schema": { "type": "number" },
-            "description": "The balance in the wallet."
-          }
-        },
-        {
-          "name": "sendTransaction",
-          "summary": "Send crypto to another address.",
-          "params": [
-            { "name": "from", "schema": { "type": "string" }, "description": "Sender address." },
-            { "name": "to", "schema": { "type": "string" }, "description": "Recipient address." },
-            { "name": "amount", "schema": { "type": "number" }, "description": "Amount to send." }
-          ],
-          "result": {
-            "name": "txId",
-            "schema": { "type": "string" },
-            "description": "Transaction ID."
-          }
-        }
-      ]
-    }
-    ```
-  </Accordion>
-</AccordionGroup>
-
-## Why create an API Definition ? 
-
-Once you have an API definition, Fern will use it as an input to generate artifacts 
-like SDKs and API Reference documentation. Every time you update the API definition,
-you can regenerate these artifacts and ensure they are always up-to-date.
-
-<CardGroup cols={2}>
-  <Card
-    title="SDKs"
-    icon="brands github"
-  >
-    Client libraries in multiple languages.
-  </Card>
-  <Card
-    title="Documentation"
-    icon="regular browser"
-  >
-    A Stripe-like API documentation website.
-  </Card>
-  <Card
-    title="Postman Collection"
-    icon={<img src="https://cdn.worldvectorlogo.com/logos/postman.svg" alt="Postman logo"/>}
-  >
-    A published Postman collection, with example request and responses. 
-  </Card>
-  <Card
-    title="Server Boilerplate"
-    icon={<img src="https://cdn.worldvectorlogo.com/logos/fastapi-1.svg" alt="FastAPI logo" />}
-  >
-    Pydantic models for FastAPI or controllers for your Spring Boot application.
-  </Card>
-</CardGroup>
diff --git a/fern/products/api-definition/pages/introduction/what-is-the-fern-folder.mdx b/fern/products/api-definition/pages/introduction/what-is-the-fern-folder.mdx
deleted file mode 100644
index 7f06ece3..00000000
--- a/fern/products/api-definition/pages/introduction/what-is-the-fern-folder.mdx
+++ /dev/null
@@ -1,127 +0,0 @@
----
-title: The Fern Folder
-description: Describes the fern folder structure and its contents
----
-
-Configuring fern starts with the `fern` folder which contains your API definitions, generators, and your CLI version.
-
-## Directory structure
-
-When you run `fern init`, your fern folder will be initialized with the following files:
-```bash
-fern/
-  ├─ fern.config.json
-  ├─ generators.yml
-  └─ definition/
-    ├─ api.yml
-    └─ imdb.yml
-```
-
-If you want to initialize Fern with an OpenAPI Specification, run `fern init --openapi path/to/openapi` instead.
-```yaml
-fern/
-  ├─ fern.config.json
-  ├─ generators.yml # required on Fern version 0.41.0 and above
-  └─ openapi/
-    ├─ openapi.yml
-```
-
-### `fern.config.json`
-
-Every fern folder has a single `fern.config.json` file. This file stores the organization and
-the version of the Fern CLI that you are using. 
-
-```json
-{
-    "organization": "imdb",
-    "version": "<Markdown src="/products/sdks/snippets/fern-config-json-version.mdx"/>"
-}
-```
-
-Every time you run a fern CLI command, the CLI downloads itself at the correct version to ensure 
-determinism. 
-
-<Note>To upgrade the CLI, run `fern upgrade`. This will update the version field in `fern.config.json` </Note>
-
-### `generators.yml`
-
-The `generators.yml` file can include information about where your API specification is located, along with which generators you are using, where each package gets published, as well as configuration specific to each generator. 
-
-<AccordionGroup>
-<Accordion title="generators.yml for Python + TypeScript SDKs">
-```yaml
-api: 
-  path: ./path/to/openapi.yml
-groups:
-  public:
-    generators:
-      - name: fernapi/fern-python-sdk
-        version: 3.0.0
-        output:
-          location: pypi
-          package-name: imdb
-          token: ${PYPI_TOKEN}
-        github:
-          repository: imdb/imdb-python
-        config:
-          client_class_name: imdb
-      - name: fernapi/fern-typescript-node-sdk
-        version: 0.31.0
-        output:
-          location: npm
-          package-name: imdb
-          token: ${NPM_TOKEN}
-        github:
-          repository: imdb/imdb-node
-        config:
-          namespaceExport: imdb
-```
-</Accordion>
-<Accordion title="generators.yml for locating an API definition">
-```yaml
-api: 
-  path: ./path/to/openapi.yml
-```
-</Accordion>
-</AccordionGroup>
-
-## Multiple APIs
-
-The fern folder is capable of housing multiple API definitions. Instead of placing your API definition at the top-level, you can nest them within an `apis` folder. Be sure to include a `generators.yml` file within each API folder that specifies the location of the API definition. 
-
-<Tabs>
-<Tab title="OpenAPI Definition">
-```bash
-fern/
-  ├─ fern.config.json
-  ├─ generators.yml
-  └─ apis/
-    └─ imdb/
-        ├─ generators.yml
-        └─ openapi/
-          ├─ openapi.yml
-    └─ disney/
-        ├─ generators.yml
-        └─ openapi/
-          ├─ openapi.yml
-```
-</Tab>
-<Tab title="Fern Definition">
-```bash
-fern/
-  ├─ fern.config.json
-  ├─ generators.yml
-  └─ apis/
-    └─ imdb/
-        ├─ generators.yml
-        └─ definition/
-          ├─ api.yml
-          └─ imdb.yml
-    └─ disney/
-        ├─ generators.yml
-        └─ definition/
-          ├─ api.yml
-          └─ disney.yml
-```
-</Tab>
-</Tabs>
diff --git a/fern/products/api-definition/pages/openapi/auth.mdx b/fern/products/api-definition/pages/openapi/auth.mdx
deleted file mode 100644
index 1f9ea850..00000000
--- a/fern/products/api-definition/pages/openapi/auth.mdx
+++ /dev/null
@@ -1,196 +0,0 @@
----
-title: Authentication
-subtitle: Model auth schemes such as bearer, basic, and api key. 
----
-
-Configuring authentication schemes happens in the `components.securitySchemes` section of OpenAPI. 
-
-```yml title="openapi.yml" {2-3}
-components: 
-  securitySchemes: 
-    ...
-```
-
-<Note>
-To apply a security scheme across all endpoints, reference the `securityScheme` within the `security` section of your OpenAPI Specification. 
-
-```yml title="openapi.yml" {3, 5-6}
-components: 
-  securitySchemes: 
-    AuthScheme:
-      ...
-security: 
-  - AuthScheme: []
-```
-</Note>
-
-## Bearer security scheme
-
-Start by defining a `bearer` security scheme in your `openapi.yml`: 
-
-```yml title="openapi.yml" {3-5}
-components:
-  securitySchemes:
-    BearerAuth:
-      type: http
-      scheme: bearer
-```
-
-This will generate an SDK where the user would have to provide 
-a mandatory argument called `token`. 
-
-```ts index.ts
-const client = new Client({
-  token: "ey34..."
-})
-```
-
-If you want to control variable naming and the environment variable to scan, 
-use the configuration below: 
-
-```yaml title="openapi.yml" {6-8}
-components:
-  securitySchemes:
-    BearerAuth:
-      type: http
-      scheme: bearer
-      x-fern-bearer: 
-        name: apiKey
-        env: PLANTSTORE_API_KEY
-```
-
-The generated SDK would look like: 
-
-```ts index.ts
-
-// Uses process.env.PLANTSTORE_API_KEY
-let client = new Client(); 
-
-// token has been renamed to apiKey
-client = new Client({
-  apiKey: "ey34..."
-})
-```
-
-## Basic security scheme
-
-Start by defining a `basic` security scheme in your `openapi.yml`: 
-
-```yaml title="openapi.yml" {3-5}
-components:
-  securitySchemes:
-    BasicAuth:
-      type: http
-      scheme: basic
-```
-
-This will generate an SDK where the user would have to provide 
-a mandatory arguments called `username` and `password`. 
-
-```ts index.ts
-const client = new Client({
-  username: "joeschmoe"
-  password: "ey34..."
-})
-```
-
-If you want to control variable naming and environment variables to scan, 
-use the configuration below: 
-
-```yaml title="openapi.yml" {6-12}
-components:
-  securitySchemes:
-    BasicAuth:
-      type: http
-      scheme: basic
-      x-fern-basic:
-        username:
-          name: clientId
-          env: PLANTSTORE_CLIENT_ID
-        password:
-          name: clientSecret
-          env: PLANTSTORE_CLIENT_SECRET
-```
-
-The generated SDK would look like: 
-
-```ts index.ts
-
-// Uses process.env.PLANTSTORE_CLIENT_ID and process.env.PLANTSTORE_CLIENT_SECRET
-let client = new Client(); 
-
-// parameters have been renamed
-client = new Client({
-  clientId: "joeschmoe", 
-  clientSecret: "ey34..."
-})
-```
-
-## ApiKey security scheme
-
-Start by defining an `apiKey` security scheme in your `openapi.yml`: 
-
-```yml title="openapi.yml" {3-5}
-components: 
-  securitySchemes: 
-    ApiKey: 
-      type: apiKey
-      in: header
-      name: X_API_KEY
-```
-
-This will generate an SDK where the user would have to provide 
-a mandatory argument called `apiKey`. 
-
-```ts index.ts
-const client = new Client({
-  apiKey: "ey34..."
-})
-```
-
-If you want to control variable naming and environment variables to scan, 
-use the configuration below: 
-
-```yaml title="openapi.yml" {7-10}
-components: 
-  securitySchemes: 
-    ApiKey: 
-      type: apiKey
-      in: header
-      name: X_API_KEY
-      x-fern-header:
-        name: apiToken
-        env: PLANTSTORE_API_KEY
-        prefix: "Token " # Optional      
-```
-
-The generated SDK would look like: 
-
-```ts index.ts
-
-// Uses process.env.PLANTSTORE_API_KEY
-let client = new Client(); 
-
-// parameters have been renamed
-client = new Client({
-  apiToken: "ey34..."
-})
-```
-
-## Multiple security schemes
-
-If you would like to define multiple security schemes, simply 
-list them under `components.securitySchemes`. For example, if you wanted to support 
-`basic` and `apiKey` security schemes, see the example below: 
-
-```yaml title="openapi.yml" {3,6}
-components: 
-  securitySchemes: 
-    BearerAuth:
-      type: http
-      scheme: bearer  
-    ApiKey: 
-      type: apiKey
-      in: header
-      name: X_API_KEY   
-```
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/openapi/automation.mdx b/fern/products/api-definition/pages/openapi/automation.mdx
deleted file mode 100644
index 042f3955..00000000
--- a/fern/products/api-definition/pages/openapi/automation.mdx
+++ /dev/null
@@ -1,54 +0,0 @@
----
-title: Sync your OpenAPI Specification
-subtitle: Pull your latest OpenAPI Specification into your fern folder automatically.
----
-
-If you host your OpenAPI Specification at a publicly available URL, you can have Fern programmatically fetch the latest spec on a preconfigured cadence through the [sync-openapi GitHub Action](https://github.com/fern-api/sync-openapi). This ensures your committed OpenAPI spec stays up to date with your live API.
-## Setup
-<Steps>
-  <Step title="Configure the origin URL">
-   Add the origin field to your generators.yml to specify where your OpenAPI spec is hosted:
-   ```yml title="generators.yml"
-    api:
-      path: openapi/openapi.json
-      origin: https://api.example.com/openapi.json
-    ```
-  </Step>
-  <Step title="Add the GitHub Action">
-    Create `.github/workflows/sync-openapi.yml` in your repository:
-    ```yml
-    name: Sync OpenAPI Specs # can be customized
-    on:                               # additional custom triggers can be configured
-      workflow_dispatch:              # manual dispatch
-      push:                                          
-        branches:
-          - main                      # on push to main
-      schedule:
-        - cron: '0 3 * * *'           # everyday at 3:00 AM UTC
-  jobs:
-    update-from-source:
-      runs-on: ubuntu-latest
-      steps:
-        - uses: actions/checkout@v4
-          with:
-            token: ${{ secrets.OPENAPI_SYNC_TOKEN }}
-        - name: Update API with Fern
-          uses: fern-api/sync-openapi@v2
-          with:
-            update_from_source: true
-            token: ${{ secrets.OPENAPI_SYNC_TOKEN }}
-            branch: 'update-api'
-            auto_merge: false
-            add_timestamp: true
-```
-  </Step>
-  <Step title="Add a GitHub token">
-    Generate a [fine-grained personal access token](https://github.com/settings/personal-access-tokens) with read/write access to your repository.
-  </Step>
-  <Step title="Add to Repository Secrets">
-    Navigate to your repository's `Settings > Secrets and variables > Actions`. Select **New repository secret**, name it `OPENAPI_SYNC_TOKEN`, add your token, and click **Add secret**.
-  </Step>
-</Steps>
-By default, this will create daily PRs with API spec updates to the repo containing your fern folder. If you would like to adjust the frequency, learn more about GitHub's [schedule event](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule).
-
-<Info> For detailed configuration options and other use cases, see the [sync-openapi GitHub Action README](https://github.com/fern-api/sync-openapi). </Info>
diff --git a/fern/products/api-definition/pages/openapi/endpoints/multipart.mdx b/fern/products/api-definition/pages/openapi/endpoints/multipart.mdx
deleted file mode 100644
index 800fa0fa..00000000
--- a/fern/products/api-definition/pages/openapi/endpoints/multipart.mdx
+++ /dev/null
@@ -1,75 +0,0 @@
----
-title: Multipart File Upload
-subtitle: Document endpoints with the `multipart/form-data` content type
----
-
-Multipart requests combine one or more sets of data into a single body, separated by boundaries.
-You typically use these requests for file uploads and for transferring data of several types in a single request
-(for example, a file along with a JSON object).
-
-```yml title="openapi.yml" maxLines=0 {12-24}
-paths:
-  /upload:
-    post:
-      summary: Upload a file
-      description: Upload a file using multipart/form-data encoding
-      operationId: uploadFile
-      tags:
-        - file
-      requestBody:
-        required: true
-        content:
-          multipart/form-data:
-            schema:
-              type: object
-              properties:
-                file:
-                  type: string
-                  format: binary
-                  description: The file to upload
-                description:
-                  type: string
-                  description: A description of the file (optional)
-              required:
-                - file
-      responses:
-        "200":
-          description: Successful upload
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  message:
-                    type: string
-                  fileId:
-                    type: string
-```
-Any request body that is defined with a `multipart/form-data` content type, will be 
-treated as a multipart request. Within a given multipart request, a string parameter with 
-`format:binary` will represent an arbitrary file. 
-
-## Array of Files
-
-If your endpoint supports an array of files, then your request body must use
-an array type.
-
-```yml openapi.yml {12-17}
-paths:
-  /upload:
-    post:
-      summary: Upload multiple files
-      operationId: uploadFiles
-      requestBody:
-        content:
-          multipart/form-data:
-            schema:
-              type: object
-              properties:
-                files:
-                  type: array
-                  items:
-                    type: string
-                    format: binary
-                  description: An array of files to upload
-```
diff --git a/fern/products/api-definition/pages/openapi/endpoints/rest.mdx b/fern/products/api-definition/pages/openapi/endpoints/rest.mdx
deleted file mode 100644
index a4dd8fcb..00000000
--- a/fern/products/api-definition/pages/openapi/endpoints/rest.mdx
+++ /dev/null
@@ -1,66 +0,0 @@
----
-title: HTTP JSON Endpoints
-subtitle: Document HTTP JSON APIs with the `application/json` content type
----
-
-Endpoints in OpenAPI are defined underneath the `paths` key. Below is an example of defining 
-a single endpoint: 
-
-```yml title="openapi.yml" maxLines=0 {2-18}
-paths:
-  /pets:
-    post:
-      summary: Create a new pet
-      description: Creates a new pet with the provided information
-      requestBody:
-        required: true
-        content:
-          application/json:
-            schema:
-              $ref: '#/components/schemas/Pet'
-      responses:
-        '200':
-          description: User created successfully
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/Pet'
-```
-
-## Examples
-
-You can provide examples of requests and responses by using the `examples` key.   
-
-```yaml title="openapi.yml" {12-17,25-30}
-paths:
-  /pets:
-    post:
-      summary: Create a new pet
-      description: Creates a new pet with the provided information
-      requestBody:
-        required: true
-        content:
-          application/json:
-            schema:
-              $ref: '#/components/schemas/Pet'
-            examples:
-              PetExample:
-                summary: This is an example of a Pet
-                value: 
-                  name: Markley
-                  id: 44
-      responses:
-        '200':
-          description: A Pet object
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/Pet'
-              examples:
-                PetExample:
-                  summary: This is an example of a Pet
-                  value: 
-                    name: Markley
-                    id: 44
-```
-
diff --git a/fern/products/api-definition/pages/openapi/endpoints/sse.mdx b/fern/products/api-definition/pages/openapi/endpoints/sse.mdx
deleted file mode 100644
index bc680cfe..00000000
--- a/fern/products/api-definition/pages/openapi/endpoints/sse.mdx
+++ /dev/null
@@ -1,109 +0,0 @@
----
-title: Server-Sent Events and Streaming APIs
-subtitle: Use the `x-fern-streaming` extension to model streaming endpoints
----
-
-<Note>
-  The `x-fern-streaming` extension allows you to represent endpoints that are streaming. 
-</Note>
-
-
-## JSON streaming
-
-If your API returns a series of `JSON` chunks as seen below
-
-```json
-{ "text": "Hi, I am a" }
-{ "text": "chatbot. Do you have any"}
-{ "text": "questions for me"}
-```
-
-then simply add the `x-fern-streaming: true` to your OpenAPI operation. 
-
-```yaml title="openapi.yml" {4}
-paths:
-  /logs:
-    post:
-      x-fern-streaming: true
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Chat"
-components:
-  schemas:
-    Chat:
-      type: object
-      properties:
-        text:
-          type: string
-```
-
-## Server-sent events 
-
-If your API returns server-sent-events, with the `data` and `event` keys as seen below
-
-```json
-data: { "text": "Hi, I am a" }
-data: { "text": "chatbot. Do you have any"}
-data: { "text": "questions for me"}
-```
-
-then make sure to include `format: sse`. 
-
-```yaml title="openapi.yml" {4-5}
-paths:
-  /logs:
-    post:
-      x-fern-streaming: 
-        format: sse
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Chat"
-components:
-  schemas:
-    Chat:
-      type: object
-      properties:
-        text:
-          type: string
-```
-
-## `Stream` parameter
-
-It has become common practice for endpoints to have a `stream` parameter that 
-controls whether the response is streamed or not. Fern supports this pattern in a first 
-class way. 
-
-Simply specify the `stream-condition` as well as the ordinary response and the streaming response: 
-
-```yaml title="openapi.yml" {4-10}
-paths:
-  /logs:
-    post:
-      x-fern-streaming: 
-        format: sse
-        stream-condition: $request.stream
-        response: 
-          $ref: '#/components/schemas/Chat'
-        response-stream: 
-          $ref: '#/components/schemas/ChatChunk'
-components:
-  schemas:
-    Chat:
-      type: object
-      properties:
-        text:
-          type: string
-        tokens: 
-          type: number
-    ChatChunk: 
-      type: object
-      properties: 
-        text: 
-          type: string
-```
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/openapi/examples.mdx b/fern/products/api-definition/pages/openapi/examples.mdx
deleted file mode 100644
index e4a77240..00000000
--- a/fern/products/api-definition/pages/openapi/examples.mdx
+++ /dev/null
@@ -1,93 +0,0 @@
----
-title: How to use examples in OpenAPI
-description: Use the examples feature of OpenAPI to add example values in your API definition. Fern then uses your examples when generating SDKs and documentation.
----
-
-Using examples in OpenAPI shows API consumers what requests and responses look like. They can be provided for request bodies, response bodies, and individual parameters.
-
-## Inline examples
-
-Examples can be placed directly within the operation definition under `paths`. Here's an example:
-
-```yaml
-paths:
-  /pet:
-    post:
-      summary: Add a new pet to the store
-      operationId: addPet
-      responses:
-        '200':
-          description: A Pet object
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/Pet'
-              examples:
-                PetExample:
-                  summary: This is an example of a Pet
-                  value: 
-                    name: Markley
-                    id: 44
-```
-
-## Reusable examples
-
-For more general examples that apply to multiple parts of the API, you can define them under the `components/examples` section. These can be referenced elsewhere in the documentation.
-
-```yaml
-components:
-  examples:
-    PetExample:
-      summary: Example of a Pet object
-      value:
-        name: Markley
-        id: 44
-
-paths:
-  /pet:
-    post:
-      summary: Add a new pet to the store
-      operationId: addPet
-      responses:
-        '200':
-          description: Successful operation
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/Pet'
-              examples:
-                PetExample:
-                  $ref: '#/components/examples/PetExample'
-```
-
-## How examples are used in Fern SDKs
-
-Fern SDKs use examples from your OpenAPI document to generate comments that show up in your IDE. For example, in a Node.js SDK:
-
-<CodeBlock title="resources/pets/types/Pet.ts">
-```ts
-
-import * as Petstore from "../../..";
-
-/**
- * @example
- *     {
- *         name: "Markley",
- *         id: "44"
- *     }
- */
-```
-</CodeBlock>
-
-Here's an [example in GitHub](https://github.com/FlatFilers/flatfile-node/blob/ab955a0a337c40ea00755e24df08f8c9a146c39c/src/api/resources/documents/types/DocumentResponse.ts#L8-L27) from Flatfile's Node.js SDK. 
-
-## How examples are used in Fern Docs 
-
-In the request and response code snippets, you'll see the example values used.
-
-<Frame>
-![Screenshot of an example used in response code in Fern Docs API reference](https://fern-image-hosting.s3.amazonaws.com/movie+example.png)
-</Frame>
-
-If you generate SDKs with Fern, the code examples for each language will also be populated with the example values. [Check out Flatfile's Docs to see this in action](https://reference.flatfile.com/api-reference/documents/create). Change the language toggle to see the examples in different languages.
-
diff --git a/fern/products/api-definition/pages/openapi/extensions/audiences.mdx b/fern/products/api-definition/pages/openapi/extensions/audiences.mdx
deleted file mode 100644
index 8bd80fc5..00000000
--- a/fern/products/api-definition/pages/openapi/extensions/audiences.mdx
+++ /dev/null
@@ -1,118 +0,0 @@
----
-title: Use audiences to filter your API
-subtitle: Use `x-fern-audiences` to filter to relevant endpoints, schemas and properties
----
-
-Audiences are a useful tool for segmenting your API for different consumers. Common examples of audiences include `public`
-and `beta`.
-
-<Info>
-  Remember to filter your SDKs and Docs after specifying audiences. If **no audiences** are specified,
-  nothing will be filtered.
-
-<AccordionGroup>
-
-<Accordion title="SDKs">
-The following example configures the SDK to filter to the `public` audience:
-
-```yaml title="generators.yml" {3-4}
-groups:
-  sdks:
-    audiences:
-      - public
-    generators:
-      - name: fernapi/fern-typescript-node-sdk
-        version: 0.8.8
-```
-</Accordion>
-
-<Accordion title="Docs">
-The following example configures the docs to filter to the `public` audience:
-
-```yaml title="docs.yml" {3-4}
-navigation:
-  - api: API Reference
-    audiences:
-      - public
-```
-</Accordion>
-
-</AccordionGroup>
-
-</Info>
-
-## Audiences for servers
-
-To mark a server with a particular audience, add the `x-fern-server-name` and `x-fern-audiences` extension to the relevant server.
-
-In the example below, the `Production` server is only available to public consumers:
-
-```yaml title="openapi.yml" {3-5}
-servers:
-  - url: https://api.com
-    x-fern-server-name: Production
-    x-fern-audiences:
-      - public
-```
-
-## Audiences for endpoints
-
-To mark an endpoint with a particular audience, add the `x-fern-audiences` extension to the relevant endpoint.
-
-In the example below, the `POST /users/sendEmail` endpoint is only available to public consumers:
-
-```yaml title="openapi.yml" {4-5}
-paths:
-  /users/sendEmail:
-    post:
-      x-fern-audiences:
-        - public
-      operationId: send_email
-```
-
-## Audiences for schemas
-
-Schemas can be marked for different audiences, as well.
-
-In this example, the `Email` type is available to both public and beta customers.
-
-```yaml title="openapi.yml" {13-15}
-components:
-  schemas:
-    Email:
-      title: Email
-      type: object
-      properties:
-        subject:
-          type: string
-        body:
-          type: string
-        to:
-          type: string
-      x-fern-audiences:
-        - public
-        - beta
-```
-
-## Audiences for properties
-
-Properties can be marked for different audiences, as well.
-
-In this example, the `to` property is available to beta customers only.
-
-```yaml title="openapi.yml" {13-17}
-components:
-  schemas:
-    Email:
-      title: Email
-      type: object
-      properties:
-        subject:
-          type: string
-        body:
-          type: string
-        to:
-          type: string
-          x-fern-audiences:
-            - beta
-```
diff --git a/fern/products/api-definition/pages/openapi/extensions/method-names.mdx b/fern/products/api-definition/pages/openapi/extensions/method-names.mdx
deleted file mode 100644
index 6a5c4230..00000000
--- a/fern/products/api-definition/pages/openapi/extensions/method-names.mdx
+++ /dev/null
@@ -1,58 +0,0 @@
----
-title: Customize SDK Method Names
-description: Use `x-fern-sdk-method-name` and `x-fern-sdk-group-name` to finetune SDK naming.
----
-
-## Operation IDs
-
-By default, if you have no extensions present, Fern will try to use your operation ID to generate idiomatic 
-method names for the SDK. We typically recommend formatting your operation IDs like `{tag_name}_{operation_name}`. 
-
-For example, for an endpoint that has the tag `users` and the operation id `users_get`, we will generate an SDK 
-method that is `users.get()`. If your operation id does not start with a tag, then we will simply use it as the method name. 
-
-## Usage
-
-<Note>
-  The `x-fern-sdk-group-name` and `x-fern-sdk-method-name` extensions allow you to customize the generated SDK method
-  names.
-</Note>
-
-In the example below, Fern will generate a method called `client.users.create()` for the `POST /users` endpoint.
-
-```yaml title="openapi.yaml"
-paths:
-  /users:
-    post:
-      x-fern-sdk-group-name: users
-      x-fern-sdk-method-name: create
-```
-
-## Top level methods
-
-If you omit the `x-fern-sdk-group-name` extension, then the generated SDK method will live at the root.
-In the example below, Fern will generate a method called `client.send()`:
-
-```yaml title="openapi.yaml"
-paths:
-  /send:
-    post:
-      x-fern-sdk-method-name: send
-```
-
-## Multiple levels of nesting
-
-If you add more than one `x-fern-sdk-group-name` extension, then the generated SDK will nest group names.
-The order of the group names is preserved in the generated SDK method.
-
-In the example below, Fern will generate a method called `client.users.notifications.send()`:
-
-```yaml title="openapi.yaml"
-paths:
-  /users/notifications:
-    post:
-      x-fern-sdk-group-name:
-        - users
-        - notifications
-      x-fern-sdk-method-name: send
-```
diff --git a/fern/products/api-definition/pages/openapi/extensions/others.mdx b/fern/products/api-definition/pages/openapi/extensions/others.mdx
deleted file mode 100644
index b3aa4b3e..00000000
--- a/fern/products/api-definition/pages/openapi/extensions/others.mdx
+++ /dev/null
@@ -1,416 +0,0 @@
----
-title: Other extensions
-description: Learn about Fern's OpenAPI extensions for authentication overrides, global headers, enum descriptions and names, audiences, and more.
----
-
-Fern supports different OpenAPI extensions so that you can generate higher-quality SDKs.
-
-## API version
-
-You can define your API version scheme, such as a `X-API-Version` header. The supported versions and default value are specified like so:
-
-```yaml title="openapi.yaml"
-x-fern-version:
-  version:
-    header: X-API-Version
-    default: "2.0.0"
-    values:
-      - "1.0.0"
-      - "2.0.0"
-      - "latest"
-paths: ...
-```
-
-## Global headers
-
-At times, your API will leverage certain headers for every endpoint, or the majority of them, we call these "global headers". For convenience, generated Fern SDKs expose "global headers" to easily be updated on API calls. Take for example an API key, if we declare the API key as a global header, a user will be able to plug theirs in easily:
-
-```python
-import os
-
-class Client:
-
-  def __init__(self, *, apiKey: str):
-```
-
-To configure global headers, Fern will automatically pull out headers that are used in every request, or the majority of requests, and mark them as global.
-In order to label additional headers as global, or to alias the names of global headers, you can leverage the `x-fern-global-headers` extension:
-
-```yaml title="openapi.yml"
-x-fern-global-headers:
-  - header: custom_api_key
-    name: api_key
-  - header: userpool_id
-    optional: true
-```
-
-yields the following client:
-
-```python
-import os
-
-class Client:
-
-  def __init__(self, *, apiKey: str, userpoolId: typing.Optional[str])
-```
-
-## Enum descriptions and names
-
-OpenAPI doesn't natively support adding descriptions to enum values. To do this in Fern you can use the `x-fern-enum`
-extension.
-
-In the example below, we've added some descriptions to enum values. These descriptions will
-propagate into the generated SDK and docs website.
-
-```yaml title="openapi.yml" {9-13}
-components:
-  schemas:
-    CardSuit:
-      enum:
-        - clubs
-        - diamonds
-        - hearts
-        - spades
-      x-fern-enum:
-        clubs:
-          description: Some docs about clubs
-        spades:
-          description: Some docs about spades
-```
-
-`x-fern-enum` also supports a `name` field that allows you to customize the name of the enum in code.
-This is particularly useful when you have enums that rely on symbolic characters that would otherwise cause
-generated code not to compile.
-
-For example, the following OpenAPI
-
-```yaml title="openapi.yml" {9,12}
-components:
-  schemas:
-    Operand:
-      enum:
-        - '>'
-        - '<'
-      x-fern-enum:
-        '>':
-          name: GreaterThan
-          description: Checks if value is greater than
-        '<':
-          name: LessThan
-          description: Checks if value is less than
-```
-
-would generate
-
-```typescript title="operand.ts"
-export enum Operand {
-  GreaterThan = ">",
-  LessThan = "<"
-}
-```
-
-## Schema names
-
-OpenAPI allows you to define inlined schemas that do not have names.
-
-```yaml title="Inline type in openapi.yml" {11}
-components:
-  schemas:
-    Movie:
-      type: object
-      properties:
-        name:
-          type: string
-        cast:
-          type: array
-          items:
-            type: object
-            properties:
-              firstName:
-                type: string
-              lastName:
-                type: string
-              age:
-                type: integer
-```
-
-Fern automatically generates names for all the inlined schemas. For example, in this example,
-Fern would generate the name `CastItem` for the inlined array item schema.
-
-```typescript title="Auto-generated name" {6}
-export interface Movie {
-  name?: string;
-  cast?: CastItem[];
-}
-
-export interface CastItem {
-  firstName?: string;
-  lastName?: string;
-  age?: integer;
-}
-```
-
-If you want to override the generated name, you can use the extension `x-fern-type-name`.
-
-```yaml title="openapi.yml" {12}
-components:
-  schemas:
-    Movie:
-      type: object
-      properties:
-        name:
-          type: string
-        cast:
-          type: array
-          items:
-            type: object
-            x-fern-type-name: Person
-            properties:
-              firstName:
-                type: string
-              lastName:
-                type: string
-              age:
-                type: integer
-```
-
-This would replace `CastItem` with `Person` and the generated code would read more idiomatically:
-
-```typescript title="Overridden name" {6}
-export interface Movie {
-  name?: string;
-  cast?: Person[];
-}
-
-export interface Person {
-  firstName?: string;
-  lastName?: string;
-  age?: integer;
-}
-```
-
-## Property names
-
-The `x-fern-property-name` extension allows you to customize the variable name for object
-properties.
-
-For example, if you had a property called `_metadata` in your schema but you wanted the
-variable to be called `data` in your SDK you would do the following:
-
-```yaml {6}
-components:
-  schemas:
-    MyUser:
-      _metadata:
-        type: object
-        x-fern-property-name: data
-```
-
-## Server names
-
-The `x-fern-server-name` extension is used to name your servers.
-
-```yaml title="openapi.yml"
-servers:
-  - url: https://api.example.com
-    x-fern-server-name: Production
-  - url: https://sandbox.example.com
-    x-fern-server-name: Sandbox
-```
-
-In a generated TypeScript SDK, you'd see:
-
-```typescript title="environment.ts"
-export const ExampleEnvironment = {
-  Production: "https://api.example.com"
-} as const;
-
-export type ExampleEnvironment = typeof ExampleEnvironment.Production;
-```
-
-## Base path
-
-The `x-fern-base-path` extension is used to configure the base path prepended to every endpoint.
-
-In the example below, we have configured the `/v1` base path so the full endpoint path is
-`https://api.example.com/v1/users`.
-
-```yaml title="Set the base path in openapi.yml" {1}
-x-fern-base-path: /v1
-servers:
-  - url: https://api.example.com
-paths:
-  /users: ...
-```
-
-## Ignoring schemas or endpoints
-
-If you want Fern to skip reading any endpoints or schemas, use the `x-fern-ignore` extension.
-
-To skip an endpoint, add `x-fern-ignore: true` at the operation level.
-
-```yaml title="x-fern-ignore at operation level in openapi.yml" {4}
-paths:
-  /users:
-    get:
-      x-fern-ignore: true
-      ...
-```
-
-To skip a schema, add `x-fern-ignore: true` at the schema level.
-
-```yaml title="x-fern-ignore at schema level in openapi.yml" {4}
-components:
-  schemas:
-    SchemaToSkip:
-      x-fern-ignore: true
-      ...
-```
-
-## Overlaying extensions
-
-Because of the number of tools that use OpenAPI, it may be more convenient to
-"overlay" your fern specific OpenAPI extensions onto your original definition. \
-In order to do this you can specify your overrides file in `generators.yml`.
-
-Below is an example of how someone can overlay the extensions `x-fern-sdk-method-name` and
-`x-fern-sdk-group-name` without polluting their original OpenAPI. The combined result is
-shown in the third tab.
-
-<CodeBlocks>
-    ```yaml title="generators.yml" {3}
-    api:
-      path: ./openapi/openapi.yaml
-      overrides: ./openapi/overrides.yaml
-    default-group: sdk
-    groups:
-      sdk:
-        generators:
-          - name: fernapi/fern-python-sdk
-            version: 2.2.0
-    ```
-
-    ```yaml title="overrides.yml"
-    paths:
-      /users:
-        get:
-          x-fern-sdk-group-name: users
-          x-fern-sdk-method-name: get
-    ```
-
-    ```yaml title="Overlaid OpenAPI" {4-5}
-    paths:
-      /users:
-        get:
-          x-fern-sdk-group-name: users
-          x-fern-sdk-method-name: get
-          summary: Get a list of users
-          description: Retrieve a list of users from the system.
-          responses:
-            '200':
-              description: Successful response
-            '500':
-              description: Internal Server Error
-    ```
-
-</CodeBlocks>
-
-## Embedding extensions
-
-If instead of overlaying your extensions within an overrides file, as mentioned above. Certain frameworks that generate OpenAPI Specifications make it easy to embed extensions directly from code.
-
-### FastAPI
-
-Please view our page on [FastAPI](/learn/api-definition/openapi/frameworks/fastapi) for more information on how to extend your OpenAPI Specification within FastAPI.
-
-## Request + response examples
-
-While OpenAPI has several fields for examples, there is no easy way
-to associate a request with a response. This is especially useful when
-you want to show more than one example in your documentation.
-
-`x-fern-examples` is an array of examples. Each element of the array
-can contain `path-parameters`, `query-parameters`, `request` and `response`
-examples values that are all associated.
-
-```yaml title="openapi.yml" {5-16}
-paths:
-  /users/{userId}:
-    get:
-      x-fern-examples:
-        - path-parameters:
-            userId: user-1234
-          response:
-            body:
-              name: Foo
-              ssn: 1234
-        - path-parameters:
-            userId: user-4567
-          response:
-            body:
-              name: Foo
-              ssn: 4567
-components:
-  schemas:
-    User:
-      type: object
-      properties:
-        name:
-          type: string
-        ssn:
-          type: integer
-```
-
-### Code samples
-
-If you'd like to specify custom code samples for your example, use `code-samples`.
-
-```yaml title="openapi.yml" {11-16}
-paths:
-  /users/{userId}:
-    get:
-      x-fern-examples:
-        - path-parameters:
-            userId: user-1234
-          response:
-            body:
-              name: Foo
-              ssn: 1234
-          code-samples:
-            - sdk: typescript
-              code: |
-                import { UserClient } from "...";
-
-                client.users.get("user-1234")
-```
-
-If you're on the Fern Basic plan or higher for SDKs you won't have to worry about manually adding code samples! Our generators do that for you.
-
-## Availability
-
-The `x-fern-availability` extension is used to mark the availability of an endpoint. The availability information propagates into the generated Fern Docs website as visual tags.
-
-The options are:
-
-- `beta`
-- `generally-available`
-- `deprecated`
-
-The example below marks that the `POST /pet` endpoint is `deprecated`.
-
-```yaml title="x-fern-availability in openapi.yml" {4}
-paths:
-  /pet:
-    post:
-      x-fern-availability: deprecated
-```
-
-This renders as:
-
-<Frame caption="A deprecated endpoint">
-![Screenshot of API Reference endpoint with tag showing deprecated](https://fern-image-hosting.s3.amazonaws.com/fern/x-fern-availability-example.png)
-</Frame>
-
-### Request new extensions
-
-If there's an extension you want that doesn't already exist, file an [issue](https://github.com/fern-api/fern/issues/new) to start a discussion about it.
diff --git a/fern/products/api-definition/pages/openapi/extensions/parameter-names.mdx b/fern/products/api-definition/pages/openapi/extensions/parameter-names.mdx
deleted file mode 100644
index ff60bf61..00000000
--- a/fern/products/api-definition/pages/openapi/extensions/parameter-names.mdx
+++ /dev/null
@@ -1,65 +0,0 @@
----
-title: Customize parameter names
-description: Use `x-fern-parameter-name` to customize query parameter, header and path parameter naming.
----
-
-<Note>
-  The `x-fern-parameter-name` extension allows you to customize the variable names of parameters in your generated SDKs.
-</Note>
-
-## Headers
-
-In the example below, the header `X-API-Version` is renamed to `version` in the
-generated SDK. The rename makes the SDK more human readable.
-
-```yaml {8}
-paths:
-  "/user":
-    get:
-      operationId: list_user
-      parameters:
-        - in: header
-          name: X-API-Version
-          x-fern-parameter-name: version
-          schema:
-            type: string
-          required: true
-```
-
-## Query parameters
-
-In the example below, the query parameter `q` is renamed to `search_terms` in the
-generated SDK. The rename makes the parameter more approachable for end users.
-
-```yaml {8}
-paths:
-  "/user/search":
-    get:
-      operationId: search_user
-      parameters:
-        - in: query
-          name: q
-          x-fern-parameter-name: search_terms
-          schema:
-            type: string
-          required: false
-```
-
-## Path parameters
-
-In the example below, the path parameter `userId` is renamed to `id` in the
-generated SDK. The rename makes the SDK less verbose.
-
-```yaml {8}
-paths:
-  "/user/{userId}":
-    get:
-      operationId: get_user
-      parameters:
-        - in: path
-          name: userId
-          x-fern-parameter-name: id
-          schema:
-            type: string
-          required: false
-```
diff --git a/fern/products/api-definition/pages/openapi/overrides.mdx b/fern/products/api-definition/pages/openapi/overrides.mdx
deleted file mode 100644
index 50d6462d..00000000
--- a/fern/products/api-definition/pages/openapi/overrides.mdx
+++ /dev/null
@@ -1,74 +0,0 @@
----
-title: Overlay customizations on an existing OpenAPI spec
-subtitle: Can't directly modify your OpenAPI spec? No worries, use an overrides file instead. 
----
-
-If you generate your OpenAPI from server code, you may want to tweak your OpenAPI Spec without having to 
-touch the generated file.  Fern supports this via an `overrides` file. 
-
-<CodeGroup>
-```yml openapi.yml
-paths: 
- /users: 
-   post: 
-    description: Create a User
-    operationId: users_post
-    requestBody: 
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/User'
-```
-```yml title="overrides.yml" {4-5}
-paths: 
- /users: 
-   post: 
-    x-fern-sdk-group-name: users
-    x-fern-sdk-method-name: create
-```
-```yml title="combined" {4-5}
-paths: 
- /users/post: 
-  post: 
-    x-fern-sdk-group-name: users
-    x-fern-sdk-method-name: create      
-    description: Create a User
-    operationId: users_post
-    requestBody: 
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/User'   
-```
-</CodeGroup>
-
-## Configuration
-
-Follow the steps below to configure your OpenAPI overrides: 
-
-<Steps>
-### Create an `overrides.yml` 
-
-Simply create a yaml file and write down all the overrides you want to add: 
-
-```yaml overrides.yml 
-paths:
-  /v1/history:
-    get:
-      x-fern-sdk-group-name:
-        - history
-      x-fern-sdk-method-name: get_all    
-```
-
-### Reference the file in your `generators.yml` 
-
-```yml generators.yml
-api: 
-  path: ../openapi.yml
-  overrides: ../overrides.yml 
-```
-
-<Note> The format of the overrides file is independent from the spec. For example, even if your OpenAPI spec is in JSON format, you can write the overrides in yaml. </Note>
-
-</Steps>
-
diff --git a/fern/products/api-definition/pages/openapi/overview.mdx b/fern/products/api-definition/pages/openapi/overview.mdx
deleted file mode 100644
index c6cc053e..00000000
--- a/fern/products/api-definition/pages/openapi/overview.mdx
+++ /dev/null
@@ -1,128 +0,0 @@
----
-title: What is an OpenAPI Specification?
-subtitle: OpenAPI is a standard for documenting REST APIs
----
-
-The OpenAPI Specification (OAS) is a framework used by developers to document REST APIs. The specification 
-written in JSON or YAML and contains all of your endpoints, parameters, schemas, and authentication schemes. 
-Fern is compatible with the latest OAS release, which is currently [v3.1.1](https://spec.openapis.org/#openapi-specification).
-
-<Info> Considering options to generate an OpenAPI spec? Get live support [here](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email) </Info>
-
-Below is an example of an OpenAPI file: 
-
-```yaml openapi.yml 
-openapi: 3.0.2
-info:
-  title: Petstore - OpenAPI 3.0
-  description: |-
-    This is a sample Pet Store Server based on the OpenAPI 3.0 specification.
-paths:
-  "/pet":
-    put:
-      tags:
-      - pet
-      summary: Update an existing pet
-      description: Update an existing pet by Id
-      operationId: updatePet
-      requestBody:
-        description: Update an existent pet in the store
-        content:
-          application/json:
-            schema:
-              "$ref": "#/components/schemas/Pet"
-        required: true
-      responses:
-        '200':
-          description: Successful operation
-          content:
-            application/json:
-              schema:
-                "$ref": "#/components/schemas/Pet"
-        '400':
-          description: Invalid ID supplied
-        '404':
-          description: Pet not found
-        '405':
-          description: Validation exception
-      security:
-      - api_key
-components:
-  schemas:
-    Category:
-      type: object
-      properties:
-        id:
-          type: integer
-          format: int64
-          example: 1
-        name:
-          type: string
-          example: Dogs
-    Tag:
-      type: object
-      properties:
-        id:
-          type: integer
-          format: int64
-        name:
-          type: string
-    Pet:
-      required:
-      - name
-      - photoUrls
-      type: object
-      properties:
-        id:
-          type: integer
-          format: int64
-          example: 10
-        name:
-          type: string
-          example: doggie
-        category:
-          "$ref": "#/components/schemas/Category"
-        photoUrls:
-          type: array
-          items:
-            type: string
-        tags:
-          type: array
-          items:
-            "$ref": "#/components/schemas/Tag"
-        status:
-          type: string
-          description: pet status in the store
-          enum:
-          - available
-          - pending
-          - sold
-  securitySchemes:
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-```
-
-## Setup your fern folder
-
-Start by initializing your fern folder with an OpenAPI spec
-
-<CodeGroup>
-```sh file
-fern init --openapi ./path/to/openapi
-```
-```sh url
-fern init --openapi https://host/path/to/openapi
-```
-</CodeGroup>
-
-This will initialize a directory like the following 
-```
-fern/
-  ├─ fern.config.json
-  ├─ generators.yml
-  └─ openapi/
-    ├─ openapi.yml
-```    
-
diff --git a/fern/products/api-definition/pages/openapi/server-frameworks/fastapi.mdx b/fern/products/api-definition/pages/openapi/server-frameworks/fastapi.mdx
deleted file mode 100644
index df5bcbe3..00000000
--- a/fern/products/api-definition/pages/openapi/server-frameworks/fastapi.mdx
+++ /dev/null
@@ -1,92 +0,0 @@
----
-title: FastAPI Instrumentation
-description: Learn about best practices for creating rich OpenAPI Specifications when instrumenting FastAPI applications.
----
-
-[FastAPI](https://fastapi.tiangolo.com/) is a popular Python web framework developed by [tiangolo](https://github.com/tiangolo).
-
-The offering brands itself as
-
-> FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints.
-
-FastAPI plays very nicely with Fern because it has the power to output OpenAPI Specifications! Below we'll outline some tips for generating a rich OpenAPI with FastAPI.
-
-
-## OpenAPI generation
-
-By default, FastAPI will generate an OpenAPI Specification for you based on your routes and your data models! You can access this spec by visiting `/docs` on your FastAPI server.
-
-If you are not seeing any OpenAPI Specification (or the Swagger UI), you may need to review your FastAPI server configuration as the path may have been changed, or completely omitted.
-
-```python {6-8}
-from fastapi import FastAPI
-
-...
-
-FastAPI(
-    openapi_url="/openapi.json",  # <-- this is the file and URL needed to access the OpenAPI Specification, `docs_url` and `redoc_url` are convenient wrappers that display the file in a UI!
-    docs_url="/docs",             # <-- this is the URL to access the Swagger UI, which will point to your OpenAPI Specification
-    redoc_url="/redoc"            # <-- this is the URL to access the ReDoc UI, which will point to your OpenAPI Specification
-)
-```
-
-## Specifying servers
-
-Fern will automatically generate clients that point to the servers you configure within your OpenAPI Specification, so it's important to specify the servers that your API will be hosted on.
-
-```python {5}
-from fastapi import FastAPI
-
-...
-
-app = FastAPI(servers=[{"url": "http://prod.test.com", "description": "Production server"}])
-# This creates the following server object in your OpenAPI Specification:
-# "servers":[{"url":"http://prod.test.com","description":"Production server"}],
-```
-
-## OpenAPI extensions
-
-FastAPI allows you to add in extra OpenAPI configuration directly within your route, through the use of the `openapi_extra` parameter.
-Below, we've annotated a "good" route within FastAPI that has it's typings as well as Fern extensions to assist in naming.
-
-```python {5-9}
-@router.post(
-    "/your/post/endpoint",
-    response_model=YourResponseModel,            #  <-- with FastAPI, it is important to specify your response model so that it comes through to the OpenAPI Specification
-    summary="Get some response for your req",    #  <-- if you'd like to add a description to your endpoint, you can do so here
-    openapi_extra={                              #  <-- finally, you can add in your Fern extensions here, these extensions will produce SDK code that looks something like: `client.endpoints.create(...)` in python
-        "x-fern-sdk-method-name": "create",
-        "x-fern-sdk-group-name": "endpoints",
-        "x-fern-availability": "beta",
-    },
-)
-async def your_post_endpoint(
-    payload: YourRequestModel,
-) -> YourResponseModel:
-```
-
-## Specifying examples
-
-FastAPI allows you to specify examples for your data models, which Fern will pick up and use within your generated SDKs and documentation automatically.
-
-For more information on leveraging examples within Fern, please refer to the [Fern documentation](/learn/api-definition/openapi/extensions/others#request--response-examples).
-
-For more information on this FastAPI functionality, please refer to the [FastAPI documentation](https://fastapi.tiangolo.com/tutorial/schema-extra-example/).
-
-```python {7-11}
-from pydantic import BaseModel
-
-class MyObject(BaseModel):
-    id: str
-
-    class Config:
-        schema_extra = {
-            "example": {
-                "id": "a-cool-uuid",
-            }
-        }
-```
-
-## Additional customization
-
-FastAPI has a lot of flexibility in how you can customize your OpenAPI Specification. Please refer to the [FastAPI documentation](https://fastapi.tiangolo.com/how-to/extending-openapi/#modify-the-openapi-schema) for more information.
diff --git a/fern/products/api-definition/pages/openapi/servers.mdx b/fern/products/api-definition/pages/openapi/servers.mdx
deleted file mode 100644
index 28ce155a..00000000
--- a/fern/products/api-definition/pages/openapi/servers.mdx
+++ /dev/null
@@ -1,84 +0,0 @@
----
-title: Servers
-description: Configure server URLs and environments to help users connect to your API.
-subtitle: Define server URLs and environments to help users connect to your API.
----
-
-OpenAPI allows you to specify one or more base URLs under the `servers` key.
-
-```yml openapi.yml 
-
-servers: 
-  - url: https://api.yourcompany.com/
-  - url: https://api.eu.yourcompany.com/
-```
-
-Specifying servers is valuable for both SDKs and Docs: 
-- For SDKs, your users won't need to manually specify the baseURL at client instantiation
-- For Docs, your API playground will automatically hit the correct server
-
-## Naming your servers
-
-If you have more than one server, we recommend specifying an `x-fern-server-name` to name 
-the server. 
-
-```yml openapi.yml {3,5}
-servers: 
-  - x-fern-server-name: Production
-    url: https://api.yourcompany.com/
-  - x-fern-server-name: Production_EU
-    url: https://api.eu.yourcompany.com/
-```
-
-## Multiple Base URLs for a single API
-
-If you have a microservice architecture, it is possible that you may have different endpoints hosted
-at different URLs. For example, your AI endpoints might be hosted at `ai.yourcompany.com` and the rest 
-of your endpoints might be hosted at `api.yourcompany.com`. 
-
-To specify this, you will need to add configuration to both your `generators.yml` and OpenAPI spec. The 
-snippet directly below shows how to configure an environment with multiple urls in your `generators.yml`. 
-
-```yml generators.yml {3-8}
-api: 
-  default-environment: Production
-  default-url: api
-  environments: 
-    Production: 
-      api: api.yourcompany.com
-      ai: ai.yourcompany.com
-  specs: 
-    - openapi: ./path/to/your/openapi
-      overrides: ./path/to/your/overrides # optional
-```
-
-Once you've specified the environments in your `generators.yml`, you can use the `x-fern-server-name` 
-extension to specify which server the operation belongs to. 
-
-```yml openapi.yml {4}
-paths: 
-  /chat: 
-    post: 
-      x-fern-server-name: ai 
-```
-
-If you have multiple environments like development or staging, you can model those in your `generators.yml` 
-as well. 
-
-```yml generators.yml {7-12}
-api: 
-  default-environment: Production
-  default-url: api
-  environments: 
-    Production: 
-      api: api.yourcompany.com
-      ai: ai.yourcompany.com
-    Staging: 
-      api: api.staging.yourcompany.com
-      ai: ai.staging.yourcompany.com    
-    Dev: 
-      api: api.dev.yourcompany.com
-      ai: ai.dev.yourcompany.com          
-```
-
-<Info>To see an example of this in production, check out the Chariot [generators.yml](https://github.com/chariot-giving/chariot-openapi/blob/main/fern/apis/2025-02-24/generators.yml)</Info>
\ No newline at end of file
diff --git a/fern/products/api-definition/pages/openapi/webhooks.mdx b/fern/products/api-definition/pages/openapi/webhooks.mdx
deleted file mode 100644
index 0bf5a269..00000000
--- a/fern/products/api-definition/pages/openapi/webhooks.mdx
+++ /dev/null
@@ -1,33 +0,0 @@
----
-title: Define Webhooks in OpenAPI
-subtitle: Use the `x-fern-webhook` extension to define webhooks in your OpenAPI spec
----
-
-To define a webhook in your OpenAPI specification, add the `x-fern-webhook: true` extension to your endpoint. OpenAPI 3.0.0 or higher is required. Fern will treat the `requestBody` as the webhook payload. 
-
-```yaml openapi.yml {6}
-paths: 
-  /payment/updated/: 
-    post: 
-      summary: Payment Initiated
-      operationId: initiatePayment
-      x-fern-webhook: true
-      requestBody:
-        content:
-          application/json:
-            schema:
-              type: object
-              properties:
-                amount:
-                  type: number
-                currency:
-                  $ref: '#/components/schemas/Currency'
-              required:
-                - amount
-                - currency 
-```
-
-<Info> 
-The path that you choose when defining a webhook can be arbitrary. Since webhooks 
-can be sent to any server, Fern just ignores the path.
-</Info>

From c0b09ffb725d826afc6194057be56089377b4598 Mon Sep 17 00:00:00 2001
From: Kapil Gowru <kapil-fern@mac.mynetworksettings.com>
Date: Fri, 1 Aug 2025 02:07:58 -0400
Subject: [PATCH 5/7] feat: added scribe to comment what changes it didn't make

---
 .github/scripts/fern-scribe.js | 320 ++++++++++++++++++++++++++++++++-
 1 file changed, 316 insertions(+), 4 deletions(-)

diff --git a/.github/scripts/fern-scribe.js b/.github/scripts/fern-scribe.js
index f21b2be7..de11424d 100644
--- a/.github/scripts/fern-scribe.js
+++ b/.github/scripts/fern-scribe.js
@@ -126,6 +126,9 @@ class FernScribeGitHub {
     this.urlMapper = new FernUrlMapper(process.env.GITHUB_TOKEN, process.env.REPOSITORY);
     this.productSlugToDir = parseProductRootMapping();
     this.learnToFile = parseLearnToFileMapping();
+    
+    // Track files that failed MDX validation
+    this.mdxValidationFailures = [];
   }
 
   async init() {
@@ -642,6 +645,17 @@ class FernScribeGitHub {
   }
 
   async generateContent(filePath, existingContent, context, fernStructure) {
+    // Check if content needs chunking
+    const CHUNK_THRESHOLD = 12000; // Chars threshold to decide when to chunk
+    if (existingContent.length <= CHUNK_THRESHOLD) {
+      return this.generateSingleContent(filePath, existingContent, context, fernStructure);
+    } else {
+      console.log(`   📊 Large file detected (${existingContent.length} chars) - using chunked processing`);
+      return this.generateChunkedContent(filePath, existingContent, context, fernStructure);  
+    }
+  }
+
+  async generateSingleContent(filePath, existingContent, context, fernStructure) {
     const prompt = `${this.systemPrompt}
 
 ## Context
@@ -713,6 +727,159 @@ Complete updated file content:`;
     }
   }
 
+  async generateChunkedContent(filePath, existingContent, context, fernStructure) {
+    const chunks = this.chunkContent(existingContent, 8000);
+    const updatedChunks = [];
+    let hasChanges = false;
+
+    console.log(`   🧩 Processing ${chunks.length} chunks for ${filePath}`);
+
+    for (let i = 0; i < chunks.length; i++) {
+      const chunk = chunks[i];
+      console.log(`   📝 Processing chunk ${i + 1}/${chunks.length}${chunk.section ? ` (${chunk.section})` : ''}`);
+
+      const chunkPrompt = `${this.systemPrompt}
+
+## Context
+File: ${filePath}
+Chunk: ${i + 1} of ${chunks.length}${chunk.section ? ` - Section: "${chunk.section}"` : ''}
+Request: ${context.requestDescription}
+Existing Instructions: ${context.existingInstructions}
+Why Current Approach Doesn't Work: ${context.whyNotWork}
+Additional Context: ${context.additionalContext}
+${context.slackThreadContent ? `\n## Slack Discussion Context\n${context.slackThreadContent}` : ''}
+
+## Fern Docs Structure Reference
+${fernStructure}
+
+## Current Chunk Content
+${chunk.content}
+
+## Instructions
+${chunk.isComplete ? 
+  'This is the final chunk of the file. Update this section to address the documentation request.' :
+  `This is chunk ${i + 1} of ${chunks.length} from a larger file. Update only this section as needed to address the documentation request. Do not add or remove section headers unless specifically needed for this chunk.`
+}
+
+Focus on:
+- Addressing the specific documentation gaps mentioned in the request
+- Improving clarity and completeness within this chunk
+- Maintaining consistency with Fern documentation patterns
+- Preserving the existing structure and flow
+
+CRITICAL MDX SYNTAX REQUIREMENTS:
+- ALL opening tags MUST have corresponding closing tags (e.g., <ParamField> must have </ParamField>)
+- Self-closing tags must use proper syntax (e.g., <ParamField param="value" />)
+- Preserve existing MDX component structure exactly
+- When adding new ParamField, CodeBlock, or other components, ensure they are properly closed
+- Check that every < has a matching >
+- Validate that nested components are properly structured
+
+IMPORTANT: Return ONLY the updated chunk content. Do not include any explanatory text, meta-commentary, or descriptions about what you're doing.
+
+Updated chunk content:`;
+
+      try {
+        const response = await httpRequest('https://api.anthropic.com/v1/messages', {
+          method: 'POST',
+          headers: {
+            'x-api-key': this.anthropicApiKey,
+            'content-type': 'application/json',
+            'anthropic-version': '2023-06-01'
+          },
+          body: JSON.stringify({
+            model: 'claude-3-5-sonnet-20241022',
+            max_tokens: 4096,
+            messages: [{
+              role: 'user',
+              content: chunkPrompt
+            }]
+          })
+        });
+
+        if (!response.ok) {
+          const errorText = await response.text();
+          console.error(`❌ Anthropic API error for chunk ${i + 1}:`, errorText);
+          updatedChunks.push(chunk.content); // Use original chunk
+          continue;
+        }
+
+        const data = await response.json();
+        const updatedChunkContent = data.content[0]?.text || chunk.content;
+        
+        // Validate the chunk
+        const validationResult = this.validateMDXContent(updatedChunkContent);
+        if (!validationResult.isValid) {
+          console.warn(`⚠️  MDX validation warnings for chunk ${i + 1}:`, validationResult.warnings);
+          updatedChunks.push(chunk.content); // Use original chunk if validation fails
+        } else {
+          updatedChunks.push(updatedChunkContent);
+          if (updatedChunkContent !== chunk.content) {
+            hasChanges = true;
+            console.log(`   ✅ Updated chunk ${i + 1} (${chunk.content.length} → ${updatedChunkContent.length} chars)`);
+          } else {
+            console.log(`   ℹ️  No changes for chunk ${i + 1}`);
+          }
+        }
+
+      } catch (error) {
+        console.error(`❌ Error processing chunk ${i + 1}:`, error.message);
+        updatedChunks.push(chunk.content); // Use original chunk
+      }
+
+      // Add a small delay between chunks to be respectful to the API
+      if (i < chunks.length - 1) {
+        await new Promise(resolve => setTimeout(resolve, 1000));
+      }
+    }
+
+    // Reassemble the chunks
+    const finalContent = this.reassembleChunks(updatedChunks, chunks);
+    
+    console.log(`   🔧 Reassembled content: ${existingContent.length} → ${finalContent.length} chars`);
+    
+    return hasChanges ? finalContent : existingContent;
+  }
+
+  reassembleChunks(updatedChunks, originalChunks) {
+    // If there's only one chunk, return it directly
+    if (updatedChunks.length === 1) {
+      return updatedChunks[0];
+    }
+
+    // For multiple chunks, we need to carefully reassemble
+    let reassembled = '';
+    
+    for (let i = 0; i < updatedChunks.length; i++) {
+      const chunk = updatedChunks[i];
+      const originalChunk = originalChunks[i];
+      
+      if (i === 0) {
+        // First chunk should include frontmatter if present
+        reassembled = chunk;
+      } else {
+        // For subsequent chunks, remove frontmatter if it was duplicated
+        let cleanChunk = chunk;
+        if (cleanChunk.startsWith('---\n') && reassembled.includes('---\n')) {
+          // Remove frontmatter from subsequent chunks
+          const frontmatterEnd = cleanChunk.indexOf('---\n', 4);
+          if (frontmatterEnd !== -1) {
+            cleanChunk = cleanChunk.substring(frontmatterEnd + 4);
+          }
+        }
+        
+        // Add proper spacing between chunks
+        if (reassembled.trim() && cleanChunk.trim()) {
+          reassembled += '\n\n' + cleanChunk;
+        } else {
+          reassembled += cleanChunk;
+        }
+      }
+    }
+
+    return reassembled;
+  }
+
   // Basic MDX validation to catch common issues
   validateMDXContent(content) {
     const warnings = [];
@@ -747,6 +914,109 @@ Complete updated file content:`;
     };
   }
 
+  // Intelligent content chunking for large files
+  chunkContent(content, maxChunkSize = 8000) {
+    // If content is small enough, return as single chunk
+    if (content.length <= maxChunkSize) {
+      return [{ content, isComplete: true, chunkIndex: 0, totalChunks: 1 }];
+    }
+
+    const chunks = [];
+    const lines = content.split('\n');
+    let currentChunk = '';
+    let frontmatter = '';
+    let inFrontmatter = false;
+    let frontmatterEnded = false;
+
+    // Extract frontmatter first
+    if (lines[0] === '---') {
+      inFrontmatter = true;
+      for (let i = 0; i < lines.length; i++) {
+        if (i > 0 && lines[i] === '---') {
+          inFrontmatter = false;
+          frontmatterEnded = true;
+          frontmatter = lines.slice(0, i + 1).join('\n') + '\n';
+          break;
+        }
+      }
+    }
+
+    // Start processing from after frontmatter
+    const startIndex = frontmatterEnded ? lines.findIndex((line, idx) => idx > 0 && line === '---') + 1 : 0;
+    const contentLines = lines.slice(startIndex);
+
+    let sectionBuffer = [];
+    let currentSection = null;
+
+    for (let i = 0; i < contentLines.length; i++) {
+      const line = contentLines[i];
+      
+      // Detect section headers (## or ###)
+      if (line.match(/^#{2,3}\s+/)) {
+        // If we have accumulated content and adding this section would exceed limit
+        if (sectionBuffer.length > 0 && (currentChunk + sectionBuffer.join('\n')).length > maxChunkSize) {
+          // Save current chunk
+          chunks.push({
+            content: (chunks.length === 0 ? frontmatter : '') + currentChunk.trim(),
+            isComplete: false,
+            chunkIndex: chunks.length,
+            section: currentSection,
+            hasMore: true
+          });
+          currentChunk = '';
+          currentSection = null;
+        }
+        
+        // Start new section
+        currentSection = line.replace(/^#+\s+/, '').trim();
+        sectionBuffer = [line];
+      } else {
+        sectionBuffer.push(line);
+      }
+
+      // Check if we need to break at this point
+      const potentialChunk = currentChunk + sectionBuffer.join('\n') + '\n';
+      if (potentialChunk.length > maxChunkSize && currentChunk.length > 0) {
+        // Save current chunk without the current section
+        chunks.push({
+          content: (chunks.length === 0 ? frontmatter : '') + currentChunk.trim(),
+          isComplete: false,
+          chunkIndex: chunks.length,
+          section: chunks.length > 0 ? currentSection : null,
+          hasMore: true
+        });
+        currentChunk = sectionBuffer.join('\n') + '\n';
+        sectionBuffer = [];
+      } else {
+        currentChunk += sectionBuffer.join('\n') + '\n';
+        sectionBuffer = [];
+      }
+    }
+
+    // Add remaining content as final chunk
+    if (currentChunk.trim()) {
+      chunks.push({
+        content: (chunks.length === 0 ? frontmatter : '') + currentChunk.trim(),
+        isComplete: true,
+        chunkIndex: chunks.length,
+        section: currentSection,
+        hasMore: false
+      });
+    }
+
+    // Update totalChunks for all chunks
+    chunks.forEach(chunk => {
+      chunk.totalChunks = chunks.length;
+    });
+
+    console.log(`   📊 Split content into ${chunks.length} chunks (${content.length} chars total)`);
+    chunks.forEach((chunk, i) => {
+      console.log(`      Chunk ${i + 1}: ${chunk.content.length} chars${chunk.section ? ` (${chunk.section})` : ''}`);
+    });
+
+    return chunks;
+  }
+
   async analyzeDocumentationNeeds(context) {
     if (!this.anthropicApiKey) {
       console.log('⚠️ No Anthropic API key provided - skipping documentation analysis');
@@ -1227,7 +1497,9 @@ Changelog entry:`;
 
   async createPullRequest(branchName, context, filesUpdated) {
     const title = `🌿 Fern Scribe: ${context.requestDescription.substring(0, 50)}...`;
-    const body = `## 🌿 Fern Scribe Documentation Update
+    
+    // Build the main PR body
+    let body = `## 🌿 Fern Scribe Documentation Update
 
 **Original Request:** ${context.requestDescription}
 
@@ -1238,9 +1510,37 @@ ${filesUpdated.map(file => `- \`${file}\``).join('\n')}
 
 ${context.slackThread ? `**Related Discussion:** ${context.slackThread}` : ''}
 
-${context.additionalContext ? `**Additional Context:** ${context.additionalContext}` : ''}
+${context.additionalContext ? `**Additional Context:** ${context.additionalContext}` : ''}`;
 
----
+    // Add section for files that failed MDX validation
+    if (this.mdxValidationFailures.length > 0) {
+      body += `\n\n## ⚠️ Files with MDX Validation Issues
+
+The following files could not be updated due to MDX validation failures after 3 attempts:
+
+${this.mdxValidationFailures.map((failure, index) => {
+  const warnings = failure.warnings.map(w => `  - ${w}`).join('\n');
+  const truncatedContent = failure.suggestedContent && failure.suggestedContent.length > 4000 
+    ? failure.suggestedContent.substring(0, 4000) + '\n\n... [Content truncated due to length]'
+    : failure.suggestedContent;
+  
+  return `### ${index + 1}. **\`${failure.filePath}\`** (${failure.title || 'Untitled'})
+
+- **URL**: ${failure.url || 'N/A'}
+- **Validation Issues**:
+${warnings}
+
+**Suggested Content** (needs manual MDX fixes):
+
+\`\`\`mdx
+${truncatedContent || 'No suggested content available'}
+\`\`\``;
+}).join('\n\n')}
+
+**Note**: These files require manual review and correction of their MDX component structure before the content can be applied.`;
+    }
+
+    body += `\n\n---
 *This PR was automatically generated by Fern Scribe based on issue #${this.issueNumber}*
 
 **Please review the changes carefully before merging.**`;
@@ -1397,8 +1697,20 @@ ${context.additionalContext ? `**Additional Context:** ${context.additionalConte
             }
           }
           if (!valid) {
-            const msg = `❌ Skipping file due to invalid MDX after 3 attempts: ${filePath}\nWarnings: ${JSON.stringify(this.validateMDXContent(suggestedContent).warnings)}`;
+            const validationResult = this.validateMDXContent(suggestedContent);
+            const msg = `❌ Skipping file due to invalid MDX after 3 attempts: ${filePath}\nWarnings: ${JSON.stringify(validationResult.warnings)}`;
             console.warn(msg);
+            
+            // Track this failure for the PR description
+            this.mdxValidationFailures.push({
+              filePath,
+              warnings: validationResult.warnings,
+              attempts: 3,
+              url: result.url,
+              title: result.title,
+              suggestedContent: suggestedContent // Store the suggested content despite validation issues
+            });
+            
             // If running in GitHub Actions, comment on the issue
             if (process.env.GITHUB_TOKEN && process.env.REPOSITORY && process.env.ISSUE_NUMBER) {
               const [owner, repo] = process.env.REPOSITORY.split('/');

From c25e166fc1b35a9f961e3ba9a6d18f34e8003894 Mon Sep 17 00:00:00 2001
From: Kapil Gowru <kapil-fern@mac.mynetworksettings.com>
Date: Fri, 1 Aug 2025 10:28:39 -0400
Subject: [PATCH 6/7] feat: added env tokens

---
 .github/workflows/fern-scribe.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.github/workflows/fern-scribe.yml b/.github/workflows/fern-scribe.yml
index 99ebdb93..a758b3fd 100644
--- a/.github/workflows/fern-scribe.yml
+++ b/.github/workflows/fern-scribe.yml
@@ -34,6 +34,9 @@ jobs:
 
       # --- NEW STEP: Generate my-mappings.md ---
       - name: Generate Fern URL Mappings
+        env: 
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          REPOSITORY: ${{ github.repository }}
         run: |
           cd .github/scripts
           node generate-mappings.js

From 899e1f57a6b3c5e714fa773eb2149d3b0f6dc966 Mon Sep 17 00:00:00 2001
From: Kapil Gowru <kapil-fern@mac.mynetworksettings.com>
Date: Fri, 1 Aug 2025 10:39:30 -0400
Subject: [PATCH 7/7] feat: clean node modules

---
 .github/workflows/fern-scribe.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/fern-scribe.yml b/.github/workflows/fern-scribe.yml
index a758b3fd..10cbf016 100644
--- a/.github/workflows/fern-scribe.yml
+++ b/.github/workflows/fern-scribe.yml
@@ -27,9 +27,10 @@ jobs:
           cache: 'npm'
           cache-dependency-path: .github/scripts/package.json
 
-      - name: Install dependencies
+      - name: Clean install dependencies
         run: |
           cd .github/scripts
+          rm -rf node_modules package-lock.json
           npm install
 
       # --- NEW STEP: Generate my-mappings.md ---
