Bug: feat(users): support tenant level users



- Add api to create tenant_admin
- Create org by tenant level user
- Support tenant entity: tenant level roles/permission 
diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs
index 15146e304af..a244f69f543 100644
--- a/crates/api_models/src/events/user.rs
+++ b/crates/api_models/src/events/user.rs
@@ -9,15 +9,15 @@ use crate::user::{
     theme::{CreateThemeRequest, GetThemeResponse, UpdateThemeRequest, UploadFileRequest},
     AcceptInviteFromEmailRequest, AuthSelectRequest, AuthorizeResponse, BeginTotpResponse,
     ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest,
-    CreateUserAuthenticationMethodRequest, ForgotPasswordRequest, GetSsoAuthUrlRequest,
-    GetUserAuthenticationMethodsRequest, GetUserDetailsResponse, GetUserRoleDetailsRequest,
-    GetUserRoleDetailsResponseV2, InviteUserRequest, ReInviteUserRequest, RecoveryCodes,
-    ResetPasswordRequest, RotatePasswordRequest, SendVerifyEmailRequest, SignUpRequest,
-    SignUpWithMerchantIdRequest, SsoSignInRequest, SwitchMerchantRequest,
-    SwitchOrganizationRequest, SwitchProfileRequest, TokenResponse, TwoFactorAuthStatusResponse,
-    TwoFactorStatus, UpdateUserAccountDetailsRequest, UpdateUserAuthenticationMethodRequest,
-    UserFromEmailRequest, UserMerchantCreate, VerifyEmailRequest, VerifyRecoveryCodeRequest,
-    VerifyTotpRequest,
+    CreateTenantUserRequest, CreateUserAuthenticationMethodRequest, ForgotPasswordRequest,
+    GetSsoAuthUrlRequest, GetUserAuthenticationMethodsRequest, GetUserDetailsResponse,
+    GetUserRoleDetailsRequest, GetUserRoleDetailsResponseV2, InviteUserRequest,
+    ReInviteUserRequest, RecoveryCodes, ResetPasswordRequest, RotatePasswordRequest,
+    SendVerifyEmailRequest, SignUpRequest, SignUpWithMerchantIdRequest, SsoSignInRequest,
+    SwitchMerchantRequest, SwitchOrganizationRequest, SwitchProfileRequest, TokenResponse,
+    TwoFactorAuthStatusResponse, TwoFactorStatus, UpdateUserAccountDetailsRequest,
+    UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, UserMerchantCreate,
+    UserOrgMerchantCreateRequest, VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest,
 };
 
 common_utils::impl_api_event_type!(
@@ -34,6 +34,8 @@ common_utils::impl_api_event_type!(
         SwitchMerchantRequest,
         SwitchProfileRequest,
         CreateInternalUserRequest,
+        CreateTenantUserRequest,
+        UserOrgMerchantCreateRequest,
         UserMerchantCreate,
         AuthorizeResponse,
         ConnectAccountRequest,
diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs
index 82291ddc92a..da4f7c3cd2c 100644
--- a/crates/api_models/src/user.rs
+++ b/crates/api_models/src/user.rs
@@ -114,6 +114,21 @@ pub struct CreateInternalUserRequest {
     pub password: Secret<String>,
 }
 
+#[derive(serde::Deserialize, Debug, serde::Serialize)]
+pub struct CreateTenantUserRequest {
+    pub name: Secret<String>,
+    pub email: pii::Email,
+    pub password: Secret<String>,
+}
+
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
+pub struct UserOrgMerchantCreateRequest {
+    pub organization_name: Secret<String>,
+    pub organization_details: Option<pii::SecretSerdeValue>,
+    pub metadata: Option<pii::SecretSerdeValue>,
+    pub merchant_name: Secret<String>,
+}
+
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
 pub struct UserMerchantCreate {
     pub company_name: String,
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index d966902af89..f32fc71265d 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -3205,6 +3205,7 @@ pub enum ApiVersion {
 #[strum(serialize_all = "snake_case")]
 #[serde(rename_all = "snake_case")]
 pub enum EntityType {
+    Tenant = 3,
     Organization = 2,
     Merchant = 1,
     Profile = 0,
diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs
index b43efcb1feb..e430d59e54d 100644
--- a/crates/common_utils/src/consts.rs
+++ b/crates/common_utils/src/consts.rs
@@ -119,6 +119,8 @@ pub const MAX_ALLOWED_MERCHANT_NAME_LENGTH: usize = 64;
 /// Default locale
 pub const DEFAULT_LOCALE: &str = "en";
 
+/// Role ID for Tenant Admin
+pub const ROLE_ID_TENANT_ADMIN: &str = "tenant_admin";
 /// Role ID for Org Admin
 pub const ROLE_ID_ORGANIZATION_ADMIN: &str = "org_admin";
 /// Role ID for Internal View Only
diff --git a/crates/diesel_models/src/query/merchant_account.rs b/crates/diesel_models/src/query/merchant_account.rs
index fd5a171b7eb..03945646014 100644
--- a/crates/diesel_models/src/query/merchant_account.rs
+++ b/crates/diesel_models/src/query/merchant_account.rs
@@ -121,6 +121,26 @@ impl MerchantAccount {
         .await
     }
 
+    pub async fn list_all_merchant_accounts(
+        conn: &PgPooledConn,
+        limit: u32,
+        offset: Option<u32>,
+    ) -> StorageResult<Vec<Self>> {
+        generics::generic_filter::<
+            <Self as HasTable>::Table,
+            _,
+            <<Self as HasTable>::Table as Table>::PrimaryKey,
+            _,
+        >(
+            conn,
+            dsl_identifier.ne_all(vec![""]),
+            Some(i64::from(limit)),
+            offset.map(i64::from),
+            None,
+        )
+        .await
+    }
+
     pub async fn update_all_merchant_accounts(
         conn: &PgPooledConn,
         merchant_account: MerchantAccountUpdateInternal,
diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs
index d957c3071ff..752c3a52284 100644
--- a/crates/router/src/analytics.rs
+++ b/crates/router/src/analytics.rs
@@ -1931,6 +1931,9 @@ pub mod routes {
                                 EntityType::Organization => Some(AuthInfo::OrgLevel {
                                     org_id: user_role.org_id.clone()?,
                                 }),
+                                EntityType::Tenant => Some(AuthInfo::OrgLevel {
+                                    org_id: auth.org_id.clone(),
+                                }),
                             })
                     })
                     .collect();
@@ -2054,6 +2057,9 @@ pub mod routes {
                                 EntityType::Organization => Some(AuthInfo::OrgLevel {
                                     org_id: user_role.org_id.clone()?,
                                 }),
+                                EntityType::Tenant => Some(AuthInfo::OrgLevel {
+                                    org_id: auth.org_id.clone(),
+                                }),
                             })
                     })
                     .collect();
diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs
index 32ca4ad31d7..d4eb12e39bf 100644
--- a/crates/router/src/consts/user.rs
+++ b/crates/router/src/consts/user.rs
@@ -19,6 +19,8 @@ pub const TOTP_TOLERANCE: u8 = 1;
 pub const TOTP_MAX_ATTEMPTS: u8 = 4;
 /// Number of maximum attempts user has for recovery code
 pub const RECOVERY_CODE_MAX_ATTEMPTS: u8 = 4;
+/// The default number of organizations to fetch for a tenant-level user
+pub const ORG_LIST_LIMIT_FOR_TENANT: u32 = 20;
 
 pub const MAX_PASSWORD_LENGTH: usize = 70;
 pub const MIN_PASSWORD_LENGTH: usize = 8;
diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs
index 4f77b219044..d1cd6f21d91 100644
--- a/crates/router/src/core/user.rs
+++ b/crates/router/src/core/user.rs
@@ -25,9 +25,13 @@ use router_env::logger;
 #[cfg(not(feature = "email"))]
 use user_api::dashboard_metadata::SetMetaDataRequest;
 
+#[cfg(feature = "v1")]
+use super::admin;
 use super::errors::{StorageErrorExt, UserErrors, UserResponse, UserResult};
 #[cfg(feature = "email")]
 use crate::services::email::types as email_types;
