diff --git a/autogpt_platform/backend/backend/server/routers/v1.py b/autogpt_platform/backend/backend/server/routers/v1.py
index aca22e5c5..9e8bf50d6 100644
--- a/autogpt_platform/backend/backend/server/routers/v1.py
+++ b/autogpt_platform/backend/backend/server/routers/v1.py
@@ -541,7 +541,7 @@ def get_execution_schedules(
@v1_router.post(
"/api-keys",
- response_model=list[CreateAPIKeyResponse] | dict[str, str],
+ response_model=CreateAPIKeyResponse,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -583,7 +583,7 @@ async def get_api_keys(
@v1_router.get(
"/api-keys/{key_id}",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -604,7 +604,7 @@ async def get_api_key(
@v1_router.delete(
"/api-keys/{key_id}",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -626,7 +626,7 @@ async def delete_api_key(
@v1_router.post(
"/api-keys/{key_id}/suspend",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
@@ -648,7 +648,7 @@ async def suspend_key(
@v1_router.put(
"/api-keys/{key_id}/permissions",
- response_model=list[APIKeyWithoutHash] | dict[str, str],
+ response_model=APIKeyWithoutHash,
tags=["api-keys"],
dependencies=[Depends(auth_middleware)],
)
diff --git a/autogpt_platform/frontend/src/app/store/(user)/api_keys/page.tsx b/autogpt_platform/frontend/src/app/store/(user)/api_keys/page.tsx
new file mode 100644
index 000000000..87f3d58b4
--- /dev/null
+++ b/autogpt_platform/frontend/src/app/store/(user)/api_keys/page.tsx
@@ -0,0 +1,11 @@
+import { APIKeysSection } from "@/components/agptui/composite/APIKeySection";
+
+const ApiKeysPage = () => {
+ return (
+
+ );
+};
+
+export default ApiKeysPage;
diff --git a/autogpt_platform/frontend/src/app/store/(user)/layout.tsx b/autogpt_platform/frontend/src/app/store/(user)/layout.tsx
index 0f90e5bd3..64900562a 100644
--- a/autogpt_platform/frontend/src/app/store/(user)/layout.tsx
+++ b/autogpt_platform/frontend/src/app/store/(user)/layout.tsx
@@ -8,6 +8,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
{ text: "Creator Dashboard", href: "/store/dashboard" },
{ text: "Agent dashboard", href: "/store/agent-dashboard" },
{ text: "Integrations", href: "/store/integrations" },
+ { text: "API Keys", href: "/store/api_keys" },
{ text: "Profile", href: "/store/profile" },
{ text: "Settings", href: "/store/settings" },
],
@@ -17,7 +18,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
return (
-
{children}
+
{children}
);
}
diff --git a/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx b/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx
index 5cad3fddc..545d82b76 100644
--- a/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx
+++ b/autogpt_platform/frontend/src/components/agptui/Sidebar.tsx
@@ -2,7 +2,7 @@ import * as React from "react";
import Link from "next/link";
import { Button } from "./Button";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
-import { Menu } from "lucide-react";
+import { KeyIcon, Menu } from "lucide-react";
import {
IconDashboardLayout,
IconIntegrations,
@@ -58,6 +58,15 @@ export const Sidebar: React.FC = ({ linkGroups }) => {
Integrations
+
+
+
+ API Keys
+
+
= ({ linkGroups }) => {
Integrations
+
+
+
+ API Keys
+
+
([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isCreateOpen, setIsCreateOpen] = useState(false);
+ const [isKeyDialogOpen, setIsKeyDialogOpen] = useState(false);
+ const [newKeyName, setNewKeyName] = useState("");
+ const [newKeyDescription, setNewKeyDescription] = useState("");
+ const [newApiKey, setNewApiKey] = useState("");
+ const [selectedPermissions, setSelectedPermissions] = useState<
+ APIKeyPermission[]
+ >([]);
+ const { toast } = useToast();
+ const api = useBackendAPI();
+
+ useEffect(() => {
+ loadAPIKeys();
+ }, []);
+
+ const loadAPIKeys = async () => {
+ setIsLoading(true);
+ try {
+ const keys = await api.listAPIKeys();
+ setApiKeys(keys.filter((key) => key.status === "ACTIVE"));
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleCreateKey = async () => {
+ try {
+ const response = await api.createAPIKey(
+ newKeyName,
+ selectedPermissions,
+ newKeyDescription,
+ );
+
+ setNewApiKey(response.plain_text_key);
+ setIsCreateOpen(false);
+ setIsKeyDialogOpen(true);
+ loadAPIKeys();
+ } catch (error) {
+ toast({
+ title: "Error",
+ description: "Failed to create AutoGPT Platform API key",
+ variant: "destructive",
+ });
+ }
+ };
+
+ const handleCopyKey = () => {
+ navigator.clipboard.writeText(newApiKey);
+ toast({
+ title: "Copied",
+ description: "AutoGPT Platform API key copied to clipboard",
+ });
+ };
+
+ const handleRevokeKey = async (keyId: string) => {
+ try {
+ await api.revokeAPIKey(keyId);
+ toast({
+ title: "Success",
+ description: "AutoGPT Platform API key revoked successfully",
+ });
+ loadAPIKeys();
+ } catch (error) {
+ toast({
+ title: "Error",
+ description: "Failed to revoke AutoGPT Platform API key",
+ variant: "destructive",
+ });
+ }
+ };
+
+ return (
+
+
+ AutoGPT Platform API Keys
+
+ Manage your AutoGPT Platform API keys for programmatic access
+
+
+
+
+
+
+
+
+
+ {isLoading ? (
+
+
+
+ ) : (
+ apiKeys.length > 0 && (
+
+
+
+ Name
+ API Key
+ Status
+ Created
+ Last Used
+
+
+
+
+ {apiKeys.map((key) => (
+
+ {key.name}
+
+
+ {`${key.prefix}******************${key.postfix}`}
+
+
+
+
+ {key.status}
+
+
+
+ {new Date(key.created_at).toLocaleDateString()}
+
+
+ {key.last_used_at
+ ? new Date(key.last_used_at).toLocaleDateString()
+ : "Never"}
+
+
+
+
+
+
+
+ handleRevokeKey(key.id)}
+ >
+ Revoke
+
+
+
+
+
+ ))}
+
+
+ )
+ )}
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
index 1f74e67f6..169c17ac8 100644
--- a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
+++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
@@ -29,6 +29,9 @@ import {
StoreReview,
ScheduleCreatable,
Schedule,
+ APIKeyPermission,
+ CreateAPIKeyResponse,
+ APIKey,
} from "./types";
import { createBrowserClient } from "@supabase/ssr";
import getServerSupabase from "../supabase/getServerSupabase";
@@ -221,6 +224,36 @@ export default class BackendAPI {
);
}
+ // API Key related requests
+ async createAPIKey(
+ name: string,
+ permissions: APIKeyPermission[],
+ description?: string,
+ ): Promise {
+ return this._request("POST", "/api-keys", {
+ name,
+ permissions,
+ description,
+ });
+ }
+
+ async listAPIKeys(): Promise {
+ return this._get("/api-keys");
+ }
+
+ async revokeAPIKey(keyId: string): Promise {
+ return this._request("DELETE", `/api-keys/${keyId}`);
+ }
+
+ async updateAPIKeyPermissions(
+ keyId: string,
+ permissions: APIKeyPermission[],
+ ): Promise {
+ return this._request("PUT", `/api-keys/${keyId}/permissions`, {
+ permissions,
+ });
+ }
+
/**
* @returns `true` if a ping event was received, `false` if provider doesn't support pinging but the webhook exists.
* @throws `Error` if the webhook does not exist.
diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
index 2f2b8fb96..d38320666 100644
--- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
+++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts
@@ -513,3 +513,36 @@ export type StoreReviewCreate = {
score: number;
comments?: string;
};
+
+// API Key Types
+
+export enum APIKeyPermission {
+ EXECUTE_GRAPH = "EXECUTE_GRAPH",
+ READ_GRAPH = "READ_GRAPH",
+ EXECUTE_BLOCK = "EXECUTE_BLOCK",
+ READ_BLOCK = "READ_BLOCK",
+}
+
+export enum APIKeyStatus {
+ ACTIVE = "ACTIVE",
+ REVOKED = "REVOKED",
+ SUSPENDED = "SUSPENDED",
+}
+
+export interface APIKey {
+ id: string;
+ name: string;
+ prefix: string;
+ postfix: string;
+ status: APIKeyStatus;
+ permissions: APIKeyPermission[];
+ created_at: string;
+ last_used_at?: string;
+ revoked_at?: string;
+ description?: string;
+}
+
+export interface CreateAPIKeyResponse {
+ api_key: APIKey;
+ plain_text_key: string;
+}