diff --git a/services/platform/convex/_generated/api.d.ts b/services/platform/convex/_generated/api.d.ts
index 57084e314..1987ba122 100644
--- a/services/platform/convex/_generated/api.d.ts
+++ b/services/platform/convex/_generated/api.d.ts
@@ -287,6 +287,8 @@ import type * as model_integrations_update_integration_internal from "../model/i
 import type * as model_integrations_update_integration_logic from "../model/integrations/update_integration_logic.js";
 import type * as model_integrations_update_sync_stats from "../model/integrations/update_sync_stats.js";
 import type * as model_integrations_validators from "../model/integrations/validators.js";
+import type * as model_members_index from "../model/members/index.js";
+import type * as model_members_validators from "../model/members/validators.js";
 import type * as model_onedrive_create_sync_configs_logic from "../model/onedrive/create_sync_configs_logic.js";
 import type * as model_onedrive_get_user_token_logic from "../model/onedrive/get_user_token_logic.js";
 import type * as model_onedrive_index from "../model/onedrive/index.js";
@@ -296,6 +298,7 @@ import type * as model_onedrive_refresh_token_logic from "../model/onedrive/refr
 import type * as model_onedrive_update_sync_config_logic from "../model/onedrive/update_sync_config_logic.js";
 import type * as model_onedrive_upload_and_create_document_logic from "../model/onedrive/upload_and_create_document_logic.js";
 import type * as model_onedrive_upload_to_storage_logic from "../model/onedrive/upload_to_storage_logic.js";
+import type * as model_onedrive_validators from "../model/onedrive/validators.js";
 import type * as model_organizations_create_organization from "../model/organizations/create_organization.js";
 import type * as model_organizations_delete_organization from "../model/organizations/delete_organization.js";
 import type * as model_organizations_delete_organization_logo from "../model/organizations/delete_organization_logo.js";
@@ -331,6 +334,7 @@ import type * as model_threads_get_thread_messages_streaming from "../model/thre
 import type * as model_threads_index from "../model/threads/index.js";
 import type * as model_threads_list_threads from "../model/threads/list_threads.js";
 import type * as model_threads_update_chat_thread from "../model/threads/update_chat_thread.js";
+import type * as model_threads_validators from "../model/threads/validators.js";
 import type * as model_tone_of_voice_add_example_message from "../model/tone_of_voice/add_example_message.js";
 import type * as model_tone_of_voice_delete_example_message from "../model/tone_of_voice/delete_example_message.js";
 import type * as model_tone_of_voice_generate_tone_of_voice from "../model/tone_of_voice/generate_tone_of_voice.js";
@@ -357,6 +361,8 @@ import type * as model_users_get_user_by_email from "../model/users/get_user_by_
 import type * as model_users_has_any_users from "../model/users/has_any_users.js";
 import type * as model_users_index from "../model/users/index.js";
 import type * as model_users_update_user_password from "../model/users/update_user_password.js";
+import type * as model_vendors_index from "../model/vendors/index.js";
+import type * as model_vendors_validators from "../model/vendors/validators.js";
 import type * as model_websites_bulk_create_websites from "../model/websites/bulk_create_websites.js";
 import type * as model_websites_bulk_upsert_pages from "../model/websites/bulk_upsert_pages.js";
 import type * as model_websites_create_website from "../model/websites/create_website.js";
