Bug: [FEATURE] Contract Routing Integration



### Feature Description

Integration of Contract based routing in Router

### Possible Implementation

Need to build interface, perform core integration and populate metrics for contract routing

### Have you spent some time checking if this feature request has been raised before?

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs
index f3e169336bf..122d1f8d3c8 100644
--- a/crates/api_models/src/events/routing.rs
+++ b/crates/api_models/src/events/routing.rs
@@ -1,12 +1,13 @@
 use common_utils::events::{ApiEventMetric, ApiEventsType};
 
 use crate::routing::{
-    LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig,
-    RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind,
-    RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery,
+    ContractBasedRoutingPayloadWrapper, ContractBasedRoutingSetupPayloadWrapper,
+    DynamicRoutingUpdateConfigQuery, LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm,
+    ProfileDefaultRoutingConfig, RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord,
+    RoutingKind, RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery,
     RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplitWrapper,
-    SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper,
-    SuccessBasedRoutingUpdateConfigQuery, ToggleDynamicRoutingQuery, ToggleDynamicRoutingWrapper,
+    SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, ToggleDynamicRoutingQuery,
+    ToggleDynamicRoutingWrapper,
 };
 
 impl ApiEventMetric for RoutingKind {
@@ -97,13 +98,25 @@ impl ApiEventMetric for SuccessBasedRoutingPayloadWrapper {
     }
 }
 
+impl ApiEventMetric for ContractBasedRoutingPayloadWrapper {
+    fn get_api_event_type(&self) -> Option<ApiEventsType> {
+        Some(ApiEventsType::Routing)
+    }
+}
+
+impl ApiEventMetric for ContractBasedRoutingSetupPayloadWrapper {
+    fn get_api_event_type(&self) -> Option<ApiEventsType> {
+        Some(ApiEventsType::Routing)
+    }
+}
+
 impl ApiEventMetric for ToggleDynamicRoutingWrapper {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
         Some(ApiEventsType::Routing)
     }
 }
 
-impl ApiEventMetric for SuccessBasedRoutingUpdateConfigQuery {
+impl ApiEventMetric for DynamicRoutingUpdateConfigQuery {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
         Some(ApiEventsType::Routing)
     }
diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs
index 8e502767575..d4b4f76e178 100644
--- a/crates/api_models/src/routing.rs
+++ b/crates/api_models/src/routing.rs
@@ -513,17 +513,27 @@ pub struct RoutingLinkWrapper {
     pub algorithm_id: RoutingAlgorithmId,
 }
 
-#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 pub struct DynamicAlgorithmWithTimestamp<T> {
     pub algorithm_id: Option<T>,
     pub timestamp: i64,
 }
 
+impl<T> DynamicAlgorithmWithTimestamp<T> {
+    pub fn new(algorithm_id: Option<T>) -> Self {
+        Self {
+            algorithm_id,
+            timestamp: common_utils::date_time::now_unix_timestamp(),
+        }
+    }
+}
+
 #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
 pub struct DynamicRoutingAlgorithmRef {
     pub success_based_algorithm: Option<SuccessBasedAlgorithm>,
     pub dynamic_routing_volume_split: Option<u8>,
     pub elimination_routing_algorithm: Option<EliminationRoutingAlgorithm>,
+    pub contract_based_routing: Option<ContractRoutingAlgorithm>,
 }
 
 pub trait DynamicRoutingAlgoAccessor {
@@ -555,6 +565,43 @@ impl DynamicRoutingAlgoAccessor for EliminationRoutingAlgorithm {
     }
 }
 
+impl DynamicRoutingAlgoAccessor for ContractRoutingAlgorithm {
+    fn get_algorithm_id_with_timestamp(
+        self,
+    ) -> DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId> {
+        self.algorithm_id_with_timestamp
+    }
+    fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures {
+        &mut self.enabled_feature
+    }
+}
+
+impl EliminationRoutingAlgorithm {
+    pub fn new(
+        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp<
+            common_utils::id_type::RoutingId,
+        >,
+    ) -> Self {
+        Self {
+            algorithm_id_with_timestamp,
+            enabled_feature: DynamicRoutingFeatures::None,
+        }
+    }
+}
+
+impl SuccessBasedAlgorithm {
+    pub fn new(
+        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp<
+            common_utils::id_type::RoutingId,
+        >,
+    ) -> Self {
+        Self {
+            algorithm_id_with_timestamp,
+            enabled_feature: DynamicRoutingFeatures::None,
+        }
+    }
+}
+
 impl DynamicRoutingAlgorithmRef {
     pub fn update(&mut self, new: Self) {
         if let Some(elimination_routing_algorithm) = new.elimination_routing_algorithm {
@@ -563,9 +610,12 @@ impl DynamicRoutingAlgorithmRef {
         if let Some(success_based_algorithm) = new.success_based_algorithm {
             self.success_based_algorithm = Some(success_based_algorithm)
         }
+        if let Some(contract_based_routing) = new.contract_based_routing {
+            self.contract_based_routing = Some(contract_based_routing)
+        }
     }
 
-    pub fn update_specific_ref(
+    pub fn update_enabled_features(
         &mut self,
         algo_type: DynamicRoutingType,
         feature_to_enable: DynamicRoutingFeatures,
@@ -581,6 +631,11 @@ impl DynamicRoutingAlgorithmRef {
                     .as_mut()
                     .map(|algo| algo.enabled_feature = feature_to_enable);
             }
+            DynamicRoutingType::ContractBasedRouting => {
+                self.contract_based_routing
+                    .as_mut()
+                    .map(|algo| algo.enabled_feature = feature_to_enable);
+            }
         }
     }
 
@@ -589,32 +644,6 @@ impl DynamicRoutingAlgorithmRef {
     }
 }
 
-impl EliminationRoutingAlgorithm {
-    pub fn new(
-        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp<
-            common_utils::id_type::RoutingId,
-        >,
-    ) -> Self {
-        Self {
-            algorithm_id_with_timestamp,
-            enabled_feature: DynamicRoutingFeatures::None,
-        }
-    }
-}
-
-impl SuccessBasedAlgorithm {
-    pub fn new(
-        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp<
-            common_utils::id_type::RoutingId,
-        >,
-    ) -> Self {
-        Self {
-            algorithm_id_with_timestamp,
-            enabled_feature: DynamicRoutingFeatures::None,
-        }
-    }
-}
-
 #[derive(Debug, Default, Clone, Copy, serde::Serialize, serde::Deserialize)]
 pub struct RoutingVolumeSplit {
     pub routing_type: RoutingType,
@@ -648,6 +677,14 @@ pub struct SuccessBasedAlgorithm {
     pub enabled_feature: DynamicRoutingFeatures,
 }
 
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct ContractRoutingAlgorithm {
+    pub algorithm_id_with_timestamp:
+        DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId>,
+    #[serde(default)]
+    pub enabled_feature: DynamicRoutingFeatures,
+}
+
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 pub struct EliminationRoutingAlgorithm {
     pub algorithm_id_with_timestamp:
@@ -678,24 +715,53 @@ impl DynamicRoutingAlgorithmRef {
         match dynamic_routing_type {
             DynamicRoutingType::SuccessRateBasedRouting => {
                 self.success_based_algorithm = Some(SuccessBasedAlgorithm {
-                    algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp {
-                        algorithm_id: Some(new_id),
-                        timestamp: common_utils::date_time::now_unix_timestamp(),
-                    },
+                    algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(Some(new_id)),
                     enabled_feature,
                 })
             }
             DynamicRoutingType::EliminationRouting => {
                 self.elimination_routing_algorithm = Some(EliminationRoutingAlgorithm {
-                    algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp {
-                        algorithm_id: Some(new_id),
-                        timestamp: common_utils::date_time::now_unix_timestamp(),
-                    },
+                    algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(Some(new_id)),
+                    enabled_feature,
+                })
+            }
+            DynamicRoutingType::ContractBasedRouting => {
+                self.contract_based_routing = Some(ContractRoutingAlgorithm {
+                    algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(Some(new_id)),
                     enabled_feature,
                 })
             }
         };
     }
+
+    pub fn disable_algorithm_id(&mut self, dynamic_routing_type: DynamicRoutingType) {
+        match dynamic_routing_type {
+            DynamicRoutingType::SuccessRateBasedRouting => {
+                if let Some(success_based_algo) = &self.success_based_algorithm {
+                    self.success_based_algorithm = Some(SuccessBasedAlgorithm {
+                        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
+                        enabled_feature: success_based_algo.enabled_feature,
+                    });
+                }
+            }
+            DynamicRoutingType::EliminationRouting => {
+                if let Some(elimination_based_algo) = &self.elimination_routing_algorithm {
+                    self.elimination_routing_algorithm = Some(EliminationRoutingAlgorithm {
+                        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
+                        enabled_feature: elimination_based_algo.enabled_feature,
+                    });
+                }
+            }
+            DynamicRoutingType::ContractBasedRouting => {
+                if let Some(contract_based_algo) = &self.contract_based_routing {
+                    self.contract_based_routing = Some(ContractRoutingAlgorithm {
+                        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
+                        enabled_feature: contract_based_algo.enabled_feature,
+                    });
+                }
+            }
+        }
+    }
 }
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
@@ -708,7 +774,9 @@ pub struct DynamicRoutingVolumeSplitQuery {
     pub split: u8,
 }
 
-#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, ToSchema)]
+#[derive(
+    Debug, Default, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, ToSchema,
+)]
 #[serde(rename_all = "snake_case")]
 pub enum DynamicRoutingFeatures {
     Metrics,
@@ -718,7 +786,7 @@ pub enum DynamicRoutingFeatures {
 }
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
-pub struct SuccessBasedRoutingUpdateConfigQuery {
+pub struct DynamicRoutingUpdateConfigQuery {
     #[schema(value_type = String)]
     pub algorithm_id: common_utils::id_type::RoutingId,
     #[schema(value_type = String)]
@@ -827,10 +895,27 @@ pub struct SuccessBasedRoutingPayloadWrapper {
     pub profile_id: common_utils::id_type::ProfileId,
 }
 
-#[derive(Debug, Clone, strum::Display, serde::Serialize, serde::Deserialize)]
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct ContractBasedRoutingPayloadWrapper {
+    pub updated_config: ContractBasedRoutingConfig,
+    pub algorithm_id: common_utils::id_type::RoutingId,
+    pub profile_id: common_utils::id_type::ProfileId,
+}
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct ContractBasedRoutingSetupPayloadWrapper {
+    pub config: Option<ContractBasedRoutingConfig>,
+    pub profile_id: common_utils::id_type::ProfileId,
+    pub features_to_enable: DynamicRoutingFeatures,
+}
+
+#[derive(
+    Debug, Clone, Copy, strum::Display, serde::Serialize, serde::Deserialize, PartialEq, Eq,
+)]
 pub enum DynamicRoutingType {
     SuccessRateBasedRouting,
     EliminationRouting,
+    ContractBasedRouting,
 }
 
 impl SuccessBasedRoutingConfig {
@@ -872,6 +957,91 @@ impl CurrentBlockThreshold {
     }
 }
 
+#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct ContractBasedRoutingConfig {
+    pub config: Option<ContractBasedRoutingConfigBody>,
+    pub label_info: Option<Vec<LabelInformation>>,
+}
+
+#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct ContractBasedRoutingConfigBody {
+    pub constants: Option<Vec<f64>>,
+    pub time_scale: Option<ContractBasedTimeScale>,
+}
+
+#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct LabelInformation {
+    pub label: String,
+    pub target_count: u64,
+    pub target_time: u64,
+    #[schema(value_type = String)]
+    pub mca_id: common_utils::id_type::MerchantConnectorAccountId,
+}
+
+impl LabelInformation {
+    pub fn update_target_time(&mut self, new: &Self) {
+        self.target_time = new.target_time;
+    }
+
+    pub fn update_target_count(&mut self, new: &Self) {
+        self.target_count = new.target_count;
+    }
+}
+
+#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ContractBasedTimeScale {
+    Day,
+    Month,
+}
+
+impl Default for ContractBasedRoutingConfig {
+    fn default() -> Self {
+        Self {
+            config: Some(ContractBasedRoutingConfigBody {
+                constants: Some(vec![0.7, 0.35]),
+                time_scale: Some(ContractBasedTimeScale::Day),
+            }),
+            label_info: None,
+        }
+    }
+}
+
+impl ContractBasedRoutingConfig {
+    pub fn update(&mut self, new: Self) {
+        if let Some(new_config) = new.config {
+            self.config.as_mut().map(|config| config.update(new_config));
+        }
+        if let Some(new_label_info) = new.label_info {
+            new_label_info.iter().for_each(|new_label_info| {
+                if let Some(existing_label_infos) = &mut self.label_info {
+                    for existing_label_info in existing_label_infos {
+                        if existing_label_info.mca_id == new_label_info.mca_id {
+                            existing_label_info.update_target_time(new_label_info);
+                            existing_label_info.update_target_count(new_label_info);
+                        }
+                    }
+                } else {
+                    self.label_info = Some(vec![new_label_info.clone()]);
+                }
+            });
+        }
+    }
+}
+
+impl ContractBasedRoutingConfigBody {
+    pub fn update(&mut self, new: Self) {
+        if let Some(new_cons) = new.constants {
+            self.constants = Some(new_cons)
+        }
+        if let Some(new_time_scale) = new.time_scale {
+            self.time_scale = Some(new_time_scale)
+        }
+    }
+}
 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 pub struct RoutableConnectorChoiceWithBucketName {
     pub routable_connector_choice: RoutableConnectorChoice,
diff --git a/crates/external_services/build.rs b/crates/external_services/build.rs
index b3fa1a8fed2..8dba0bb3e98 100644
--- a/crates/external_services/build.rs
+++ b/crates/external_services/build.rs
@@ -6,6 +6,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 
         let proto_path = router_env::workspace_path().join("proto");
         let success_rate_proto_file = proto_path.join("success_rate.proto");
+        let contract_routing_proto_file = proto_path.join("contract_routing.proto");
         let elimination_proto_file = proto_path.join("elimination_rate.proto");
         let health_check_proto_file = proto_path.join("health_check.proto");
         let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?);
