diff --git a/.changeset/stable-api-keys.md b/.changeset/stable-api-keys.md
new file mode 100644
index 00000000000..1da7ad36ebc
--- /dev/null
+++ b/.changeset/stable-api-keys.md
@@ -0,0 +1,41 @@
+---
+"@clerk/shared": minor
+"@clerk/react": minor
+"@clerk/clerk-js": minor
+"@clerk/ui": minor
+---
+
+API keys is now generally available.
+
+### `` component
+
+```tsx
+import { APIKeys } from '@clerk/react';
+
+export default function Page() {
+ return ;
+}
+```
+
+### `useAPIKeys()` hook
+
+```tsx
+import { useAPIKeys } from '@clerk/react';
+
+export default function CustomAPIKeys() {
+ const { data, isLoading, page, pageCount, fetchNext, fetchPrevious } = useAPIKeys({
+ pageSize: 10,
+ initialPage: 1,
+ });
+
+ if (isLoading) return
Loading...
;
+
+ return (
+
+ {data?.map((key) => (
+ - {key.name}
+ ))}
+
+ );
+}
+```
diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts
index 1a362b2edac..c845eb2c9eb 100644
--- a/packages/clerk-js/src/core/clerk.ts
+++ b/packages/clerk-js/src/core/clerk.ts
@@ -1332,15 +1332,11 @@ export class Clerk implements ClerkInterface {
};
/**
- * @experimental This API is in early access and may change in future releases.
- *
- * Mount a API keys component at the target element.
+ * Mount an API keys component at the target element.
* @param targetNode Target to mount the APIKeys component.
* @param props Configuration parameters.
*/
public mountAPIKeys = (node: HTMLDivElement, props?: APIKeysProps) => {
- logger.warnOnce('Clerk: component is in early access and not yet recommended for production use.');
-
if (disabledAllAPIKeysFeatures(this, this.environment)) {
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, {
@@ -1385,9 +1381,7 @@ export class Clerk implements ClerkInterface {
};
/**
- * @experimental This API is in early access and may change in future releases.
- *
- * Unmount a API keys component from the target element.
+ * Unmount an API keys component from the target element.
* If there is no component mounted at the target node, results in a noop.
*
* @param targetNode Target node to unmount the APIKeys component from.
diff --git a/packages/clerk-js/src/core/modules/apiKeys/index.ts b/packages/clerk-js/src/core/modules/apiKeys/index.ts
index 2c8a05a49aa..77949b70ae0 100644
--- a/packages/clerk-js/src/core/modules/apiKeys/index.ts
+++ b/packages/clerk-js/src/core/modules/apiKeys/index.ts
@@ -15,10 +15,12 @@ import { convertPageToOffsetSearchParams } from '@/utils/convertPageToOffsetSear
import { APIKey, BaseResource } from '../../resources/internal';
export class APIKeys implements APIKeysNamespace {
+ static readonly #pathRoot = '/api_keys';
+
/**
* Returns the base options for the FAPI proxy requests.
*/
- private async getBaseFapiProxyOptions(): Promise {
+ async #getBaseFapiProxyOptions(): Promise {
const token = await BaseResource.clerk.session?.getToken();
if (!token) {
throw new ClerkRuntimeError('No valid session token available', { code: 'no_session_token' });
@@ -27,7 +29,7 @@ export class APIKeys implements APIKeysNamespace {
return {
// Set to an empty string because FAPI Proxy does not include the version in the path.
pathPrefix: '',
- // Set the session token as a Bearer token in the Authorization header for authentication.
+ // FAPI Proxy looks for the session token in the Authorization header.
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
@@ -37,11 +39,19 @@ export class APIKeys implements APIKeysNamespace {
};
}
+ /**
+ * Retrieves a paginated list of API keys.
+ *
+ * The subject (owner) is resolved in the following order:
+ * 1. Explicit `subject` param (user or organization ID)
+ * 2. Active organization ID
+ * 3. Current user ID
+ */
async getAll(params?: GetAPIKeysParams): Promise> {
return BaseResource._fetch({
- ...(await this.getBaseFapiProxyOptions()),
+ ...(await this.#getBaseFapiProxyOptions()),
method: 'GET',
- path: '/api_keys',
+ path: APIKeys.#pathRoot,
search: convertPageToOffsetSearchParams({
...params,
subject: params?.subject ?? BaseResource.clerk.organization?.id ?? BaseResource.clerk.user?.id ?? '',
@@ -59,8 +69,8 @@ export class APIKeys implements APIKeysNamespace {
async create(params: CreateAPIKeyParams): Promise {
const json = (await BaseResource._fetch({
- ...(await this.getBaseFapiProxyOptions()),
- path: '/api_keys',
+ ...(await this.#getBaseFapiProxyOptions()),
+ path: APIKeys.#pathRoot,
method: 'POST',
body: JSON.stringify({
type: 'api_key',
@@ -76,9 +86,9 @@ export class APIKeys implements APIKeysNamespace {
async revoke(params: RevokeAPIKeyParams): Promise {
const json = (await BaseResource._fetch({
- ...(await this.getBaseFapiProxyOptions()),
+ ...(await this.#getBaseFapiProxyOptions()),
method: 'POST',
- path: `/api_keys/${params.apiKeyID}/revoke`,
+ path: `${APIKeys.#pathRoot}/${params.apiKeyID}/revoke`,
body: JSON.stringify({
revocation_reason: params.revocationReason,
}),
diff --git a/packages/react/src/experimental.ts b/packages/react/src/experimental.ts
index 348bdfd274a..001206478e7 100644
--- a/packages/react/src/experimental.ts
+++ b/packages/react/src/experimental.ts
@@ -9,7 +9,6 @@ export type {
} from '@clerk/shared/types';
export {
- __experimental_useAPIKeys as useAPIKeys,
__experimental_PaymentElementProvider as PaymentElementProvider,
__experimental_usePaymentElement as usePaymentElement,
__experimental_PaymentElement as PaymentElement,
diff --git a/packages/react/src/hooks/index.ts b/packages/react/src/hooks/index.ts
index ddd62c4fdb4..0db65cab7f7 100644
--- a/packages/react/src/hooks/index.ts
+++ b/packages/react/src/hooks/index.ts
@@ -10,6 +10,7 @@ export {
useUser,
useSession,
useReverification,
+ useAPIKeys,
__experimental_useCheckout,
__experimental_CheckoutProvider,
__experimental_usePaymentElement,
diff --git a/packages/shared/src/react/hooks/index.ts b/packages/shared/src/react/hooks/index.ts
index 9935f6b9440..3bc5d2b1b58 100644
--- a/packages/shared/src/react/hooks/index.ts
+++ b/packages/shared/src/react/hooks/index.ts
@@ -1,5 +1,5 @@
export { assertContextExists, createContextAndHook } from './createContextAndHook';
-export { useAPIKeys as __experimental_useAPIKeys } from './useAPIKeys';
+export { useAPIKeys } from './useAPIKeys';
export { useOrganization } from './useOrganization';
export { useOrganizationCreationDefaults } from './useOrganizationCreationDefaults';
export type {
diff --git a/packages/shared/src/react/hooks/useAPIKeys.tsx b/packages/shared/src/react/hooks/useAPIKeys.tsx
index cff886d12c8..69bd41b552b 100644
--- a/packages/shared/src/react/hooks/useAPIKeys.tsx
+++ b/packages/shared/src/react/hooks/useAPIKeys.tsx
@@ -7,7 +7,7 @@ import { createCacheKeys } from './createCacheKeys';
import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite';
/**
- * @internal
+ * @interface
*/
export type UseAPIKeysParams = PaginatedHookConfig<
GetAPIKeysParams & {
@@ -21,7 +21,7 @@ export type UseAPIKeysParams = PaginatedHookConfig<
>;
/**
- * @internal
+ * @interface
*/
export type UseAPIKeysReturn = PaginatedResources<
APIKeyResource,
@@ -29,8 +29,6 @@ export type UseAPIKeysReturn = PaginatedResources<
>;
/**
- * @internal
- *
* The `useAPIKeys()` hook provides access to paginated API keys for the current user or organization.
*
* @example
diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts
index 5dc3c124015..09679504ecf 100644
--- a/packages/shared/src/types/apiKeys.ts
+++ b/packages/shared/src/types/apiKeys.ts
@@ -23,20 +23,14 @@ export interface APIKeyResource extends ClerkResource {
export interface APIKeysNamespace {
/**
- * @experimental This API is in early access and may change in future releases.
- *
* Retrieves a paginated list of API keys for the current user or organization.
*/
getAll(params?: GetAPIKeysParams): Promise>;
/**
- * @experimental This API is in early access and may change in future releases.
- *
* Creates a new API key.
*/
create(params: CreateAPIKeyParams): Promise;
/**
- * @experimental This API is in early access and may change in future releases.
- *
* Revokes a given API key by ID.
*/
revoke(params: RevokeAPIKeyParams): Promise;
diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts
index 670a1a21ba0..1a6921a6717 100644
--- a/packages/shared/src/types/clerk.ts
+++ b/packages/shared/src/types/clerk.ts
@@ -644,11 +644,7 @@ export interface Clerk {
unmountPricingTable: (targetNode: HTMLDivElement) => void;
/**
- * This API is in early access and may change in future releases.
- *
- * Mount a api keys component at the target element.
- *
- * @experimental
+ * Mount an API keys component at the target element.
*
* @param targetNode - Target to mount the APIKeys component.
* @param props - Configuration parameters.
@@ -656,14 +652,10 @@ export interface Clerk {
mountAPIKeys: (targetNode: HTMLDivElement, props?: APIKeysProps) => void;
/**
- * This API is in early access and may change in future releases.
- *
- * Unmount a api keys component from the target element.
+ * Unmount an API keys component from the target element.
* If there is no component mounted at the target node, results in a noop.
*
- * @experimental
- *
- * @param targetNode - Target node to unmount the ApiKeys component from.
+ * @param targetNode - Target node to unmount the APIKeys component from.
*/
unmountAPIKeys: (targetNode: HTMLDivElement) => void;
@@ -1032,9 +1024,6 @@ export interface Clerk {
/**
* API Keys Object
- *
- * @experimental
- * This API is in early access and may change in future releases.
*/
apiKeys: APIKeysNamespace;
diff --git a/packages/ui/src/components/APIKeys/APIKeys.tsx b/packages/ui/src/components/APIKeys/APIKeys.tsx
index f137abff203..352ff251a8c 100644
--- a/packages/ui/src/components/APIKeys/APIKeys.tsx
+++ b/packages/ui/src/components/APIKeys/APIKeys.tsx
@@ -1,11 +1,6 @@
import { isClerkAPIResponseError } from '@clerk/shared/error';
import { isOrganizationId } from '@clerk/shared/internal/clerk-js/organization';
-import {
- __experimental_useAPIKeys as useAPIKeys,
- __internal_useOrganizationBase,
- useClerk,
- useUser,
-} from '@clerk/shared/react';
+import { useAPIKeys, __internal_useOrganizationBase, useClerk, useUser } from '@clerk/shared/react';
import type { APIKeyResource } from '@clerk/shared/types';
import { lazy, useState } from 'react';