@@ -977,6 +983,8 @@ declare const fullApi: ApiFromModules<{
   "model/integrations/update_integration_logic": typeof model_integrations_update_integration_logic;
   "model/integrations/update_sync_stats": typeof model_integrations_update_sync_stats;
   "model/integrations/validators": typeof model_integrations_validators;
+  "model/members/index": typeof model_members_index;
+  "model/members/validators": typeof model_members_validators;
   "model/onedrive/create_sync_configs_logic": typeof model_onedrive_create_sync_configs_logic;
   "model/onedrive/get_user_token_logic": typeof model_onedrive_get_user_token_logic;
   "model/onedrive/index": typeof model_onedrive_index;
@@ -986,6 +994,7 @@ declare const fullApi: ApiFromModules<{
   "model/onedrive/update_sync_config_logic": typeof model_onedrive_update_sync_config_logic;
   "model/onedrive/upload_and_create_document_logic": typeof model_onedrive_upload_and_create_document_logic;
   "model/onedrive/upload_to_storage_logic": typeof model_onedrive_upload_to_storage_logic;
+  "model/onedrive/validators": typeof model_onedrive_validators;
   "model/organizations/create_organization": typeof model_organizations_create_organization;
   "model/organizations/delete_organization": typeof model_organizations_delete_organization;
   "model/organizations/delete_organization_logo": typeof model_organizations_delete_organization_logo;
@@ -1021,6 +1030,7 @@ declare const fullApi: ApiFromModules<{
   "model/threads/index": typeof model_threads_index;
   "model/threads/list_threads": typeof model_threads_list_threads;
   "model/threads/update_chat_thread": typeof model_threads_update_chat_thread;
+  "model/threads/validators": typeof model_threads_validators;
   "model/tone_of_voice/add_example_message": typeof model_tone_of_voice_add_example_message;
   "model/tone_of_voice/delete_example_message": typeof model_tone_of_voice_delete_example_message;
   "model/tone_of_voice/generate_tone_of_voice": typeof model_tone_of_voice_generate_tone_of_voice;
@@ -1047,6 +1057,8 @@ declare const fullApi: ApiFromModules<{
   "model/users/has_any_users": typeof model_users_has_any_users;
   "model/users/index": typeof model_users_index;
   "model/users/update_user_password": typeof model_users_update_user_password;
+  "model/vendors/index": typeof model_vendors_index;
+  "model/vendors/validators": typeof model_vendors_validators;
   "model/websites/bulk_create_websites": typeof model_websites_bulk_create_websites;
   "model/websites/bulk_upsert_pages": typeof model_websites_bulk_upsert_pages;
   "model/websites/create_website": typeof model_websites_create_website;
diff --git a/services/platform/convex/documents.ts b/services/platform/convex/documents.ts
index 9fedb1f27..51ae923aa 100644
--- a/services/platform/convex/documents.ts
+++ b/services/platform/convex/documents.ts
@@ -26,6 +26,30 @@ import {
   documentItemValidator as DocumentItem,
   documentListResponseValidator as DocumentListResponse,
   documentRecordValidator as DocumentRecord,
+  sourceProviderValidator,
+  excelSheetValidator,
+  uploadBase64ResponseValidator,
+  readFileBase64ResponseValidator,
+  sourceTypeValidator,
+  outputFormatValidator,
+  pdfOptionsValidator,
+  imageOptionsValidator,
+  urlOptionsValidator,
+  generateDocumentResponseValidator,
+  membershipValidator,
+  signedUrlResponseValidator,
+  documentByExtensionItemValidator,
+  deleteFromRagResponseValidator,
+  uploadFileResponseValidator,
+  createOneDriveSyncConfigResponseValidator,
+  analyzePptxResponseValidator,
+  slideContentDataValidator,
+  pptxBrandingDataValidator,
+  generatePptxResponseValidator,
+  docxContentValidator,
+  generateDocxResponseValidator,
+  retryRagIndexingResponseValidator,
+  sortOrderValidator,
 } from './model/documents/validators';
 import { ragAction } from './workflow/actions/rag/rag_action';
 
@@ -46,9 +70,7 @@ export const createDocument = internalMutation({
     mimeType: v.optional(v.string()),
     extension: v.optional(v.string()),
     metadata: v.optional(v.any()),
-    sourceProvider: v.optional(
-      v.union(v.literal('onedrive'), v.literal('upload')),
-    ),
+    sourceProvider: v.optional(sourceProviderValidator),
     externalItemId: v.optional(v.string()),
   },
   returns: v.object({
@@ -73,14 +95,7 @@ export const uploadBase64Internal = internalAction({
     contentType: v.string(),
     dataBase64: v.string(),
   },
-  returns: v.object({
-    success: v.boolean(),
-    fileId: v.id('_storage'),
-    url: v.string(),
-    fileName: v.string(),
-    size: v.number(),
-    contentType: v.string(),
-  }),
+  returns: uploadBase64ResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.uploadBase64ToStorage(ctx, args);
   },
@@ -97,15 +112,7 @@ export const uploadBase64Internal = internalAction({
 export const generateExcelInternal = internalAction({
   args: {
     fileName: v.string(),
-    sheets: v.array(
-      v.object({
-        name: v.string(),
-        headers: v.array(v.string()),
-        rows: v.array(
-          v.array(v.union(v.string(), v.number(), v.boolean(), v.null())),
-        ),
-      }),
-    ),
+    sheets: v.array(excelSheetValidator),
   },
   returns: v.object({
     success: v.boolean(),
@@ -173,13 +180,7 @@ export const readFileBase64Internal = internalAction({
   args: {
     fileId: v.id('_storage'),
   },
-  returns: v.object({
-    success: v.boolean(),
-    fileId: v.id('_storage'),
-    dataBase64: v.string(),
-    contentType: v.string(),
-    size: v.number(),
-  }),
+  returns: readFileBase64ResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.readFileBase64FromStorage(ctx, args);
   },
@@ -193,59 +194,16 @@ export const readFileBase64Internal = internalAction({
 export const generateDocumentInternal = internalAction({
   args: {
     fileName: v.string(),
-    sourceType: v.union(
-      v.literal('markdown'),
-      v.literal('html'),
-      v.literal('url'),
-    ),
-    outputFormat: v.union(v.literal('pdf'), v.literal('image')),
+    sourceType: sourceTypeValidator,
+    outputFormat: outputFormatValidator,
     content: v.string(),
-    pdfOptions: v.optional(
-      v.object({
-        format: v.optional(v.string()),
-        landscape: v.optional(v.boolean()),
-        marginTop: v.optional(v.string()),
-        marginBottom: v.optional(v.string()),
-        marginLeft: v.optional(v.string()),
-        marginRight: v.optional(v.string()),
-        printBackground: v.optional(v.boolean()),
-      }),
-    ),
-    imageOptions: v.optional(
-      v.object({
-        imageType: v.optional(v.string()),
-        quality: v.optional(v.number()),
-        fullPage: v.optional(v.boolean()),
-        width: v.optional(v.number()),
-        height: v.optional(v.number()),
-        scale: v.optional(v.number()),
-      }),
-    ),
-    urlOptions: v.optional(
-      v.object({
-        waitUntil: v.optional(
-          v.union(
-            v.literal('load'),
-            v.literal('domcontentloaded'),
-            v.literal('networkidle'),
-            v.literal('commit'),
-          ),
-        ),
-        timeout: v.optional(v.number()),
-      }),
-    ),
+    pdfOptions: v.optional(pdfOptionsValidator),
+    imageOptions: v.optional(imageOptionsValidator),
+    urlOptions: v.optional(urlOptionsValidator),
     extraCss: v.optional(v.string()),
     wrapInTemplate: v.optional(v.boolean()),
   },
-  returns: v.object({
-    success: v.boolean(),
-    fileId: v.id('_storage'),
-    url: v.string(),
-    fileName: v.string(),
-    contentType: v.string(),
-    extension: v.string(),
-    size: v.number(),
-  }),
+  returns: generateDocumentResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.generateDocument(ctx, args);
   },
@@ -270,9 +228,7 @@ export const getDocumentById = internalQuery({
 export const queryDocuments = internalQuery({
   args: {
     organizationId: v.string(),
-    sourceProvider: v.optional(
-      v.union(v.literal('onedrive'), v.literal('upload')),
-    ),
+    sourceProvider: v.optional(sourceProviderValidator),
     paginationOpts: cursorPaginationOptsValidator,
   },
   returns: v.object({
@@ -314,9 +270,7 @@ export const updateDocumentInternal = internalMutation({
     fileId: v.optional(v.id('_storage')),
     mimeType: v.optional(v.string()),
     extension: v.optional(v.string()),
-    sourceProvider: v.optional(
-      v.union(v.literal('onedrive'), v.literal('upload')),
-    ),
+    sourceProvider: v.optional(sourceProviderValidator),
     externalItemId: v.optional(v.string()),
   },
   returns: v.null(),
@@ -334,15 +288,7 @@ export const checkMembership = internalQuery({
     organizationId: v.string(),
     userId: v.string(),
   },
-  returns: v.union(
-    v.null(),
-    v.object({
-      _id: v.string(),
-      organizationId: v.string(),
-      identityId: v.optional(v.string()),
-      role: v.optional(v.string()),
-    }),
-  ),
+  returns: v.union(v.null(), membershipValidator),
   handler: async (ctx, args) => {
     return await DocumentsModel.checkMembership(ctx, args);
   },
@@ -355,16 +301,7 @@ export const generateSignedUrl = internalQuery({
   args: {
     documentId: v.id('documents'),
   },
-  returns: v.union(
-    v.object({
-      success: v.boolean(),
-      url: v.string(),
-    }),
-    v.object({
-      success: v.boolean(),
-      error: v.string(),
-    }),
-  ),
+  returns: signedUrlResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.generateSignedUrl(ctx, args.documentId);
   },
@@ -382,17 +319,7 @@ export const listDocumentsByExtension = internalQuery({
     extension: v.string(),
     limit: v.optional(v.number()),
   },
-  returns: v.array(
-    v.object({
-      _id: v.id('documents'),
-      _creationTime: v.number(),
-      title: v.optional(v.string()),
-      fileId: v.optional(v.id('_storage')),
-      mimeType: v.optional(v.string()),
-      extension: v.optional(v.string()),
-      metadata: v.optional(v.any()),
-    }),
-  ),
+  returns: v.array(documentByExtensionItemValidator),
   handler: async (ctx, args) => {
     return await DocumentsModel.listDocumentsByExtension(ctx, args);
   },
@@ -410,13 +337,7 @@ export const deleteDocumentFromRagInternal = internalAction({
     documentId: v.string(),
     mode: v.optional(v.union(v.literal('soft'), v.literal('hard'))),
   },
-  returns: v.object({
-    success: v.boolean(),
-    deletedCount: v.number(),
-    deletedDataIds: v.array(v.string()),
-    message: v.string(),
-    error: v.optional(v.string()),
-  }),
+  returns: deleteFromRagResponseValidator,
   handler: async (_ctx, args) => {
     const { getRagConfig } = await import(
       './workflow/actions/rag/helpers/get_rag_config'
@@ -484,7 +405,7 @@ export const getDocuments = queryWithRLS({
     query: v.optional(v.string()),
     folderPath: v.optional(v.string()),
     sortField: v.optional(v.string()),
-    sortOrder: v.optional(v.union(v.literal('asc'), v.literal('desc'))),
+    sortOrder: v.optional(sortOrderValidator),
   },
   returns: DocumentListResponse,
   handler: async (ctx, args) => {
@@ -612,12 +533,7 @@ export const uploadFile = action({
     contentType: v.string(),
     metadata: v.optional(v.any()),
   },
-  returns: v.object({
-    success: v.boolean(),
-    fileId: v.optional(v.string()),
-    documentId: v.optional(v.string()),
-    error: v.optional(v.string()),
-  }),
+  returns: uploadFileResponseValidator,
   handler: async (
     ctx,
     args,
@@ -752,11 +668,7 @@ export const createOneDriveSyncConfig = mutationWithRLS({
     targetBucket: v.string(),
     storagePrefix: v.optional(v.string()),
   },
-  returns: v.object({
-    success: v.boolean(),
-    configId: v.optional(v.string()),
-    error: v.optional(v.string()),
-  }),
+  returns: createOneDriveSyncConfigResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.createOneDriveSyncConfig(ctx, args);
   },
@@ -773,57 +685,7 @@ export const analyzePptxInternal = internalAction({
   args: {
     templateStorageId: v.id('_storage'),
   },
-  returns: v.object({
-    success: v.boolean(),
-    slideCount: v.number(),
-    slides: v.array(
-      v.object({
-        slideNumber: v.number(),
-        layoutName: v.string(),
-        title: v.union(v.string(), v.null()),
-        subtitle: v.union(v.string(), v.null()),
-        textContent: v.array(
-          v.object({
-            text: v.string(),
-            isPlaceholder: v.boolean(),
-          }),
-        ),
-        tables: v.array(
-          v.object({
-            rowCount: v.number(),
-            columnCount: v.number(),
-            headers: v.array(v.string()),
-            rows: v.array(v.array(v.string())),
-          }),
-        ),
-        charts: v.array(
-          v.object({
-            chartType: v.string(),
-            hasLegend: v.optional(v.boolean()),
-            seriesCount: v.optional(v.number()),
-          }),
-        ),
-        images: v.array(
-          v.object({
-            width: v.optional(v.number()),
-            height: v.optional(v.number()),
-          }),
-        ),
-      }),
-    ),
-    availableLayouts: v.array(v.string()),
-    branding: v.object({
-      slideWidth: v.optional(v.number()),
-      slideHeight: v.optional(v.number()),
-      titleFontName: v.optional(v.union(v.string(), v.null())),
-      bodyFontName: v.optional(v.union(v.string(), v.null())),
-      titleFontSize: v.optional(v.number()),
-      bodyFontSize: v.optional(v.number()),
-      primaryColor: v.optional(v.union(v.string(), v.null())),
-      secondaryColor: v.optional(v.union(v.string(), v.null())),
-      accentColor: v.optional(v.union(v.string(), v.null())),
-    }),
-  }),
+  returns: analyzePptxResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.analyzePptx(ctx, {
       templateStorageId: args.templateStorageId,
@@ -840,45 +702,11 @@ export const analyzePptxInternal = internalAction({
 export const generatePptxInternal = internalAction({
   args: {
     fileName: v.string(),
-    slidesContent: v.array(
-      v.object({
-        title: v.optional(v.string()),
-        subtitle: v.optional(v.string()),
-        textContent: v.optional(v.array(v.string())),
-        bulletPoints: v.optional(v.array(v.string())),
-        tables: v.optional(
-          v.array(
-            v.object({
-              headers: v.array(v.string()),
-              rows: v.array(v.array(v.string())),
-            }),
-          ),
-        ),
-      }),
-    ),
-    branding: v.optional(
-      v.object({
-        slideWidth: v.optional(v.number()),
-        slideHeight: v.optional(v.number()),
-        titleFontName: v.optional(v.string()),
-        bodyFontName: v.optional(v.string()),
-        titleFontSize: v.optional(v.number()),
-        bodyFontSize: v.optional(v.number()),
-        primaryColor: v.optional(v.string()),
-        secondaryColor: v.optional(v.string()),
-        accentColor: v.optional(v.string()),
-      }),
-    ),
+    slidesContent: v.array(slideContentDataValidator),
+    branding: v.optional(pptxBrandingDataValidator),
     templateStorageId: v.id('_storage'),
   },
-  returns: v.object({
-    success: v.boolean(),
-    fileId: v.id('_storage'),
-    url: v.string(),
-    fileName: v.string(),
-    contentType: v.string(),
-    size: v.number(),
-  }),
+  returns: generatePptxResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.generatePptx(ctx, {
       fileName: args.fileName,
@@ -895,37 +723,9 @@ export const generatePptxInternal = internalAction({
 export const generateDocxInternal = internalAction({
   args: {
     fileName: v.string(),
-    content: v.object({
-      title: v.optional(v.string()),
-      subtitle: v.optional(v.string()),
-      sections: v.array(
-        v.object({
-          type: v.union(
-            v.literal('heading'),
-            v.literal('paragraph'),
-            v.literal('bullets'),
-            v.literal('numbered'),
-            v.literal('table'),
-            v.literal('quote'),
-            v.literal('code'),
-          ),
-          text: v.optional(v.string()),
-          level: v.optional(v.number()),
-          items: v.optional(v.array(v.string())),
-          headers: v.optional(v.array(v.string())),
-          rows: v.optional(v.array(v.array(v.string()))),
-        }),
-      ),
-    }),
+    content: docxContentValidator,
   },
-  returns: v.object({
-    success: v.boolean(),
-    fileId: v.id('_storage'),
-    url: v.string(),
-    fileName: v.string(),
-    contentType: v.string(),
-    size: v.number(),
-  }),
+  returns: generateDocxResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.generateDocx(ctx, {
       fileName: args.fileName,
@@ -945,38 +745,10 @@ export const generateDocxInternal = internalAction({
 export const generateDocxFromTemplateInternal = internalAction({
   args: {
     fileName: v.string(),
-    content: v.object({
-      title: v.optional(v.string()),
-      subtitle: v.optional(v.string()),
-      sections: v.array(
-        v.object({
-          type: v.union(
-            v.literal('heading'),
-            v.literal('paragraph'),
-            v.literal('bullets'),
-            v.literal('numbered'),
-            v.literal('table'),
-            v.literal('quote'),
-            v.literal('code'),
-          ),
-          text: v.optional(v.string()),
-          level: v.optional(v.number()),
-          items: v.optional(v.array(v.string())),
-          headers: v.optional(v.array(v.string())),
-          rows: v.optional(v.array(v.array(v.string()))),
-        }),
-      ),
-    }),
+    content: docxContentValidator,
     templateStorageId: v.id('_storage'),
   },
-  returns: v.object({
-    success: v.boolean(),
-    fileId: v.id('_storage'),
-    url: v.string(),
-    fileName: v.string(),
-    contentType: v.string(),
-    size: v.number(),
-  }),
+  returns: generateDocxResponseValidator,
   handler: async (ctx, args) => {
     return await DocumentsModel.generateDocxFromTemplate(ctx, {
       fileName: args.fileName,
@@ -996,11 +768,7 @@ export const generateDocxFromTemplateInternal = internalAction({
  */
 export const retryRagIndexing = action({
   args: { documentId: v.id('documents') },
-  returns: v.object({
-    success: v.boolean(),
-    jobId: v.optional(v.string()),
-    error: v.optional(v.string()),
-  }),
+  returns: retryRagIndexingResponseValidator,
   handler: async (ctx, args) => {
     try {
       const identity = await ctx.auth.getUserIdentity();
diff --git a/services/platform/convex/member.ts b/services/platform/convex/member.ts
index fc8b5c713..35b096ce8 100644
--- a/services/platform/convex/member.ts
+++ b/services/platform/convex/member.ts
@@ -3,7 +3,13 @@ import { queryWithRLS, mutationWithRLS, getAuthenticatedUser } from './lib/rls';
 import { query } from './_generated/server';
 import { components } from './_generated/api';
 
-const _role = v.string();
+// Import validators from model
+import {
+  sortOrderValidator,
+  memberListItemValidator,
+  memberContextValidator,
+  addMemberResponseValidator,
+} from './model/members/validators';
 
 /**
  * Public query to get user ID by email (for adding existing users to organizations).
@@ -47,21 +53,10 @@ export const getUserIdByEmail = query({
 export const listByOrganization = queryWithRLS({
   args: {
     organizationId: v.string(),
-    sortOrder: v.optional(v.union(v.literal('asc'), v.literal('desc'))),
+    sortOrder: v.optional(sortOrderValidator),
     search: v.optional(v.string()),
   },
-  returns: v.array(
-    v.object({
-      _id: v.string(),
-      _creationTime: v.number(),
-      organizationId: v.string(),
-      identityId: v.optional(v.string()),
-      email: v.optional(v.string()),
-      role: v.optional(v.string()),
-      displayName: v.optional(v.string()),
-      metadata: v.optional(v.any()),
-    }),
-  ),
+  returns: v.array(memberListItemValidator),
   handler: async (ctx, args) => {
     // RLS handles authorization automatically
     const result = await ctx.runQuery(components.betterAuth.adapter.findMany, {
@@ -127,24 +122,7 @@ export const getCurrentMemberContext = queryWithRLS({
   args: {
     organizationId: v.string(),
   },
-  returns: v.object({
-    member: v.union(
-      v.object({
-        _id: v.string(),
-        _creationTime: v.number(),
-        organizationId: v.string(),
-        identityId: v.optional(v.string()),
-        email: v.optional(v.string()),
-        role: v.optional(v.string()),
-        displayName: v.optional(v.string()),
-      }),
-      v.null(),
-    ),
-    role: v.union(v.string(), v.null()),
-    isAdmin: v.boolean(),
-    canManageMembers: v.boolean(),
-    canChangePassword: v.boolean(),
-  }),
+  returns: memberContextValidator,
   handler: async (ctx, args) => {
     // Get current authenticated user through RLS context
     const authUser = await getAuthenticatedUser(ctx);
@@ -257,9 +235,7 @@ export const addMember = mutationWithRLS({
     role: v.optional(v.string()),
     displayName: v.optional(v.string()),
   },
-  returns: v.object({
-    memberId: v.string(),
-  }),
+  returns: addMemberResponseValidator,
   handler: async (ctx, args) => {
     const trustedHeadersEnabled =
       process.env.TRUSTED_HEADERS_ENABLED === 'true';
diff --git a/services/platform/convex/model/documents/validators.ts b/services/platform/convex/model/documents/validators.ts
index 516f2a24f..90c5c0f08 100644
--- a/services/platform/convex/model/documents/validators.ts
+++ b/services/platform/convex/model/documents/validators.ts
@@ -101,3 +101,370 @@ export const documentRecordValidator = v.object({
   externalItemId: v.optional(v.string()),
 });
 
+/**
+ * Sort order validator
+ */
+export const sortOrderValidator = v.union(v.literal('asc'), v.literal('desc'));
+
+/**
+ * Excel sheet validator (for generateExcelInternal)
+ */
+export const excelSheetValidator = v.object({
+  name: v.string(),
+  headers: v.array(v.string()),
+  rows: v.array(
+    v.array(v.union(v.string(), v.number(), v.boolean(), v.null())),
+  ),
+});
+
+/**
+ * Generate Excel response validator
+ */
+export const generateExcelResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.id('_storage'),
+  url: v.string(),
+  fileName: v.string(),
+  rowCount: v.number(),
+  sheetCount: v.number(),
+});
+
+/**
+ * Upload base64 response validator
+ */
+export const uploadBase64ResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.id('_storage'),
+  url: v.string(),
+  fileName: v.string(),
+  size: v.number(),
+  contentType: v.string(),
+});
+
+/**
+ * Read file base64 response validator
+ */
+export const readFileBase64ResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.id('_storage'),
+  dataBase64: v.string(),
+  contentType: v.string(),
+  size: v.number(),
+});
+
+/**
+ * Source type validator (for document generation)
+ */
+export const sourceTypeValidator = v.union(
+  v.literal('markdown'),
+  v.literal('html'),
+  v.literal('url'),
+);
+
+/**
+ * Output format validator (for document generation)
+ */
+export const outputFormatValidator = v.union(
+  v.literal('pdf'),
+  v.literal('image'),
+);
+
+/**
+ * PDF options validator
+ */
+export const pdfOptionsValidator = v.object({
+  format: v.optional(v.string()),
+  landscape: v.optional(v.boolean()),
+  marginTop: v.optional(v.string()),
+  marginBottom: v.optional(v.string()),
+  marginLeft: v.optional(v.string()),
+  marginRight: v.optional(v.string()),
+  printBackground: v.optional(v.boolean()),
+});
+
+/**
+ * Image options validator
+ */
+export const imageOptionsValidator = v.object({
+  imageType: v.optional(v.string()),
+  quality: v.optional(v.number()),
+  fullPage: v.optional(v.boolean()),
+  width: v.optional(v.number()),
+  height: v.optional(v.number()),
+  scale: v.optional(v.number()),
+});
+
+/**
+ * Wait until validator for URL options
+ */
+export const waitUntilValidator = v.union(
+  v.literal('load'),
+  v.literal('domcontentloaded'),
+  v.literal('networkidle'),
+  v.literal('commit'),
+);
+
+/**
+ * URL options validator
+ */
+export const urlOptionsValidator = v.object({
+  waitUntil: v.optional(waitUntilValidator),
+  timeout: v.optional(v.number()),
+});
+
+/**
+ * Generate document response validator
+ */
+export const generateDocumentResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.id('_storage'),
+  url: v.string(),
+  fileName: v.string(),
+  contentType: v.string(),
+  extension: v.string(),
+  size: v.number(),
+});
+
+/**
+ * Membership validator (for checkMembership)
+ */
+export const membershipValidator = v.object({
+  _id: v.string(),
+  organizationId: v.string(),
+  identityId: v.optional(v.string()),
+  role: v.optional(v.string()),
+});
+
+/**
+ * Signed URL response validator
+ */
+export const signedUrlResponseValidator = v.union(
+  v.object({
+    success: v.boolean(),
+    url: v.string(),
+  }),
+  v.object({
+    success: v.boolean(),
+    error: v.string(),
+  }),
+);
+
+/**
+ * Document by extension item validator
+ */
+export const documentByExtensionItemValidator = v.object({
+  _id: v.id('documents'),
+  _creationTime: v.number(),
+  title: v.optional(v.string()),
+  fileId: v.optional(v.id('_storage')),
+  mimeType: v.optional(v.string()),
+  extension: v.optional(v.string()),
+  metadata: v.optional(v.any()),
+});
+
+/**
+ * Delete from RAG response validator
+ */
+export const deleteFromRagResponseValidator = v.object({
+  success: v.boolean(),
+  deletedCount: v.number(),
+  deletedDataIds: v.array(v.string()),
+  message: v.string(),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Upload file response validator
+ */
+export const uploadFileResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.optional(v.string()),
+  documentId: v.optional(v.string()),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Create OneDrive sync config response validator
+ */
+export const createOneDriveSyncConfigResponseValidator = v.object({
+  success: v.boolean(),
+  configId: v.optional(v.string()),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Text content info validator (for PPTX analysis)
+ */
+export const textContentInfoValidator = v.object({
+  text: v.string(),
+  isPlaceholder: v.boolean(),
+});
+
+/**
+ * Table info validator (for PPTX analysis)
+ */
+export const tableInfoValidator = v.object({
+  rowCount: v.number(),
+  columnCount: v.number(),
+  headers: v.array(v.string()),
+  rows: v.array(v.array(v.string())),
+});
+
+/**
+ * Chart info validator (for PPTX analysis)
+ */
+export const chartInfoValidator = v.object({
+  chartType: v.string(),
+  hasLegend: v.optional(v.boolean()),
+  seriesCount: v.optional(v.number()),
+});
+
+/**
+ * Image info validator (for PPTX analysis)
+ */
+export const imageInfoValidator = v.object({
+  width: v.optional(v.number()),
+  height: v.optional(v.number()),
+});
+
+/**
+ * Slide info validator (for PPTX analysis)
+ */
+export const slideInfoValidator = v.object({
+  slideNumber: v.number(),
+  layoutName: v.string(),
+  title: v.union(v.string(), v.null()),
+  subtitle: v.union(v.string(), v.null()),
+  textContent: v.array(textContentInfoValidator),
+  tables: v.array(tableInfoValidator),
+  charts: v.array(chartInfoValidator),
+  images: v.array(imageInfoValidator),
+});
+
+/**
+ * Branding info validator (for PPTX analysis)
+ */
+export const brandingInfoValidator = v.object({
+  slideWidth: v.optional(v.number()),
+  slideHeight: v.optional(v.number()),
+  titleFontName: v.optional(v.union(v.string(), v.null())),
+  bodyFontName: v.optional(v.union(v.string(), v.null())),
+  titleFontSize: v.optional(v.number()),
+  bodyFontSize: v.optional(v.number()),
+  primaryColor: v.optional(v.union(v.string(), v.null())),
+  secondaryColor: v.optional(v.union(v.string(), v.null())),
+  accentColor: v.optional(v.union(v.string(), v.null())),
+});
+
+/**
+ * Analyze PPTX response validator
+ */
+export const analyzePptxResponseValidator = v.object({
+  success: v.boolean(),
+  slideCount: v.number(),
+  slides: v.array(slideInfoValidator),
+  availableLayouts: v.array(v.string()),
+  branding: brandingInfoValidator,
+});
+
+/**
+ * Table data validator (for PPTX generation)
+ */
+export const tableDataValidator = v.object({
+  headers: v.array(v.string()),
+  rows: v.array(v.array(v.string())),
+});
+
+/**
+ * Slide content data validator (for PPTX generation)
+ */
+export const slideContentDataValidator = v.object({
+  title: v.optional(v.string()),
+  subtitle: v.optional(v.string()),
+  textContent: v.optional(v.array(v.string())),
+  bulletPoints: v.optional(v.array(v.string())),
+  tables: v.optional(v.array(tableDataValidator)),
+});
+
+/**
+ * PPTX branding data validator (for PPTX generation)
+ */
+export const pptxBrandingDataValidator = v.object({
+  slideWidth: v.optional(v.number()),
+  slideHeight: v.optional(v.number()),
+  titleFontName: v.optional(v.string()),
+  bodyFontName: v.optional(v.string()),
+  titleFontSize: v.optional(v.number()),
+  bodyFontSize: v.optional(v.number()),
+  primaryColor: v.optional(v.string()),
+  secondaryColor: v.optional(v.string()),
+  accentColor: v.optional(v.string()),
+});
+
+/**
+ * Generate PPTX response validator
+ */
+export const generatePptxResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.id('_storage'),
+  url: v.string(),
+  fileName: v.string(),
+  contentType: v.string(),
+  size: v.number(),
+});
+
+/**
+ * DOCX section type validator
+ */
+export const docxSectionTypeValidator = v.union(
+  v.literal('heading'),
+  v.literal('paragraph'),
+  v.literal('bullets'),
+  v.literal('numbered'),
+  v.literal('table'),
+  v.literal('quote'),
+  v.literal('code'),
+);
+
+/**
+ * DOCX section validator
+ */
+export const docxSectionValidator = v.object({
+  type: docxSectionTypeValidator,
+  text: v.optional(v.string()),
+  level: v.optional(v.number()),
+  items: v.optional(v.array(v.string())),
+  headers: v.optional(v.array(v.string())),
+  rows: v.optional(v.array(v.array(v.string()))),
+});
+
+/**
+ * DOCX content validator (for DOCX generation)
+ */
+export const docxContentValidator = v.object({
+  title: v.optional(v.string()),
+  subtitle: v.optional(v.string()),
+  sections: v.array(docxSectionValidator),
+});
+
+/**
+ * Generate DOCX response validator
+ */
+export const generateDocxResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.id('_storage'),
+  url: v.string(),
+  fileName: v.string(),
+  contentType: v.string(),
+  size: v.number(),
+});
+
+/**
+ * Retry RAG indexing response validator
+ */
+export const retryRagIndexingResponseValidator = v.object({
+  success: v.boolean(),
+  jobId: v.optional(v.string()),
+  error: v.optional(v.string()),
+});
+
diff --git a/services/platform/convex/model/email_providers/validators.ts b/services/platform/convex/model/email_providers/validators.ts
index 81c40ff89..e9890a497 100644
--- a/services/platform/convex/model/email_providers/validators.ts
+++ b/services/platform/convex/model/email_providers/validators.ts
@@ -35,6 +35,106 @@ export const imapConfigValidator = v.object({
   secure: v.boolean(),
 });
 
+/**
+ * Send method validator (SMTP or API)
+ */
+export const sendMethodValidator = v.union(
+  v.literal('smtp'),
+  v.literal('api'),
+);
+
+/**
+ * Account type validator (for OAuth2)
+ */
+export const accountTypeValidator = v.union(
+  v.literal('personal'),
+  v.literal('organizational'),
+  v.literal('both'),
+);
+
+/**
+ * Password auth validator
+ */
+export const passwordAuthValidator = v.object({
+  user: v.string(),
+  pass: v.string(),
+});
+
+/**
+ * Password auth encrypted validator (for internal storage)
+ */
+export const passwordAuthEncryptedValidator = v.object({
+  user: v.string(),
+  passEncrypted: v.string(),
+});
+
+/**
+ * OAuth2 auth validator (for input)
+ */
+export const oauth2AuthInputValidator = v.object({
+  provider: v.string(),
+  clientId: v.string(),
+  clientSecret: v.string(),
+  tokenUrl: v.optional(v.string()),
+});
+
+/**
+ * OAuth2 auth validator (for internal storage with encrypted fields)
+ */
+export const oauth2AuthStoredValidator = v.object({
+  provider: v.string(),
+  clientId: v.string(),
+  clientSecretEncrypted: v.string(),
+  accessTokenEncrypted: v.optional(v.string()),
+  refreshTokenEncrypted: v.optional(v.string()),
+  tokenExpiry: v.optional(v.number()),
+  tokenUrl: v.optional(v.string()),
+});
+
+/**
+ * OAuth2 auth simple validator (for testing connection)
+ */
+export const oauth2AuthSimpleValidator = v.object({
+  user: v.string(),
+  accessToken: v.string(),
+});
+
+/**
+ * Connection test result item validator
+ */
+export const connectionTestItemValidator = v.object({
+  success: v.boolean(),
+  latencyMs: v.number(),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Connection test result validator
+ */
+export const connectionTestResultValidator = v.object({
+  success: v.boolean(),
+  smtp: connectionTestItemValidator,
+  imap: connectionTestItemValidator,
+});
+
+/**
+ * OAuth2 callback response validator
+ */
+export const oauth2CallbackResponseValidator = v.object({
+  success: v.boolean(),
+  userEmail: v.optional(v.string()),
+  redirectUri: v.optional(v.string()),
+  redirectOrigin: v.optional(v.string()),
+});
+
+/**
+ * Send message response validator
+ */
+export const sendMessageResponseValidator = v.object({
+  success: v.boolean(),
+  messageId: v.optional(v.string()),
+});
+
 /**
  * Validator for email provider document (for query returns)
  */
@@ -45,24 +145,9 @@ export const emailProviderDocValidator = v.object({
   name: v.string(),
   vendor: emailProviderVendorValidator,
   authMethod: emailProviderAuthMethodValidator,
-  sendMethod: v.optional(v.union(v.literal('smtp'), v.literal('api'))),
-  passwordAuth: v.optional(
-    v.object({
-      user: v.string(),
-      passEncrypted: v.string(),
-    }),
-  ),
-  oauth2Auth: v.optional(
-    v.object({
-      provider: v.string(),
-      clientId: v.string(),
-      clientSecretEncrypted: v.string(),
-      accessTokenEncrypted: v.optional(v.string()),
-      refreshTokenEncrypted: v.optional(v.string()),
-      tokenExpiry: v.optional(v.number()),
-      tokenUrl: v.optional(v.string()),
-    }),
-  ),
+  sendMethod: v.optional(sendMethodValidator),
+  passwordAuth: v.optional(passwordAuthEncryptedValidator),
+  oauth2Auth: v.optional(oauth2AuthStoredValidator),
   smtpConfig: v.optional(smtpConfigValidator),
   imapConfig: v.optional(imapConfigValidator),
   isActive: v.optional(v.boolean()),
diff --git a/services/platform/convex/model/members/index.ts b/services/platform/convex/model/members/index.ts
new file mode 100644
index 000000000..27d3e458f
--- /dev/null
+++ b/services/platform/convex/model/members/index.ts
@@ -0,0 +1,6 @@
+/**
+ * Members Model - Validators for member operations
+ */
+
+// Export validators
+export * from './validators';
diff --git a/services/platform/convex/model/members/validators.ts b/services/platform/convex/model/members/validators.ts
new file mode 100644
index 000000000..ae036587e
--- /dev/null
+++ b/services/platform/convex/model/members/validators.ts
@@ -0,0 +1,57 @@
+/**
+ * Convex validators for members model
+ */
+
+import { v } from 'convex/values';
+
+/**
+ * Sort order validator
+ */
+export const sortOrderValidator = v.union(v.literal('asc'), v.literal('desc'));
+
+/**
+ * Member list item validator (for listing organization members)
+ * Note: _id is a string because members come from Better Auth adapter, not native Convex tables
+ */
+export const memberListItemValidator = v.object({
+  _id: v.string(),
+  _creationTime: v.number(),
+  organizationId: v.string(),
+  identityId: v.optional(v.string()),
+  email: v.optional(v.string()),
+  role: v.optional(v.string()),
+  displayName: v.optional(v.string()),
+  metadata: v.optional(v.any()),
+});
+
+/**
+ * Member object validator (for member details)
+ * Note: _id is a string because members come from Better Auth adapter, not native Convex tables
+ */
+export const memberValidator = v.object({
+  _id: v.string(),
+  _creationTime: v.number(),
+  organizationId: v.string(),
+  identityId: v.optional(v.string()),
+  email: v.optional(v.string()),
+  role: v.optional(v.string()),
+  displayName: v.optional(v.string()),
+});
+
+/**
+ * Member context response validator (for getCurrentMemberContext)
+ */
+export const memberContextValidator = v.object({
+  member: v.union(memberValidator, v.null()),
+  role: v.union(v.string(), v.null()),
+  isAdmin: v.boolean(),
+  canManageMembers: v.boolean(),
+  canChangePassword: v.boolean(),
+});
+
+/**
+ * Add member response validator
+ */
+export const addMemberResponseValidator = v.object({
+  memberId: v.string(),
+});
diff --git a/services/platform/convex/model/onedrive/index.ts b/services/platform/convex/model/onedrive/index.ts
index 4e6582ee4..21973d116 100644
--- a/services/platform/convex/model/onedrive/index.ts
+++ b/services/platform/convex/model/onedrive/index.ts
@@ -2,6 +2,9 @@
  * OneDrive Model - Business logic for OneDrive operations
  */
 
+// Export validators
+export * from './validators';
+
 export * from './get_user_token_logic';
 export * from './refresh_token_logic';
 export * from './read_file_logic';
diff --git a/services/platform/convex/model/onedrive/validators.ts b/services/platform/convex/model/onedrive/validators.ts
new file mode 100644
index 000000000..a63bd6231
--- /dev/null
+++ b/services/platform/convex/model/onedrive/validators.ts
@@ -0,0 +1,164 @@
+/**
+ * Convex validators for OneDrive model
+ */
+
+import { v } from 'convex/values';
+
+/**
+ * OneDrive sync config status validator
+ */
+export const syncConfigStatusValidator = v.union(
+  v.literal('active'),
+  v.literal('inactive'),
+  v.literal('error'),
+);
+
+/**
+ * OneDrive item type validator
+ */
+export const onedriveItemTypeValidator = v.union(
+  v.literal('file'),
+  v.literal('folder'),
+);
+
+/**
+ * File hash validator for OneDrive files
+ */
+export const fileHashValidator = v.object({
+  sha1Hash: v.optional(v.string()),
+  sha256Hash: v.optional(v.string()),
+});
+
+/**
+ * File metadata validator for OneDrive files
+ */
+export const onedriveFileMetadataValidator = v.object({
+  mimeType: v.string(),
+  hashes: v.optional(fileHashValidator),
+});
+
+/**
+ * Folder metadata validator for OneDrive folders
+ */
+export const onedriveFolderMetadataValidator = v.object({
+  childCount: v.number(),
+});
+
+/**
+ * Parent reference validator for OneDrive items
+ */
+export const parentReferenceValidator = v.object({
+  driveId: v.string(),
+  driveType: v.string(),
+  id: v.string(),
+  path: v.string(),
+});
+
+/**
+ * Drive item validator (single OneDrive item)
+ */
+export const driveItemValidator = v.object({
+  id: v.string(),
+  name: v.string(),
+  size: v.optional(v.number()),
+  createdDateTime: v.string(),
+  lastModifiedDateTime: v.string(),
+  webUrl: v.string(),
+  downloadUrl: v.optional(v.string()),
+  file: v.optional(onedriveFileMetadataValidator),
+  folder: v.optional(onedriveFolderMetadataValidator),
+  parentReference: v.optional(parentReferenceValidator),
+});
+
+/**
+ * Drive items response validator (paginated list of items)
+ */
+export const driveItemsResponseValidator = v.object({
+  nextLink: v.optional(v.string()),
+  value: v.array(driveItemValidator),
+});
+
+/**
+ * File item validator (simplified for folder contents listing)
+ */
+export const fileItemValidator = v.object({
+  id: v.string(),
+  name: v.string(),
+  size: v.number(),
+  mimeType: v.optional(v.string()),
+  lastModified: v.optional(v.number()),
+  isFolder: v.boolean(),
+});
+
+/**
+ * List files response validator
+ */
+export const listFilesResponseValidator = v.object({
+  success: v.boolean(),
+  data: v.optional(driveItemsResponseValidator),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Read file response validator
+ */
+export const readFileResponseValidator = v.object({
+  success: v.boolean(),
+  data: v.optional(
+    v.object({
+      content: v.bytes(),
+      mimeType: v.string(),
+      size: v.number(),
+    }),
+  ),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Read file from OneDrive response validator (internal action)
+ */
+export const readFileFromOnedriveResponseValidator = v.object({
+  success: v.boolean(),
+  content: v.optional(v.bytes()),
+  mimeType: v.optional(v.string()),
+  size: v.optional(v.number()),
+  error: v.optional(v.string()),
+});
+
+/**
+ * List folder contents response validator (internal action)
+ */
+export const listFolderContentsResponseValidator = v.object({
+  success: v.boolean(),
+  files: v.optional(v.array(fileItemValidator)),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Upload to storage response validator (internal action)
+ */
+export const uploadToStorageResponseValidator = v.object({
+  success: v.boolean(),
+  fileId: v.optional(v.string()),
+  documentId: v.optional(v.string()),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Refresh token response validator
+ */
+export const refreshTokenResponseValidator = v.object({
+  success: v.boolean(),
+  accessToken: v.optional(v.string()),
+  error: v.optional(v.string()),
+});
+
+/**
+ * Get user token response validator (internal query)
+ */
+export const getUserTokenResponseValidator = v.object({
+  token: v.union(v.string(), v.null()),
+  needsRefresh: v.boolean(),
+  accountId: v.union(v.string(), v.null()),
+  refreshToken: v.union(v.string(), v.null()),
+});
diff --git a/services/platform/convex/model/threads/index.ts b/services/platform/convex/model/threads/index.ts
index efd4593e1..bfef9eff1 100644
--- a/services/platform/convex/model/threads/index.ts
+++ b/services/platform/convex/model/threads/index.ts
@@ -2,6 +2,9 @@
  * Threads model - Business logic for thread operations
  */
 
+// Export validators
+export * from './validators';
+
 export { createChatThread } from './create_chat_thread';
 export { deleteChatThread } from './delete_chat_thread';
 export { getThreadMessages } from './get_thread_messages';
diff --git a/services/platform/convex/model/threads/validators.ts b/services/platform/convex/model/threads/validators.ts
new file mode 100644
index 000000000..393a743ea
--- /dev/null
+++ b/services/platform/convex/model/threads/validators.ts
@@ -0,0 +1,77 @@
+/**
+ * Convex validators for threads model
+ */
+
+import { v } from 'convex/values';
+
+/**
+ * Chat type validator for thread creation
+ */
+export const chatTypeValidator = v.union(
+  v.literal('general'),
+  v.literal('workflow_assistant'),
+);
+
+/**
+ * Message role validator for thread messages
+ */
+export const messageRoleValidator = v.union(
+  v.literal('user'),
+  v.literal('assistant'),
+);
+
+/**
+ * Thread status validator
+ */
+export const threadStatusValidator = v.union(
+  v.literal('active'),
+  v.literal('archived'),
+);
+
+/**
+ * Tool execution status validator
+ */
+export const toolStatusValidator = v.union(
+  v.literal('calling'),
+  v.literal('completed'),
+  v.null(),
+);
+
+/**
+ * Thread message validator (single message in a thread)
+ * Note: _id is a string because messages come from Agent Component, not native Convex tables
+ */
+export const threadMessageValidator = v.object({
+  _id: v.string(),
+  _creationTime: v.number(),
+  role: messageRoleValidator,
+  content: v.string(),
+});
+
+/**
+ * Thread messages response validator
+ */
+export const threadMessagesResponseValidator = v.object({
+  messages: v.array(threadMessageValidator),
+});
+
+/**
+ * Thread list item validator (for listing threads)
+ * Note: _id is a string because threads come from Agent Component, not native Convex tables
+ */
+export const threadListItemValidator = v.object({
+  _id: v.string(),
+  _creationTime: v.number(),
+  title: v.optional(v.string()),
+  status: threadStatusValidator,
+  userId: v.optional(v.string()),
+});
+
+/**
+ * Latest tool message validator
+ */
+export const latestToolMessageValidator = v.object({
+  toolNames: v.array(v.string()),
+  status: toolStatusValidator,
+  timestamp: v.union(v.number(), v.null()),
+});
diff --git a/services/platform/convex/model/vendors/index.ts b/services/platform/convex/model/vendors/index.ts
new file mode 100644
index 000000000..e711204bf
--- /dev/null
+++ b/services/platform/convex/model/vendors/index.ts
@@ -0,0 +1,6 @@
+/**
+ * Vendors Model - Validators for vendor operations
+ */
+
+// Export validators
+export * from './validators';
diff --git a/services/platform/convex/model/vendors/validators.ts b/services/platform/convex/model/vendors/validators.ts
new file mode 100644
index 000000000..e0758434c
--- /dev/null
+++ b/services/platform/convex/model/vendors/validators.ts
@@ -0,0 +1,77 @@
+/**
+ * Convex validators for vendors model
+ */
+
+import { v } from 'convex/values';
+
+/**
+ * Sort order validator
+ */
+export const sortOrderValidator = v.union(v.literal('asc'), v.literal('desc'));
+
+/**
+ * Vendor source validator
+ */
+export const vendorSourceValidator = v.union(
+  v.literal('manual_import'),
+  v.literal('file_upload'),
+  v.literal('circuly'),
+);
+
+/**
+ * Vendor address validator
+ */
+export const vendorAddressValidator = v.object({
+  street: v.optional(v.string()),
+  city: v.optional(v.string()),
+  state: v.optional(v.string()),
+  country: v.optional(v.string()),
+  postalCode: v.optional(v.string()),
+});
+
+/**
+ * Vendor input validator (for creating/updating vendors)
+ */
+export const vendorInputValidator = v.object({
+  name: v.optional(v.string()),
+  email: v.string(),
+  phone: v.optional(v.string()),
+  externalId: v.optional(v.string()),
+  source: vendorSourceValidator,
+  locale: v.optional(v.string()),
+  address: v.optional(vendorAddressValidator),
+  tags: v.optional(v.array(v.string())),
+  metadata: v.optional(v.any()),
+  notes: v.optional(v.string()),
+});
+
+/**
+ * Vendor list response validator (paginated list with metadata)
+ */
+export const vendorListResponseValidator = v.object({
+  items: v.array(v.any()),
+  total: v.number(),
+  page: v.number(),
+  pageSize: v.number(),
+  totalPages: v.number(),
+  hasNextPage: v.boolean(),
+  hasPreviousPage: v.boolean(),
+});
+
+/**
+ * Bulk create error item validator
+ */
+export const bulkCreateErrorItemValidator = v.object({
+  index: v.number(),
+  error: v.string(),
+  vendor: v.any(),
+});
+
+/**
+ * Bulk create vendors response validator
+ */
+export const bulkCreateVendorsResponseValidator = v.object({
+  success: v.number(),
+  failed: v.number(),
+  errors: v.array(bulkCreateErrorItemValidator),
+});
diff --git a/services/platform/convex/onedrive.ts b/services/platform/convex/onedrive.ts
index a58b248ff..9f0259857 100644
--- a/services/platform/convex/onedrive.ts
+++ b/services/platform/convex/onedrive.ts
@@ -19,6 +19,20 @@ import type { ActionCtx, QueryCtx } from './_generated/server';
 import type { Id } from './_generated/dataModel';
 import { checkUserRateLimit } from './lib/rate_limiter/helpers';
 
+// Import validators from model
+import {
+  driveItemsResponseValidator,
+  syncConfigStatusValidator,
+  fileItemValidator,
+  listFilesResponseValidator,
+  readFileResponseValidator,
+  readFileFromOnedriveResponseValidator,
+  listFolderContentsResponseValidator,
+  uploadToStorageResponseValidator,
+  refreshTokenResponseValidator,
+  getUserTokenResponseValidator,
+} from './model/onedrive/validators';
+
 // =============================================================================
 // TYPES
 // =============================================================================
@@ -65,49 +79,6 @@ interface MicrosoftAccount {
   refreshTokenExpiresAt: number | null;
 }
 
-// =============================================================================
-// VALIDATORS
-// =============================================================================
-
-const driveItemValidator = v.object({
-  id: v.string(),
-  name: v.string(),
-  size: v.optional(v.number()),
-  createdDateTime: v.string(),
-  lastModifiedDateTime: v.string(),
-  webUrl: v.string(),
-  downloadUrl: v.optional(v.string()),
-  file: v.optional(
-    v.object({
-      mimeType: v.string(),
-      hashes: v.optional(
-        v.object({
-          sha1Hash: v.optional(v.string()),
-          sha256Hash: v.optional(v.string()),
-        }),
-      ),
-    }),
-  ),
-  folder: v.optional(
-    v.object({
-      childCount: v.number(),
-    }),
-  ),
-  parentReference: v.optional(
-    v.object({
-      driveId: v.string(),
-      driveType: v.string(),
-      id: v.string(),
-      path: v.string(),
-    }),
-  ),
-});
-
-const driveItemsResponseValidator = v.object({
-  nextLink: v.optional(v.string()),
-  value: v.array(driveItemValidator),
-});
-
 // =============================================================================
 // INTERNAL HELPERS
 // =============================================================================
@@ -942,9 +913,7 @@ export const uploadToStorage = internalAction({
 export const updateSyncConfig = internalMutation({
   args: {
     configId: v.id('onedriveSyncConfigs'),
-    status: v.optional(
-      v.union(v.literal('active'), v.literal('inactive'), v.literal('error')),
-    ),
+    status: v.optional(syncConfigStatusValidator),
     lastSyncAt: v.optional(v.number()),
     lastSyncStatus: v.optional(v.string()),
     errorMessage: v.optional(v.string()),
diff --git a/services/platform/convex/threads.ts b/services/platform/convex/threads.ts
index e5025ea21..cfa6a833c 100644
--- a/services/platform/convex/threads.ts
+++ b/services/platform/convex/threads.ts
@@ -12,6 +12,14 @@ import { vStreamArgs } from '@convex-dev/agent';
 import { validateOrganizationAccess, getAuthenticatedUser } from './lib/rls';
 import * as ThreadsModel from './model/threads';
 
+// Import validators from model
+import {
+  chatTypeValidator,
+  threadMessagesResponseValidator,
+  threadListItemValidator,
+  latestToolMessageValidator,
+} from './model/threads/validators';
+
 /**
  * Create a new chat thread using Convex Agent Component.
  * Simply wraps createThread and validates organization access.
@@ -20,9 +28,7 @@ export const createChatThread = mutation({
   args: {
     organizationId: v.string(),
     title: v.optional(v.string()),
-    chatType: v.optional(
-      v.union(v.literal('general'), v.literal('workflow_assistant')),
-    ),
+    chatType: v.optional(chatTypeValidator),
   },
   returns: v.string(),
   handler: async (ctx, args) => {
@@ -72,16 +78,7 @@ export const getThreadMessages = query({
   args: {
     threadId: v.string(),
   },
-  returns: v.object({
-    messages: v.array(
-      v.object({
-        _id: v.string(),
-        _creationTime: v.number(),
-        role: v.union(v.literal('user'), v.literal('assistant')),
-        content: v.string(),
-      }),
-    ),
-  }),
+  returns: threadMessagesResponseValidator,
   handler: async (ctx, args) => {
     return await ThreadsModel.getThreadMessages(ctx, args.threadId);
   },
@@ -95,15 +92,7 @@ export const listThreads = query({
   args: {
     search: v.optional(v.string()),
   },
-  returns: v.array(
-    v.object({
-      _id: v.string(),
-      _creationTime: v.number(),
-      title: v.optional(v.string()),
-      status: v.union(v.literal('active'), v.literal('archived')),
-      userId: v.optional(v.string()),
-    }),
-  ),
+  returns: v.array(threadListItemValidator),
   handler: async (ctx, args) => {
     const user = await getAuthenticatedUser(ctx);
     if (!user) return [];
@@ -145,11 +134,7 @@ export const getLatestToolMessage = query({
   args: {
     threadId: v.string(),
   },
-  returns: v.object({
-    toolNames: v.array(v.string()),
-    status: v.union(v.literal('calling'), v.literal('completed'), v.null()),
-    timestamp: v.union(v.number(), v.null()),
-  }),
+  returns: latestToolMessageValidator,
   handler: async (ctx, args) => {
     return await ThreadsModel.getLatestToolMessage(ctx, args.threadId);
   },
diff --git a/services/platform/convex/vendors.ts b/services/platform/convex/vendors.ts
index 22338faad..102a79cbd 100644
--- a/services/platform/convex/vendors.ts
+++ b/services/platform/convex/vendors.ts
@@ -8,6 +8,16 @@ import {
 } from './lib/pagination';
 import type { Doc } from './_generated/dataModel';
 
+// Import validators from model
+import {
+  sortOrderValidator,
+  vendorSourceValidator,
+  vendorAddressValidator,
+  vendorInputValidator,
+  vendorListResponseValidator,
+  bulkCreateVendorsResponseValidator,
+} from './model/vendors/validators';
+
 /**
  * Check if organization has any vendors (fast count query for empty state detection)
  */
@@ -96,17 +106,9 @@ export const listVendors = queryWithRLS({
     source: v.optional(v.array(v.string())),
     locale: v.optional(v.array(v.string())),
     sortField: v.optional(v.string()),
-    sortOrder: v.optional(v.union(v.literal('asc'), v.literal('desc'))),
+    sortOrder: v.optional(sortOrderValidator),
   },
-  returns: v.object({
-    items: v.array(v.any()),
-    total: v.number(),
-    page: v.number(),
-    pageSize: v.number(),
-    totalPages: v.number(),
-    hasNextPage: v.boolean(),
-    hasPreviousPage: v.boolean(),
-  }),
+  returns: vendorListResponseValidator,
   handler: async (ctx, args) => {
     const { page: currentPage, pageSize } = normalizePaginationOptions({
       page: args.currentPage,
@@ -208,23 +210,9 @@ export const updateVendor = mutationWithRLS({
     email: v.optional(v.string()),
     phone: v.optional(v.string()),
     externalId: v.optional(v.string()),
-    source: v.optional(
-      v.union(
-        v.literal('manual_import'),
-        v.literal('file_upload'),
-        v.literal('circuly'),
-      ),
-    ),
+    source: v.optional(vendorSourceValidator),
     locale: v.optional(v.string()),
-    address: v.optional(
-      v.object({
-        street: v.optional(v.string()),
-        city: v.optional(v.string()),
-        state: v.optional(v.string()),
-        country: v.optional(v.string()),
-        postalCode: v.optional(v.string()),
-      }),
-    ),
+    address: v.optional(vendorAddressValidator),
     tags: v.optional(v.array(v.string())),
     metadata: v.optional(v.any()),
     notes: v.optional(v.string()),
@@ -309,33 +297,9 @@ export const deleteVendor = mutationWithRLS({
 export const bulkCreateVendors = mutationWithRLS({
   args: {
     organizationId: v.string(),
-    vendors: v.array(
-      v.object({
-        name: v.optional(v.string()),
-        email: v.string(),
-        phone: v.optional(v.string()),
-        externalId: v.optional(v.string()),
-        source: v.union(
-          v.literal('manual_import'),
-          v.literal('file_upload'),
-          v.literal('circuly'),
-        ),
-        locale: v.optional(v.string()),
-        address: v.optional(
-          v.object({
-            street: v.optional(v.string()),
-            city: v.optional(v.string()),
-            state: v.optional(v.string()),
-            country: v.optional(v.string()),
-            postalCode: v.optional(v.string()),
-          }),
-        ),
-        tags: v.optional(v.array(v.string())),
-        metadata: v.optional(v.any()),
-        notes: v.optional(v.string()),
-      }),
-    ),
+    vendors: v.array(vendorInputValidator),
   },
+  returns: bulkCreateVendorsResponseValidator,
   handler: async (ctx, args) => {
     const results = {
       success: 0,
