diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py
index 0c85e9ba4f..de785b221b 100644
--- a/litellm/proxy/_types.py
+++ b/litellm/proxy/_types.py
@@ -514,6 +514,8 @@ class LiteLLMRoutes(enum.Enum):
"/user/delete",
"/user/info",
"/user/list",
+ "/user/daily/activity",
+ "/user/daily/activity/aggregated",
# team
"/team/new",
"/team/update",
@@ -526,6 +528,7 @@ class LiteLLMRoutes(enum.Enum):
"/team/available",
"/team/permissions_list",
"/team/permissions_update",
+ "/team/daily/activity",
# model
"/model/new",
"/model/update",
diff --git a/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py b/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py
index ff00cd73ca..b0b18166d2 100644
--- a/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py
+++ b/litellm/proxy/guardrails/guardrail_hooks/zscaler_ai_guard/zscaler_ai_guard.py
@@ -21,6 +21,7 @@ from litellm.types.utils import GenericGuardrailAPIInputs
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
+ from litellm.types.proxy.guardrails.guardrail_hooks.base import GuardrailConfigModel
GUARDRAIL_TIMEOUT = 5
@@ -334,3 +335,11 @@ class ZscalerAIGuard(CustomGuardrail):
user_facing_error = self._create_user_facing_error(f"{str(e)})")
# This exception will be caught by the proxy and returned to the user
raise HTTPException(status_code=500, detail=user_facing_error)
+
+ @staticmethod
+ def get_config_model() -> Optional[type["GuardrailConfigModel"]]:
+ from litellm.types.proxy.guardrails.guardrail_hooks.zscaler_ai_guard import (
+ ZscalerAIGuardConfigModel,
+ )
+
+ return ZscalerAIGuardConfigModel
diff --git a/litellm/types/proxy/guardrails/guardrail_hooks/zscaler_ai_guard.py b/litellm/types/proxy/guardrails/guardrail_hooks/zscaler_ai_guard.py
new file mode 100644
index 0000000000..7cbdf751e1
--- /dev/null
+++ b/litellm/types/proxy/guardrails/guardrail_hooks/zscaler_ai_guard.py
@@ -0,0 +1,132 @@
+from typing import Optional
+
+from pydantic import Field, model_validator
+
+from litellm._logging import verbose_proxy_logger
+from litellm.types.guardrails import GuardrailParamUITypes
+
+from .base import GuardrailConfigModel
+
+
+class ZscalerAIGuardConfigModel(GuardrailConfigModel):
+ api_key: Optional[str] = Field(
+ default=None,
+ description=(
+ "API key for Zscaler AI Guard authentication. "
+ "If not provided, falls back to ZSCALER_AI_GUARD_API_KEY environment variable."
+ ),
+ )
+
+ api_base: Optional[str] = Field(
+ default=None,
+ description=(
+ "Zscaler AI Guard API endpoint. Determines policy resolution behavior:\n"
+ "• /execute-policy (default) - Requires explicit policy_id in configuration\n"
+ "• /resolve-and-execute-policy - Infers policy from user-api-key-alias header\n"
+ "Default: https://api.us1.zseclipse.net/v1/detection/execute-policy\n"
+ "Falls back to ZSCALER_AI_GUARD_URL environment variable."
+ ),
+ json_schema_extra={
+ "examples": [
+ "https://api.us1.zseclipse.net/v1/detection/execute-policy",
+ "https://api.us1.zseclipse.net/v1/detection/resolve-and-execute-policy",
+ ]
+ },
+ )
+
+ policy_id: Optional[int] = Field(
+ default=None,
+ description=(
+ "Global policy ID for Zscaler AI Guard. Required when using /execute-policy endpoint.\n\n"
+ "Set to 0 or leave empty when using /resolve-and-execute-policy with dynamic policy resolution.\n"
+ "Falls back to ZSCALER_AI_GUARD_POLICY_ID environment variable."
+ ),
+ json_schema_extra={
+ "ui_hint": "conditional_required",
+ "condition": "Required when api_base ends with /execute-policy",
+ },
+ )
+
+ send_user_api_key_alias: Optional[bool] = Field(
+ default=False,
+ description=(
+ "Send user API key alias in request headers as 'user-api-key-alias'. "
+ "CRITICAL when using /resolve-and-execute-policy endpoint - the policy is inferred from this value. "
+ "Also useful for tracking/auditing with /execute-policy endpoint."
+ ),
+ json_schema_extra={
+ "ui_type": GuardrailParamUITypes.BOOL,
+ "ui_hint": "recommended_when",
+ "condition": "Recommended when api_base ends with /resolve-and-execute-policy",
+ },
+ )
+
+ send_user_api_key_user_id: Optional[bool] = Field(
+ default=False,
+ description=(
+ "Send user API key user_id in request headers as 'user-api-key-user-id'. "
+ "Enables user-level tracking and analytics in Zscaler AI Guard."
+ ),
+ json_schema_extra={"ui_type": GuardrailParamUITypes.BOOL},
+ )
+
+ send_user_api_key_team_id: Optional[bool] = Field(
+ default=False,
+ description=(
+ "Send user API key team_id in request headers as 'user-api-key-team-id'. "
+ "Enables team-level tracking and analytics in Zscaler AI Guard."
+ ),
+ json_schema_extra={"ui_type": GuardrailParamUITypes.BOOL},
+ )
+
+ @model_validator(mode="after")
+ def validate_endpoint_configuration(self) -> "ZscalerAIGuardConfigModel":
+ """
+ Validate configuration consistency between api_base and other fields.
+ Provides warnings but doesn't block (since env vars might provide values).
+ """
+ import os
+
+ # Resolve actual api_base value (including env fallback)
+ api_base = self.api_base or os.getenv(
+ "ZSCALER_AI_GUARD_URL",
+ "https://api.us1.zseclipse.net/v1/detection/execute-policy",
+ )
+
+ # Resolve actual policy_id value
+ policy_id = self.policy_id
+ if policy_id is None:
+ env_policy = os.getenv("ZSCALER_AI_GUARD_POLICY_ID")
+ if env_policy:
+ try:
+ policy_id = int(env_policy)
+ except ValueError:
+ verbose_proxy_logger.warning(
+ f"ZSCALER_AI_GUARD_POLICY_ID env var is not a valid integer: {env_policy}"
+ )
+
+ # Check for configuration issues
+ is_resolve_policy = api_base.endswith("/resolve-and-execute-policy")
+ is_execute_policy = api_base.endswith("/execute-policy") and not is_resolve_policy
+
+ # Scenario A: execute-policy without policy_id
+ if is_execute_policy and (policy_id is None or policy_id < 1):
+ verbose_proxy_logger.warning(
+ "Using /execute-policy endpoint without a valid policy_id. "
+ "Ensure ZSCALER_AI_GUARD_POLICY_ID environment variable is set, "
+ "or provide policy_id via request/key/team metadata."
+ )
+
+ # Scenario B: resolve-and-execute-policy without user_api_key_alias
+ if is_resolve_policy and not self.send_user_api_key_alias:
+ verbose_proxy_logger.warning(
+ "Using /resolve-and-execute-policy endpoint without send_user_api_key_alias=true. "
+ "The endpoint requires user-api-key-alias header to resolve the policy. "
+ "Set send_user_api_key_alias to true or ensure the header is sent via other means."
+ )
+
+ return self
+
+ @staticmethod
+ def ui_friendly_name() -> str:
+ return "Zscaler AI Guard"
diff --git a/ui/litellm-dashboard/public/assets/logos/zscaler.svg b/ui/litellm-dashboard/public/assets/logos/zscaler.svg
new file mode 100644
index 0000000000..2a95cb02ae
--- /dev/null
+++ b/ui/litellm-dashboard/public/assets/logos/zscaler.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/ui/litellm-dashboard/src/components/guardrails/guardrail_info_helpers.tsx b/ui/litellm-dashboard/src/components/guardrails/guardrail_info_helpers.tsx
index c6314c95be..bde21661d8 100644
--- a/ui/litellm-dashboard/src/components/guardrails/guardrail_info_helpers.tsx
+++ b/ui/litellm-dashboard/src/components/guardrails/guardrail_info_helpers.tsx
@@ -104,6 +104,7 @@ export const shouldRenderContentFilterConfigSettings = (provider: string | null)
const asset_logos_folder = "../ui/assets/logos/";
export const guardrailLogoMap: Record = {
+ "Zscaler AI Guard": `${asset_logos_folder}zscaler.svg`,
"Presidio PII": `${asset_logos_folder}presidio.png`,
"Bedrock Guardrail": `${asset_logos_folder}bedrock.svg`,
Lakera: `${asset_logos_folder}lakeraai.jpeg`,