@@ -16,8 +17,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             .compile(
                 &[
                     success_rate_proto_file,
-                    elimination_proto_file,
                     health_check_proto_file,
+                    elimination_proto_file,
+                    contract_routing_proto_file,
                 ],
                 &[proto_path],
             )
diff --git a/crates/external_services/src/grpc_client/dynamic_routing.rs b/crates/external_services/src/grpc_client/dynamic_routing.rs
index e5050227fa8..e34f721b30e 100644
--- a/crates/external_services/src/grpc_client/dynamic_routing.rs
+++ b/crates/external_services/src/grpc_client/dynamic_routing.rs
@@ -1,14 +1,18 @@
+/// Module for Contract based routing
+pub mod contract_routing_client;
+
 use std::fmt::Debug;
 
 use common_utils::errors::CustomResult;
 use router_env::logger;
 use serde;
 /// Elimination Routing Client Interface Implementation
-pub mod elimination_rate_client;
+pub mod elimination_based_client;
 /// Success Routing Client Interface Implementation
 pub mod success_rate_client;
 
-pub use elimination_rate_client::EliminationAnalyserClient;
+pub use contract_routing_client::ContractScoreCalculatorClient;
+pub use elimination_based_client::EliminationAnalyserClient;
 pub use success_rate_client::SuccessRateCalculatorClient;
 
 use super::Client;
@@ -27,6 +31,10 @@ pub enum DynamicRoutingError {
     /// Error from Dynamic Routing Server while performing success_rate analysis
     #[error("Error from Dynamic Routing Server while perfrming success_rate analysis : {0}")]
     SuccessRateBasedRoutingFailure(String),
+
+    /// Error from Dynamic Routing Server while performing contract based routing
+    #[error("Error from Dynamic Routing Server while performing contract based routing: {0}")]
+    ContractBasedRoutingFailure(String),
     /// Error from Dynamic Routing Server while perfrming elimination
     #[error("Error from Dynamic Routing Server while perfrming elimination : {0}")]
     EliminationRateRoutingFailure(String),
@@ -37,8 +45,10 @@ pub enum DynamicRoutingError {
 pub struct RoutingStrategy {
     /// success rate service for Dynamic Routing
     pub success_rate_client: Option<SuccessRateCalculatorClient<Client>>,
+    /// contract based routing service for Dynamic Routing
+    pub contract_based_client: Option<ContractScoreCalculatorClient<Client>>,
     /// elimination service for Dynamic Routing
-    pub elimination_rate_client: Option<EliminationAnalyserClient<Client>>,
+    pub elimination_based_client: Option<EliminationAnalyserClient<Client>>,
 }
 
 /// Contains the Dynamic Routing Client Config
@@ -65,7 +75,7 @@ impl DynamicRoutingClientConfig {
         self,
         client: Client,
     ) -> Result<RoutingStrategy, Box<dyn std::error::Error>> {
-        let (success_rate_client, elimination_rate_client) = match self {
+        let (success_rate_client, contract_based_client, elimination_based_client) = match self {
             Self::Enabled { host, port, .. } => {
                 let uri = format!("http://{}:{}", host, port).parse::<tonic::transport::Uri>()?;
                 logger::info!("Connection established with dynamic routing gRPC Server");
@@ -74,14 +84,19 @@ impl DynamicRoutingClientConfig {
                         client.clone(),
                         uri.clone(),
                     )),
+                    Some(ContractScoreCalculatorClient::with_origin(
+                        client.clone(),
+                        uri.clone(),
+                    )),
                     Some(EliminationAnalyserClient::with_origin(client, uri)),
                 )
             }
-            Self::Disabled => (None, None),
+            Self::Disabled => (None, None, None),
         };
         Ok(RoutingStrategy {
             success_rate_client,
-            elimination_rate_client,
+            contract_based_client,
+            elimination_based_client,
         })
     }
 }
