Bug: Add endpoints to support ai chat interactions



- Add endpoints to talk to different ai services.
- Add config for URLS
- make feature flag for ai chat service that will help to manage feature across different environments.
diff --git a/config/config.example.toml b/config/config.example.toml
index 2c35b1778cf..22ad2d6c666 100644
--- a/config/config.example.toml
+++ b/config/config.example.toml
@@ -1132,4 +1132,8 @@ version = "HOSTNAME"        # value of HOSTNAME from deployment which tells its
 
 [platform]
 enabled = true # Enable or disable platform features
-allow_connected_merchants = false # Enable or disable connected merchant account features
\ No newline at end of file
+allow_connected_merchants = false # Enable or disable connected merchant account features
+
+[chat]
+enabled = false                                # Enable or disable chat features
+hyperswitch_ai_host = "http://0.0.0.0:8000"    # Hyperswitch ai workflow host
\ No newline at end of file
diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml
index cab8777aa97..4428b508017 100644
--- a/config/deployments/env_specific.toml
+++ b/config/deployments/env_specific.toml
@@ -380,4 +380,8 @@ connector_names = "connector_names"     # Comma-separated list of allowed connec
 [grpc_client.unified_connector_service]
 host = "localhost"                      # Unified Connector Service Client Host
 port = 8000                             # Unified Connector Service Client Port
-connection_timeout = 10                 # Connection Timeout Duration in Seconds
\ No newline at end of file
+connection_timeout = 10                 # Connection Timeout Duration in Seconds
+
+[chat]
+enabled = false                                # Enable or disable chat features
+hyperswitch_ai_host = "http://0.0.0.0:8000"    # Hyperswitch ai workflow host
diff --git a/config/development.toml b/config/development.toml
index 5a03b7a4b20..8c9a20362d1 100644
--- a/config/development.toml
+++ b/config/development.toml
@@ -1225,3 +1225,7 @@ connector_names = "stripe, adyen"               # Comma-separated list of allowe
 [infra_values]
 cluster = "CLUSTER"
 version = "HOSTNAME"
+
+[chat]
+enabled = false
+hyperswitch_ai_host = "http://0.0.0.0:8000"
\ No newline at end of file
diff --git a/config/docker_compose.toml b/config/docker_compose.toml
index 448fa0390f0..299644a8632 100644
--- a/config/docker_compose.toml
+++ b/config/docker_compose.toml
@@ -1108,6 +1108,10 @@ background_color = "#FFFFFF"
 enabled = true
 allow_connected_merchants = true
 
+[chat]
+enabled = false
+hyperswitch_ai_host = "http://0.0.0.0:8000"
+
 [authentication_providers]
 click_to_pay = {connector_list = "adyen, cybersource"}
 
diff --git a/crates/api_models/src/chat.rs b/crates/api_models/src/chat.rs
new file mode 100644
index 00000000000..c66b42cc4f4
--- /dev/null
+++ b/crates/api_models/src/chat.rs
@@ -0,0 +1,18 @@
+use common_utils::id_type;
+use masking::Secret;
+
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
+pub struct ChatRequest {
+    pub message: Secret<String>,
+}
+
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct ChatResponse {
+    pub response: Secret<serde_json::Value>,
+    pub merchant_id: id_type::MerchantId,
+    pub status: String,
+    #[serde(skip_serializing)]
+    pub query_executed: Option<Secret<String>>,
+    #[serde(skip_serializing)]
+    pub row_count: Option<i32>,
+}
diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs
index 768aed75a7a..dd54cd63222 100644
--- a/crates/api_models/src/events.rs
+++ b/crates/api_models/src/events.rs
@@ -1,4 +1,5 @@
 pub mod apple_pay_certificates_migration;
+pub mod chat;
 pub mod connector_onboarding;
 pub mod customer;
 pub mod dispute;
