diff --git a/src/controllers/model.controller.js b/src/controllers/model.controller.js
index c7b173559aa7830c20a4e7911852e682b50f440d..f52b4164568e07e7be1c4f663c63d7c2da4dd06a 100644
--- a/src/controllers/model.controller.js
+++ b/src/controllers/model.controller.js
@@ -35,6 +35,7 @@ const createModel = catchAsync(async (req, res) => {
             type: api.type,
             datatype: api.datatype,
             isWishlist: api.isWishlist || false,
+            unit: api.unit,
           })
         )
       );
@@ -64,6 +65,7 @@ const createModel = catchAsync(async (req, res) => {
               type: api.type || 'branch',
               datatype: api.datatype || (api.type !== 'branch' ? 'string' : null),
               isWishlist: api.isWishlist || false,
+              unit: api.unit,
             })
           )
         );
@@ -105,16 +107,18 @@ const listAllModels = catchAsync(async (req, res) => {
     req.user?.id
   );
 
-  const contributedModels = await modelService.queryModels(
-    {
-      is_contributor: req.user?.id,
-    },
-    {
-      limit: 1000,
-    },
-    {},
-    req.user?.id
-  );
+  const contributedModels = req.user?.id
+    ? await modelService.queryModels(
+        {},
+        {
+          limit: 1000,
+        },
+        {
+          is_contributor: req.user?.id,
+        },
+        req.user?.id
+      )
+    : { results: [] };
 
   const publicReleasedModels = await modelService.queryModels(
     {
@@ -241,6 +245,68 @@ const getComputedVSSApi = catchAsync(async (req, res) => {
   res.send(data);
 });
 
+const getApiDetail = catchAsync(async (req, res) => {
+  if (!(await permissionService.canAccessModel(req.user?.id, req.params.id))) {
+    throw new ApiError(httpStatus.FORBIDDEN, 'Forbidden');
+  }
+  const api = await apiService.getApiDetail(req.params.id, req.params.apiName);
+  if (!api) {
+    throw new ApiError(httpStatus.NOT_FOUND, 'Api not found');
+  }
+  res.send(api);
+});
+
+const replaceApi = catchAsync(async (req, res) => {
+  const modelId = req.params.id;
+  const { extended_apis, api_version, main_api } = await modelService.processApiDataUrl(req.body.api_data_url);
+
+  const updateBody = {
+    custom_apis: [], // Remove all custom_apis
+    main_api,
+    api_version: null,
+  };
+  if (api_version) {
+    updateBody.api_version = api_version;
+  }
+
+  // Validate extended_apis
+  if (Array.isArray(extended_apis)) {
+    for (const extended_api of extended_apis) {
+      const error = await extendedApiService.validateExtendedApi({
+        ...extended_api,
+        model: modelId,
+      });
+      if (error) {
+        throw new ApiError(
+          httpStatus.BAD_REQUEST,
+          `Error in validating extended API ${extended_api.name || extended_api.apiName} - ${error.details.join(', ')}`
+        );
+      }
+    }
+  }
+
+  await modelService.updateModelById(modelId, updateBody, req.user?.id);
+  await extendedApiService.deleteExtendedApisByModelId(modelId);
+
+  await Promise.all(
+    (extended_apis || []).map((api) =>
+      extendedApiService.createExtendedApi({
+        model: modelId,
+        apiName: api.apiName,
+        description: api.description,
+        skeleton: api.skeleton,
+        tags: api.tags,
+        type: api.type,
+        datatype: api.datatype,
+        isWishlist: api.isWishlist || false,
+        unit: api.unit,
+      })
+    )
+  );
+
+  res.status(httpStatus.OK).send();
+});
+
 module.exports = {
   createModel,
   listModels,
@@ -251,4 +317,6 @@ module.exports = {
   deleteAuthorizedUser,
   getComputedVSSApi,
   listAllModels,
+  getApiDetail,
+  replaceApi,
 };
diff --git a/src/models/prototype.model.js b/src/models/prototype.model.js
index e172d3b430b18712e59a0ffc1a45c62570881b06..0c14d3aa3974fa38994ccd5a636222a9cc22237a 100644
--- a/src/models/prototype.model.js
+++ b/src/models/prototype.model.js
@@ -201,6 +201,10 @@ const prototypeSchema = mongoose.Schema(
     flow: {
       type: mongoose.SchemaTypes.Mixed,
     },
+    editors_choice: {
+      type: Boolean,
+      default: false,
+    },
   },
   {
     timestamps: true,
diff --git a/src/routes/v2/model.route.js b/src/routes/v2/model.route.js
index 4888c6543daec90a7bee76add9b48dbda1c07d0c..c0929f30eb8932696a84e2a58fdadbd5a9a4d197 100644
--- a/src/routes/v2/model.route.js
+++ b/src/routes/v2/model.route.js
@@ -50,6 +50,10 @@ router
     modelController.deleteModel
   );
 
+router
+  .route('/:id/replace-api')
+  .post(auth(), checkPermission(PERMISSIONS.WRITE_MODEL), validate(modelValidation.replaceApi), modelController.replaceApi);
+
 router.route('/:id/api').get(
   auth({
     optional: !config.strictAuth,
@@ -58,6 +62,8 @@ router.route('/:id/api').get(
   modelController.getComputedVSSApi
 );
 
+router.route('/:id/api/:apiName').get(auth({ optional: !config.strictAuth }), modelController.getApiDetail);
+
 router
   .route('/:id/permissions')
   .post(
diff --git a/src/services/api.service.js b/src/services/api.service.js
index 3ed36a9c78b6ced3a5ed029753c6af188faebffa..89d7ca98985c34ea98294070ea46ee8b1d0f4118 100644
--- a/src/services/api.service.js
+++ b/src/services/api.service.js
@@ -6,6 +6,8 @@ const fs = require('fs');
 const path = require('path');
 const logger = require('../config/logger');
 const { sortObject } = require('../utils/sort');
+const _ = require('lodash');
+const crypto = require('crypto');
 
 /**
  *
@@ -184,6 +186,35 @@ const getUsedApis = (code, apiList) => {
   }
 };
 
+const ensureParentApiHierarchy = (root, api) => {
+  if (!api) return root;
+
+  let parentNode = root;
+
+  for (const currentApi of api.split('.')) {
+    parentNode.children ??= {};
+
+    parentNode = parentNode.children[currentApi] ??= {
+      type: 'branch',
+      children: {},
+    };
+  }
+
+  parentNode.children ??= {};
+
+  return parentNode;
+};
+
+const traverse = (api, callback, prefix = '') => {
+  if (!api) return;
+  if (api.children) {
+    for (const [key, child] of Object.entries(api.children)) {
+      traverse(child, callback, `${prefix}.${key}`);
+    }
+  }
+  callback(api, prefix);
+};
+
 /**
  *
  * @param {string} modelId
@@ -211,22 +242,31 @@ const computeVSSApi = async (modelId) => {
 
   const extendedApis = await ExtendedApi.find({
     model: modelId,
-    isWishlist: true,
   });
   extendedApis.forEach((extendedApi) => {
     try {
       const name = extendedApi.apiName.split('.').slice(1).join('.');
       if (!name) return;
-      ret[mainApi].children[name] = {
-        description: extendedApi.description,
-        type: extendedApi.type || 'branch',
-        id: extendedApi._id,
-        datatype: extendedApi.datatype,
-        name: extendedApi.apiName,
-        isWishlist: extendedApi.isWishlist,
-      };
-      if (extendedApi.unit) {
-        ret[mainApi].children[name].unit = extendedApi.unit;
+
+      // Only add the extended API if it doesn't exist in the current CVI
+      const keys = name.split('.');
+      let current = ret[mainApi].children;
+      for (const key of keys) {
+        if (!current || !current[key]) {
+          ret[mainApi].children[name] = {
+            description: extendedApi.description,
+            type: extendedApi.type || 'branch',
+            id: extendedApi._id,
+            datatype: extendedApi.datatype,
+            name: extendedApi.apiName,
+            isWishlist: extendedApi.isWishlist,
+          };
+          if (extendedApi.unit) {
+            ret[mainApi].children[name].unit = extendedApi.unit;
+          }
+          break;
+        }
+        current = current[key].children;
       }
     } catch (error) {
       logger.warn(`Error while processing extended API ${extendedApi._id} with name ${extendedApi.apiName}: ${error}`);
@@ -240,18 +280,70 @@ const computeVSSApi = async (modelId) => {
   }
 
   // Nest parent/children apis
-  const len = Object.keys(ret[mainApi].children).length;
-  for (let i = len - 1; i >= 0; i--) {
-    const key = Object.keys(ret[mainApi].children)[i];
-    const parent = key.split('.').slice(0, -1).join('.');
-    if (parent && ret[mainApi].children[parent]) {
-      ret[mainApi].children[parent].children = ret[mainApi].children[parent].children || {};
-      const childKey = key.replace(`${parent}.`, '');
-      ret[mainApi].children[parent].children[childKey] = ret[mainApi].children[key];
-      delete ret[mainApi].children[key];
-    }
+  const keys = Object.keys(ret[mainApi]?.children || {}).filter((key) => key.includes('.'));
+
+  for (const key of keys) {
+    const parts = key.split('.');
+    const parent = parts.slice(0, -1).join('.');
+    const childKey = parts[parts.length - 1];
+
+    const parentNode = ensureParentApiHierarchy(ret[mainApi], parent);
+
+    parentNode.children[childKey] = {
+      ...ret[mainApi].children[key],
+      children: ret[mainApi].children[key].children || {},
+    };
+
+    delete ret[mainApi].children[key];
   }
 
+  // Refine tree
+  traverse(
+    ret[mainApi],
+    (node, prefix) => {
+      // Delete empty children
+      if (_.isEmpty(node.children)) {
+        delete node.children;
+      }
+      // Ensure name and id
+      if (!node.name) {
+        node.name = prefix;
+      }
+      if (!node.id) {
+        node.id = crypto.randomBytes(12).toString('hex');
+      }
+      if (!node.description) {
+        node.description = 'nan';
+      }
+      // Ensure datatype
+      if (node.type === 'branch') {
+        delete node.datatype;
+      } else if (!node.datatype) {
+        node.datatype = 'string';
+      }
+    },
+    mainApi
+  );
+
+  return ret;
+};
+
+const getApiDetail = async (modelId, apiName) => {
+  const tree = await computeVSSApi(modelId);
+
+  const mainApi = Object.keys(tree)[0] || 'Vehicle';
+  let ret = null;
+
+  traverse(
+    tree[mainApi],
+    (api, prefix) => {
+      if (prefix === apiName || api?.name === apiName || api?.apiName == apiName) {
+        ret = api;
+      }
+    },
+    mainApi
+  );
+
   return ret;
 };
 
@@ -265,3 +357,5 @@ module.exports.getVSSVersion = getVSSVersion;
 module.exports.computeVSSApi = computeVSSApi;
 module.exports.parseCvi = parseCvi;
 module.exports.getUsedApis = getUsedApis;
+module.exports.getApiDetail = getApiDetail;
+module.exports.traverse = traverse;
diff --git a/src/services/extendedApi.service.js b/src/services/extendedApi.service.js
index 264bb5c1f4016eace709720b70670253d2287df5..8adb34c049de8cf8ef4c13c10fcd69382a62f79b 100644
--- a/src/services/extendedApi.service.js
+++ b/src/services/extendedApi.service.js
@@ -3,6 +3,8 @@ const { ExtendedApi } = require('../models');
 const ApiError = require('../utils/ApiError');
 const { permissionService } = require('.');
 const { PERMISSIONS } = require('../config/roles');
+const Joi = require('joi');
+const { extendedApiValidation } = require('../validations');
 
 /**
  * Create a new ExtendedApi
@@ -82,11 +84,34 @@ const getExtendedApiByApiNameAndModel = async (apiName, model) => {
   return ExtendedApi.findOne({ apiName, model });
 };
 
-module.exports = {
-  createExtendedApi,
-  queryExtendedApis,
-  getExtendedApiById,
-  updateExtendedApiById,
-  deleteExtendedApiById,
-  getExtendedApiByApiNameAndModel,
+const deleteExtendedApisByModelId = async (modelId) => {
+  await ExtendedApi.deleteMany({ model: modelId });
 };
+
+/**
+ *
+ * @param {import('../typedefs').ExtendedApi} extendedApi
+ * @returns {Promise<null | {details: string[]}>}
+ */
+const validateExtendedApi = async (extendedApi) => {
+  const { _, error } = Joi.compile(extendedApiValidation.createExtendedApi.body.unknown())
+    .prefs({ errors: { label: 'key' }, abortEarly: false })
+    .validate(extendedApi);
+
+  if (error) {
+    return {
+      details: error.details.map((details) => details.message),
+    };
+  }
+
+  return null;
+};
+
+module.exports.createExtendedApi = createExtendedApi;
+module.exports.queryExtendedApis = queryExtendedApis;
+module.exports.getExtendedApiById = getExtendedApiById;
+module.exports.updateExtendedApiById = updateExtendedApiById;
+module.exports.deleteExtendedApiById = deleteExtendedApiById;
+module.exports.getExtendedApiByApiNameAndModel = getExtendedApiByApiNameAndModel;
+module.exports.deleteExtendedApisByModelId = deleteExtendedApisByModelId;
+module.exports.validateExtendedApi = validateExtendedApi;
diff --git a/src/services/model.service.js b/src/services/model.service.js
index 52bd38c45263b2c6b9c45b3ba36c1b1d5996153b..84b82dfe4110b3fe7a1aa4f98bc38e46a69e8358 100644
--- a/src/services/model.service.js
+++ b/src/services/model.service.js
@@ -446,7 +446,7 @@ const traverse = (api, callback, prefix = '') => {
 /**
  *
  * @param {string} apiDataUrl
- * @returns {Promise<{api_version: string; extended_apis: any[]} | undefined>}
+ * @returns {Promise<{main_api: string; api_version: string; extended_apis: any[]} | undefined>}
  */
 const processApiDataUrl = async (apiDataUrl) => {
   try {
@@ -462,7 +462,13 @@ const processApiDataUrl = async (apiDataUrl) => {
       (api, prefix) => {
         for (const [key, value] of Object.entries(api.children || {})) {
           if (value.isWishlist) {
-            extendedApis.push(convertToExtendedApiFormat(value));
+            const name = value?.name || `${prefix}.${key}`;
+            extendedApis.push(
+              convertToExtendedApiFormat({
+                ...value,
+                name,
+              })
+            );
             delete api.children[key];
           }
         }
@@ -491,7 +497,13 @@ const processApiDataUrl = async (apiDataUrl) => {
         data[mainApi],
         (api, prefix) => {
           for (const [key, value] of Object.entries(api.children || {})) {
-            extendedApis.push(convertToExtendedApiFormat(value));
+            const name = value?.name || `${prefix}.${key}`;
+            extendedApis.push(
+              convertToExtendedApiFormat({
+                ...value,
+                name,
+              })
+            );
             delete api.children[key];
           }
         },
@@ -505,7 +517,11 @@ const processApiDataUrl = async (apiDataUrl) => {
 
     return result;
   } catch (error) {
-    logger.warn(`Error in processing api data url: ${error}`);
+    logger.error(`Error in processing api data: ${error}`);
+    throw new ApiError(
+      httpStatus.BAD_REQUEST,
+      error?.message || `Error in processing api data. Please check content of the file again.`
+    );
   }
 };
 
diff --git a/src/services/prototype.service.js b/src/services/prototype.service.js
index d2c331cbfd9bcbceef68d7af3f6df702a1f3ec26..b3efcd9d348c3ebe942e42079e76a644e565a0a2 100644
--- a/src/services/prototype.service.js
+++ b/src/services/prototype.service.js
@@ -66,7 +66,13 @@ const bulkCreatePrototypes = async (userId, prototypes) => {
  * @returns {Promise<QueryResult>}
  */
 const queryPrototypes = async (filter, options) => {
-  const prototypes = await Prototype.paginate(filter, options);
+  const prototypes = await Prototype.paginate(filter, {
+    ...options,
+    // Default sort by editors_choice and createdAt
+    sortBy: options?.sortBy
+      ? ['editors_choice:desc,createdAt:asc', options.sortBy].join(',')
+      : 'editors_choice:desc,createdAt:asc',
+  });
   return prototypes;
 };
 
diff --git a/src/services/search.service.js b/src/services/search.service.js
index baa9caa9745fec1c85617f427346609b50709248..0f574864439a7fe58fba81a6629416812c5527d3 100644
--- a/src/services/search.service.js
+++ b/src/services/search.service.js
@@ -21,7 +21,13 @@ const search = async (query, options, userId) => {
         },
       ],
     },
-    options
+    {
+      ...options,
+      // Default sort by editors_choice and createdAt
+      sortBy: options?.sortBy
+        ? ['editors_choice:desc,createdAt:asc', options.sortBy].join(',')
+        : 'editors_choice:desc,createdAt:asc',
+    }
   );
 
   return {
@@ -45,7 +51,10 @@ const searchUserByEmail = async (email) => {
  * @param {string} signal
  */
 const searchPrototypesBySignal = async (signal) => {
-  const prototypes = await Prototype.find().select('model_id code name image_file').populate('model_id');
+  const prototypes = await Prototype.find()
+    .select('model_id code name image_file')
+    .sort('-editors_choice createdAt')
+    .populate('model_id');
   return prototypes.filter((prototype) => prototype.code?.includes(signal));
 };
 
diff --git a/src/typedefs/index.js b/src/typedefs/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..b204b09a96e0319136d699b52a2af4bcf060b7ee
--- /dev/null
+++ b/src/typedefs/index.js
@@ -0,0 +1,13 @@
+/**
+ * @typedef ExtendedApi
+ * @property apiName: string
+ * @property model: string
+ * @property skeleton: string
+ * @property unit: string
+ * @property type: string
+ * @property datatype: string
+ * @property description: string
+ * @property isWishlist: boolean
+ */
+
+exports.unused = {};
diff --git a/src/validations/extendedApi.validation.js b/src/validations/extendedApi.validation.js
index 8fc0b4437c724b6dbb9b3a687ac34ef5d44ee180..ac6f56e7d60ec7eebad34609149bcc461b31a542 100644
--- a/src/validations/extendedApi.validation.js
+++ b/src/validations/extendedApi.validation.js
@@ -7,7 +7,11 @@ const createExtendedApi = {
     model: Joi.string().custom(objectId).required(),
     skeleton: Joi.string().optional(),
     type: Joi.string(),
-    datatype: Joi.string(),
+    datatype: Joi.alternatives().conditional('type', {
+      is: 'branch',
+      then: Joi.string().allow(null).optional(),
+      otherwise: Joi.string().required(),
+    }),
     description: Joi.string().allow('').default(''),
     tags: Joi.array().items(
       Joi.object().keys({
@@ -16,7 +20,7 @@ const createExtendedApi = {
       })
     ),
     isWishlist: Joi.boolean().default(false),
-    unit: Joi.string(),
+    unit: Joi.string().allow('', null),
   }),
 };
 
@@ -47,7 +51,11 @@ const updateExtendedApi = {
         .message('apiName must start with Vehicle.'),
       skeleton: Joi.string().optional(),
       type: Joi.string(),
-      datatype: Joi.string(),
+      datatype: Joi.alternatives().conditional('type', {
+        is: 'branch',
+        then: Joi.string().allow(null).optional(),
+        otherwise: Joi.string().required(),
+      }),
       description: Joi.string().allow(''),
       tags: Joi.array().items(
         Joi.object().keys({
@@ -56,6 +64,7 @@ const updateExtendedApi = {
         })
       ),
       isWishlist: Joi.boolean(),
+      unit: Joi.string().allow('', null),
     })
     .min(1),
 };
diff --git a/src/validations/model.validation.js b/src/validations/model.validation.js
index 47b1f5beccfe75f008f2e2c75a15ea0f1ca576bc..47b9e3f6c352bf315416b0c08d20c0ca7ed27404 100644
--- a/src/validations/model.validation.js
+++ b/src/validations/model.validation.js
@@ -118,6 +118,15 @@ const getApiByModelId = {
   }),
 };
 
+const replaceApi = {
+  params: Joi.object().keys({
+    id: Joi.string().custom(objectId),
+  }),
+  body: Joi.object().keys({
+    api_data_url: Joi.string().required(),
+  }),
+};
+
 module.exports = {
   createModel,
   listModels,
@@ -127,4 +136,5 @@ module.exports = {
   addAuthorizedUser,
   deleteAuthorizedUser,
   getApiByModelId,
+  replaceApi,
 };
diff --git a/src/validations/prototype.validation.js b/src/validations/prototype.validation.js
index 2e83c2c6b67c8649d255055354c0992fe22c33ed..bf3cf583a236f84095ba24182723053ccffa7002 100644
--- a/src/validations/prototype.validation.js
+++ b/src/validations/prototype.validation.js
@@ -43,6 +43,7 @@ const bodyValidation = Joi.object().keys({
   partner_logo: Joi.string().allow(''),
   language: Joi.string().default('python'),
   requirements: Joi.string().allow(''),
+  editors_choice: Joi.boolean(),
 });
 
 const createPrototype = {
@@ -116,6 +117,7 @@ const updatePrototype = {
     partner_logo: Joi.string().allow(''),
     requirements: Joi.string().allow(''),
     language: Joi.string(),
+    editors_choice: Joi.boolean(),
     // rated_by: Joi.object().pattern(
     //   /^[0-9a-fA-F]{24}$/,
     //   Joi.object()