diff --git a/crates/external_services/src/grpc_client/dynamic_routing/contract_routing_client.rs b/crates/external_services/src/grpc_client/dynamic_routing/contract_routing_client.rs
new file mode 100644
index 00000000000..b210d996bc0
--- /dev/null
+++ b/crates/external_services/src/grpc_client/dynamic_routing/contract_routing_client.rs
@@ -0,0 +1,192 @@
+use api_models::routing::{
+    ContractBasedRoutingConfig, ContractBasedRoutingConfigBody, ContractBasedTimeScale,
+    LabelInformation, RoutableConnectorChoice, RoutableConnectorChoiceWithStatus,
+};
+use common_utils::{
+    ext_traits::OptionExt,
+    transformers::{ForeignFrom, ForeignTryFrom},
+};
+pub use contract_routing::{
+    contract_score_calculator_client::ContractScoreCalculatorClient, CalContractScoreConfig,
+    CalContractScoreRequest, CalContractScoreResponse, InvalidateContractRequest,
+    InvalidateContractResponse, LabelInformation as ProtoLabelInfo, TimeScale,
+    UpdateContractRequest, UpdateContractResponse,
+};
+use error_stack::ResultExt;
+use router_env::logger;
+
+use crate::grpc_client::{self, GrpcHeaders};
+#[allow(
+    missing_docs,
+    unused_qualifications,
+    clippy::unwrap_used,
+    clippy::as_conversions,
+    clippy::use_self
+)]
+pub mod contract_routing {
+    tonic::include_proto!("contract_routing");
+}
+use super::{Client, DynamicRoutingError, DynamicRoutingResult};
+/// The trait ContractBasedDynamicRouting would have the functions required to support the calculation and updation window
+#[async_trait::async_trait]
+pub trait ContractBasedDynamicRouting: dyn_clone::DynClone + Send + Sync {
+    /// To calculate the contract scores for the list of chosen connectors
+    async fn calculate_contract_score(
+        &self,
+        id: String,
+        config: ContractBasedRoutingConfig,
+        params: String,
+        label_input: Vec<RoutableConnectorChoice>,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<CalContractScoreResponse>;
+    /// To update the contract scores with the given labels
+    async fn update_contracts(
+        &self,
+        id: String,
+        label_info: Vec<LabelInformation>,
+        params: String,
+        response: Vec<RoutableConnectorChoiceWithStatus>,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<UpdateContractResponse>;
+    /// To invalidates the contract scores against the id
+    async fn invalidate_contracts(
+        &self,
+        id: String,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<InvalidateContractResponse>;
+}
+
+#[async_trait::async_trait]
+impl ContractBasedDynamicRouting for ContractScoreCalculatorClient<Client> {
+    async fn calculate_contract_score(
+        &self,
+        id: String,
+        config: ContractBasedRoutingConfig,
+        params: String,
+        label_input: Vec<RoutableConnectorChoice>,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<CalContractScoreResponse> {
+        let labels = label_input
+            .into_iter()
+            .map(|conn_choice| conn_choice.to_string())
+            .collect::<Vec<_>>();
+
+        let config = config
+            .config
+            .map(ForeignTryFrom::foreign_try_from)
+            .transpose()?;
+
+        let request = grpc_client::create_grpc_request(
+            CalContractScoreRequest {
+                id,
+                params,
+                labels,
+                config,
+            },
+            headers,
+        );
+
+        let response = self
+            .clone()
+            .fetch_contract_score(request)
+            .await
+            .change_context(DynamicRoutingError::ContractBasedRoutingFailure(
+                "Failed to fetch the contract score".to_string(),
+            ))?
+            .into_inner();
+
+        logger::info!(dynamic_routing_response=?response);
+
+        Ok(response)
+    }
+
+    async fn update_contracts(
+        &self,
+        id: String,
+        label_info: Vec<LabelInformation>,
+        params: String,
+        _response: Vec<RoutableConnectorChoiceWithStatus>,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<UpdateContractResponse> {
+        let labels_information = label_info
+            .into_iter()
+            .map(ProtoLabelInfo::foreign_from)
+            .collect::<Vec<_>>();
+
+        let request = grpc_client::create_grpc_request(
+            UpdateContractRequest {
+                id,
+                params,
+                labels_information,
+            },
+            headers,
+        );
+
+        let response = self
+            .clone()
+            .update_contract(request)
+            .await
+            .change_context(DynamicRoutingError::ContractBasedRoutingFailure(
+                "Failed to update the contracts".to_string(),
+            ))?
+            .into_inner();
+
+        logger::info!(dynamic_routing_response=?response);
+
+        Ok(response)
+    }
+    async fn invalidate_contracts(
+        &self,
+        id: String,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<InvalidateContractResponse> {
+        let request = grpc_client::create_grpc_request(InvalidateContractRequest { id }, headers);
+
+        let response = self
+            .clone()
+            .invalidate_contract(request)
+            .await
+            .change_context(DynamicRoutingError::ContractBasedRoutingFailure(
+                "Failed to invalidate the contracts".to_string(),
+            ))?
+            .into_inner();
+        Ok(response)
+    }
+}
+
+impl ForeignFrom<ContractBasedTimeScale> for TimeScale {
+    fn foreign_from(scale: ContractBasedTimeScale) -> Self {
+        Self {
+            time_scale: match scale {
+                ContractBasedTimeScale::Day => 0,
+                _ => 1,
+            },
+        }
+    }
+}
+
+impl ForeignTryFrom<ContractBasedRoutingConfigBody> for CalContractScoreConfig {
+    type Error = error_stack::Report<DynamicRoutingError>;
+    fn foreign_try_from(config: ContractBasedRoutingConfigBody) -> Result<Self, Self::Error> {
+        Ok(Self {
+            constants: config
+                .constants
+                .get_required_value("constants")
+                .change_context(DynamicRoutingError::MissingRequiredField {
+                    field: "constants".to_string(),
+                })?,
+            time_scale: config.time_scale.clone().map(TimeScale::foreign_from),
+        })
+    }
+}
+
+impl ForeignFrom<LabelInformation> for ProtoLabelInfo {
+    fn foreign_from(config: LabelInformation) -> Self {
+        Self {
+            label: config.label,
+            target_count: config.target_count,
+            target_time: config.target_time,
+            current_count: 1,
+        }
+    }
+}
diff --git a/crates/external_services/src/grpc_client/dynamic_routing/elimination_rate_client.rs b/crates/external_services/src/grpc_client/dynamic_routing/elimination_based_client.rs
similarity index 100%
rename from crates/external_services/src/grpc_client/dynamic_routing/elimination_rate_client.rs
rename to crates/external_services/src/grpc_client/dynamic_routing/elimination_based_client.rs
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index 7102c23d17e..026eae764c4 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -166,6 +166,8 @@ Never share your secret api keys. Keep them guarded and secure.
         routes::routing::success_based_routing_update_configs,
         routes::routing::toggle_success_based_routing,
         routes::routing::toggle_elimination_routing,
+        routes::routing::contract_based_routing_setup_config,
+        routes::routing::contract_based_routing_update_configs,
 
         // Routes for blocklist
         routes::blocklist::remove_entry_from_blocklist,
@@ -615,6 +617,10 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::routing::DynamicRoutingConfigParams,
         api_models::routing::CurrentBlockThreshold,
         api_models::routing::SuccessBasedRoutingConfigBody,
+        api_models::routing::ContractBasedRoutingConfig,
+        api_models::routing::ContractBasedRoutingConfigBody,
+        api_models::routing::LabelInformation,
+        api_models::routing::ContractBasedTimeScale,
         api_models::routing::LinkedRoutingConfigRetrieveResponse,
         api_models::routing::RoutingRetrieveResponse,
         api_models::routing::ProfileDefaultRoutingConfig,
diff --git a/crates/openapi/src/routes/routing.rs b/crates/openapi/src/routes/routing.rs
index 6b968f80172..c21669a9932 100644
--- a/crates/openapi/src/routes/routing.rs
+++ b/crates/openapi/src/routes/routing.rs
@@ -292,7 +292,7 @@ pub async fn toggle_success_based_routing() {}
         ("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"),
         ("algorithm_id" = String, Path, description = "Success based routing algorithm id which was last activated to update the config"),
     ),
-    request_body = DynamicRoutingFeatures,
+    request_body = SuccessBasedRoutingConfig,
     responses(
         (status = 200, description = "Routing Algorithm updated", body = RoutingDictionaryRecord),
         (status = 400, description = "Update body is malformed"),
@@ -317,7 +317,7 @@ pub async fn success_based_routing_update_configs() {}
     params(
         ("account_id" = String, Path, description = "Merchant id"),
         ("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"),
-        ("enable" = DynamicRoutingFeatures, Query, description = "Feature to enable for success based routing"),
+        ("enable" = DynamicRoutingFeatures, Query, description = "Feature to enable for elimination based routing"),
     ),
     responses(
         (status = 200, description = "Routing Algorithm created", body = RoutingDictionaryRecord),
@@ -332,3 +332,57 @@ pub async fn success_based_routing_update_configs() {}
    security(("api_key" = []), ("jwt_key" = []))
 )]
 pub async fn toggle_elimination_routing() {}
+
+#[cfg(feature = "v1")]
+/// Routing - Toggle Contract routing for profile
+///
+/// Create a Contract based dynamic routing algorithm
+#[utoipa::path(
+    post,
+    path = "/account/:account_id/business_profile/:profile_id/dynamic_routing/contracts/toggle",
+    params(
+        ("account_id" = String, Path, description = "Merchant id"),
+        ("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"),
+        ("enable" = DynamicRoutingFeatures, Query, description = "Feature to enable for contract based routing"),
+    ),
+    request_body = ContractBasedRoutingConfig,
+    responses(
+        (status = 200, description = "Routing Algorithm created", body = RoutingDictionaryRecord),
+        (status = 400, description = "Request body is malformed"),
+        (status = 500, description = "Internal server error"),
+        (status = 404, description = "Resource missing"),
+        (status = 422, description = "Unprocessable request"),
+        (status = 403, description = "Forbidden"),
+    ),
+   tag = "Routing",
+   operation_id = "Toggle contract routing algorithm",
+   security(("api_key" = []), ("jwt_key" = []))
+)]
+pub async fn contract_based_routing_setup_config() {}
+
+#[cfg(feature = "v1")]
+/// Routing - Update contract based dynamic routing config for profile
+///
+/// Update contract based dynamic routing algorithm
+#[utoipa::path(
+    patch,
+    path = "/account/{account_id}/business_profile/{profile_id}/dynamic_routing/contracts/config/{algorithm_id}",
+    params(
+        ("account_id" = String, Path, description = "Merchant id"),
+        ("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"),
+        ("algorithm_id" = String, Path, description = "Contract based routing algorithm id which was last activated to update the config"),
+    ),
+    request_body = ContractBasedRoutingConfig,
+    responses(
+        (status = 200, description = "Routing Algorithm updated", body = RoutingDictionaryRecord),
+        (status = 400, description = "Update body is malformed"),
+        (status = 500, description = "Internal server error"),
+        (status = 404, description = "Resource missing"),
+        (status = 422, description = "Unprocessable request"),
+        (status = 403, description = "Forbidden"),
+    ),
+   tag = "Routing",
+   operation_id = "Update contract based dynamic routing configs",
+   security(("api_key" = []), ("jwt_key" = []))
+)]
+pub async fn contract_based_routing_update_configs() {}
diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs
index b4c1c1c2c03..54cae42ceb3 100644
--- a/crates/router/src/core/errors.rs
+++ b/crates/router/src/core/errors.rs
@@ -398,6 +398,16 @@ pub enum RoutingError {
     GenericNotFoundError { field: String },
     #[error("Unable to deserialize from '{from}' to '{to}'")]
     DeserializationError { from: String, to: String },
+    #[error("Unable to retrieve contract based routing config")]
+    ContractBasedRoutingConfigError,
+    #[error("Params not found in contract based routing config")]
+    ContractBasedRoutingParamsNotFoundError,
+    #[error("Unable to calculate contract score from dynamic routing service")]
+    ContractScoreCalculationError,
+    #[error("contract routing client from dynamic routing gRPC service not initialized")]
+    ContractRoutingClientInitializationError,
+    #[error("Invalid contract based connector label received from dynamic routing service: '{0}'")]
+    InvalidContractBasedConnectorLabel(String),
 }
 
 #[derive(Debug, Clone, thiserror::Error)]
diff --git a/crates/router/src/core/metrics.rs b/crates/router/src/core/metrics.rs
index a98a4ffb259..efb463b3a37 100644
--- a/crates/router/src/core/metrics.rs
+++ b/crates/router/src/core/metrics.rs
@@ -82,6 +82,7 @@ counter_metric!(
     GLOBAL_METER
 );
 counter_metric!(DYNAMIC_SUCCESS_BASED_ROUTING, GLOBAL_METER);
+counter_metric!(DYNAMIC_CONTRACT_BASED_ROUTING, GLOBAL_METER);
 
 #[cfg(feature = "partial-auth")]
 counter_metric!(PARTIAL_AUTH_FAILURE, GLOBAL_METER);
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index dce89107afd..f86072fdcc2 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -6599,8 +6599,8 @@ where
                     .attach_printable("failed to perform volume split on routing type")?;
 
             if routing_choice.routing_type.is_dynamic_routing() {
-                let success_based_routing_config_params_interpolator =
-                    routing_helpers::SuccessBasedRoutingConfigParamsInterpolator::new(
+                let dynamic_routing_config_params_interpolator =
+                    routing_helpers::DynamicRoutingConfigParamsInterpolator::new(
                         payment_data.get_payment_attempt().payment_method,
                         payment_data.get_payment_attempt().payment_method_type,
                         payment_data.get_payment_attempt().authentication_type,
@@ -6630,14 +6630,15 @@ where
                             .and_then(|card_isin| card_isin.as_str())
                             .map(|card_isin| card_isin.to_string()),
                     );
-                routing::perform_success_based_routing(
+
+                routing::perform_dynamic_routing(
                     state,
                     connectors.clone(),
                     business_profile,
-                    success_based_routing_config_params_interpolator,
+                    dynamic_routing_config_params_interpolator,
                 )
                 .await
-                .map_err(|e| logger::error!(success_rate_routing_error=?e))
+                .map_err(|e| logger::error!(dynamic_routing_error=?e))
                 .unwrap_or(connectors)
             } else {
                 connectors
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index 748b71469ba..abbefd793d7 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -5,6 +5,8 @@ use api_models::payments::{ConnectorMandateReferenceId, MandateReferenceId};
 use api_models::routing::RoutableConnectorChoice;
 use async_trait::async_trait;
 use common_enums::{AuthorizationStatus, SessionUpdateStatus};
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+use common_utils::ext_traits::ValueExt;
 use common_utils::{
     ext_traits::{AsyncExt, Encode},
     types::{keymanager::KeyManagerState, ConnectorTransactionId, MinorUnit},
@@ -1963,11 +1965,22 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
         if payment_intent.status.is_in_terminal_state()
             && business_profile.dynamic_routing_algorithm.is_some()
         {
+            let dynamic_routing_algo_ref: api_models::routing::DynamicRoutingAlgorithmRef =
+                business_profile
+                    .dynamic_routing_algorithm
+                    .clone()
+                    .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
+                    .transpose()
+                    .change_context(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?
+                    .ok_or(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable("DynamicRoutingAlgorithmRef not found in profile")?;
+
             let state = state.clone();
-            let business_profile = business_profile.clone();
+            let profile_id = business_profile.get_id().to_owned();
             let payment_attempt = payment_attempt.clone();
-            let success_based_routing_config_params_interpolator =
-                routing_helpers::SuccessBasedRoutingConfigParamsInterpolator::new(
+            let dynamic_routing_config_params_interpolator =
+                routing_helpers::DynamicRoutingConfigParamsInterpolator::new(
                     payment_attempt.payment_method,
                     payment_attempt.payment_method_type,
                     payment_attempt.authentication_type,
@@ -1999,14 +2012,27 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
             tokio::spawn(
                 async move {
                     routing_helpers::push_metrics_with_update_window_for_success_based_routing(
+                        &state,
+                        &payment_attempt,
+                        routable_connectors.clone(),
+                        &profile_id,
+                        dynamic_routing_algo_ref.clone(),
+                        dynamic_routing_config_params_interpolator.clone(),
+                    )
+                    .await
+                    .map_err(|e| logger::error!(success_based_routing_metrics_error=?e))
+                    .ok();
+
+                    routing_helpers::push_metrics_with_update_window_for_contract_based_routing(
                         &state,
                         &payment_attempt,
                         routable_connectors,
-                        &business_profile,
-                        success_based_routing_config_params_interpolator,
+                        &profile_id,
+                        dynamic_routing_algo_ref,
+                        dynamic_routing_config_params_interpolator,
                     )
                     .await
-                    .map_err(|e| logger::error!(dynamic_routing_metrics_error=?e))
+                    .map_err(|e| logger::error!(contract_based_routing_metrics_error=?e))
                     .ok();
                 }
                 .in_current_span(),
diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs
index dd592bc3064..41be7e6e2d3 100644
--- a/crates/router/src/core/payments/routing.rs
+++ b/crates/router/src/core/payments/routing.rs
@@ -14,6 +14,8 @@ use api_models::{
     enums::{self as api_enums, CountryAlpha2},
     routing::ConnectorSelection,
 };
+#[cfg(feature = "dynamic_routing")]
+use common_utils::ext_traits::AsyncExt;
 use diesel_models::enums as storage_enums;
 use error_stack::ResultExt;
 use euclid::{
@@ -23,8 +25,9 @@ use euclid::{
     frontend::{ast, dir as euclid_dir},
 };
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
-use external_services::grpc_client::dynamic_routing::success_rate_client::{
-    CalSuccessRateResponse, SuccessBasedDynamicRouting,
+use external_services::grpc_client::dynamic_routing::{
+    contract_routing_client::{CalContractScoreResponse, ContractBasedDynamicRouting},
+    success_rate_client::{CalSuccessRateResponse, SuccessBasedDynamicRouting},
 };
 use hyperswitch_domain_models::address::Address;
 use kgraph_utils::{
@@ -1281,41 +1284,93 @@ pub fn make_dsl_input_for_surcharge(
     Ok(backend_input)
 }
 
-/// success based dynamic routing
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
-#[instrument(skip_all)]
-pub async fn perform_success_based_routing(
+pub async fn perform_dynamic_routing(
     state: &SessionState,
     routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
-    business_profile: &domain::Profile,
-    success_based_routing_config_params_interpolator: routing::helpers::SuccessBasedRoutingConfigParamsInterpolator,
+    profile: &domain::Profile,
+    dynamic_routing_config_params_interpolator: routing::helpers::DynamicRoutingConfigParamsInterpolator,
 ) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>> {
-    let success_based_dynamic_routing_algo_ref: api_routing::DynamicRoutingAlgorithmRef =
-        business_profile
-            .dynamic_routing_algorithm
-            .clone()
-            .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
-            .transpose()
-            .change_context(errors::RoutingError::DeserializationError {
-                from: "JSON".to_string(),
-                to: "DynamicRoutingAlgorithmRef".to_string(),
-            })
-            .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?
-            .unwrap_or_default();
+    let dynamic_routing_algo_ref: api_routing::DynamicRoutingAlgorithmRef = profile
+        .dynamic_routing_algorithm
+        .clone()
+        .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
+        .transpose()
+        .change_context(errors::RoutingError::DeserializationError {
+            from: "JSON".to_string(),
+            to: "DynamicRoutingAlgorithmRef".to_string(),
+        })
+        .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?
+        .ok_or(errors::RoutingError::GenericNotFoundError {
+            field: "dynamic_routing_algorithm".to_string(),
+        })?;
+
+    logger::debug!(
+        "performing dynamic_routing for profile {}",
+        profile.get_id().get_string_repr()
+    );
 
-    let success_based_algo_ref = success_based_dynamic_routing_algo_ref
+    let connector_list = match dynamic_routing_algo_ref
         .success_based_algorithm
-        .ok_or(errors::RoutingError::GenericNotFoundError { field: "success_based_algorithm".to_string() })
-        .attach_printable(
-            "success_based_algorithm not found in dynamic_routing_algorithm from business_profile table",
-        )?;
+        .as_ref()
+        .async_map(|algorithm| {
+            perform_success_based_routing(
+                state,
+                routable_connectors.clone(),
+                profile.get_id(),
+                dynamic_routing_config_params_interpolator.clone(),
+                algorithm.clone(),
+            )
+        })
+        .await
+        .transpose()
+        .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
+        .ok()
+        .flatten()
+    {
+        Some(success_based_list) => success_based_list,
+        None => {
+            // Only run contract based if success based returns None
+            dynamic_routing_algo_ref
+                .contract_based_routing
+                .as_ref()
+                .async_map(|algorithm| {
+                    perform_contract_based_routing(
+                        state,
+                        routable_connectors.clone(),
+                        profile.get_id(),
+                        dynamic_routing_config_params_interpolator,
+                        algorithm.clone(),
+                    )
+                })
+                .await
+                .transpose()
+                .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
+                .ok()
+                .flatten()
+                .unwrap_or(routable_connectors)
+        }
+    };
 
+    Ok(connector_list)
+}
+
+/// success based dynamic routing
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+#[instrument(skip_all)]
+pub async fn perform_success_based_routing(
+    state: &SessionState,
+    routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
+    profile_id: &common_utils::id_type::ProfileId,
+    success_based_routing_config_params_interpolator: routing::helpers::DynamicRoutingConfigParamsInterpolator,
+    success_based_algo_ref: api_routing::SuccessBasedAlgorithm,
+) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>> {
     if success_based_algo_ref.enabled_feature
         == api_routing::DynamicRoutingFeatures::DynamicConnectorSelection
     {
         logger::debug!(
             "performing success_based_routing for profile {}",
-            business_profile.get_id().get_string_repr()
+            profile_id.get_string_repr()
         );
         let client = state
             .grpc_client
@@ -1325,18 +1380,18 @@ pub async fn perform_success_based_routing(
             .ok_or(errors::RoutingError::SuccessRateClientInitializationError)
             .attach_printable("success_rate gRPC client not found")?;
 
-        let success_based_routing_configs = routing::helpers::fetch_success_based_routing_configs(
+        let success_based_routing_configs = routing::helpers::fetch_dynamic_routing_configs::<
+            api_routing::SuccessBasedRoutingConfig,
+        >(
             state,
-            business_profile,
+            profile_id,
             success_based_algo_ref
                 .algorithm_id_with_timestamp
                 .algorithm_id
                 .ok_or(errors::RoutingError::GenericNotFoundError {
                     field: "success_based_routing_algorithm_id".to_string(),
                 })
-                .attach_printable(
-                    "success_based_routing_algorithm_id not found in business_profile",
-                )?,
+                .attach_printable("success_based_routing_algorithm_id not found in profile_id")?,
         )
         .await
         .change_context(errors::RoutingError::SuccessBasedRoutingConfigError)
@@ -1352,7 +1407,7 @@ pub async fn perform_success_based_routing(
 
         let success_based_connectors: CalSuccessRateResponse = client
             .calculate_success_rate(
-                business_profile.get_id().get_string_repr().into(),
+                profile_id.get_string_repr().into(),
                 success_based_routing_configs,
                 success_based_routing_config_params,
                 routable_connectors,
@@ -1398,3 +1453,95 @@ pub async fn perform_success_based_routing(
         Ok(routable_connectors)
     }
 }
+
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+pub async fn perform_contract_based_routing(
+    state: &SessionState,
+    routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
+    profile_id: &common_utils::id_type::ProfileId,
+    _dynamic_routing_config_params_interpolator: routing::helpers::DynamicRoutingConfigParamsInterpolator,
+    contract_based_algo_ref: api_routing::ContractRoutingAlgorithm,
+) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>> {
+    if contract_based_algo_ref.enabled_feature
+        == api_routing::DynamicRoutingFeatures::DynamicConnectorSelection
+    {
+        logger::debug!(
+            "performing contract_based_routing for profile {}",
+            profile_id.get_string_repr()
+        );
+        let client = state
+            .grpc_client
+            .dynamic_routing
+            .contract_based_client
+            .as_ref()
+            .ok_or(errors::RoutingError::ContractRoutingClientInitializationError)
+            .attach_printable("contract routing gRPC client not found")?;
+
+        let contract_based_routing_configs = routing::helpers::fetch_dynamic_routing_configs::<
+            api_routing::ContractBasedRoutingConfig,
+        >(
+            state,
+            profile_id,
+            contract_based_algo_ref
+                .algorithm_id_with_timestamp
+                .algorithm_id
+                .ok_or(errors::RoutingError::GenericNotFoundError {
+                    field: "contract_based_routing_algorithm_id".to_string(),
+                })
+                .attach_printable("contract_based_routing_algorithm_id not found in profile_id")?,
+        )
+        .await
+        .change_context(errors::RoutingError::ContractBasedRoutingConfigError)
+        .attach_printable("unable to fetch contract based dynamic routing configs")?;
+
+        let contract_based_connectors: CalContractScoreResponse = client
+            .calculate_contract_score(
+                profile_id.get_string_repr().into(),
+                contract_based_routing_configs,
+                "".to_string(),
+                routable_connectors,
+                state.get_grpc_headers(),
+            )
+            .await
+            .change_context(errors::RoutingError::ContractScoreCalculationError)
+            .attach_printable(
+                "unable to calculate/fetch contract score from dynamic routing service",
+            )?;
+
+        let mut connectors = Vec::with_capacity(contract_based_connectors.labels_with_score.len());
+
+        for label_with_score in contract_based_connectors.labels_with_score {
+            let (connector, merchant_connector_id) = label_with_score.label
+                .split_once(':')
+                .ok_or(errors::RoutingError::InvalidContractBasedConnectorLabel(label_with_score.label.to_string()))
+                .attach_printable(
+                    "unable to split connector_name and mca_id from the label obtained by the dynamic routing service",
+                )?;
+
+            connectors.push(api_routing::RoutableConnectorChoice {
+                choice_kind: api_routing::RoutableChoiceKind::FullStruct,
+                connector: common_enums::RoutableConnectors::from_str(connector)
+                    .change_context(errors::RoutingError::GenericConversionError {
+                        from: "String".to_string(),
+                        to: "RoutableConnectors".to_string(),
+                    })
+                    .attach_printable("unable to convert String to RoutableConnectors")?,
+                merchant_connector_id: Some(
+                    common_utils::id_type::MerchantConnectorAccountId::wrap(
+                        merchant_connector_id.to_string(),
+                    )
+                    .change_context(errors::RoutingError::GenericConversionError {
+                        from: "String".to_string(),
+                        to: "MerchantConnectorAccountId".to_string(),
+                    })
+                    .attach_printable("unable to convert MerchantConnectorAccountId from string")?,
+                ),
+            });
+        }
+
+        logger::debug!(contract_based_routing_connectors=?connectors);
+        Ok(connectors)
+    } else {
+        Ok(routable_connectors)
+    }
+}
diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs
index 99bd2b00209..8d03e82a328 100644
--- a/crates/router/src/core/routing.rs
+++ b/crates/router/src/core/routing.rs
@@ -2,6 +2,8 @@ pub mod helpers;
 pub mod transformers;
 use std::collections::HashSet;
 
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+use api_models::routing::DynamicRoutingAlgoAccessor;
 use api_models::{
     enums, mandates as mandates_api, routing,
     routing::{self as routing_types, RoutingRetrieveQuery},
@@ -12,7 +14,10 @@ use common_utils::ext_traits::AsyncExt;
 use diesel_models::routing_algorithm::RoutingAlgorithm;
 use error_stack::ResultExt;
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
-use external_services::grpc_client::dynamic_routing::success_rate_client::SuccessBasedDynamicRouting;
+use external_services::grpc_client::dynamic_routing::{
+    contract_routing_client::ContractBasedDynamicRouting,
+    success_rate_client::SuccessBasedDynamicRouting,
+};
 use hyperswitch_domain_models::{mandates, payment_address};
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
 use router_env::logger;
@@ -462,6 +467,16 @@ pub async fn link_routing_config(
                         },
                         enabled_feature: _
                     }) if id == &algorithm_id
+                ) || matches!(
+                    dynamic_routing_ref.contract_based_routing,
+                    Some(routing::ContractRoutingAlgorithm {
+                        algorithm_id_with_timestamp:
+                        routing_types::DynamicAlgorithmWithTimestamp {
+                            algorithm_id: Some(ref id),
+                            timestamp: _
+                        },
+                        enabled_feature: _
+                    }) if id == &algorithm_id
                 ),
                 || {
                     Err(errors::ApiErrorResponse::PreconditionFailed {
@@ -470,7 +485,8 @@ pub async fn link_routing_config(
                 },
             )?;
 
-            dynamic_routing_ref.update_algorithm_id(
+            if routing_algorithm.name == helpers::SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM {
+                dynamic_routing_ref.update_algorithm_id(
                 algorithm_id,
                 dynamic_routing_ref
                     .success_based_algorithm
@@ -482,6 +498,34 @@ pub async fn link_routing_config(
                     .enabled_feature,
                 routing_types::DynamicRoutingType::SuccessRateBasedRouting,
             );
+            } else if routing_algorithm.name == helpers::ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM
+            {
+                dynamic_routing_ref.update_algorithm_id(
+                algorithm_id,
+                dynamic_routing_ref
+                    .elimination_routing_algorithm
+                    .clone()
+                    .ok_or(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable(
+                        "missing elimination_routing_algorithm in dynamic_algorithm_ref from business_profile table",
+                    )?
+                    .enabled_feature,
+                routing_types::DynamicRoutingType::EliminationRouting,
+            );
+            } else if routing_algorithm.name == helpers::CONTRACT_BASED_DYNAMIC_ROUTING_ALGORITHM {
+                dynamic_routing_ref.update_algorithm_id(
+                algorithm_id,
+                dynamic_routing_ref
+                    .contract_based_routing
+                    .clone()
+                    .ok_or(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable(
+                        "missing contract_based_routing in dynamic_algorithm_ref from business_profile table",
+                    )?
+                    .enabled_feature,
+                routing_types::DynamicRoutingType::ContractBasedRouting,
+            );
+            }
 
             helpers::update_business_profile_active_dynamic_algorithm_ref(
                 db,
@@ -1419,6 +1463,304 @@ pub async fn success_based_routing_update_configs(
     Ok(service_api::ApplicationResponse::Json(new_record))
 }
 
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+pub async fn contract_based_dynamic_routing_setup(
+    state: SessionState,
+    key_store: domain::MerchantKeyStore,
+    merchant_account: domain::MerchantAccount,
+    profile_id: common_utils::id_type::ProfileId,
+    feature_to_enable: routing_types::DynamicRoutingFeatures,
+    config: Option<routing_types::ContractBasedRoutingConfig>,
+) -> RouterResult<service_api::ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
+    let db = state.store.as_ref();
+    let key_manager_state = &(&state).into();
+
+    let business_profile: domain::Profile = core_utils::validate_and_get_business_profile(
+        db,
+        key_manager_state,
+        &key_store,
+        Some(&profile_id),
+        merchant_account.get_id(),
+    )
+    .await?
+    .get_required_value("Profile")
+    .change_context(errors::ApiErrorResponse::ProfileNotFound {
+        id: profile_id.get_string_repr().to_owned(),
+    })?;
+
+    let mut dynamic_routing_algo_ref: Option<routing_types::DynamicRoutingAlgorithmRef> =
+        business_profile
+            .dynamic_routing_algorithm
+            .clone()
+            .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
+            .transpose()
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable(
+                "unable to deserialize dynamic routing algorithm ref from business profile",
+            )
+            .ok()
+            .flatten();
+
+    utils::when(
+        dynamic_routing_algo_ref
+            .as_mut()
+            .and_then(|algo| {
+                algo.contract_based_routing.as_mut().map(|contract_algo| {
+                    *contract_algo.get_enabled_features() == feature_to_enable
+                        && contract_algo
+                            .clone()
+                            .get_algorithm_id_with_timestamp()
+                            .algorithm_id
+                            .is_some()
+                })
+            })
+            .unwrap_or(false),
+        || {
+            Err(errors::ApiErrorResponse::PreconditionFailed {
+                message: "Contract Routing with specified features is already enabled".to_string(),
+            })
+        },
+    )?;
+
+    if feature_to_enable == routing::DynamicRoutingFeatures::None {
+        let algorithm = dynamic_routing_algo_ref
+            .clone()
+            .get_required_value("dynamic_routing_algo_ref")
+            .attach_printable("Failed to get dynamic_routing_algo_ref")?;
+        return helpers::disable_dynamic_routing_algorithm(
+            &state,
+            key_store,
+            business_profile,
+            algorithm,
+            routing_types::DynamicRoutingType::ContractBasedRouting,
+        )
+        .await;
+    }
+
+    let config = config
+        .get_required_value("ContractBasedRoutingConfig")
+        .attach_printable("Failed to get ContractBasedRoutingConfig from request")?;
+
+    let merchant_id = business_profile.merchant_id.clone();
+    let algorithm_id = common_utils::generate_routing_id_of_default_length();
+    let timestamp = common_utils::date_time::now();
+
+    let algo = RoutingAlgorithm {
+        algorithm_id: algorithm_id.clone(),
+        profile_id: profile_id.clone(),
+        merchant_id,
+        name: helpers::CONTRACT_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
+        description: None,
+        kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
+        algorithm_data: serde_json::json!(config),
+        created_at: timestamp,
+        modified_at: timestamp,
+        algorithm_for: common_enums::TransactionType::Payment,
+    };
+
+    // 1. if dynamic_routing_algo_ref already present, insert contract based algo and disable success based
+    // 2. if dynamic_routing_algo_ref is not present, create a new dynamic_routing_algo_ref with contract algo set up
+    let final_algorithm = if let Some(mut algo) = dynamic_routing_algo_ref {
+        algo.update_algorithm_id(
+            algorithm_id,
+            feature_to_enable,
+            routing_types::DynamicRoutingType::ContractBasedRouting,
+        );
+        if feature_to_enable == routing::DynamicRoutingFeatures::DynamicConnectorSelection {
+            algo.disable_algorithm_id(routing_types::DynamicRoutingType::SuccessRateBasedRouting);
+        }
+        algo
+    } else {
+        let contract_algo = routing_types::ContractRoutingAlgorithm {
+            algorithm_id_with_timestamp: routing_types::DynamicAlgorithmWithTimestamp::new(Some(
+                algorithm_id.clone(),
+            )),
+            enabled_feature: feature_to_enable,
+        };
+        routing_types::DynamicRoutingAlgorithmRef {
+            success_based_algorithm: None,
+            elimination_routing_algorithm: None,
+            dynamic_routing_volume_split: None,
+            contract_based_routing: Some(contract_algo),
+        }
+    };
+
+    // validate the contained mca_ids
+    if let Some(info_vec) = &config.label_info {
+        let validation_futures: Vec<_> = info_vec
+            .iter()
+            .map(|info| async {
+                let mca_id = info.mca_id.clone();
+                let label = info.label.clone();
+                let mca = db
+                    .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
+                        key_manager_state,
+                        merchant_account.get_id(),
+                        &mca_id,
+                        &key_store,
+                    )
+                    .await
+                    .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
+                        id: mca_id.get_string_repr().to_owned(),
+                    })?;
+
+                utils::when(mca.connector_name != label, || {
+                    Err(error_stack::Report::new(
+                        errors::ApiErrorResponse::InvalidRequestData {
+                            message: "Incorrect mca configuration received".to_string(),
+                        },
+                    ))
+                })?;
+
+                Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(())
+            })
+            .collect();
+
+        futures::future::try_join_all(validation_futures).await?;
+    }
+
+    let record = db
+        .insert_routing_algorithm(algo)
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Unable to insert record in routing algorithm table")?;
+
+    helpers::update_business_profile_active_dynamic_algorithm_ref(
+        db,
+        key_manager_state,
+        &key_store,
+        business_profile,
+        final_algorithm,
+    )
+    .await?;
+
+    let new_record = record.foreign_into();
+
+    metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
+        1,
+        router_env::metric_attributes!(("profile_id", profile_id.get_string_repr().to_string())),
+    );
+    Ok(service_api::ApplicationResponse::Json(new_record))
+}
+
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+pub async fn contract_based_routing_update_configs(
+    state: SessionState,
+    request: routing_types::ContractBasedRoutingConfig,
+    merchant_account: domain::MerchantAccount,
+    key_store: domain::MerchantKeyStore,
+    algorithm_id: common_utils::id_type::RoutingId,
+    profile_id: common_utils::id_type::ProfileId,
+) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
+    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE.add(
+        1,
+        router_env::metric_attributes!(("profile_id", profile_id.get_string_repr().to_owned())),
+    );
+    let db = state.store.as_ref();
+    let key_manager_state = &(&state).into();
+
+    let dynamic_routing_algo_to_update = db
+        .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algorithm_id)
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
+
+    let mut config_to_update: routing::ContractBasedRoutingConfig = dynamic_routing_algo_to_update
+        .algorithm_data
+        .parse_value::<routing::ContractBasedRoutingConfig>("ContractBasedRoutingConfig")
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("unable to deserialize algorithm data from routing table into ContractBasedRoutingConfig")?;
+
+    // validate the contained mca_ids
+    if let Some(info_vec) = &request.label_info {
+        for info in info_vec {
+            let mca = db
+                .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
+                    key_manager_state,
+                    merchant_account.get_id(),
+                    &info.mca_id,
+                    &key_store,
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
+                    id: info.mca_id.get_string_repr().to_owned(),
+                })?;
+
+            utils::when(mca.connector_name != info.label, || {
+                Err(errors::ApiErrorResponse::InvalidRequestData {
+                    message: "Incorrect mca configuration received".to_string(),
+                })
+            })?;
+        }
+    }
+
+    config_to_update.update(request);
+
+    let updated_algorithm_id = common_utils::generate_routing_id_of_default_length();
+    let timestamp = common_utils::date_time::now();
+    let algo = RoutingAlgorithm {
+        algorithm_id: updated_algorithm_id,
+        profile_id: dynamic_routing_algo_to_update.profile_id,
+        merchant_id: dynamic_routing_algo_to_update.merchant_id,
+        name: dynamic_routing_algo_to_update.name,
+        description: dynamic_routing_algo_to_update.description,
+        kind: dynamic_routing_algo_to_update.kind,
+        algorithm_data: serde_json::json!(config_to_update),
+        created_at: timestamp,
+        modified_at: timestamp,
+        algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
+    };
+    let record = db
+        .insert_routing_algorithm(algo)
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Unable to insert record in routing algorithm table")?;
+
+    // redact cache for contract based routing configs
+    let cache_key = format!(
+        "{}_{}",
+        profile_id.get_string_repr(),
+        algorithm_id.get_string_repr()
+    );
+    let cache_entries_to_redact = vec![cache::CacheKind::ContractBasedDynamicRoutingCache(
+        cache_key.into(),
+    )];
+    let _ = cache::redact_from_redis_and_publish(
+        state.store.get_cache_store().as_ref(),
+        cache_entries_to_redact,
+    )
+    .await
+    .map_err(|e| logger::error!("unable to publish into the redact channel for evicting the contract based routing config cache {e:?}"));
+
+    let new_record = record.foreign_into();
+
+    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE.add(
+        1,
+        router_env::metric_attributes!(("profile_id", profile_id.get_string_repr().to_owned())),
+    );
+
+    state
+        .grpc_client
+        .clone()
+        .dynamic_routing
+        .contract_based_client
+        .clone()
+        .async_map(|ct_client| async move {
+            ct_client
+                .invalidate_contracts(
+                    profile_id.get_string_repr().into(),
+                    state.get_grpc_headers(),
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::GenericNotFoundError {
+                    message: "Failed to invalidate the contract based routing keys".to_string(),
+                })
+        })
+        .await
+        .transpose()?;
+
+    Ok(service_api::ApplicationResponse::Json(new_record))
+}
+
 #[async_trait]
 pub trait GetRoutableConnectorsForChoice {
     async fn get_routable_connectors(
diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs
index 84de32483a8..9b6396013f4 100644
--- a/crates/router/src/core/routing/helpers.rs
+++ b/crates/router/src/core/routing/helpers.rs
@@ -2,6 +2,7 @@
 //!
 //! Functions that are used to perform the retrieval of merchant's
 //! routing dict, configs, defaults
+use std::fmt::Debug;
 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
 use std::str::FromStr;
 #[cfg(any(feature = "dynamic_routing", feature = "v1"))]
@@ -18,7 +19,10 @@ use diesel_models::dynamic_routing_stats::DynamicRoutingStatsNew;
 use diesel_models::routing_algorithm;
 use error_stack::ResultExt;
 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
-use external_services::grpc_client::dynamic_routing::success_rate_client::SuccessBasedDynamicRouting;
+use external_services::grpc_client::dynamic_routing::{
+    contract_routing_client::ContractBasedDynamicRouting,
+    success_rate_client::SuccessBasedDynamicRouting,
+};
 #[cfg(feature = "v1")]
 use hyperswitch_domain_models::api::ApplicationResponse;
 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
@@ -26,7 +30,7 @@ use router_env::logger;
 #[cfg(any(feature = "dynamic_routing", feature = "v1"))]
 use router_env::{instrument, tracing};
 use rustc_hash::FxHashSet;
-use storage_impl::redis::cache;
+use storage_impl::redis::cache::{self, Cacheable};
 
 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
 use crate::db::errors::StorageErrorExt;
@@ -45,6 +49,8 @@ pub const SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
     "Success rate based dynamic routing algorithm";
 pub const ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
     "Elimination based dynamic routing algorithm";
+pub const CONTRACT_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
+    "Contract based dynamic routing algorithm";
 
 /// Provides us with all the configured configs of the Merchant in the ascending time configured
 /// manner and chooses the first of them
@@ -562,78 +568,146 @@ pub fn get_default_config_key(
     }
 }
 
-/// Retrieves cached success_based routing configs specific to tenant and profile
-#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
-pub async fn get_cached_success_based_routing_config_for_profile(
-    state: &SessionState,
-    key: &str,
-) -> Option<Arc<routing_types::SuccessBasedRoutingConfig>> {
-    cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
-        .get_val::<Arc<routing_types::SuccessBasedRoutingConfig>>(cache::CacheKey {
-            key: key.to_string(),
-            prefix: state.tenant.redis_key_prefix.clone(),
-        })
+#[async_trait::async_trait]
+pub trait DynamicRoutingCache {
+    async fn get_cached_dynamic_routing_config_for_profile(
+        state: &SessionState,
+        key: &str,
+    ) -> Option<Arc<Self>>;
+
+    async fn refresh_dynamic_routing_cache<T, F, Fut>(
+        state: &SessionState,
+        key: &str,
+        func: F,
+    ) -> RouterResult<T>
+    where
+        F: FnOnce() -> Fut + Send,
+        T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone,
+        Fut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>> + Send;
+}
+
+#[async_trait::async_trait]
+impl DynamicRoutingCache for routing_types::SuccessBasedRoutingConfig {
+    async fn get_cached_dynamic_routing_config_for_profile(
+        state: &SessionState,
+        key: &str,
+    ) -> Option<Arc<Self>> {
+        cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
+            .get_val::<Arc<Self>>(cache::CacheKey {
+                key: key.to_string(),
+                prefix: state.tenant.redis_key_prefix.clone(),
+            })
+            .await
+    }
+
+    async fn refresh_dynamic_routing_cache<T, F, Fut>(
+        state: &SessionState,
+        key: &str,
+        func: F,
+    ) -> RouterResult<T>
+    where
+        F: FnOnce() -> Fut + Send,
+        T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone,
+        Fut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>> + Send,
+    {
+        cache::get_or_populate_in_memory(
+            state.store.get_cache_store().as_ref(),
+            key,
+            func,
+            &cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE,
+        )
         .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("unable to populate SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE")
+    }
 }
 
-/// Refreshes the cached success_based routing configs specific to tenant and profile
-#[cfg(feature = "v1")]
-pub async fn refresh_success_based_routing_cache(
-    state: &SessionState,
-    key: &str,
-    success_based_routing_config: routing_types::SuccessBasedRoutingConfig,
-) -> Arc<routing_types::SuccessBasedRoutingConfig> {
-    let config = Arc::new(success_based_routing_config);
-    cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
-        .push(
-            cache::CacheKey {
+#[async_trait::async_trait]
+impl DynamicRoutingCache for routing_types::ContractBasedRoutingConfig {
+    async fn get_cached_dynamic_routing_config_for_profile(
+        state: &SessionState,
+        key: &str,
+    ) -> Option<Arc<Self>> {
+        cache::CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE
+            .get_val::<Arc<Self>>(cache::CacheKey {
                 key: key.to_string(),
                 prefix: state.tenant.redis_key_prefix.clone(),
-            },
-            config.clone(),
+            })
+            .await
+    }
+
+    async fn refresh_dynamic_routing_cache<T, F, Fut>(
+        state: &SessionState,
+        key: &str,
+        func: F,
+    ) -> RouterResult<T>
+    where
+        F: FnOnce() -> Fut + Send,
+        T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone,
+        Fut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>> + Send,
+    {
+        cache::get_or_populate_in_memory(
+            state.store.get_cache_store().as_ref(),
+            key,
+            func,
+            &cache::CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE,
         )
-        .await;
-    config
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("unable to populate CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE")
+    }
 }
 
-/// Checked fetch of success based routing configs
+/// Cfetch dynamic routing configs
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
 #[instrument(skip_all)]
-pub async fn fetch_success_based_routing_configs(
+pub async fn fetch_dynamic_routing_configs<T>(
     state: &SessionState,
-    business_profile: &domain::Profile,
-    success_based_routing_id: id_type::RoutingId,
-) -> RouterResult<routing_types::SuccessBasedRoutingConfig> {
+    profile_id: &id_type::ProfileId,
+    routing_id: id_type::RoutingId,
+) -> RouterResult<T>
+where
+    T: serde::de::DeserializeOwned
+        + Clone
+        + DynamicRoutingCache
+        + Cacheable
+        + serde::Serialize
+        + Debug,
+{
     let key = format!(
         "{}_{}",
-        business_profile.get_id().get_string_repr(),
-        success_based_routing_id.get_string_repr()
+        profile_id.get_string_repr(),
+        routing_id.get_string_repr()
     );
 
     if let Some(config) =
-        get_cached_success_based_routing_config_for_profile(state, key.as_str()).await
+        T::get_cached_dynamic_routing_config_for_profile(state, key.as_str()).await
     {
         Ok(config.as_ref().clone())
     } else {
-        let success_rate_algorithm = state
-            .store
-            .find_routing_algorithm_by_profile_id_algorithm_id(
-                business_profile.get_id(),
-                &success_based_routing_id,
-            )
-            .await
-            .change_context(errors::ApiErrorResponse::ResourceIdNotFound)
-            .attach_printable("unable to retrieve success_rate_algorithm for profile from db")?;
-
-        let success_rate_config = success_rate_algorithm
-            .algorithm_data
-            .parse_value::<routing_types::SuccessBasedRoutingConfig>("SuccessBasedRoutingConfig")
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("unable to parse success_based_routing_config struct")?;
+        let func = || async {
+            let routing_algorithm = state
+                .store
+                .find_routing_algorithm_by_profile_id_algorithm_id(profile_id, &routing_id)
+                .await
+                .change_context(errors::StorageError::ValueNotFound(
+                    "RoutingAlgorithm".to_string(),
+                ))
+                .attach_printable("unable to retrieve routing_algorithm for profile from db")?;
+
+            let dynamic_routing_config = routing_algorithm
+                .algorithm_data
+                .parse_value::<T>("dynamic_routing_config")
+                .change_context(errors::StorageError::DeserializationFailed)
+                .attach_printable("unable to parse dynamic_routing_config")?;
+
+            Ok(dynamic_routing_config)
+        };
 
-        refresh_success_based_routing_cache(state, key.as_str(), success_rate_config.clone()).await;
+        let dynamic_routing_config =
+            T::refresh_dynamic_routing_cache(state, key.as_str(), func).await?;
 
-        Ok(success_rate_config)
+        Ok(dynamic_routing_config)
     }
 }
 
@@ -644,20 +718,11 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
     state: &SessionState,
     payment_attempt: &storage::PaymentAttempt,
     routable_connectors: Vec<routing_types::RoutableConnectorChoice>,
-    business_profile: &domain::Profile,
-    success_based_routing_config_params_interpolator: SuccessBasedRoutingConfigParamsInterpolator,
+    profile_id: &id_type::ProfileId,
+    dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
+    dynamic_routing_config_params_interpolator: DynamicRoutingConfigParamsInterpolator,
 ) -> RouterResult<()> {
-    let success_based_dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef =
-        business_profile
-            .dynamic_routing_algorithm
-            .clone()
-            .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
-            .transpose()
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("Failed to deserialize DynamicRoutingAlgorithmRef from JSON")?
-            .unwrap_or_default();
-
-    let success_based_algo_ref = success_based_dynamic_routing_algo_ref
+    let success_based_algo_ref = dynamic_routing_algo_ref
         .success_based_algorithm
         .ok_or(errors::ApiErrorResponse::InternalServerError)
         .attach_printable("success_based_algorithm not found in dynamic_routing_algorithm from business_profile table")?;
@@ -678,22 +743,23 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
             },
         )?;
 
-        let success_based_routing_configs = fetch_success_based_routing_configs(
-            state,
-            business_profile,
-            success_based_algo_ref
-                .algorithm_id_with_timestamp
-                .algorithm_id
-                .ok_or(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable(
-                    "success_based_routing_algorithm_id not found in business_profile",
-                )?,
-        )
-        .await
-        .change_context(errors::ApiErrorResponse::InternalServerError)
-        .attach_printable("unable to retrieve success_rate based dynamic routing configs")?;
+        let success_based_routing_configs =
+            fetch_dynamic_routing_configs::<routing_types::SuccessBasedRoutingConfig>(
+                state,
+                profile_id,
+                success_based_algo_ref
+                    .algorithm_id_with_timestamp
+                    .algorithm_id
+                    .ok_or(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable(
+                        "success_based_routing_algorithm_id not found in business_profile",
+                    )?,
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("unable to retrieve success_rate based dynamic routing configs")?;
 
-        let success_based_routing_config_params = success_based_routing_config_params_interpolator
+        let success_based_routing_config_params = dynamic_routing_config_params_interpolator
             .get_string_val(
                 success_based_routing_configs
                     .params
@@ -704,7 +770,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
 
         let success_based_connectors = client
             .calculate_entity_and_global_success_rate(
-                business_profile.get_id().get_string_repr().into(),
+                profile_id.get_string_repr().into(),
                 success_based_routing_configs.clone(),
                 success_based_routing_config_params.clone(),
                 routable_connectors.clone(),
@@ -717,7 +783,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
             )?;
 
         let payment_status_attribute =
-            get_desired_payment_status_for_success_routing_metrics(payment_attempt.status);
+            get_desired_payment_status_for_dynamic_routing_metrics(payment_attempt.status);
 
         let first_merchant_success_based_connector = &success_based_connectors
             .entity_scores_with_labels
@@ -743,7 +809,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
                 "unable to fetch the first global connector from list of connectors obtained from dynamic routing service",
             )?;
 
-        let outcome = get_success_based_metrics_outcome_for_payment(
+        let outcome = get_dynamic_routing_based_metrics_outcome_for_payment(
             payment_status_attribute,
             payment_connector.to_string(),
             first_merchant_success_based_connector_label.to_string(),
@@ -852,7 +918,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
 
         client
             .update_success_rate(
-                business_profile.get_id().get_string_repr().into(),
+                profile_id.get_string_repr().into(),
                 success_based_routing_configs,
                 success_based_routing_config_params,
                 vec![routing_types::RoutableConnectorChoiceWithStatus::new(
@@ -880,8 +946,212 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
     }
 }
 
+/// metrics for contract based dynamic routing
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+#[instrument(skip_all)]
+pub async fn push_metrics_with_update_window_for_contract_based_routing(
+    state: &SessionState,
+    payment_attempt: &storage::PaymentAttempt,
+    routable_connectors: Vec<routing_types::RoutableConnectorChoice>,
+    profile_id: &id_type::ProfileId,
+    dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
+    _dynamic_routing_config_params_interpolator: DynamicRoutingConfigParamsInterpolator,
+) -> RouterResult<()> {
+    let contract_routing_algo_ref = dynamic_routing_algo_ref
+        .contract_based_routing
+        .ok_or(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("contract_routing_algorithm not found in dynamic_routing_algorithm from business_profile table")?;
+
+    if contract_routing_algo_ref.enabled_feature != routing_types::DynamicRoutingFeatures::None {
+        let client = state
+            .grpc_client
+            .dynamic_routing
+            .contract_based_client
+            .clone()
+            .ok_or(errors::ApiErrorResponse::GenericNotFoundError {
+                message: "contract_routing gRPC client not found".to_string(),
+            })?;
+
+        let payment_connector = &payment_attempt.connector.clone().ok_or(
+            errors::ApiErrorResponse::GenericNotFoundError {
+                message: "unable to derive payment connector from payment attempt".to_string(),
+            },
+        )?;
+
+        let contract_based_routing_config =
+            fetch_dynamic_routing_configs::<routing_types::ContractBasedRoutingConfig>(
+                state,
+                profile_id,
+                contract_routing_algo_ref
+                    .algorithm_id_with_timestamp
+                    .algorithm_id
+                    .ok_or(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable(
+                        "contract_based_routing_algorithm_id not found in business_profile",
+                    )?,
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("unable to retrieve contract based dynamic routing configs")?;
+
+        let mut existing_label_info = None;
+
+        contract_based_routing_config
+            .label_info
+            .as_ref()
+            .map(|label_info_vec| {
+                for label_info in label_info_vec {
+                    if Some(&label_info.mca_id) == payment_attempt.merchant_connector_id.as_ref() {
+                        existing_label_info = Some(label_info.clone());
+                    }
+                }
+            });
+
+        let final_label_info = existing_label_info
+            .ok_or(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("unable to get LabelInformation from ContractBasedRoutingConfig")?;
+
+        logger::debug!(
+            "contract based routing: matched LabelInformation - {:?}",
+            final_label_info
+        );
+
+        let request_label_info = routing_types::LabelInformation {
+            label: format!(
+                "{}:{}",
+                final_label_info.label.clone(),
+                final_label_info.mca_id.get_string_repr()
+            ),
+            target_count: final_label_info.target_count,
+            target_time: final_label_info.target_time,
+            mca_id: final_label_info.mca_id.to_owned(),
+        };
+
+        let payment_status_attribute =
+            get_desired_payment_status_for_dynamic_routing_metrics(payment_attempt.status);
+
+        if payment_status_attribute == common_enums::AttemptStatus::Charged {
+            client
+                .update_contracts(
+                    profile_id.get_string_repr().into(),
+                    vec![request_label_info],
+                    "".to_string(),
+                    vec![],
+                    state.get_grpc_headers(),
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable(
+                    "unable to update contract based routing window in dynamic routing service",
+                )?;
+        }
+
+        let contract_scores = client
+            .calculate_contract_score(
+                profile_id.get_string_repr().into(),
+                contract_based_routing_config.clone(),
+                "".to_string(),
+                routable_connectors.clone(),
+                state.get_grpc_headers(),
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable(
+                "unable to calculate/fetch contract scores from dynamic routing service",
+            )?;
+
+        let first_contract_based_connector = &contract_scores
+            .labels_with_score
+            .first()
+            .ok_or(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable(
+                "unable to fetch the first connector from list of connectors obtained from dynamic routing service",
+            )?;
+
+        let (first_contract_based_connector, connector_score, current_payment_cnt) = (first_contract_based_connector.label
+            .split_once(':')
+            .ok_or(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable(format!(
+                "unable to split connector_name and mca_id from the first connector {:?} obtained from dynamic routing service",
+                first_contract_based_connector
+            ))?
+            .0, first_contract_based_connector.score, first_contract_based_connector.current_count );
+
+        core_metrics::DYNAMIC_CONTRACT_BASED_ROUTING.add(
+            1,
+            router_env::metric_attributes!(
+                (
+                    "tenant",
+                    state.tenant.tenant_id.get_string_repr().to_owned(),
+                ),
+                (
+                    "merchant_profile_id",
+                    format!(
+                        "{}:{}",
+                        payment_attempt.merchant_id.get_string_repr(),
+                        payment_attempt.profile_id.get_string_repr()
+                    ),
+                ),
+                (
+                    "contract_based_routing_connector",
+                    first_contract_based_connector.to_string(),
+                ),
+                (
+                    "contract_based_routing_connector_score",
+                    connector_score.to_string(),
+                ),
+                (
+                    "current_payment_count_contract_based_routing_connector",
+                    current_payment_cnt.to_string(),
+                ),
+                ("payment_connector", payment_connector.to_string()),
+                (
+                    "currency",
+                    payment_attempt
+                        .currency
+                        .map_or_else(|| "None".to_string(), |currency| currency.to_string()),
+                ),
+                (
+                    "payment_method",
+                    payment_attempt.payment_method.map_or_else(
+                        || "None".to_string(),
+                        |payment_method| payment_method.to_string(),
+                    ),
+                ),
+                (
+                    "payment_method_type",
+                    payment_attempt.payment_method_type.map_or_else(
+                        || "None".to_string(),
+                        |payment_method_type| payment_method_type.to_string(),
+                    ),
+                ),
+                (
+                    "capture_method",
+                    payment_attempt.capture_method.map_or_else(
+                        || "None".to_string(),
+                        |capture_method| capture_method.to_string(),
+                    ),
+                ),
+                (
+                    "authentication_type",
+                    payment_attempt.authentication_type.map_or_else(
+                        || "None".to_string(),
+                        |authentication_type| authentication_type.to_string(),
+                    ),
+                ),
+                ("payment_status", payment_attempt.status.to_string()),
+            ),
+        );
+        logger::debug!("successfully pushed contract_based_routing metrics");
+
+        Ok(())
+    } else {
+        Ok(())
+    }
+}
+
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
-fn get_desired_payment_status_for_success_routing_metrics(
+fn get_desired_payment_status_for_dynamic_routing_metrics(
     attempt_status: common_enums::AttemptStatus,
 ) -> common_enums::AttemptStatus {
     match attempt_status {
@@ -917,7 +1187,7 @@ fn get_desired_payment_status_for_success_routing_metrics(
 }
 
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
-fn get_success_based_metrics_outcome_for_payment(
+fn get_dynamic_routing_based_metrics_outcome_for_payment(
     payment_status_attribute: common_enums::AttemptStatus,
     payment_connector: String,
     first_success_based_connector: String,
@@ -957,7 +1227,6 @@ pub async fn disable_dynamic_routing_algorithm(
 ) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
     let db = state.store.as_ref();
     let key_manager_state = &state.into();
-    let timestamp = common_utils::date_time::now_unix_timestamp();
     let profile_id = business_profile.get_id().clone();
     let (algorithm_id, dynamic_routing_algorithm, cache_entries_to_redact) =
         match dynamic_routing_type {
@@ -988,14 +1257,12 @@ pub async fn disable_dynamic_routing_algorithm(
                     routing_types::DynamicRoutingAlgorithmRef {
                         success_based_algorithm: Some(routing_types::SuccessBasedAlgorithm {
                             algorithm_id_with_timestamp:
-                                routing_types::DynamicAlgorithmWithTimestamp {
-                                    algorithm_id: None,
-                                    timestamp,
-                                },
+                                routing_types::DynamicAlgorithmWithTimestamp::new(None),
                             enabled_feature: routing_types::DynamicRoutingFeatures::None,
                         }),
                         elimination_routing_algorithm: dynamic_routing_algo_ref
                             .elimination_routing_algorithm,
+                        contract_based_routing: dynamic_routing_algo_ref.contract_based_routing,
                         dynamic_routing_volume_split: dynamic_routing_algo_ref
                             .dynamic_routing_volume_split,
                     },
@@ -1033,13 +1300,49 @@ pub async fn disable_dynamic_routing_algorithm(
                         elimination_routing_algorithm: Some(
                             routing_types::EliminationRoutingAlgorithm {
                                 algorithm_id_with_timestamp:
-                                    routing_types::DynamicAlgorithmWithTimestamp {
-                                        algorithm_id: None,
-                                        timestamp,
-                                    },
+                                    routing_types::DynamicAlgorithmWithTimestamp::new(None),
                                 enabled_feature: routing_types::DynamicRoutingFeatures::None,
                             },
                         ),
+                        contract_based_routing: dynamic_routing_algo_ref.contract_based_routing,
+                    },
+                    cache_entries_to_redact,
+                )
+            }
+            routing_types::DynamicRoutingType::ContractBasedRouting => {
+                let Some(algorithm_ref) = dynamic_routing_algo_ref.contract_based_routing else {
+                    Err(errors::ApiErrorResponse::PreconditionFailed {
+                        message: "Contract routing is already disabled".to_string(),
+                    })?
+                };
+                let Some(algorithm_id) = algorithm_ref.algorithm_id_with_timestamp.algorithm_id
+                else {
+                    Err(errors::ApiErrorResponse::PreconditionFailed {
+                        message: "Algorithm is already inactive".to_string(),
+                    })?
+                };
+                let cache_key = format!(
+                    "{}_{}",
+                    business_profile.get_id().get_string_repr(),
+                    algorithm_id.get_string_repr()
+                );
+                let cache_entries_to_redact =
+                    vec![cache::CacheKind::ContractBasedDynamicRoutingCache(
+                        cache_key.into(),
+                    )];
+                (
+                    algorithm_id,
+                    routing_types::DynamicRoutingAlgorithmRef {
+                        success_based_algorithm: dynamic_routing_algo_ref.success_based_algorithm,
+                        elimination_routing_algorithm: dynamic_routing_algo_ref
+                            .elimination_routing_algorithm,
+                        dynamic_routing_volume_split: dynamic_routing_algo_ref
+                            .dynamic_routing_volume_split,
+                        contract_based_routing: Some(routing_types::ContractRoutingAlgorithm {
+                            algorithm_id_with_timestamp:
+                                routing_types::DynamicAlgorithmWithTimestamp::new(None),
+                            enabled_feature: routing_types::DynamicRoutingFeatures::None,
+                        }),
                     },
                     cache_entries_to_redact,
                 )
@@ -1088,15 +1391,18 @@ pub async fn enable_dynamic_routing_algorithm(
     dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
     dynamic_routing_type: routing_types::DynamicRoutingType,
 ) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
-    let dynamic_routing = dynamic_routing_algo_ref.clone();
+    let mut dynamic_routing = dynamic_routing_algo_ref.clone();
     match dynamic_routing_type {
         routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
+            dynamic_routing
+                .disable_algorithm_id(routing_types::DynamicRoutingType::ContractBasedRouting);
+
             enable_specific_routing_algorithm(
                 state,
                 key_store,
                 business_profile,
                 feature_to_enable,
-                dynamic_routing_algo_ref,
+                dynamic_routing.clone(),
                 dynamic_routing_type,
                 dynamic_routing.success_based_algorithm,
             )
@@ -1108,12 +1414,18 @@ pub async fn enable_dynamic_routing_algorithm(
                 key_store,
                 business_profile,
                 feature_to_enable,
-                dynamic_routing_algo_ref,
+                dynamic_routing.clone(),
                 dynamic_routing_type,
                 dynamic_routing.elimination_routing_algorithm,
             )
             .await
         }
+        routing_types::DynamicRoutingType::ContractBasedRouting => {
+            Err((errors::ApiErrorResponse::InvalidRequestData {
+                message: "Contract routing cannot be set as default".to_string(),
+            })
+            .into())
+        }
     }
 }
 
@@ -1128,7 +1440,7 @@ pub async fn enable_specific_routing_algorithm<A>(
     algo_type: Option<A>,
 ) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>>
 where
-    A: routing_types::DynamicRoutingAlgoAccessor + Clone + std::fmt::Debug,
+    A: routing_types::DynamicRoutingAlgoAccessor + Clone + Debug,
 {
     // Algorithm wasn't created yet
     let Some(mut algo_type) = algo_type else {
@@ -1169,9 +1481,8 @@ where
         }
         .into());
     };
-    *algo_type_enabled_features = feature_to_enable.clone();
-    dynamic_routing_algo_ref
-        .update_specific_ref(dynamic_routing_type.clone(), feature_to_enable.clone());
+    *algo_type_enabled_features = feature_to_enable;
+    dynamic_routing_algo_ref.update_enabled_features(dynamic_routing_type, feature_to_enable);
     update_business_profile_active_dynamic_algorithm_ref(
         db,
         &state.into(),
@@ -1242,6 +1553,13 @@ pub async fn default_specific_dynamic_routing_setup(
                 algorithm_for: common_enums::TransactionType::Payment,
             }
         }
+
+        routing_types::DynamicRoutingType::ContractBasedRouting => {
+            return Err((errors::ApiErrorResponse::InvalidRequestData {
+                message: "Contract routing cannot be set as default".to_string(),
+            })
+            .into())
+        }
     };
 
     let record = db
@@ -1273,7 +1591,8 @@ pub async fn default_specific_dynamic_routing_setup(
     Ok(ApplicationResponse::Json(new_record))
 }
 
-pub struct SuccessBasedRoutingConfigParamsInterpolator {
+#[derive(Debug, Clone)]
+pub struct DynamicRoutingConfigParamsInterpolator {
     pub payment_method: Option<common_enums::PaymentMethod>,
     pub payment_method_type: Option<common_enums::PaymentMethodType>,
     pub authentication_type: Option<common_enums::AuthenticationType>,
@@ -1283,7 +1602,7 @@ pub struct SuccessBasedRoutingConfigParamsInterpolator {
     pub card_bin: Option<String>,
 }
 
-impl SuccessBasedRoutingConfigParamsInterpolator {
+impl DynamicRoutingConfigParamsInterpolator {
     pub fn new(
         payment_method: Option<common_enums::PaymentMethod>,
         payment_method_type: Option<common_enums::PaymentMethodType>,
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 7f7ea767108..c2363500c04 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -1870,6 +1870,10 @@ impl Profile {
                                 }),
                             )),
                     )
+                    .service(
+                        web::resource("/set_volume_split")
+                            .route(web::post().to(routing::set_dynamic_routing_volume_split)),
+                    )
                     .service(
                         web::scope("/elimination").service(
                             web::resource("/toggle")
@@ -1877,8 +1881,17 @@ impl Profile {
                         ),
                     )
                     .service(
-                        web::resource("/set_volume_split")
-                            .route(web::post().to(routing::set_dynamic_routing_volume_split)),
+                        web::scope("/contracts")
+                            .service(web::resource("/toggle").route(
+                                web::post().to(routing::contract_based_routing_setup_config),
+                            ))
+                            .service(web::resource("/config/{algorithm_id}").route(
+                                web::patch().to(|state, req, path, payload| {
+                                    routing::contract_based_routing_update_configs(
+                                        state, req, path, payload,
+                                    )
+                                }),
+                            )),
                     ),
             );
         }
diff --git a/crates/router/src/routes/metrics/bg_metrics_collector.rs b/crates/router/src/routes/metrics/bg_metrics_collector.rs
index f3ba7076e53..c0f4062e15d 100644
--- a/crates/router/src/routes/metrics/bg_metrics_collector.rs
+++ b/crates/router/src/routes/metrics/bg_metrics_collector.rs
@@ -14,6 +14,9 @@ pub fn spawn_metrics_collector(metrics_collection_interval_in_secs: Option<u16>)
         &cache::PM_FILTERS_CGRAPH_CACHE,
         &cache::DECISION_MANAGER_CACHE,
         &cache::SURCHARGE_CACHE,
+        &cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE,
+        &cache::CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE,
+        &cache::ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE,
     ];
 
     tokio::spawn(async move {
diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs
index e7227e1f752..ac0f997a278 100644
--- a/crates/router/src/routes/routing.rs
+++ b/crates/router/src/routes/routing.rs
@@ -1130,7 +1130,7 @@ pub async fn toggle_success_based_routing(
 pub async fn success_based_routing_update_configs(
     state: web::Data<AppState>,
     req: HttpRequest,
-    path: web::Path<routing_types::SuccessBasedRoutingUpdateConfigQuery>,
+    path: web::Path<routing_types::DynamicRoutingUpdateConfigQuery>,
     json_payload: web::Json<routing_types::SuccessBasedRoutingConfig>,
 ) -> impl Responder {
     let flow = Flow::UpdateDynamicRoutingConfigs;
@@ -1165,6 +1165,100 @@ pub async fn success_based_routing_update_configs(
     ))
     .await
 }
+
+#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
+#[instrument(skip_all)]
+pub async fn contract_based_routing_setup_config(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    path: web::Path<routing_types::ToggleDynamicRoutingPath>,
+    query: web::Query<api_models::routing::ToggleDynamicRoutingQuery>,
+    json_payload: Option<web::Json<routing_types::ContractBasedRoutingConfig>>,
+) -> impl Responder {
+    let flow = Flow::ToggleDynamicRouting;
+    let routing_payload_wrapper = routing_types::ContractBasedRoutingSetupPayloadWrapper {
+        config: json_payload.map(|json| json.into_inner()),
+        profile_id: path.into_inner().profile_id,
+        features_to_enable: query.into_inner().enable,
+    };
+    Box::pin(oss_api::server_wrap(
+        flow,
+        state,
+        &req,
+        routing_payload_wrapper.clone(),
+        |state,
+         auth: auth::AuthenticationData,
+         wrapper: routing_types::ContractBasedRoutingSetupPayloadWrapper,
+         _| async move {
+            Box::pin(routing::contract_based_dynamic_routing_setup(
+                state,
+                auth.key_store,
+                auth.merchant_account,
+                wrapper.profile_id,
+                wrapper.features_to_enable,
+                wrapper.config,
+            ))
+            .await
+        },
+        auth::auth_type(
+            &auth::HeaderAuth(auth::ApiKeyAuth),
+            &auth::JWTAuthProfileFromRoute {
+                profile_id: routing_payload_wrapper.profile_id,
+                required_permission: Permission::ProfileRoutingWrite,
+            },
+            req.headers(),
+        ),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
+#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
+#[instrument(skip_all)]
+pub async fn contract_based_routing_update_configs(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    path: web::Path<routing_types::DynamicRoutingUpdateConfigQuery>,
+    json_payload: web::Json<routing_types::ContractBasedRoutingConfig>,
+) -> impl Responder {
+    let flow = Flow::UpdateDynamicRoutingConfigs;
+    let routing_payload_wrapper = routing_types::ContractBasedRoutingPayloadWrapper {
+        updated_config: json_payload.into_inner(),
+        algorithm_id: path.algorithm_id.clone(),
+        profile_id: path.profile_id.clone(),
+    };
+    Box::pin(oss_api::server_wrap(
+        flow,
+        state,
+        &req,
+        routing_payload_wrapper.clone(),
+        |state,
+         auth: auth::AuthenticationData,
+         wrapper: routing_types::ContractBasedRoutingPayloadWrapper,
+         _| async {
+            Box::pin(routing::contract_based_routing_update_configs(
+                state,
+                wrapper.updated_config,
+                auth.merchant_account,
+                auth.key_store,
+                wrapper.algorithm_id,
+                wrapper.profile_id,
+            ))
+            .await
+        },
+        auth::auth_type(
+            &auth::HeaderAuth(auth::ApiKeyAuth),
+            &auth::JWTAuthProfileFromRoute {
+                profile_id: routing_payload_wrapper.profile_id,
+                required_permission: Permission::ProfileRoutingWrite,
+            },
+            req.headers(),
+        ),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 #[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
 #[instrument(skip_all)]
 pub async fn toggle_elimination_routing(
diff --git a/crates/storage_impl/src/redis/cache.rs b/crates/storage_impl/src/redis/cache.rs
index 8302b5bf933..fb752e64b09 100644
--- a/crates/storage_impl/src/redis/cache.rs
+++ b/crates/storage_impl/src/redis/cache.rs
@@ -92,6 +92,16 @@ pub static ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy<Cache> = Lazy::new(||
     )
 });
 
+/// Contract Routing based Dynamic Algorithm Cache
+pub static CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy<Cache> = Lazy::new(|| {
+    Cache::new(
+        "CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE",
+        CACHE_TTL,
+        CACHE_TTI,
+        Some(MAX_CAPACITY),
+    )
+});
+
 /// Trait which defines the behaviour of types that's gonna be stored in Cache
 pub trait Cacheable: Any + Send + Sync + DynClone {
     fn as_any(&self) -> &dyn Any;
@@ -113,6 +123,7 @@ pub enum CacheKind<'a> {
     CGraph(Cow<'a, str>),
     SuccessBasedDynamicRoutingCache(Cow<'a, str>),
     EliminationBasedDynamicRoutingCache(Cow<'a, str>),
+    ContractBasedDynamicRoutingCache(Cow<'a, str>),
     PmFiltersCGraph(Cow<'a, str>),
     All(Cow<'a, str>),
 }
@@ -128,6 +139,7 @@ impl CacheKind<'_> {
             | CacheKind::CGraph(key)
             | CacheKind::SuccessBasedDynamicRoutingCache(key)
             | CacheKind::EliminationBasedDynamicRoutingCache(key)
+            | CacheKind::ContractBasedDynamicRoutingCache(key)
             | CacheKind::PmFiltersCGraph(key)
             | CacheKind::All(key) => key,
         }
diff --git a/crates/storage_impl/src/redis/pub_sub.rs b/crates/storage_impl/src/redis/pub_sub.rs
index 373ac370e2f..28b76148765 100644
--- a/crates/storage_impl/src/redis/pub_sub.rs
+++ b/crates/storage_impl/src/redis/pub_sub.rs
@@ -6,8 +6,9 @@ use router_env::{logger, tracing::Instrument};
 
 use crate::redis::cache::{
     CacheKey, CacheKind, CacheRedact, ACCOUNTS_CACHE, CGRAPH_CACHE, CONFIG_CACHE,
-    DECISION_MANAGER_CACHE, ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE, PM_FILTERS_CGRAPH_CACHE,
-    ROUTING_CACHE, SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE, SURCHARGE_CACHE,
+    CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE, DECISION_MANAGER_CACHE,
+    ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE, PM_FILTERS_CGRAPH_CACHE, ROUTING_CACHE,
+    SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE, SURCHARGE_CACHE,
 };
 
 #[async_trait::async_trait]
@@ -147,6 +148,15 @@ impl PubSubInterface for std::sync::Arc<redis_interface::RedisConnectionPool> {
                                 .await;
                             key
                         }
+                        CacheKind::ContractBasedDynamicRoutingCache(key) => {
+                            CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE
+                                .remove(CacheKey {
+                                    key: key.to_string(),
+                                    prefix: message.tenant.clone(),
+                                })
+                                .await;
+                            key
+                        }
                         CacheKind::SuccessBasedDynamicRoutingCache(key) => {
                             SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
                                 .remove(CacheKey {
@@ -220,6 +230,12 @@ impl PubSubInterface for std::sync::Arc<redis_interface::RedisConnectionPool> {
                                     prefix: message.tenant.clone(),
                                 })
                                 .await;
+                            CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE
+                                .remove(CacheKey {
+                                    key: key.to_string(),
+                                    prefix: message.tenant.clone(),
+                                })
+                                .await;
                             ROUTING_CACHE
                                 .remove(CacheKey {
                                     key: key.to_string(),
diff --git a/proto/contract_routing.proto b/proto/contract_routing.proto
new file mode 100644
index 00000000000..6b842e3096e
--- /dev/null
+++ b/proto/contract_routing.proto
@@ -0,0 +1,76 @@
+syntax = "proto3";
+package contract_routing;
+
+service ContractScoreCalculator {
+    rpc FetchContractScore (CalContractScoreRequest) returns (CalContractScoreResponse);
+
+    rpc UpdateContract (UpdateContractRequest) returns (UpdateContractResponse);
+
+    rpc InvalidateContract (InvalidateContractRequest) returns (InvalidateContractResponse);
+}
+
+// API-1 types
+message CalContractScoreRequest {
+    string id = 1;
+    string params = 2;
+    repeated string labels = 3;
+    CalContractScoreConfig config = 4;
+}
+
+message CalContractScoreConfig {
+    repeated double constants = 1;
+    TimeScale time_scale = 2;
+}
+
+message TimeScale {
+    enum Scale {
+        Day = 0;
+        Month = 1;
+    }
+    Scale time_scale = 1;
+}
+
+message CalContractScoreResponse {
+    repeated ScoreData labels_with_score = 1;
+}
+
+message ScoreData {
+    double score = 1;
+    string label = 2;
+    uint64 current_count = 3;
+}
+
+// API-2 types
+message UpdateContractRequest {
+    string id = 1;
+    string params = 2;
+    repeated LabelInformation labels_information = 3;
+}
+
+message LabelInformation {
+    string label = 1;
+    uint64 target_count = 2;
+    uint64 target_time = 3;
+    uint64 current_count = 4;
+}
+
+message UpdateContractResponse {
+   enum UpdationStatus {
+      CONTRACT_UPDATION_SUCCEEDED = 0;
+      CONTRACT_UPDATION_FAILED = 1;
+   }
+   UpdationStatus status = 1;
+}
+
+// API-3 types
+message InvalidateContractRequest {
+    string id = 1;
+}
+
+message InvalidateContractResponse {
+   enum InvalidationStatus {
+      CONTRACT_INVALIDATION_SUCCEEDED = 0;
+      CONTRACT_INVALIDATION_FAILED = 1;
+   }
+   InvalidationStatus status = 1;
+}
\ No newline at end of file

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
- Built an interface for Contract based routing
- Integrated Contract based routing with hyperswitch
- Built new APIs for contract routing config setup
- Refactored some existing dynamic routing code


### Additional Changes

- [x] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
1. Enable Contract routing config
```
curl --location --request POST 'http://localhost:8080/account/sarthak2/business_profile/pro_YozTgS8HebvBlk0UaeWW/dynamic_routing/contracts/toggle?enable=dynamic_connector_selection' \
--header 'api-key: dev_1aD8YuFd6Ovanf3oOtAYobqI6qw4ZBRSfw6BgYbifWHpaopkisBtm8obNXkFbVJn' \
--header 'Content-Type: application/json' \
--data-raw '{
    "config": {
        "constants": [0.7,0.35],
        "time_scale": "day"
    },
    "label_info": [{
        "label": "stripe",
        "target_count": 10000,
        "incremental_count": 0,
        "target_time": 1780486655,
        "mca_id": "mca_5Mk0Qcum2tnbmKoyqeuL"
    }]
}'
```
Response - 
```
{
    "id": "routing_FlQ49V71J8ZCXEwPzFy9",
    "profile_id": "pro_YozTgS8HebvBlk0UaeWW",
    "name": "Contract based dynamic routing algorithm",
    "kind": "dynamic",
    "description": "",
    "created_at": 1734009361,
    "modified_at": 1734009361,
    "algorithm_for": "payment"
}
```
2. Set Volume split for dynamic routing 
```
curl --location --request POST 'http://localhost:8080/account/sarthak2/business_profile/pro_YozTgS8HebvBlk0UaeWW/dynamic_routing/set_volume_split?split=80' \
--header 'api-key: dev_1aD8YuFd6Ovanf3oOtAYobqI6qw4ZBRSfw6BgYbifWHpaopkisBtm8obNXkFbVJn'
```
3. Create a Payment
Metrics Population post payment - 
<img width="1512" alt="image" src="https://github.com/user-attachments/assets/3d21293d-7e2c-4b8f-8758-cf5fecf5c528" />



## Checklist
<!-- Put an `x` in the boxes that apply -->

- [ ] I formatted the code `cargo +nightly fmt --all`
- [ ] I addressed lints thrown by `cargo clippy`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