+#[cfg(feature = "v1")]
+use crate::types::transformers::ForeignFrom;
 use crate::{
     consts,
     core::encryption::send_request_to_key_service_for_user,
@@ -648,6 +652,12 @@ async fn handle_existing_user_invitation(
     }
 
     let (org_id, merchant_id, profile_id) = match role_info.get_entity_type() {
+        EntityType::Tenant => {
+            return Err(UserErrors::InvalidRoleOperationWithMessage(
+                "Tenant roles are not allowed for this operation".to_string(),
+            )
+            .into());
+        }
         EntityType::Organization => (Some(&user_from_token.org_id), None, None),
         EntityType::Merchant => (
             Some(&user_from_token.org_id),
@@ -701,6 +711,12 @@ async fn handle_existing_user_invitation(
     };
 
     let _user_role = match role_info.get_entity_type() {
+        EntityType::Tenant => {
+            return Err(UserErrors::InvalidRoleOperationWithMessage(
+                "Tenant roles are not allowed for this operation".to_string(),
+            )
+            .into());
+        }
         EntityType::Organization => {
             user_role
                 .add_entity(domain::OrganizationLevel {
@@ -747,6 +763,12 @@ async fn handle_existing_user_invitation(
     {
         let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?;
         let entity = match role_info.get_entity_type() {
+            EntityType::Tenant => {
+                return Err(UserErrors::InvalidRoleOperationWithMessage(
+                    "Tenant roles are not allowed for this operation".to_string(),
+                )
+                .into());
+            }
             EntityType::Organization => email_types::Entity {
                 entity_id: user_from_token.org_id.get_string_repr().to_owned(),
                 entity_type: EntityType::Organization,
@@ -830,6 +852,12 @@ async fn handle_new_user_invitation(
     };
 
     let _user_role = match role_info.get_entity_type() {
+        EntityType::Tenant => {
+            return Err(UserErrors::InvalidRoleOperationWithMessage(
+                "Tenant roles are not allowed for this operation".to_string(),
+            )
+            .into());
+        }
         EntityType::Organization => {
             user_role
                 .add_entity(domain::OrganizationLevel {
@@ -880,6 +908,12 @@ async fn handle_new_user_invitation(
         let _ = req_state.clone();
         let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?;
         let entity = match role_info.get_entity_type() {
+            EntityType::Tenant => {
+                return Err(UserErrors::InvalidRoleOperationWithMessage(
+                    "Tenant roles are not allowed for this operation".to_string(),
+                )
+                .into());
+            }
             EntityType::Organization => email_types::Entity {
                 entity_id: user_from_token.org_id.get_string_repr().to_owned(),
                 entity_type: EntityType::Organization,
@@ -1235,6 +1269,83 @@ pub async fn create_internal_user(
     Ok(ApplicationResponse::StatusOk)
 }
 
+pub async fn create_tenant_user(
+    state: SessionState,
+    request: user_api::CreateTenantUserRequest,
+) -> UserResponse<()> {
+    let key_manager_state = &(&state).into();
+
+    let (merchant_id, org_id) = state
+        .store
+        .list_merchant_and_org_ids(key_manager_state, 1, None)
+        .await
+        .change_context(UserErrors::InternalServerError)
+        .attach_printable("Failed to get merchants list for org")?
+        .pop()
+        .ok_or(UserErrors::InternalServerError)
+        .attach_printable("No merchants found in the tenancy")?;
+
+    let new_user = domain::NewUser::try_from((
+        request,
+        domain::MerchantAccountIdentifier {
+            merchant_id,
+            org_id,
+        },
+    ))?;
+    let mut store_user: storage_user::UserNew = new_user.clone().try_into()?;
+    store_user.set_is_verified(true);
+
+    state
+        .global_store
+        .insert_user(store_user)
+        .await
+        .map_err(|e| {
+            if e.current_context().is_db_unique_violation() {
+                e.change_context(UserErrors::UserExists)
+            } else {
+                e.change_context(UserErrors::InternalServerError)
+            }
+        })
+        .map(domain::user::UserFromStorage::from)?;
+
+    new_user
+        .get_no_level_user_role(
+            common_utils::consts::ROLE_ID_TENANT_ADMIN.to_string(),
+            UserStatus::Active,
+        )
+        .add_entity(domain::TenantLevel {
+            tenant_id: state.tenant.tenant_id.clone(),
+        })
+        .insert_in_v2(&state)
+        .await
+        .change_context(UserErrors::InternalServerError)?;
+
+    Ok(ApplicationResponse::StatusOk)
+}
+
+#[cfg(feature = "v1")]
+pub async fn create_org_merchant_for_user(
+    state: SessionState,
+    req: user_api::UserOrgMerchantCreateRequest,
+) -> UserResponse<()> {
+    let db_organization = ForeignFrom::foreign_from(req.clone());
+    let org: diesel_models::organization::Organization = state
+        .store
+        .insert_organization(db_organization)
+        .await
+        .change_context(UserErrors::InternalServerError)?;
+
+    let merchant_account_create_request =
+        utils::user::create_merchant_account_request_for_org(req, org)?;
+
+    admin::create_merchant_account(state.clone(), merchant_account_create_request)
+        .await
+        .change_context(UserErrors::InternalServerError)
+        .attach_printable("Error while creating a merchant")?;
+
+    Ok(ApplicationResponse::StatusOk)
+}
+
 pub async fn create_merchant_account(
     state: SessionState,
     user_from_token: auth::UserFromToken,
@@ -1352,7 +1463,7 @@ pub async fn list_user_roles_details(
                     merchant.push(merchant_id.clone());
                     merchant_profile.push((merchant_id, profile_id))
                 }
-                EntityType::Organization => (),
+                EntityType::Tenant | EntityType::Organization => (),
             };
 
             Ok::<_, error_stack::Report<UserErrors>>((merchant, merchant_profile))
@@ -1438,7 +1549,7 @@ pub async fn list_user_roles_details(
                 .ok_or(UserErrors::InternalServerError)?;
 
             let (merchant, profile) = match entity_type {
-                EntityType::Organization => (None, None),
+                EntityType::Tenant | EntityType::Organization => (None, None),
                 EntityType::Merchant => {
                     let merchant_id = &user_role
                         .merchant_id
@@ -2437,28 +2548,44 @@ pub async fn list_orgs_for_user(
         )
         .into());
     }
-
-    let orgs = state
-        .global_store
-        .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
-            user_id: user_from_token.user_id.as_str(),
-            tenant_id: user_from_token
-                .tenant_id
-                .as_ref()
-                .unwrap_or(&state.tenant.tenant_id),
-            org_id: None,
-            merchant_id: None,
-            profile_id: None,
-            entity_id: None,
-            version: None,
-            status: Some(UserStatus::Active),
-            limit: None,
-        })
-        .await
-        .change_context(UserErrors::InternalServerError)?
-        .into_iter()
-        .filter_map(|user_role| user_role.org_id)
-        .collect::<HashSet<_>>();
+    let orgs = match role_info.get_entity_type() {
+        EntityType::Tenant => {
+            let key_manager_state = &(&state).into();
+            state
+                .store
+                .list_merchant_and_org_ids(
+                    key_manager_state,
+                    consts::user::ORG_LIST_LIMIT_FOR_TENANT,
+                    None,
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)?
+                .into_iter()
+                .map(|(_, org_id)| org_id)
+                .collect::<HashSet<_>>()
+        }
+        EntityType::Organization | EntityType::Merchant | EntityType::Profile => state
+            .global_store
+            .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
+                user_id: user_from_token.user_id.as_str(),
+                tenant_id: user_from_token
+                    .tenant_id
+                    .as_ref()
+                    .unwrap_or(&state.tenant.tenant_id),
+                org_id: None,
+                merchant_id: None,
+                profile_id: None,
+                entity_id: None,
+                version: None,
+                status: Some(UserStatus::Active),
+                limit: None,
+            })
+            .await
+            .change_context(UserErrors::InternalServerError)?
+            .into_iter()
+            .filter_map(|user_role| user_role.org_id)
+            .collect::<HashSet<_>>(),
+    };
 
     let resp = futures::future::try_join_all(
         orgs.iter()
@@ -2501,7 +2628,7 @@ pub async fn list_merchants_for_user_in_org(
     }
 
     let merchant_accounts = match role_info.get_entity_type() {
-        EntityType::Organization => state
+        EntityType::Tenant | EntityType::Organization => state
             .store
             .list_merchant_accounts_by_organization_id(&(&state).into(), &user_from_token.org_id)
             .await
@@ -2580,7 +2707,7 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account(
         .await
         .change_context(UserErrors::InternalServerError)?;
     let profiles = match role_info.get_entity_type() {
-        EntityType::Organization | EntityType::Merchant => state
+        EntityType::Tenant | EntityType::Organization | EntityType::Merchant => state
             .store
             .list_profile_by_merchant_id(
                 key_manager_state,
@@ -2670,39 +2797,80 @@ pub async fn switch_org_for_user(
         .into());
     }
 
-    let user_role = state
-        .global_store
-        .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
-            user_id: &user_from_token.user_id,
-            tenant_id: user_from_token
-                .tenant_id
-                .as_ref()
-                .unwrap_or(&state.tenant.tenant_id),
-            org_id: Some(&request.org_id),
-            merchant_id: None,
-            profile_id: None,
-            entity_id: None,
-            version: None,
-            status: Some(UserStatus::Active),
-            limit: Some(1),
-        })
-        .await
-        .change_context(UserErrors::InternalServerError)
-        .attach_printable("Failed to list user roles by user_id and org_id")?
-        .pop()
-        .ok_or(UserErrors::InvalidRoleOperationWithMessage(
-            "No user role found for the requested org_id".to_string(),
-        ))?;
+    let (merchant_id, profile_id, role_id) = match role_info.get_entity_type() {
+        EntityType::Tenant => {
+            let merchant_id = state
+                .store
+                .list_merchant_accounts_by_organization_id(&(&state).into(), &request.org_id)
+                .await
+                .change_context(UserErrors::InternalServerError)
+                .attach_printable("Failed to get merchant list for org")?
+                .pop()
+                .ok_or(UserErrors::InvalidRoleOperation)
+                .attach_printable("No merchants found for the org id")?
+                .get_id()
+                .to_owned();
 
-    let (merchant_id, profile_id) =
-        utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role).await?;
+            let key_store = state
+                .store
+                .get_merchant_key_store_by_merchant_id(
+                    &(&state).into(),
+                    &merchant_id,
+                    &state.store.get_master_key().to_vec().into(),
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)?;
+
+            let profile_id = state
+                .store
+                .list_profile_by_merchant_id(&(&state).into(), &key_store, &merchant_id)
+                .await
+                .change_context(UserErrors::InternalServerError)?
+                .pop()
+                .ok_or(UserErrors::InternalServerError)?
+                .get_id()
+                .to_owned();
+
+            (merchant_id, profile_id, user_from_token.role_id)
+        }
+        EntityType::Organization | EntityType::Merchant | EntityType::Profile => {
+            let user_role = state
+                .global_store
+                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
+                    user_id: &user_from_token.user_id,
+                    tenant_id: user_from_token
+                        .tenant_id
+                        .as_ref()
+                        .unwrap_or(&state.tenant.tenant_id),
+                    org_id: Some(&request.org_id),
+                    merchant_id: None,
+                    profile_id: None,
+                    entity_id: None,
+                    version: None,
+                    status: Some(UserStatus::Active),
+                    limit: Some(1),
+                })
+                .await
+                .change_context(UserErrors::InternalServerError)
+                .attach_printable("Failed to list user roles by user_id and org_id")?
+                .pop()
+                .ok_or(UserErrors::InvalidRoleOperationWithMessage(
+                    "No user role found for the requested org_id".to_string(),
+                ))?;
+
+            let (merchant_id, profile_id) =
+                utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role).await?;
+
+            (merchant_id, profile_id, user_role.role_id)
+        }
+    };
 
     let token = utils::user::generate_jwt_auth_token_with_attributes(
         &state,
         user_from_token.user_id,
         merchant_id.clone(),
         request.org_id.clone(),
-        user_role.role_id.clone(),
+        role_id.clone(),
         profile_id.clone(),
         user_from_token.tenant_id,
     )
@@ -2710,7 +2878,7 @@ pub async fn switch_org_for_user(
 
     utils::user_role::set_role_permissions_in_cache_by_role_id_merchant_id_org_id(
         &state,
-        &user_role.role_id,
+        &role_id,
         &merchant_id,
         &request.org_id,
     )
@@ -2794,7 +2962,7 @@ pub async fn switch_merchant_for_user_in_org(
     } else {
         // Match based on the other entity types
         match role_info.get_entity_type() {
-            EntityType::Organization => {
+            EntityType::Tenant | EntityType::Organization => {
                 let merchant_key_store = state
                     .store
                     .get_merchant_key_store_by_merchant_id(
@@ -2937,7 +3105,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant(
     .attach_printable("Failed to retrieve role information")?;
 
     let (profile_id, role_id) = match role_info.get_entity_type() {
-        EntityType::Organization | EntityType::Merchant => {
+        EntityType::Tenant | EntityType::Organization | EntityType::Merchant => {
             let merchant_key_store = state
                 .store
                 .get_merchant_key_store_by_merchant_id(
diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs
index 273b863afe8..31ec665b2ab 100644
--- a/crates/router/src/core/user_role.rs
+++ b/crates/router/src/core/user_role.rs
@@ -698,7 +698,7 @@ pub async fn list_users_in_lineage(
         requestor_role_info.get_entity_type(),
         request.entity_type,
     )? {
-        EntityType::Organization => {
+        EntityType::Tenant | EntityType::Organization => {
             utils::user_role::fetch_user_roles_by_payload(
                 &state,
                 ListUserRolesByOrgIdPayload {
@@ -871,6 +871,10 @@ pub async fn list_invitations_for_user(
                 .attach_printable("Failed to compute entity id and type")?;
 
             match entity_type {
+                EntityType::Tenant => {
+                    return Err(report!(UserErrors::InternalServerError))
+                        .attach_printable("Tenant roles are not allowed for this operation");
+                }
                 EntityType::Organization => org_ids.push(
                     user_role
                         .org_id
@@ -975,6 +979,10 @@ pub async fn list_invitations_for_user(
                 .attach_printable("Failed to compute entity id and type")?;
 
             let entity_name = match entity_type {
+                EntityType::Tenant => {
+                    return Err(report!(UserErrors::InternalServerError))
+                        .attach_printable("Tenant roles are not allowed for this operation");
+                }
                 EntityType::Organization => user_role
                     .org_id
                     .as_ref()
diff --git a/crates/router/src/core/user_role/role.rs b/crates/router/src/core/user_role/role.rs
index c01d585feb7..9f251f55bdf 100644
--- a/crates/router/src/core/user_role/role.rs
+++ b/crates/router/src/core/user_role/role.rs
@@ -274,7 +274,7 @@ pub async fn list_roles_with_info(
     let user_role_entity = user_role_info.get_entity_type();
     let custom_roles =
         match utils::user_role::get_min_entity(user_role_entity, request.entity_type)? {
-            EntityType::Organization => state
+            EntityType::Tenant | EntityType::Organization => state
                 .store
                 .list_roles_for_org_by_parameters(
                     &user_from_token.org_id,
@@ -347,7 +347,7 @@ pub async fn list_roles_at_entity_level(
         .collect::<Vec<_>>();
 
     let custom_roles = match req.entity_type {
-        EntityType::Organization => state
+        EntityType::Tenant | EntityType::Organization => state
             .store
             .list_roles_for_org_by_parameters(
                 &user_from_token.org_id,
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index 9a284f1399d..47c9adafb71 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -1048,6 +1048,19 @@ impl MerchantAccountInterface for KafkaStore {
             .list_multiple_merchant_accounts(state, merchant_ids)
             .await
     }
+
+    #[cfg(feature = "olap")]
+    async fn list_merchant_and_org_ids(
+        &self,
+        state: &KeyManagerState,
+        limit: u32,
+        offset: Option<u32>,
+    ) -> CustomResult<Vec<(id_type::MerchantId, id_type::OrganizationId)>, errors::StorageError>
+    {
+        self.diesel_store
+            .list_merchant_and_org_ids(state, limit, offset)
+            .await
+    }
 }
 
 #[async_trait::async_trait]
diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs
index 6eb355e4434..4f4a3f1cf00 100644
--- a/crates/router/src/db/merchant_account.rs
+++ b/crates/router/src/db/merchant_account.rs
@@ -87,6 +87,20 @@ where
         state: &KeyManagerState,
         merchant_ids: Vec<common_utils::id_type::MerchantId>,
     ) -> CustomResult<Vec<domain::MerchantAccount>, errors::StorageError>;
+
+    #[cfg(feature = "olap")]
+    async fn list_merchant_and_org_ids(
+        &self,
+        state: &KeyManagerState,
+        limit: u32,
+        offset: Option<u32>,
+    ) -> CustomResult<
+        Vec<(
+            common_utils::id_type::MerchantId,
+            common_utils::id_type::OrganizationId,
+        )>,
+        errors::StorageError,
+    >;
 }
 
 #[async_trait::async_trait]
@@ -411,6 +425,37 @@ impl MerchantAccountInterface for Store {
         Ok(merchant_accounts)
     }
 
+    #[cfg(feature = "olap")]
+    #[instrument(skip_all)]
+    async fn list_merchant_and_org_ids(
+        &self,
+        _state: &KeyManagerState,
+        limit: u32,
+        offset: Option<u32>,
+    ) -> CustomResult<
+        Vec<(
+            common_utils::id_type::MerchantId,
+            common_utils::id_type::OrganizationId,
+        )>,
+        errors::StorageError,
+    > {
+        let conn = connection::pg_connection_read(self).await?;
+        let encrypted_merchant_accounts =
+            storage::MerchantAccount::list_all_merchant_accounts(&conn, limit, offset)
+                .await
+                .map_err(|error| report!(errors::StorageError::from(error)))?;
+
+        let merchant_and_org_ids = encrypted_merchant_accounts
+            .into_iter()
+            .map(|merchant_account| {
+                let merchant_id = merchant_account.get_id().clone();
+                let org_id = merchant_account.organization_id;
+                (merchant_id, org_id)
+            })
+            .collect();
+        Ok(merchant_and_org_ids)
+    }
+
     async fn update_all_merchant_account(
         &self,
         merchant_account: storage::MerchantAccountUpdate,
@@ -694,6 +739,33 @@ impl MerchantAccountInterface for MockDb {
             .into_iter()
             .collect()
     }
+
+    #[cfg(feature = "olap")]
+    async fn list_merchant_and_org_ids(
+        &self,
+        _state: &KeyManagerState,
+        limit: u32,
+        offset: Option<u32>,
+    ) -> CustomResult<
+        Vec<(
+            common_utils::id_type::MerchantId,
+            common_utils::id_type::OrganizationId,
+        )>,
+        errors::StorageError,
+    > {
+        let accounts = self.merchant_accounts.lock().await;
+        let limit = limit.try_into().unwrap_or(accounts.len());
+        let offset = offset.unwrap_or(0).try_into().unwrap_or(0);
+
+        let merchant_and_org_ids = accounts
+            .iter()
+            .skip(offset)
+            .take(limit)
+            .map(|account| (account.get_id().clone(), account.organization_id.clone()))
+            .collect::<Vec<_>>();
+
+        Ok(merchant_and_org_ids)
+    }
 }
 
 #[cfg(feature = "accounts_cache")]
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 0fca984cc57..6353489e81e 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -1883,6 +1883,10 @@ impl User {
             .service(
                 web::resource("/internal_signup").route(web::post().to(user::internal_user_signup)),
             )
+            .service(
+                web::resource("/tenant_signup").route(web::post().to(user::create_tenant_user)),
+            )
+            .service(web::resource("/create_org").route(web::post().to(user::user_org_create)))
             .service(
                 web::resource("/create_merchant")
                     .route(web::post().to(user::user_merchant_account_create)),
diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs
index 1c7db127ffc..adb619f3f26 100644
--- a/crates/router/src/routes/lock_utils.rs
+++ b/crates/router/src/routes/lock_utils.rs
@@ -223,9 +223,11 @@ impl From<Flow> for ApiIdentifier {
             | Flow::GetMultipleDashboardMetadata
             | Flow::VerifyPaymentConnector
             | Flow::InternalUserSignup
+            | Flow::TenantUserCreate
             | Flow::SwitchOrg
             | Flow::SwitchMerchantV2
             | Flow::SwitchProfile
+            | Flow::UserOrgMerchantCreate
             | Flow::UserMerchantAccountCreate
             | Flow::GenerateSampleData
             | Flow::DeleteSampleData
diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs
index d5f31be3965..c32746c8718 100644
--- a/crates/router/src/routes/user.rs
+++ b/crates/router/src/routes/user.rs
@@ -229,6 +229,47 @@ pub async fn internal_user_signup(
     .await
 }
 
+pub async fn create_tenant_user(
+    state: web::Data<AppState>,
+    http_req: HttpRequest,
+    json_payload: web::Json<user_api::CreateTenantUserRequest>,
+) -> HttpResponse {
+    let flow = Flow::TenantUserCreate;
+    Box::pin(api::server_wrap(
+        flow,
+        state.clone(),
+        &http_req,
+        json_payload.into_inner(),
+        |state, _, req, _| user_core::create_tenant_user(state, req),
+        &auth::AdminApiAuth,
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
+#[cfg(feature = "v1")]
+pub async fn user_org_create(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    json_payload: web::Json<user_api::UserOrgMerchantCreateRequest>,
+) -> HttpResponse {
+    let flow = Flow::UserOrgMerchantCreate;
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        json_payload.into_inner(),
+        |state, _auth: auth::UserFromToken, json_payload, _| {
+            user_core::create_org_merchant_for_user(state, json_payload)
+        },
+        &auth::JWTAuth {
+            permission: Permission::TenantAccountWrite,
+        },
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 pub async fn user_merchant_account_create(
     state: web::Data<AppState>,
     req: HttpRequest,
diff --git a/crates/router/src/services/authorization/permissions.rs b/crates/router/src/services/authorization/permissions.rs
index 6f612007425..578ccdcf9d4 100644
--- a/crates/router/src/services/authorization/permissions.rs
+++ b/crates/router/src/services/authorization/permissions.rs
@@ -33,7 +33,7 @@ generate_permissions! {
         },
         Account: {
             scopes: [Read, Write],
-            entities: [Profile, Merchant, Organization]
+            entities: [Profile, Merchant, Organization, Tenant]
         },
         Connector: {
             scopes: [Read, Write],
@@ -125,6 +125,7 @@ pub fn get_resource_name(resource: &Resource, entity_type: &EntityType) -> &'sta
         (Resource::Account, EntityType::Profile) => "Business Profile Account",
         (Resource::Account, EntityType::Merchant) => "Merchant Account",
         (Resource::Account, EntityType::Organization) => "Organization Account",
+        (Resource::Account, EntityType::Tenant) => "Tenant Account",
     }
 }
 
diff --git a/crates/router/src/services/authorization/roles/predefined_roles.rs b/crates/router/src/services/authorization/roles/predefined_roles.rs
index 9c67c12f527..fe7498de9eb 100644
--- a/crates/router/src/services/authorization/roles/predefined_roles.rs
+++ b/crates/router/src/services/authorization/roles/predefined_roles.rs
@@ -68,7 +68,42 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(|
         },
     );
 
-    // Merchant Roles
+    // Tenant Roles
+    roles.insert(
+        common_utils::consts::ROLE_ID_TENANT_ADMIN,
+        RoleInfo {
+            groups: vec![
+                PermissionGroup::OperationsView,
+                PermissionGroup::OperationsManage,
+                PermissionGroup::ConnectorsView,
+                PermissionGroup::ConnectorsManage,
+                PermissionGroup::WorkflowsView,
+                PermissionGroup::WorkflowsManage,
+                PermissionGroup::AnalyticsView,
+                PermissionGroup::UsersView,
+                PermissionGroup::UsersManage,
+                PermissionGroup::MerchantDetailsView,
+                PermissionGroup::AccountView,
+                PermissionGroup::MerchantDetailsManage,
+                PermissionGroup::AccountManage,
+                PermissionGroup::OrganizationManage,
+                PermissionGroup::ReconOpsView,
+                PermissionGroup::ReconOpsManage,
+                PermissionGroup::ReconReportsView,
+                PermissionGroup::ReconReportsManage,
+            ],
+            role_id: common_utils::consts::ROLE_ID_TENANT_ADMIN.to_string(),
+            role_name: "tenant_admin".to_string(),
+            scope: RoleScope::Organization,
+            entity_type: EntityType::Tenant,
+            is_invitable: false,
+            is_deletable: false,
+            is_updatable: false,
+            is_internal: false,
+        },
+    );
+
+    // Organization Roles
     roles.insert(
         common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN,
         RoleInfo {
diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs
index 21212bf6b62..6efaac7bfed 100644
--- a/crates/router/src/types/domain/user.rs
+++ b/crates/router/src/types/domain/user.rs
@@ -312,6 +312,40 @@ impl From<InviteeUserRequestWithInvitedUserToken> for NewUserOrganization {
     }
 }
 
+impl From<(user_api::CreateTenantUserRequest, MerchantAccountIdentifier)> for NewUserOrganization {
+    fn from(
+        (_value, merchant_account_identifier): (
+            user_api::CreateTenantUserRequest,
+            MerchantAccountIdentifier,
+        ),
+    ) -> Self {
+        let new_organization = api_org::OrganizationNew {
+            org_id: merchant_account_identifier.org_id,
+            org_name: None,
+        };
+        let db_organization = ForeignFrom::foreign_from(new_organization);
+        Self(db_organization)
+    }
+}
+
+impl ForeignFrom<api_models::user::UserOrgMerchantCreateRequest>
+    for diesel_models::organization::OrganizationNew
+{
+    fn foreign_from(item: api_models::user::UserOrgMerchantCreateRequest) -> Self {
+        let org_id = id_type::OrganizationId::default();
+        let api_models::user::UserOrgMerchantCreateRequest {
+            organization_name,
+            organization_details,
+            metadata,
+            ..
+        } = item;
+        let mut org_new_db = Self::new(org_id, Some(organization_name.expose()));
+        org_new_db.organization_details = organization_details;
+        org_new_db.metadata = metadata;
+        org_new_db
+    }
+}
+
 #[derive(Clone)]
 pub struct MerchantId(String);
 
@@ -535,6 +569,18 @@ impl TryFrom<InviteeUserRequestWithInvitedUserToken> for NewUserMerchant {
     }
 }
 
+impl From<(user_api::CreateTenantUserRequest, MerchantAccountIdentifier)> for NewUserMerchant {
+    fn from(value: (user_api::CreateTenantUserRequest, MerchantAccountIdentifier)) -> Self {
+        let merchant_id = value.1.merchant_id.clone();
+        let new_organization = NewUserOrganization::from(value);
+        Self {
+            company_name: None,
+            merchant_id,
+            new_organization,
+        }
+    }
+}
+
 type UserMerchantCreateRequestWithToken =
     (UserFromStorage, user_api::UserMerchantCreate, UserFromToken);
 
@@ -555,6 +601,12 @@ impl TryFrom<UserMerchantCreateRequestWithToken> for NewUserMerchant {
     }
 }
 
+#[derive(Debug, Clone)]
+pub struct MerchantAccountIdentifier {
+    pub merchant_id: id_type::MerchantId,
+    pub org_id: id_type::OrganizationId,
+}
+
 #[derive(Clone)]
 pub struct NewUser {
     user_id: String,
@@ -856,6 +908,34 @@ impl TryFrom<InviteeUserRequestWithInvitedUserToken> for NewUser {
     }
 }
 
+impl TryFrom<(user_api::CreateTenantUserRequest, MerchantAccountIdentifier)> for NewUser {
+    type Error = error_stack::Report<UserErrors>;
+
+    fn try_from(
+        (value, merchant_account_identifier): (
+            user_api::CreateTenantUserRequest,
+            MerchantAccountIdentifier,
+        ),
+    ) -> UserResult<Self> {
+        let user_id = uuid::Uuid::new_v4().to_string();
+        let email = value.email.clone().try_into()?;
+        let name = UserName::new(value.name.clone())?;
+        let password = NewUserPassword {
+            password: UserPassword::new(value.password.clone())?,
+            is_temporary: false,
+        };
+        let new_merchant = NewUserMerchant::from((value, merchant_account_identifier));
+
+        Ok(Self {
+            user_id,
+            name,
+            email,
+            password: Some(password),
+            new_merchant,
+        })
+    }
+}
+
 #[derive(Clone)]
 pub struct UserFromStorage(pub storage_user::User);
 
@@ -1108,6 +1188,11 @@ impl RecoveryCodes {
 #[derive(Clone)]
 pub struct NoLevel;
 
+#[derive(Clone)]
+pub struct TenantLevel {
+    pub tenant_id: id_type::TenantId,
+}
+
 #[derive(Clone)]
 pub struct OrganizationLevel {
     pub tenant_id: id_type::TenantId,
@@ -1161,20 +1246,33 @@ impl NewUserRole<NoLevel> {
 
 pub struct EntityInfo {
     tenant_id: id_type::TenantId,
-    org_id: id_type::OrganizationId,
+    org_id: Option<id_type::OrganizationId>,
     merchant_id: Option<id_type::MerchantId>,
     profile_id: Option<id_type::ProfileId>,
     entity_id: String,
     entity_type: EntityType,
 }
 
+impl From<TenantLevel> for EntityInfo {
+    fn from(value: TenantLevel) -> Self {
+        Self {
+            entity_id: value.tenant_id.get_string_repr().to_owned(),
+            entity_type: EntityType::Tenant,
+            tenant_id: value.tenant_id,
+            org_id: None,
+            merchant_id: None,
+            profile_id: None,
+        }
+    }
+}
+
 impl From<OrganizationLevel> for EntityInfo {
     fn from(value: OrganizationLevel) -> Self {
         Self {
             entity_id: value.org_id.get_string_repr().to_owned(),
             entity_type: EntityType::Organization,
             tenant_id: value.tenant_id,
-            org_id: value.org_id,
+            org_id: Some(value.org_id),
             merchant_id: None,
             profile_id: None,
         }
@@ -1187,9 +1285,9 @@ impl From<MerchantLevel> for EntityInfo {
             entity_id: value.merchant_id.get_string_repr().to_owned(),
             entity_type: EntityType::Merchant,
             tenant_id: value.tenant_id,
-            org_id: value.org_id,
-            profile_id: None,
+            org_id: Some(value.org_id),
             merchant_id: Some(value.merchant_id),
+            profile_id: None,
         }
     }
 }
@@ -1200,7 +1298,7 @@ impl From<ProfileLevel> for EntityInfo {
             entity_id: value.profile_id.get_string_repr().to_owned(),
             entity_type: EntityType::Profile,
             tenant_id: value.tenant_id,
-            org_id: value.org_id,
+            org_id: Some(value.org_id),
             merchant_id: Some(value.merchant_id),
             profile_id: Some(value.profile_id),
         }
@@ -1220,7 +1318,7 @@ where
             last_modified_by: self.last_modified_by,
             created_at: self.created_at,
             last_modified: self.last_modified,
-            org_id: Some(entity.org_id),
+            org_id: entity.org_id,
             merchant_id: entity.merchant_id,
             profile_id: entity.profile_id,
             entity_id: Some(entity.entity_id),
diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs
index 10990da6ccb..519edf4e9ce 100644
--- a/crates/router/src/types/domain/user/decision_manager.rs
+++ b/crates/router/src/types/domain/user/decision_manager.rs
@@ -1,7 +1,7 @@
 use common_enums::TokenPurpose;
 use common_utils::id_type;
 use diesel_models::{enums::UserStatus, user_role::UserRole};
-use error_stack::{report, ResultExt};
+use error_stack::ResultExt;
 use masking::Secret;
 
 use super::UserFromStorage;
@@ -124,18 +124,18 @@ impl JWTFlow {
         next_flow: &NextFlow,
         user_role: &UserRole,
     ) -> UserResult<Secret<String>> {
-        let (merchant_id, profile_id) =
-            utils::user_role::get_single_merchant_id_and_profile_id(state, user_role).await?;
+        let org_id = utils::user_role::get_single_org_id(state, user_role).await?;
+        let merchant_id =
+            utils::user_role::get_single_merchant_id(state, user_role, &org_id).await?;
+        let profile_id =
+            utils::user_role::get_single_profile_id(state, user_role, &merchant_id).await?;
+
         auth::AuthToken::new_token(
             next_flow.user.get_user_id().to_string(),
             merchant_id,
             user_role.role_id.clone(),
             &state.conf,
-            user_role
-                .org_id
-                .clone()
-                .ok_or(report!(UserErrors::InternalServerError))
-                .attach_printable("org_id not found")?,
+            org_id,
             profile_id,
             Some(user_role.tenant_id.clone()),
         )
diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs
index 281b95255c8..48b1c28a525 100644
--- a/crates/router/src/utils/user.rs
+++ b/crates/router/src/utils/user.rs
@@ -5,9 +5,11 @@ use common_enums::UserAuthType;
 use common_utils::{
     encryption::Encryption, errors::CustomResult, id_type, type_name, types::keymanager::Identifier,
 };
+use diesel_models::{organization, organization::OrganizationBridge};
 use error_stack::ResultExt;
 use masking::{ExposeInterface, Secret};
 use redis_interface::RedisConnectionPool;
+use router_env::env;
 
 use crate::{
     consts::user::{REDIS_SSO_PREFIX, REDIS_SSO_TTL},
@@ -279,3 +281,39 @@ pub fn is_sso_auth_type(auth_type: &UserAuthType) -> bool {
         UserAuthType::Password | UserAuthType::MagicLink => false,
     }
 }
+
+#[cfg(feature = "v1")]
+pub fn create_merchant_account_request_for_org(
+    req: user_api::UserOrgMerchantCreateRequest,
+    org: organization::Organization,
+) -> UserResult<api_models::admin::MerchantAccountCreate> {
+    let merchant_id = if matches!(env::which(), env::Env::Production) {
+        id_type::MerchantId::try_from(domain::MerchantId::new(req.merchant_name.clone().expose())?)?
+    } else {
+        id_type::MerchantId::new_from_unix_timestamp()
+    };
+
+    let company_name = domain::UserCompanyName::new(req.merchant_name.expose())?;
+    Ok(api_models::admin::MerchantAccountCreate {
+        merchant_id,
+        metadata: None,
+        locker_id: None,
+        return_url: None,
+        merchant_name: Some(Secret::new(company_name.get_secret())),
+        webhook_details: None,
+        publishable_key: None,
+        organization_id: Some(org.get_organization_id()),
+        merchant_details: None,
+        routing_algorithm: None,
+        parent_merchant_id: None,
+        sub_merchants_enabled: None,
+        frm_routing_algorithm: None,
+        #[cfg(feature = "payouts")]
+        payout_routing_algorithm: None,
+        primary_business_details: None,
+        payment_response_hash_key: None,
+        enable_payment_response_hash: None,
+        redirect_to_merchant_with_http_post: None,
+        pm_collect_link_config: None,
+    })
+}
diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs
index 8acab436491..3412219d0e9 100644
--- a/crates/router/src/utils/user_role.rs
+++ b/crates/router/src/utils/user_role.rs
@@ -13,7 +13,10 @@ use storage_impl::errors::StorageError;
 use crate::{
     consts,
     core::errors::{UserErrors, UserResult},
-    db::user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload},
+    db::{
+        errors::StorageErrorExt,
+        user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload},
+    },
     routes::SessionState,
     services::authorization::{self as authz, roles},
     types::domain,
@@ -179,30 +182,54 @@ pub async fn update_v1_and_v2_user_roles_in_db(
     (updated_v1_role, updated_v2_role)
 }
 
+pub async fn get_single_org_id(
+    state: &SessionState,
+    user_role: &UserRole,
+) -> UserResult<id_type::OrganizationId> {
+    let (_, entity_type) = user_role
+        .get_entity_id_and_type()
+        .ok_or(UserErrors::InternalServerError)?;
+    match entity_type {
+        EntityType::Tenant => Ok(state
+            .store
+            .list_merchant_and_org_ids(&state.into(), 1, None)
+            .await
+            .change_context(UserErrors::InternalServerError)
+            .attach_printable("Failed to get merchants list for org")?
+            .pop()
+            .ok_or(UserErrors::InternalServerError)
+            .attach_printable("No merchants to get merchant or org id")?
+            .1),
+        EntityType::Organization | EntityType::Merchant | EntityType::Profile => user_role
+            .org_id
+            .clone()
+            .ok_or(UserErrors::InternalServerError)
+            .attach_printable("Org_id not found"),
+    }
+}
+
 pub async fn get_single_merchant_id(
     state: &SessionState,
     user_role: &UserRole,
+    org_id: &id_type::OrganizationId,
 ) -> UserResult<id_type::MerchantId> {
-    match user_role.entity_type {
-        Some(EntityType::Organization) => Ok(state
+    let (_, entity_type) = user_role
+        .get_entity_id_and_type()
+        .ok_or(UserErrors::InternalServerError)?;
+    match entity_type {
+        EntityType::Tenant | EntityType::Organization => Ok(state
             .store
-            .list_merchant_accounts_by_organization_id(
-                &state.into(),
-                user_role
-                    .org_id
-                    .as_ref()
-                    .ok_or(UserErrors::InternalServerError)
-                    .attach_printable("org_id not found")?,
-            )
+            .list_merchant_accounts_by_organization_id(&state.into(), org_id)
             .await
-            .change_context(UserErrors::InternalServerError)
-            .attach_printable("Failed to get merchant list for org")?
+            .to_not_found_response(UserErrors::InvalidRoleOperationWithMessage(
+                "Invalid Org Id".to_string(),
+            ))?
             .first()
             .ok_or(UserErrors::InternalServerError)
             .attach_printable("No merchants found for org_id")?
             .get_id()
             .clone()),
-        Some(EntityType::Merchant) | Some(EntityType::Profile) | None => user_role
+        EntityType::Merchant | EntityType::Profile => user_role
             .merchant_id
             .clone()
             .ok_or(UserErrors::InternalServerError)
@@ -210,6 +237,44 @@ pub async fn get_single_merchant_id(
     }
 }
 
+pub async fn get_single_profile_id(
+    state: &SessionState,
+    user_role: &UserRole,
+    merchant_id: &id_type::MerchantId,
+) -> UserResult<id_type::ProfileId> {
+    let (_, entity_type) = user_role
+        .get_entity_id_and_type()
+        .ok_or(UserErrors::InternalServerError)?;
+    match entity_type {
+        EntityType::Tenant | EntityType::Organization | EntityType::Merchant => {
+            let key_store = state
+                .store
+                .get_merchant_key_store_by_merchant_id(
+                    &state.into(),
+                    merchant_id,
+                    &state.store.get_master_key().to_vec().into(),
+                )
+                .await
+                .change_context(UserErrors::InternalServerError)?;
+
+            Ok(state
+                .store
+                .list_profile_by_merchant_id(&state.into(), &key_store, merchant_id)
+                .await
+                .change_context(UserErrors::InternalServerError)?
+                .pop()
+                .ok_or(UserErrors::InternalServerError)?
+                .get_id()
+                .to_owned())
+        }
+        EntityType::Profile => user_role
+            .profile_id
+            .clone()
+            .ok_or(UserErrors::InternalServerError)
+            .attach_printable("profile_id not found"),
+    }
+}
+
 pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite(
     state: &SessionState,
     user_id: &str,
@@ -224,6 +289,10 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite(
     )>,
 > {
     match entity_type {
+        EntityType::Tenant => Err(UserErrors::InvalidRoleOperationWithMessage(
+            "Tenant roles are not allowed for this operation".to_string(),
+        )
+        .into()),
         EntityType::Organization => {
             let Ok(org_id) =
                 id_type::OrganizationId::try_from(std::borrow::Cow::from(entity_id.clone()))
@@ -373,37 +442,9 @@ pub async fn get_single_merchant_id_and_profile_id(
     state: &SessionState,
     user_role: &UserRole,
 ) -> UserResult<(id_type::MerchantId, id_type::ProfileId)> {
-    let merchant_id = get_single_merchant_id(state, user_role).await?;
-    let (_, entity_type) = user_role
-        .get_entity_id_and_type()
-        .ok_or(UserErrors::InternalServerError)?;
-    let profile_id = match entity_type {
-        EntityType::Organization | EntityType::Merchant => {
-            let key_store = state
-                .store
-                .get_merchant_key_store_by_merchant_id(
-                    &state.into(),
-                    &merchant_id,
-                    &state.store.get_master_key().to_vec().into(),
-                )
-                .await
-                .change_context(UserErrors::InternalServerError)?;
-
-            state
-                .store
-                .list_profile_by_merchant_id(&state.into(), &key_store, &merchant_id)
-                .await
-                .change_context(UserErrors::InternalServerError)?
-                .pop()
-                .ok_or(UserErrors::InternalServerError)?
-                .get_id()
-                .to_owned()
-        }
-        EntityType::Profile => user_role
-            .profile_id
-            .clone()
-            .ok_or(UserErrors::InternalServerError)?,
-    };
+    let org_id = get_single_org_id(state, user_role).await?;
+    let merchant_id = get_single_merchant_id(state, user_role, &org_id).await?;
+    let profile_id = get_single_profile_id(state, user_role, &merchant_id).await?;
 
     Ok((merchant_id, profile_id))
 }
diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs
index 0330c43aa45..ff6a9b75cc2 100644
--- a/crates/router_env/src/logger/types.rs
+++ b/crates/router_env/src/logger/types.rs
@@ -361,6 +361,8 @@ pub enum Flow {
     VerifyPaymentConnector,
     /// Internal user signup
     InternalUserSignup,
+    /// Create tenant level user
+    TenantUserCreate,
     /// Switch org
     SwitchOrg,
     /// Switch merchant v2
@@ -391,6 +393,8 @@ pub enum Flow {
     UpdateUserRole,
     /// Create merchant account for user in a org
     UserMerchantAccountCreate,
+    /// Create Org in a given tenancy
+    UserOrgMerchantCreate,
     /// Generate Sample Data
     GenerateSampleData,
     /// Delete Sample Data

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

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

## Description
- Add api to create tenant users
- Add api to create orgs
- Support tenant level entity for the user hierarchy

A tenant level user should see dashboard as seen by the org admin. He will be pinned to one org, merchant and profile. And he can switch between any org in the given tenancy.

### 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 #6707 

## How did you test it?
Api for create tenant:
```
curl --location 'http://localhost:8080/user/tenant_signup' \
--header 'api-key: test_admin' \
--header 'Content-Type: application/json' \
--header 'x-tenant-id: test' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTJhMmViYzItOWZiZS00MDI0LThjODEtOWYwMzMyZTJlZDMxIiwicHVycG9zZSI6InRvdHAiLCJvcmlnaW4iOiJzaWduX3VwIiwicGF0aCI6W10sImV4cCI6MTczMzMwOTI2NSwidGVuYW50X2lkIjoicHVibGljIn0.3B918rdY_XM0dRH4yx9O7Nujd3FbLdmKUmBY52KVKAo' \
--data-raw '{
    "name" : "tf2",
    "email" : "cd7@juspay.in",
    "password": "Pass@321"
}'
```
Response: 200 Ok, if tenant_admin got created successfully
A tenant can login with email and password and continue with 2FA, he will land into any one of organization existing for tenant.

Create Org Api, (works for tenant users only)
```
curl --location 'http://localhost:8080/user/create_org' \
--header 'x-tenant-id: test' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer JWT' \
--header 'Cookie: Cookie' \
--data '{
    "organization_name": "ten",
    "merchant_name": "pp_ten"
}'
```
Response will be 200 OK if the org got created success fully (1 org with 1 merchant and 1 profile)

After getting the login token tenant admin can switch to any org in the tenancy.

## 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
