diff --git a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx
index 94dd6eb3cf1..d892c4f932e 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/layout.tsx
@@ -7,6 +7,7 @@ import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useRouter, useSearchParams } from "next/navigation";
import { DebugWarningBanner } from "@/components/DebugWarningBanner";
+import { LicenseExpiryBanner } from "@/components/LicenseExpiryBanner";
/** ---- BASE URL HELPERS ---- */
function normalizeBasePrefix(raw: string | undefined | null): string {
@@ -81,6 +82,7 @@ function LayoutContent({ children }: { children: React.ReactNode }) {
toggleDarkMode={() => { }}
/>
+
({
+ default: vi.fn(() => ({ accessToken: "test-token" })),
+}));
+
+vi.mock("./networking", () => ({
+ getLicenseInfo: vi.fn().mockResolvedValue(null),
+}));
+
+import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
+import { getLicenseInfo } from "./networking";
+
+const mockUseAuthorized = vi.mocked(useAuthorized);
+const mockGetLicenseInfo = vi.mocked(getLicenseInfo);
+
+const FIXED_NOW = new Date("2026-03-15T12:00:00Z").getTime();
+let realDateNow: () => number;
+
+function makeLicense(expirationDate: string) {
+ return {
+ has_license: true,
+ license_type: "enterprise",
+ expiration_date: expirationDate,
+ allowed_features: [] as string[],
+ limits: { max_users: null, max_teams: null },
+ };
+}
+
+describe("LicenseExpiryBanner", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ realDateNow = Date.now;
+ Date.now = () => FIXED_NOW;
+ // Also override new Date() with no args
+ const OrigDate = Date;
+ const MockDate = class extends OrigDate {
+ constructor(...args: any[]) {
+ if (args.length === 0) {
+ super(FIXED_NOW);
+ } else {
+ // @ts-ignore
+ super(...args);
+ }
+ }
+ };
+ // @ts-ignore
+ global.Date = MockDate;
+ // Preserve static methods
+ global.Date.now = () => FIXED_NOW;
+ global.Date.parse = OrigDate.parse;
+ global.Date.UTC = OrigDate.UTC;
+
+ mockUseAuthorized.mockReturnValue({
+ accessToken: "test-token",
+ isLoading: false,
+ isAuthorized: true,
+ token: "tok",
+ userId: "u1",
+ userEmail: "u@e.com",
+ userRole: "proxy_admin",
+ premiumUser: true,
+ disabledPersonalKeyCreation: null,
+ showSSOBanner: false,
+ } as any);
+ mockGetLicenseInfo.mockResolvedValue(null);
+ });
+
+ afterEach(() => {
+ Date.now = realDateNow;
+ });
+
+ it("should render nothing when there is no license info", async () => {
+ const { container } = render();
+ await waitFor(() => {
+ expect(mockGetLicenseInfo).toHaveBeenCalledWith("test-token");
+ });
+ expect(container.innerHTML).toBe("");
+ });
+
+ it("should render nothing when expiration is more than 14 days away", async () => {
+ mockGetLicenseInfo.mockResolvedValue(makeLicense("2026-04-15"));
+
+ const { container } = render();
+ await waitFor(() => {
+ expect(mockGetLicenseInfo).toHaveBeenCalled();
+ });
+ await waitFor(() => {
+ expect(container.querySelector(".ant-alert")).toBeNull();
+ });
+ });
+
+ it("should show a warning banner when license expires within 14 days", async () => {
+ // Now is 2026-03-15T12:00:00Z, exp is 2026-03-22T23:59:59 => ~7.5 days => ceil = 8
+ mockGetLicenseInfo.mockResolvedValue(makeLicense("2026-03-22"));
+
+ render();
+
+ expect(
+ await screen.findByText("Enterprise License Expiring in 8 days")
+ ).toBeInTheDocument();
+ });
+
+ it("should show singular 'day' when 1 day remains", async () => {
+ // Now is 2026-03-15T12:00:00Z, exp is 2026-03-15T23:59:59 => ~0.5 days => ceil = 1
+ mockGetLicenseInfo.mockResolvedValue(makeLicense("2026-03-15"));
+
+ render();
+
+ expect(
+ await screen.findByText("Enterprise License Expiring in 1 day")
+ ).toBeInTheDocument();
+ });
+
+ it("should show an error banner when the license has expired", async () => {
+ mockGetLicenseInfo.mockResolvedValue(makeLicense("2026-03-10"));
+
+ render();
+
+ expect(
+ await screen.findByText("Enterprise License Expired")
+ ).toBeInTheDocument();
+ });
+
+ it("should include renewal instructions in the description", async () => {
+ mockGetLicenseInfo.mockResolvedValue(makeLicense("2026-03-20"));
+
+ render();
+
+ expect(
+ await screen.findByText(/Please contact support to renew before expiration/i)
+ ).toBeInTheDocument();
+ });
+
+ it("should not render when accessToken is null", async () => {
+ mockUseAuthorized.mockReturnValue({ accessToken: null } as any);
+
+ const { container } = render();
+ await waitFor(() => {
+ expect(mockGetLicenseInfo).not.toHaveBeenCalled();
+ });
+ expect(container.innerHTML).toBe("");
+ });
+
+ it("should not render when has_license is false", async () => {
+ mockGetLicenseInfo.mockResolvedValue({
+ ...makeLicense("2026-03-20"),
+ has_license: false,
+ });
+
+ const { container } = render();
+ await waitFor(() => {
+ expect(mockGetLicenseInfo).toHaveBeenCalled();
+ });
+ await waitFor(() => {
+ expect(container.querySelector(".ant-alert")).toBeNull();
+ });
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx
new file mode 100644
index 00000000000..58a7bff32a6
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx
@@ -0,0 +1,64 @@
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { Alert } from "antd";
+import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
+import { getLicenseInfo, LicenseInfo } from "./networking";
+
+const EXPIRY_WARNING_DAYS = 14;
+
+export const LicenseExpiryBanner: React.FC = () => {
+ const { accessToken } = useAuthorized();
+ const [licenseInfo, setLicenseInfo] = useState(null);
+
+ useEffect(() => {
+ if (!accessToken) return;
+
+ getLicenseInfo(accessToken)
+ .then(setLicenseInfo)
+ .catch(() => null);
+ }, [accessToken]);
+
+ if (!licenseInfo?.has_license || !licenseInfo.expiration_date) {
+ return null;
+ }
+
+ const expDate = new Date(licenseInfo.expiration_date + "T23:59:59");
+ const now = new Date();
+ const diffMs = expDate.getTime() - now.getTime();
+ const daysRemaining = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
+
+ if (daysRemaining > EXPIRY_WARNING_DAYS) {
+ return null;
+ }
+
+ const expirationDisplay = expDate.toLocaleString(undefined, {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ timeZoneName: "short",
+ });
+
+ const isExpired = daysRemaining <= 0;
+ const message = isExpired
+ ? "Enterprise License Expired"
+ : `Enterprise License Expiring in ${daysRemaining} day${daysRemaining === 1 ? "" : "s"}`;
+
+ const description = isExpired
+ ? `Your enterprise license expired on ${expirationDisplay}. Please contact support to renew.`
+ : `Your enterprise license will expire on ${expirationDisplay}. Please contact support to renew before expiration.`;
+
+ return (
+
+ );
+};