diff --git a/crates/api_models/src/events/chat.rs b/crates/api_models/src/events/chat.rs
new file mode 100644
index 00000000000..42fefe487e9
--- /dev/null
+++ b/crates/api_models/src/events/chat.rs
@@ -0,0 +1,5 @@
+use common_utils::events::{ApiEventMetric, ApiEventsType};
+
+use crate::chat::{ChatRequest, ChatResponse};
+
+common_utils::impl_api_event_type!(Chat, (ChatRequest, ChatResponse));
diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs
index cbc50ed7ff2..3b343459e9e 100644
--- a/crates/api_models/src/lib.rs
+++ b/crates/api_models/src/lib.rs
@@ -5,6 +5,7 @@ pub mod apple_pay_certificates_migration;
 pub mod authentication;
 pub mod blocklist;
 pub mod cards_info;
+pub mod chat;
 pub mod conditional_configs;
 pub mod connector_enums;
 pub mod connector_onboarding;
diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs
index c8cf1e8c579..3ef0ef555e6 100644
--- a/crates/common_utils/src/consts.rs
+++ b/crates/common_utils/src/consts.rs
@@ -191,3 +191,6 @@ pub const METRICS_HOST_TAG_NAME: &str = "host";
 
 /// API client request timeout (in seconds)
 pub const REQUEST_TIME_OUT: u64 = 30;
+
+/// API client request timeout for ai service (in seconds)
+pub const REQUEST_TIME_OUT_FOR_AI_SERVICE: u64 = 120;
diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs
index e1964296755..49be22fc931 100644
--- a/crates/common_utils/src/events.rs
+++ b/crates/common_utils/src/events.rs
@@ -137,6 +137,7 @@ pub enum ApiEventsType {
         profile_acquirer_id: id_type::ProfileAcquirerId,
     },
     ThreeDsDecisionRule,
+    Chat,
 }
 
 impl ApiEventMetric for serde_json::Value {}
diff --git a/crates/hyperswitch_domain_models/src/chat.rs b/crates/hyperswitch_domain_models/src/chat.rs
new file mode 100644
index 00000000000..31b9f806db7
--- /dev/null
+++ b/crates/hyperswitch_domain_models/src/chat.rs
@@ -0,0 +1,15 @@
+use common_utils::id_type;
+use masking::Secret;
+
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
+pub struct GetDataMessage {
+    pub message: Secret<String>,
+}
+
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
+pub struct HyperswitchAiDataRequest {
+    pub merchant_id: id_type::MerchantId,
+    pub profile_id: id_type::ProfileId,
+    pub org_id: id_type::OrganizationId,
+    pub query: GetDataMessage,
+}
diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs
index 35f619334c3..b89613c5413 100644
--- a/crates/hyperswitch_domain_models/src/lib.rs
+++ b/crates/hyperswitch_domain_models/src/lib.rs
@@ -6,6 +6,7 @@ pub mod business_profile;
 pub mod callback_mapper;
 pub mod card_testing_guard_data;
 pub mod cards_info;
+pub mod chat;
 pub mod connector_endpoints;
 pub mod consts;
 pub mod customer;
diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs
index 9129a9a78f6..d4eb34838d6 100644
--- a/crates/router/src/configs/secrets_transformers.rs
+++ b/crates/router/src/configs/secrets_transformers.rs
@@ -452,6 +452,7 @@ pub(crate) async fn fetch_raw_secrets(
 
     Settings {
         server: conf.server,
+        chat: conf.chat,
         master_database,
         redis: conf.redis,
         log: conf.log,
diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs
index 6157a4fab20..ae4c8a3cda0 100644
--- a/crates/router/src/configs/settings.rs
+++ b/crates/router/src/configs/settings.rs
@@ -70,6 +70,7 @@ pub struct Settings<S: SecretState> {
     pub server: Server,
     pub proxy: Proxy,
     pub env: Env,
+    pub chat: ChatSettings,
     pub master_database: SecretStateContainer<Database, S>,
     #[cfg(feature = "olap")]
     pub replica_database: SecretStateContainer<Database, S>,
@@ -195,6 +196,13 @@ pub struct Platform {
     pub allow_connected_merchants: bool,
 }
 
+#[derive(Debug, Deserialize, Clone, Default)]
+#[serde(default)]
+pub struct ChatSettings {
+    pub enabled: bool,
+    pub hyperswitch_ai_host: String,
+}
+
 #[derive(Debug, Clone, Default, Deserialize)]
 pub struct Multitenancy {
     pub tenants: TenantConfig,
@@ -1016,6 +1024,7 @@ impl Settings<SecuredSecret> {
         self.secrets.get_inner().validate()?;
         self.locker.validate()?;
         self.connectors.validate("connectors")?;
+        self.chat.validate()?;
 
         self.cors.validate()?;
 
diff --git a/crates/router/src/configs/validations.rs b/crates/router/src/configs/validations.rs
index 4fc254760a0..023b9ee5bec 100644
--- a/crates/router/src/configs/validations.rs
+++ b/crates/router/src/configs/validations.rs
@@ -343,3 +343,15 @@ impl super::settings::OpenRouter {
         )
     }
 }
+
+impl super::settings::ChatSettings {
+    pub fn validate(&self) -> Result<(), ApplicationError> {
+        use common_utils::fp_utils::when;
+
+        when(self.enabled && self.hyperswitch_ai_host.is_empty(), || {
+            Err(ApplicationError::InvalidConfigurationValueError(
+                "hyperswitch ai host must be set if chat is enabled".into(),
+            ))
+        })
+    }
+}
diff --git a/crates/router/src/core.rs b/crates/router/src/core.rs
index 2334eddfd64..0fe3e57f872 100644
--- a/crates/router/src/core.rs
+++ b/crates/router/src/core.rs
@@ -72,4 +72,5 @@ pub mod relay;
 #[cfg(feature = "v2")]
 pub mod revenue_recovery;
 
+pub mod chat;
 pub mod tokenization;
diff --git a/crates/router/src/core/chat.rs b/crates/router/src/core/chat.rs
new file mode 100644
index 00000000000..c30e27f0482
--- /dev/null
+++ b/crates/router/src/core/chat.rs
@@ -0,0 +1,57 @@
+use api_models::chat as chat_api;
+use common_utils::{
+    consts,
+    errors::CustomResult,
+    request::{Method, RequestBuilder, RequestContent},
+};
+use error_stack::ResultExt;
+use external_services::http_client;
+use hyperswitch_domain_models::chat as chat_domain;
+use router_env::{instrument, logger, tracing};
+
+use crate::{
+    db::errors::chat::ChatErrors,
+    routes::SessionState,
+    services::{authentication as auth, ApplicationResponse},
+};
+
+#[instrument(skip_all)]
+pub async fn get_data_from_hyperswitch_ai_workflow(
+    state: SessionState,
+    user_from_token: auth::UserFromToken,
+    req: chat_api::ChatRequest,
+) -> CustomResult<ApplicationResponse<chat_api::ChatResponse>, ChatErrors> {
+    let url = format!("{}/webhook", state.conf.chat.hyperswitch_ai_host);
+
+    let request_body = chat_domain::HyperswitchAiDataRequest {
+        query: chat_domain::GetDataMessage {
+            message: req.message,
+        },
+        org_id: user_from_token.org_id,
+        merchant_id: user_from_token.merchant_id,
+        profile_id: user_from_token.profile_id,
+    };
+    logger::info!("Request for AI service: {:?}", request_body);
+
+    let request = RequestBuilder::new()
+        .method(Method::Post)
+        .url(&url)
+        .attach_default_headers()
+        .set_body(RequestContent::Json(Box::new(request_body.clone())))
+        .build();
+
+    let response = http_client::send_request(
+        &state.conf.proxy,
+        request,
+        Some(consts::REQUEST_TIME_OUT_FOR_AI_SERVICE),
+    )
+    .await
+    .change_context(ChatErrors::InternalServerError)
+    .attach_printable("Error when sending request to AI service")?
+    .json::<_>()
+    .await
+    .change_context(ChatErrors::InternalServerError)
+    .attach_printable("Error when deserializing response from AI service")?;
+
+    Ok(ApplicationResponse::Json(response))
+}
diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs
index 93d4414a5b4..c8e7cb4e6f4 100644
--- a/crates/router/src/core/errors.rs
+++ b/crates/router/src/core/errors.rs
@@ -1,3 +1,4 @@
+pub mod chat;
 pub mod customers_error_response;
 pub mod error_handlers;
 pub mod transformers;
diff --git a/crates/router/src/core/errors/chat.rs b/crates/router/src/core/errors/chat.rs
new file mode 100644
index 00000000000..a96afa67de8
--- /dev/null
+++ b/crates/router/src/core/errors/chat.rs
@@ -0,0 +1,37 @@
+#[derive(Debug, thiserror::Error)]
+pub enum ChatErrors {
+    #[error("User InternalServerError")]
+    InternalServerError,
+    #[error("Missing Config error")]
+    MissingConfigError,
+    #[error("Chat response deserialization failed")]
+    ChatResponseDeserializationFailed,
+}
+
+impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorResponse> for ChatErrors {
+    fn switch(&self) -> api_models::errors::types::ApiErrorResponse {
+        use api_models::errors::types::{ApiError, ApiErrorResponse as AER};
+        let sub_code = "AI";
+        match self {
+            Self::InternalServerError => {
+                AER::InternalServerError(ApiError::new("HE", 0, self.get_error_message(), None))
+            }
+            Self::MissingConfigError => {
+                AER::InternalServerError(ApiError::new(sub_code, 1, self.get_error_message(), None))
+            }
+            Self::ChatResponseDeserializationFailed => {
+                AER::BadRequest(ApiError::new(sub_code, 2, self.get_error_message(), None))
+            }
+        }
+    }
+}
+
+impl ChatErrors {
+    pub fn get_error_message(&self) -> String {
+        match self {
+            Self::InternalServerError => "Something went wrong".to_string(),
+            Self::MissingConfigError => "Missing webhook url".to_string(),
+            Self::ChatResponseDeserializationFailed => "Failed to parse chat response".to_string(),
+        }
+    }
+}
diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs
index 6bcb17ad1a5..e002f7fd050 100644
--- a/crates/router/src/lib.rs
+++ b/crates/router/src/lib.rs
@@ -189,7 +189,8 @@ pub fn mk_app(
             .service(routes::MerchantAccount::server(state.clone()))
             .service(routes::User::server(state.clone()))
             .service(routes::ApiKeys::server(state.clone()))
-            .service(routes::Routing::server(state.clone()));
+            .service(routes::Routing::server(state.clone()))
+            .service(routes::Chat::server(state.clone()));
 
         #[cfg(feature = "v1")]
         {
diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs
index f639ee06ab0..fcf7270fa8a 100644
--- a/crates/router/src/routes.rs
+++ b/crates/router/src/routes.rs
@@ -75,6 +75,8 @@ pub mod process_tracker;
 #[cfg(feature = "v2")]
 pub mod proxy;
 
+pub mod chat;
+
 #[cfg(feature = "dummy_connector")]
 pub use self::app::DummyConnector;
 #[cfg(feature = "v2")]
@@ -86,7 +88,7 @@ pub use self::app::Recon;
 #[cfg(feature = "v2")]
 pub use self::app::Tokenization;
 pub use self::app::{
-    ApiKeys, AppState, ApplePayCertificatesMigration, Authentication, Cache, Cards, Configs,
+    ApiKeys, AppState, ApplePayCertificatesMigration, Authentication, Cache, Cards, Chat, Configs,
     ConnectorOnboarding, Customers, Disputes, EphemeralKey, FeatureMatrix, Files, Forex, Gsm,
     Health, Hypersense, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink,
     PaymentMethods, Payments, Poll, ProcessTracker, Profile, ProfileAcquirer, ProfileNew, Refunds,
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 9935ab5fd9f..3cbf532b951 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -59,8 +59,8 @@ use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_ve
 #[cfg(feature = "oltp")]
 use super::webhooks::*;
 use super::{
-    admin, api_keys, cache::*, connector_onboarding, disputes, files, gsm, health::*, profiles,
-    relay, user, user_role,
+    admin, api_keys, cache::*, chat, connector_onboarding, disputes, files, gsm, health::*,
+    profiles, relay, user, user_role,
 };
 #[cfg(feature = "v1")]
 use super::{apple_pay_certificates_migration, blocklist, payment_link, webhook_events};
@@ -2215,6 +2215,23 @@ impl Gsm {
     }
 }
 
+pub struct Chat;
+
+#[cfg(feature = "olap")]
+impl Chat {
+    pub fn server(state: AppState) -> Scope {
+        let mut route = web::scope("/chat").app_data(web::Data::new(state.clone()));
+        if state.conf.chat.enabled {
+            route = route.service(
+                web::scope("/ai").service(
+                    web::resource("/data")
+                        .route(web::post().to(chat::get_data_from_hyperswitch_ai_workflow)),
+                ),
+            );
+        }
+        route
+    }
+}
 pub struct ThreeDsDecisionRule;
 
 #[cfg(feature = "oltp")]
diff --git a/crates/router/src/routes/chat.rs b/crates/router/src/routes/chat.rs
new file mode 100644
index 00000000000..555bf007d23
--- /dev/null
+++ b/crates/router/src/routes/chat.rs
@@ -0,0 +1,38 @@
+use actix_web::{web, HttpRequest, HttpResponse};
+#[cfg(feature = "olap")]
+use api_models::chat as chat_api;
+use router_env::{instrument, tracing, Flow};
+
+use super::AppState;
+use crate::{
+    core::{api_locking, chat as chat_core},
+    services::{
+        api,
+        authentication::{self as auth},
+        authorization::permissions::Permission,
+    },
+};
+
+#[instrument(skip_all)]
+pub async fn get_data_from_hyperswitch_ai_workflow(
+    state: web::Data<AppState>,
+    http_req: HttpRequest,
+    payload: web::Json<chat_api::ChatRequest>,
+) -> HttpResponse {
+    let flow = Flow::GetDataFromHyperswitchAiFlow;
+    Box::pin(api::server_wrap(
+        flow.clone(),
+        state,
+        &http_req,
+        payload.into_inner(),
+        |state, user: auth::UserFromToken, payload, _| {
+            chat_core::get_data_from_hyperswitch_ai_workflow(state, user, payload)
+        },
+        // At present, the AI service retrieves data scoped to the merchant level
+        &auth::JWTAuth {
+            permission: Permission::MerchantPaymentRead,
+        },
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs
index fa616a3da3e..365d54fc84a 100644
--- a/crates/router/src/routes/lock_utils.rs
+++ b/crates/router/src/routes/lock_utils.rs
@@ -35,6 +35,7 @@ pub enum ApiIdentifier {
     UserRole,
     ConnectorOnboarding,
     Recon,
+    AiWorkflow,
     Poll,
     ApplePayCertificatesMigration,
     Relay,
@@ -300,6 +301,8 @@ impl From<Flow> for ApiIdentifier {
             | Flow::DeleteTheme
             | Flow::CloneConnector => Self::User,
 
+            Flow::GetDataFromHyperswitchAiFlow => Self::AiWorkflow,
+
             Flow::ListRolesV2
             | Flow::ListInvitableRolesAtEntityLevel
             | Flow::ListUpdatableRolesAtEntityLevel
diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs
index d395cd1f6da..0eafefd112f 100644
--- a/crates/router_env/src/logger/types.rs
+++ b/crates/router_env/src/logger/types.rs
@@ -349,6 +349,8 @@ pub enum Flow {
     ApplePayCertificatesMigration,
     /// Gsm Rule Delete flow
     GsmRuleDelete,
+    /// Get data from embedded flow
+    GetDataFromHyperswitchAiFlow,
     /// User Sign Up
     UserSignUp,
     /// User Sign Up
diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml
index 8b19ee9c1fa..7e0a123f8d7 100644
--- a/loadtest/config/development.toml
+++ b/loadtest/config/development.toml
@@ -723,3 +723,7 @@ redsys = { payment_method = "card" }
 billing_connectors_which_require_payment_sync = "stripebilling, recurly"
 [billing_connectors_invoice_sync]
 billing_connectors_which_requires_invoice_sync_call = "recurly"
+
+[chat]
+enabled = false 
+hyperswitch_ai_host = "http://0.0.0.0:8000"
\ 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
The PR does the following actions
- Adds endpoints to talk to ai services, particularly to n8n flow and embedded flow
- Introduces a feature flag to manage chat features across different envs

### Additional Changes

- [ ] 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
Closes #8584

## How did you test it?
curl to test this
```
curl --location 'http://localhost:8080/chat/ai/data' \
--header 'x-feature: integ-custom' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer JWT' \
--data '{
            "message": "get count of failed payments"
    
}'
```
<img width="1340" height="674" alt="image" src="https://github.com/user-attachments/assets/bd7722d8-a6c0-4494-ab44-d2fb821e5146" />
## Checklist
<!-- Put an `x` in the boxes that apply -->

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