From 5147b18d8f5e54fafaa323c065082115f683cde2 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 12 Jun 2026 17:34:57 -0700 Subject: [PATCH 01/18] docs(backend,shared): Generate typedoc output for backend docs Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/docs-backend-typedoc.md | 2 + .typedoc/custom-plugin.mjs | 72 +++++++++++++- .../backend/src/api/endpoints/M2MTokenApi.ts | 12 ++- packages/backend/src/api/endpoints/UserApi.ts | 96 +++++++++++++------ packages/backend/typedoc.json | 3 +- packages/shared/src/types/billing.ts | 2 + packages/shared/src/types/pagination.ts | 2 + typedoc.config.mjs | 5 + 8 files changed, 159 insertions(+), 35 deletions(-) create mode 100644 .changeset/docs-backend-typedoc.md diff --git a/.changeset/docs-backend-typedoc.md b/.changeset/docs-backend-typedoc.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/docs-backend-typedoc.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 2dd1fa921a9..869ba73a069 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -1,5 +1,5 @@ // @ts-check - Enable TypeScript checks for safer MDX post-processing and link rewriting -import { Converter } from 'typedoc'; +import { Converter, DeclarationReflection, ReflectionKind, ReflectionType, RendererEvent } from 'typedoc'; import { MarkdownPageEvent } from 'typedoc-plugin-markdown'; /** @@ -464,6 +464,33 @@ export function applyCatchAllMdReplacements(contents) { .join('\n'); } +/** + * Walk a typedoc Type and return a flat list of property declarations to render as a merged table. Used by the `@expandProperties` flattener below to handle three shapes: + * - intersection types: walk each constituent + * - inline object literals (ReflectionType): take its declaration.children + * - named references (ReferenceType): take the target's children plus any properties contributed via type arguments, which captures the `Foo<{ ... }>` instantiation pattern where typedoc otherwise loses the generic parameter at the alias boundary. + * + * @param {import('typedoc').SomeType | undefined} type + * @param {Map} reflectionsByName lookup for cross-package refs whose `.reflection` is not linked + * @returns {import('typedoc').DeclarationReflection[]} + */ +function collectPropertiesFromType(type, reflectionsByName) { + if (!type) return []; + if (type.type === 'reflection') { + return type.declaration?.children ?? []; + } + if (type.type === 'intersection') { + return type.types.flatMap(t => collectPropertiesFromType(t, reflectionsByName)); + } + if (type.type === 'reference') { + const target = type.reflection ?? reflectionsByName.get(type.name); + const targetChildren = target?.children ?? []; + const argChildren = (type.typeArguments ?? []).flatMap(t => collectPropertiesFromType(t, reflectionsByName)); + return [...targetChildren, ...argChildren]; + } + return []; +} + /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ @@ -479,6 +506,49 @@ export function load(app) { } }); + /** + * Flatten the `Foo<{...}>` generic-instantiation pattern into a single merged properties table when `Foo` opts in via `@expandProperties`. typedoc-plugin-markdown would otherwise render an empty page for these aliases because the resolved type is a `ReferenceType` with no inline declaration — see `member.declaration.js` in the plugin, which only walks `IntersectionType` sub-types and has no branch for top-level `ReferenceType`. + * + * Runs at `RendererEvent.BEGIN` rather than `EVENT_RESOLVE_END` because the resolve hook fires per package, and cross-package references (e.g. `@clerk/backend` types referencing `ClerkPaginationRequest` from `@clerk/shared`) only link up after typedoc merges packages. + * + * The opt-in tag lives on the wrapper type so we never accidentally flatten unrelated generic aliases (e.g. `SignInErrors = Errors`). + */ + app.renderer.on(RendererEvent.BEGIN, event => { + const all = Object.values(event.project.reflections); + const reflectionsByName = new Map(); + for (const r of all) { + if (r.name && !reflectionsByName.has(r.name)) reflectionsByName.set(r.name, r); + } + const expandable = new Set(); + for (const r of all) { + if (r.comment?.modifierTags?.has('@expandProperties')) { + expandable.add(r); + r.comment.modifierTags.delete('@expandProperties'); + } + } + for (const reflection of all) { + if ( + reflection.kindOf?.(ReflectionKind.TypeAlias) && + reflection.type?.type === 'reference' && + Array.isArray(reflection.type.typeArguments) && + reflection.type.typeArguments.length > 0 + ) { + const target = reflection.type.reflection ?? reflectionsByName.get(reflection.type.name); + if (!target || !expandable.has(target)) continue; + const merged = collectPropertiesFromType(reflection.type, reflectionsByName); + if (merged.length > 0) { + // typedoc's package-level `sort: 'alphabetical'` is applied during conversion, before + // our synthetic merge runs. Sort here to match the alphabetical ordering used by + // every other table in the docs. + merged.sort((a, b) => a.name.localeCompare(b.name)); + const decl = new DeclarationReflection('__type', ReflectionKind.TypeLiteral, reflection); + decl.children = merged; + reflection.type = new ReflectionType(decl); + } + } + } + }); + app.renderer.on(MarkdownPageEvent.END, output => { const fileName = output.url.split('/').pop(); diff --git a/packages/backend/src/api/endpoints/M2MTokenApi.ts b/packages/backend/src/api/endpoints/M2MTokenApi.ts index 198179b7c3f..098549f55d9 100644 --- a/packages/backend/src/api/endpoints/M2MTokenApi.ts +++ b/packages/backend/src/api/endpoints/M2MTokenApi.ts @@ -13,11 +13,7 @@ import { AbstractAPI } from './AbstractApi'; const basePath = '/m2m_tokens'; -/** - * Format of the M2M token to create. - * - 'opaque': Opaque token with mt_ prefix - * - 'jwt': JWT signed with instance keys - */ +/** @inline */ export type M2MTokenFormat = 'opaque' | 'jwt'; type GetM2MTokenListParams = ClerkPaginationRequest<{ @@ -61,6 +57,12 @@ type CreateM2MTokenParams = { */ minRemainingTtlSeconds?: number; /** + * Format of the M2M token to create. + *
    + *
  • 'opaque': Opaque token with mt_ prefix
  • + *
  • 'jwt': JWT signed with instance keys
  • + *
+ * * @default 'opaque' */ tokenFormat?: M2MTokenFormat; diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index ef64db32dac..5936c967105 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -18,43 +18,72 @@ import type { WithSign } from './util-types'; const basePath = '/users'; -type UserCountParams = { +/** @generateWithEmptyComment */ +export type UserCountParams = { + /** Counts users with emails that match the given query, via case-insensitive partial match. For example, `emailAddress=hello` will match a user with the email `HELLO@example.com`. Accepts up to 100 email addresses. */ emailAddress?: string[]; + /** Counts users with phone numbers that match the given query, via case-insensitive partial match. For example, `phoneNumber=555` will match a user with the phone number `+1555xxxxxxx`. Accepts up to 100 phone numbers. */ phoneNumber?: string[]; + /** Counts users with usernames that match the given query, via case-insensitive partial match. For example, `username=CoolUser` will match a user with the username `SomeCoolUser`. Accepts up to 100 usernames. */ username?: string[]; + /** Counts users with Web3 wallet addresses that match the given query, via case-insensitive partial match. For example, `web3Wallet=0x1234567890` will match a user with the Web3 wallet address `0x1234567890`. Accepts up to 100 Web3 wallet addresses. */ web3Wallet?: string[]; + /** Counts users matching the given query across email addresses, phone numbers, usernames, Web3 wallet addresses, user IDs, first names, and last names. Partial matches supported. For example, `query=hello` will match a user with the email `HELLO@example.com`. */ query?: string; + /** Counts users with the specified user IDs. Accepts up to 100 user IDs. */ userId?: string[]; + /** Counts users with the specified external IDs. Accepts up to 100 external IDs. */ externalId?: string[]; }; -type UserListParams = ClerkPaginationRequest< - UserCountParams & { - orderBy?: WithSign< - | 'created_at' - | 'updated_at' - | 'email_address' - | 'web3wallet' - | 'first_name' - | 'last_name' - | 'phone_number' - | 'username' - | 'last_active_at' - | 'last_sign_in_at' - >; - /** - * @deprecated Use `lastActiveAtAfter` instead. This parameter will be removed in a future version. - */ - last_active_at_since?: number; - lastActiveAtBefore?: number; - lastActiveAtAfter?: number; - createdAtBefore?: number; - createdAtAfter?: number; - lastSignInAtAfter?: number; - lastSignInAtBefore?: number; - organizationId?: string[]; - } ->; +/** @generateWithEmptyComment */ +export type UserListParams = ClerkPaginationRequest<{ + /** Filters users with the specified email addresses. Accepts up to 100 email addresses. */ + emailAddress?: string[]; + /** Filters users with the specified phone numbers. Accepts up to 100 phone numbers. */ + phoneNumber?: string[]; + /** Filters users with the specified usernames. Accepts up to 100 usernames. */ + username?: string[]; + /** Filters users with the specified Web3 wallet addresses. Accepts up to 100 Web3 wallet addresses. */ + web3Wallet?: string[]; + /** Filters users matching the given query across email addresses, phone numbers, usernames, Web3 wallet addresses, user IDs, first names, and last names. Partial matches supported. */ + query?: string; + /** Filters users with the specified user IDs. Accepts up to 100 user IDs. */ + userId?: string[]; + /** Filters users with the specified external IDs. Accepts up to 100 external IDs. */ + externalId?: string[]; + /** Returns users in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`.*/ + orderBy?: WithSign< + | 'created_at' + | 'updated_at' + | 'email_address' + | 'web3wallet' + | 'first_name' + | 'last_name' + | 'phone_number' + | 'username' + | 'last_active_at' + | 'last_sign_in_at' + >; + /** + * @deprecated Use `lastActiveAtAfter` instead. This parameter will be removed in a future version. + */ + last_active_at_since?: number; + /** Filters users who were last active before the given date (with millisecond precision). */ + lastActiveAtBefore?: number; + /** Filters users who were last active after the given date (with millisecond precision). */ + lastActiveAtAfter?: number; + /** Filters users who were created before the given date (with millisecond precision). */ + createdAtBefore?: number; + /** Filters users who were created after the given date (with millisecond precision). */ + createdAtAfter?: number; + /** Filters users who were last signed in after the given date (with millisecond precision). */ + lastSignInAtAfter?: number; + /** Filters users who were last signed in before the given date (with millisecond precision). */ + lastSignInAtBefore?: number; + /** Filters users who are members of the specified organizations. Accepts up to 100 organization IDs. */ + organizationId?: string[]; +}>; type UserMetadataParams = { publicMetadata?: UserPublicMetadata; @@ -259,6 +288,10 @@ type UserID = { }; export class UserAPI extends AbstractAPI { + /** + * Retrieves the list of users in your instance. + * @returns A [PaginatedResourceResponse](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`User`](https://clerk.com/docs/reference/backend/types/backend-user) objects, and a `totalCount` property that indicates the total number of users in your instance. + */ public async getUserList(params: UserListParams = {}) { const { limit, offset, orderBy, ...userCountParams } = params; // TODO(dimkl): Temporary change to populate totalCount using a 2nd BAPI call to /users/count endpoint @@ -275,6 +308,10 @@ export class UserAPI extends AbstractAPI { return { data, totalCount } as PaginatedResourceResponse; } + /** + * Gets a [`User`](https://clerk.com/docs/reference/backend/types/backend-user) for the specified user ID. + * @param userId - The ID of the user to retrieve. + */ public async getUser(userId: string) { this.requireId(userId); return this.request({ @@ -395,6 +432,9 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Gets the total number of users in your instance. + */ public async getCount(params: UserCountParams = {}) { return this.request({ method: 'GET', diff --git a/packages/backend/typedoc.json b/packages/backend/typedoc.json index 904b837abec..ae6a83fa433 100644 --- a/packages/backend/typedoc.json +++ b/packages/backend/typedoc.json @@ -8,6 +8,7 @@ "./src/tokens/types.ts", "./src/tokens/authObjects.ts", "./src/api/resources/index.ts", - "./src/api/resources/Deserializer.ts" + "./src/api/resources/Deserializer.ts", + "./src/api/endpoints/**/*.ts" ] } diff --git a/packages/shared/src/types/billing.ts b/packages/shared/src/types/billing.ts index 60390e1206e..421d09d1d37 100644 --- a/packages/shared/src/types/billing.ts +++ b/packages/shared/src/types/billing.ts @@ -7,6 +7,8 @@ import type { ForceNull, RemoveFunctions, Simplify } from './utils'; /** * Intersects `T` with an optional organization scope (`orgId`) for billing and related requests. + * + * @interface */ export type WithOptionalOrgType = T & { /** diff --git a/packages/shared/src/types/pagination.ts b/packages/shared/src/types/pagination.ts index b47d585d9ae..0920ca247f9 100644 --- a/packages/shared/src/types/pagination.ts +++ b/packages/shared/src/types/pagination.ts @@ -2,6 +2,7 @@ * Pagination params in request * * @interface + * @expandProperties */ export type ClerkPaginationRequest = { /** @@ -33,6 +34,7 @@ export interface ClerkPaginatedResponse { /** * @interface + * @expandProperties */ export type ClerkPaginationParams = { /** diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 77a8e44470d..0f3353cb398 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -122,6 +122,11 @@ const config = { '@standalonePage', /** Self-documenting placeholder for declarations intentionally left without a description. */ '@generateWithEmptyComment', + /** + * On a generic wrapper type (e.g. `ClerkPaginationRequest`), opts every alias of the form `Foo<{...}>` into a single merged properties table that includes the wrapper's own properties. Without this, typedoc-plugin-markdown renders such aliases as empty pages because the resolved type is a ReferenceType with no inline declaration. + * Handled by `.typedoc/custom-plugin.mjs`. + */ + '@expandProperties', ], /** * Keep `@inline` / `@inlineType` / `@standalonePage` in the model so the custom router and theme can read them. From 49b7e87119680a9fb5c4883aea9212e395c38683 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:59:56 -0700 Subject: [PATCH 02/18] docs(backend): Extract backend API methods into per-method MDX pages Co-Authored-By: Claude Opus 4.7 (1M context) --- .typedoc/__tests__/file-structure.test.ts | 2 + .typedoc/extract-methods.mjs | 124 +++++- .typedoc/reference-objects.mjs | 21 +- packages/backend/src/api/endpoints/UserApi.ts | 415 ++++++++++++++---- packages/shared/src/types/pagination.ts | 4 +- 5 files changed, 459 insertions(+), 107 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 5aca0e62a03..1adb3482236 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -48,6 +48,8 @@ describe('Typedoc output', () => { expect(nestedFolders).toMatchInlineSnapshot(` [ + "backend/user-api", + "backend/user-api/methods", "react/legacy", "shared/api-key-resource", "shared/api-key-resource/methods", diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 138a388fa9d..64398b981b4 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -35,7 +35,20 @@ import { } from './custom-plugin.mjs'; import { isCallableInterfaceProperty } from './custom-theme.mjs'; import { removeLineBreaks } from './markdown-helpers.mjs'; -import { REFERENCE_OBJECT_CONFIG } from './reference-objects.mjs'; +import { BACKEND_API_CONFIG, REFERENCE_OBJECT_CONFIG } from './reference-objects.mjs'; + +/** + * `'reference'` (default): each `methods/.mdx` opens with `### foo()` and uses an H4 `#### Parameters` heading — matches the reference-object pages that aggregate many methods. + * `'page'`: skip the method-name title and use an H2 `## Parameters` heading — for one-method-per-docs-page surfaces, like the backend API endpoints. + * @typedef {'reference' | 'page'} MethodFormat + */ + +/** @param {string} pageUrl */ +function methodFormatForPageUrl(pageUrl) { + return pageUrl in BACKEND_API_CONFIG + ? /** @type {MethodFormat} */ ('page') + : /** @type {MethodFormat} */ ('reference'); +} import { toFileSlug } from './slug.mjs'; import { isInlineModifierWithoutStandalonePage } from './standalone-page-tag.mjs'; import { unwrapOptional } from './type-utils.mjs'; @@ -220,13 +233,52 @@ function getCallSignatureFromType(t, visitedReflectionIds) { return undefined; } +/** + * @param {import('typedoc').SignatureReflection} sig + */ +function signatureIsDeprecated(sig) { + const c = sig.comment; + if (!c) return false; + if (typeof c.hasModifier === 'function' && c.hasModifier('@deprecated')) return true; + return c.blockTags?.some(t => t.tag === '@deprecated') ?? false; +} + +/** + * typedoc maps `@param paramName description` to `parameter.comment` during conversion of the + * signature that physically owns the JSDoc. With overloads + an implementation, the impl's `@param` + * tags don't transfer to overload parameters — even when typedoc copies the impl's comment onto + * each overload's `sig.comment.blockTags`. Without descriptions on `param.comment`, typedoc-plugin-markdown + * drops the Description column from the parameters table. + * + * Overlay missing parameter comments from any matching `@param` block tag on the signature comment + * so the rendered table shows the descriptions the author wrote on the implementation's JSDoc. + * + * @param {import('typedoc').SignatureReflection} sig + */ +function overlayParamCommentsFromSignatureBlockTags(sig) { + const params = sig.parameters; + if (!params?.length) return; + const blockTags = sig.comment?.blockTags; + if (!blockTags?.length) return; + for (const p of params) { + if (p.comment?.summary?.length) continue; + const tag = blockTags.find(t => t.tag === '@param' && t.name === p.name); + if (!tag?.content?.length) continue; + p.comment = new Comment(tag.content); + } +} + /** * @param {import('typedoc').DeclarationReflection} decl * @returns {import('typedoc').SignatureReflection | undefined} */ function getPrimaryCallSignature(decl) { if (decl.signatures?.length) { - return decl.signatures[0]; + // Prefer the first non-`@deprecated` overload. typedoc treats each overload as a separate + // signature, and selecting a deprecated one usually means rendering the form users shouldn't + // use — and (often) one whose JSDoc isn't where the canonical `@param` descriptions live. + const firstActive = decl.signatures.find(s => !signatureIsDeprecated(s)); + return firstActive ?? decl.signatures[0]; } const t = decl.type; if (t && 'declaration' in t && t.declaration?.signatures?.length) { @@ -872,7 +924,10 @@ function nestedParameterRowsFromDocumentedProperties(param, ctx) { const summary = child.comment?.summary; const typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; const nestedNameCol = formatNestedParamNameColumn(param, child.name); - const nestedDesc = summary?.length ? displayPartsToString(summary).trim() || '—' : '—'; + const nestedDescRaw = summary?.length ? displayPartsToString(summary).trim() || '—' : '—'; + // Strip line breaks so multi-line `
    ` / paragraph descriptions don't shatter the markdown + // table row (which must be a single line). Matches the treatment already applied to typeCell. + const nestedDesc = removeLineBreaksForTableCell(nestedDescRaw) ?? '—'; rows.push(`| ${nestedNameCol} | ${typeCell} | ${nestedDesc} |`); } return rows; @@ -981,7 +1036,12 @@ function isNominalParamTypeDocumented(typeDecl, props) { * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx * @returns {string | undefined} */ -function trySingleNominalParameterTypeSection(sig, ctx) { +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {number} [headingLevel] Defaults to 4 (reference-object format); pass 2 for page format. + */ +function trySingleNominalParameterTypeSection(sig, ctx, headingLevel = 4) { const params = sig.parameters ?? []; if (params.length !== 1) { return undefined; @@ -1013,22 +1073,23 @@ function trySingleNominalParameterTypeSection(sig, ctx) { if (!tableMd?.trim()) { return undefined; } - return [markdownHeadingInlineCode(4, nominal.sectionTitle), '', tableMd, ''].join('\n'); + return [markdownHeadingInlineCode(headingLevel, nominal.sectionTitle), '', tableMd, ''].join('\n'); } /** * @param {import('typedoc').SignatureReflection} sig * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx * @param {Map | undefined} instantiationMap + * @param {number} [headingLevel] Defaults to 4 (reference-object format); pass 2 for page format. */ -function parametersMarkdownTable(sig, ctx, instantiationMap) { +function parametersMarkdownTable(sig, ctx, instantiationMap, headingLevel = 4) { const sigForDisplay = signatureWithInstantiation(sig, instantiationMap); const params = sigForDisplay.parameters ?? []; if (params.length === 0) { return ''; } - const singleNominal = trySingleNominalParameterTypeSection(sigForDisplay, ctx); + const singleNominal = trySingleNominalParameterTypeSection(sigForDisplay, ctx, headingLevel); if (singleNominal) { return singleNominal; } @@ -1043,21 +1104,26 @@ function parametersMarkdownTable(sig, ctx, instantiationMap) { tableMd = `${tableMd.trimEnd()}\n${nested.join('\n')}\n`; } - return [markdownHeading(4, ReflectionKind.pluralString(ReflectionKind.Parameter)), '', tableMd, ''].join('\n'); + return [markdownHeading(headingLevel, ReflectionKind.pluralString(ReflectionKind.Parameter)), '', tableMd, ''].join( + '\n', + ); } /** * @param {import('typedoc').DeclarationReflection} decl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @param {{ qualifiedName?: string }} [options] Nested namespace methods use `parent.child` for headings / signatures. + * @param {{ qualifiedName?: string, methodFormat?: MethodFormat }} [options] Nested namespace methods use `parent.child` for headings / signatures. */ function buildMethodMdx(decl, ctx, options = {}) { const name = options.qualifiedName ?? decl.name; + const methodFormat = options.methodFormat ?? 'reference'; const sig = getPrimaryCallSignature(decl); if (!sig) { return ''; } - const title = `### \`${name}()\``; + // `'page'` format renders one method per docs page, so the page is anchored by a containing file/heading upstream — no in-page `### foo()` title needed. `'reference'` format aggregates many methods on one page and uses `### foo()` to disambiguate them. + const title = methodFormat === 'page' ? '' : `### \`${name}()\``; + const paramsHeadingLevel = methodFormat === 'page' ? 2 : 4; /** Prefer the declaration comment (property-style methods document `addListener` on the property, not the signature). */ const comment = decl.comment ?? sig.comment; let description = commentSummaryAndBody(comment); @@ -1070,13 +1136,17 @@ function buildMethodMdx(decl, ctx, options = {}) { const skipParametersSection = Boolean(decl.comment?.hasModifier('@skipParametersSection')) || Boolean(sig.comment?.hasModifier('@skipParametersSection')); - const paramsMd = skipParametersSection ? '' : parametersMarkdownTable(sig, ctx, instantiationMap); + if (!skipParametersSection) { + overlayParamCommentsFromSignatureBlockTags(sig); + } + const paramsMd = skipParametersSection ? '' : parametersMarkdownTable(sig, ctx, instantiationMap, paramsHeadingLevel); // Same post-process as `custom-plugin.mjs` `MarkdownPageEvent.END`: relative `.mdx` links, then catch-alls. // Skip the ```typescript``` fence so signatures stay plain code. - const head = applyCatchAllMdReplacements(applyRelativeLinkReplacements([title, '', description].join('\n'))); + const headSource = title ? [title, '', description].join('\n') : description; + const head = applyCatchAllMdReplacements(applyRelativeLinkReplacements(headSource)); const paramsProcessed = paramsMd ? applyCatchAllMdReplacements(applyRelativeLinkReplacements(paramsMd)) : ''; - const chunks = [head, ts]; + const chunks = head ? [head, ts] : [ts]; if (paramsProcessed) { chunks.push(paramsProcessed); } @@ -1156,9 +1226,10 @@ function processExtractMethodsNamespace(parentDecl, ctx, outDir) { * @param {import('typedoc').DeclarationReflection} decl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx * @param {string} outDir + * @param {MethodFormat} [methodFormat] * @returns {ExtractedFile[]} */ -function extractCallableMembersFromDeclaration(decl, ctx, outDir) { +function extractCallableMembersFromDeclaration(decl, ctx, outDir, methodFormat = 'reference') { if (!decl.children) { return []; } @@ -1176,7 +1247,7 @@ function extractCallableMembersFromDeclaration(decl, ctx, outDir) { } if (shouldExtractCallableMember(childDecl, ctx)) { - const mdx = buildMethodMdx(childDecl, ctx); + const mdx = buildMethodMdx(childDecl, ctx, { methodFormat }); if (mdx) { const fileName = `${toFileSlug(child.name)}.mdx`; const filePath = path.join(outDir, fileName); @@ -1189,16 +1260,24 @@ function extractCallableMembersFromDeclaration(decl, ctx, outDir) { /** * @param {import('typedoc-plugin-markdown').MarkdownPageEvent} output - * @returns {keyof typeof REFERENCE_OBJECT_CONFIG | undefined} + * @returns {string | undefined} */ function matchReferenceObjectPageUrl(output) { if (!output.url) { return undefined; } const normalized = output.url.replace(/\\/g, '/'); - return normalized in REFERENCE_OBJECT_CONFIG - ? /** @type {keyof typeof REFERENCE_OBJECT_CONFIG} */ (normalized) - : undefined; + if (normalized in REFERENCE_OBJECT_CONFIG) return normalized; + if (normalized in BACKEND_API_CONFIG) return normalized; + return undefined; +} + +/** @param {string} pageUrl */ +function configEntryForPageUrl(pageUrl) { + return /** @type {import('./reference-objects.mjs').REFERENCE_OBJECT_CONFIG[keyof typeof REFERENCE_OBJECT_CONFIG]} */ ( + REFERENCE_OBJECT_CONFIG[/** @type {keyof typeof REFERENCE_OBJECT_CONFIG} */ (pageUrl)] ?? + BACKEND_API_CONFIG[/** @type {keyof typeof BACKEND_API_CONFIG} */ (pageUrl)] + ); } /** @@ -1216,7 +1295,8 @@ export function load(app) { if (!pageUrl) { return; } - const entry = REFERENCE_OBJECT_CONFIG[pageUrl]; + const entry = configEntryForPageUrl(pageUrl); + const methodFormat = methodFormatForPageUrl(pageUrl); const decl = /** @type {import('typedoc').DeclarationReflection | undefined} */ (output.model); if (!decl?.children) { console.warn(`[extract-methods] No children on reflection for ${pageUrl}, skipping`); @@ -1240,7 +1320,7 @@ export function load(app) { const outDir = path.join(objectDir, 'methods'); /** @type {ExtractedFile[]} */ - const methodFiles = extractCallableMembersFromDeclaration(decl, ctx, outDir); + const methodFiles = extractCallableMembersFromDeclaration(decl, ctx, outDir, methodFormat); const extraMethodInterfaces = 'extraMethodInterfaces' in entry ? entry.extraMethodInterfaces : undefined; if (Array.isArray(extraMethodInterfaces)) { for (const extra of extraMethodInterfaces) { @@ -1249,7 +1329,7 @@ export function load(app) { console.warn(`[extract-methods] extraMethodInterfaces: could not find "${extra.symbol}" for ${pageUrl}`); continue; } - methodFiles.push(...extractCallableMembersFromDeclaration(extraDecl, ctx, outDir)); + methodFiles.push(...extractCallableMembersFromDeclaration(extraDecl, ctx, outDir, methodFormat)); } } diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 424ca40172f..4db188b2df9 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -56,13 +56,26 @@ export const REFERENCE_OBJECT_CONFIG = { }, }; -/** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ -export const REFERENCE_OBJECTS_LIST = Object.keys(REFERENCE_OBJECT_CONFIG); +/** + * Backend API endpoint pages: identical extraction machinery as {@link REFERENCE_OBJECT_CONFIG}, but each `methods/.mdx` is written in **page format** — no `### foo()` title at the top, and the `Parameters` section uses an `## H2` heading. The reference-object format uses an H3 title + H4 parameters, which suits pages that aggregate many methods on a single resource. Backend API method files are consumed standalone (one method per docs page), so the heading levels need to shift accordingly. + */ +export const BACKEND_API_CONFIG = { + 'backend/user-api/user-api.mdx': { + symbol: 'UserAPI', + declarationHint: 'api/endpoints/UserApi', + }, +}; + +/** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG} then {@link BACKEND_API_CONFIG}. */ +export const REFERENCE_OBJECTS_LIST = [...Object.keys(REFERENCE_OBJECT_CONFIG), ...Object.keys(BACKEND_API_CONFIG)]; /** * Primary interface/class documented on each reference object page (used to resolve TypeDoc reflections). - * Derived from {@link REFERENCE_OBJECT_CONFIG}; kept for callers that only need `pageUrl → symbol`. + * Includes both {@link REFERENCE_OBJECT_CONFIG} and {@link BACKEND_API_CONFIG} so the router applies the same folder-nesting rule to backend API pages. */ export const REFERENCE_OBJECT_PAGE_SYMBOLS = Object.fromEntries( - Object.entries(REFERENCE_OBJECT_CONFIG).map(([url, { symbol }]) => [url, symbol]), + [...Object.entries(REFERENCE_OBJECT_CONFIG), ...Object.entries(BACKEND_API_CONFIG)].map(([url, { symbol }]) => [ + url, + symbol, + ]), ); diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index 5936c967105..975902c4f21 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -52,7 +52,7 @@ export type UserListParams = ClerkPaginationRequest<{ userId?: string[]; /** Filters users with the specified external IDs. Accepts up to 100 external IDs. */ externalId?: string[]; - /** Returns users in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`.*/ + /** Filters users in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`.*/ orderBy?: WithSign< | 'created_at' | 'updated_at' @@ -81,17 +81,22 @@ export type UserListParams = ClerkPaginationRequest<{ lastSignInAtAfter?: number; /** Filters users who were last signed in before the given date (with millisecond precision). */ lastSignInAtBefore?: number; - /** Filters users who are members of the specified organizations. Accepts up to 100 organization IDs. */ + /** Filters users who are members of the specified Organizations. Accepts up to 100 Organization IDs. */ organizationId?: string[]; }>; -type UserMetadataParams = { +/** @inline */ +export type UserMetadataParams = { + /** Metadata that can be read from the Frontend API and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }} and can be set only from the Backend API. */ publicMetadata?: UserPublicMetadata; + /** Metadata that can be read and set only from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}. */ privateMetadata?: UserPrivateMetadata; + /** Metadata that can be read and set from the Frontend API. It's considered unsafe because it can be modified from the Frontend API. */ unsafeMetadata?: UserUnsafeMetadata; }; -type PasswordHasher = +/** @inline */ +export type PasswordHasher = | 'argon2i' | 'argon2id' | 'awscognito' @@ -108,177 +113,311 @@ type PasswordHasher = | 'md5_phpass' | 'ldap_ssha'; -type UserPasswordHashingParams = { +/** @inline */ +export type UserPasswordHashingParams = { + /** In case you already have the password digests and not the passwords, you can use them for the newly created user via this property. The digests should be generated with one of the supported algorithms. The hashing algorithm can be specified using the `password_hasher` property. */ passwordDigest: string; + /** + * The hashing algorithm that was used to generate the password digest. Must be one of the supported algorithms. For password hashers considered insecure (currently, `md5`, `md5_salted`, `sha256`, `sha256_salted`, `sha512_symfony`), the corresponding user password hashes will be transparently migrated to `bcrypt` (a secure hasher) upon the user's first successful password sign in. Insecure schemes are marked with `(insecure)` in the list below. + * + *
      + *
    • `awscognito`
    • + *
        + *
      • When set, `password_digest` must be in the format of `awscognito###`.
      • + *
      • Upon a successful migration, `password_hasher` will be updated to `bcrypt`, and `password_digest` will be updated to a `bcrypt` hash.
      • + *
      • See the [migration guide](/docs/guides/development/migrating/cognito) for usage details.
      • + *
      + *
    • [`bcrypt`](https://en.wikipedia.org/wiki/Bcrypt)
    • + *
        + *
      • When set, `password_digest` must be in the format of `$$$`.
      • + *
      + *
    • [`bcrypt_sha256_django`](https://docs.djangoproject.com/en/4.0/topics/auth/passwords/)
    • + *
        + *
      • This is the Django-specific variant of Bcrypt, using SHA256 hashing function. When set, `password_digest` must be in the format of (as exported from Django): `bcrypt_sha256$$$$`.
      • + *
      + *
    • [`bcrypt_peppered`](https://github.com/heartcombo/devise)
    • + *
        + *
      • Used in implementations such as Devise for Ruby on Rails applications. Identical to `bcrypt` except that a `pepper` string is appended to the input before hashing. When set, `password_digest` must be in the format of `$$$$`.
      • + *
      + *
    • [`md5` (insecure)](https://en.wikipedia.org/wiki/MD5)
    • + *
        + *
      • When set, `password_digest` must be in the format of `5f4dcc3b5aa765d61d8327deb882cf99`.
      • + *
      + *
    • [`md5_salted` (insecure)](https://en.wikipedia.org/wiki/MD5)
    • + *
        + *
      • When set, `password_digest` must be in the format of `salt$hash`.
      • + *
      • _salt:_ The salt used to generate the above hash.
      • + *
      • _hash:_ A 32-length hex string.
      • + *
      + *
    • [`pbkdf2_sha1`](https://en.wikipedia.org/wiki/PBKDF2)
    • + *
        + *
      • When set, `password_digest` must be in the format of `pbkdf2_sha1$$$` or `pbkdf2_sha1$$$$`.
      • + *
      • Accepts the salt as a hex-encoded string. If the salt is not a valid hex string, the raw bytes will be used instead. Accepts the hash as a hex-encoded string. Optionally accepts the key length as the last parameter (defaults to 32).
      • + *
      + *
    • [`pbkdf2_sha256`](https://en.wikipedia.org/wiki/PBKDF2)
    • + *
        + *
      • This is the PBKDF2 algorithm using the SHA256 hashing function. When set, `password_digest` must be in the format of `pbkdf2_sha256$$$`.
      • + *
      • Both the salt and the hash are expected to be base64-encoded.
      • + *
      + *
    • [`pbkdf2_sha512`](https://en.wikipedia.org/wiki/PBKDF2)
    • + *
        + *
      • This is the PBKDF2 algorithm using the SHA512 hashing function. When set, `password_digest` must be in the format of `pbkdf2_sha512$$$`.
      • + *
      • The salt is expected to be an unencoded string literal, and the hash should be hex-encoded.
      • + *
      + *
    • [`pbkdf2_sha512_hex`](https://en.wikipedia.org/wiki/PBKDF2)
    • + *
        + *
      • This is the PBKDF2 algorithm using the SHA512 hashing function. When set, `password_digest` must be in the format of `pbkdf2_sha512_hex$$$`.
      • + *
      • Both the salt and the hash are expected to be hex-encoded.
      • + *
      + *
    • [`pbkdf2_sha256_django`](https://docs.djangoproject.com/en/4.0/topics/auth/passwords/)
    • + *
        + *
      • This is the Django-specific variant of PBKDF2. When set, `password_digest` must be in the format of (as exported from Django): `pbkdf2_sha256$$$`.
      • + *
      • The salt is expected to be un-encoded, the hash is expected base64-encoded.
      • + *
      + *
    • [`phpass`](https://www.openwall.com/phpass/)
    • + *
        + *
      • Portable public domain password hashing framework for use in PHP applications. When set, `password_digest` must be in the format of `$P$`.
      • + *
      • `$P$` is the prefix used to identify `phpass` hashes.
      • + *
      • _rounds:_ A single character encoding a 6-bit integer representing the number of rounds used.
      • + *
      • _salt:_ Eight characters drawn from `[./0-9A-Za-z]`, providing a 48-bit salt.
      • + *
      • _encoded-checksum:_ 22 characters drawn from the same set, encoding the 128-bit checksum with MD5.
      • + *
      + *
    • [`scrypt_firebase`](https://firebaseopensource.com/projects/firebase/scrypt/)
    • + *
        + *
      • The Firebase-specific variant of scrypt. When set, `password_digest` must be in the format of `$$$$$`.
      • + *
      • _hash:_ The actual Base64 hash. This can be retrieved when exporting the user from Firebase.
      • + *
      • _salt:_ The salt used to generate the above hash. Again, this is given when exporting the user from Firebase.
      • + *
      • _signer key:_ The base64 encoded signer key.
      • + *
      • _salt separator:_ The base64 encoded salt separator.
      • + *
      • _rounds:_ The number of rounds the algorithm needs to run.
      • + *
      • _memory cost:_ The cost of the algorithm run.
      • + *
      + *
    • [`scrypt_werkzeug`](https://werkzeug.palletsprojects.com/en/3.0.x/utils/#werkzeug.security.generate_password_hash)
    • + *
        + *
      • The Werkzeug-specific variant of scrypt. When set, `password_digest` must be in the format of `$$$`.
      • + *
      • _algorithm args:_ The algorithm used to generate the hash.
      • + *
      • _salt:_ The salt used to generate the above hash.
      • + *
      • _hash:_ The actual Base64 hash.
      • + *
      + *
    • [`sha256` (insecure)](https://en.wikipedia.org/wiki/SHA-2)
    • + *
        + *
      • When set, `password_digest` must be a 64-length hex string. For example: `9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08`.
      • + *
      + *
    • [`sha256_salted` (insecure)](https://en.wikipedia.org/wiki/SHA-2)
    • + *
        + *
      • When set, `password_digest` must be in the format of `salt$hash`.
      • + *
      • _salt:_ The salt used to generate the above hash.
      • + *
      • _hash:_ A 64-length hex string.
      • + *
      + *
    • [`argon2`](https://argon2.online/) variants: `argon2i` and `argon2id`.
    • + *
        + *
      • Parts are demarcated by the `$` character, with the first part identifying the algorithm variant The middle part is a comma-separated list of the encoding options (memory, iterations, parallelism). The final part is the actual digest.
      • + *
      • When set, `password_digest` must be in the format of `$argon2i$v=19$m=4096,t=3,p=1$4t6CL3P7YiHBtwESXawI8Hm20zJj4cs7/4/G3c187e0$m7RQFczcKr5bIR0IIxbpO2P0tyrLjf3eUW3M3QSwnLc`.
      • + *
      • For the argon2id case, the value of the algorithm in the first part of the digest is `argon2id`: `$argon2id$v=19$m=64,t=4,p=8$Z2liZXJyaXNo$iGXEpMBTDYQ8G/71tF0qGjxRHEmR3gpGULcE93zUJVU`.
      • + *
      + *
    • [`sha512_symfony` (insecure)](https://symfony.com/doc/current/security/passwords.html)
    • + *
        + *
      • The legacy Symfony `MessageDigestPasswordEncoder` algorithm. We currently only support the SHA512 variant. When set, `password_digest` must be in the format of `sha512_symfony$iterations$salt$hash`.
      • + *
      • _iterations:_ A number greater than 0.
      • + *
      • _salt:_ The salt used to generate the above hash.
      • + *
      • _hash:_ The actual Base64 hash.
      • + *
      + *
    + * + * If you need support for any particular hashing algorithm, [contact support](https://clerk.com/contact/support). + */ passwordHasher: PasswordHasher; }; -type CreateUserParams = { +/** @generateWithEmptyComment */ +export type CreateUserParams = { + /** The ID of the user as used in your external systems or your previous authentication solution. Must be unique across your instance. */ externalId?: string; + /** The email address(es) to assign to the user. Must be unique across your instance. The first email address will be set as the users primary email address. */ emailAddress?: string[]; + /** The phone number(s) to assign to the user. Must be unique across your instance. The first phone number will be set as the users primary phone number. */ phoneNumber?: string[]; + /** The username to assign to the user. Must be unique across your instance. */ username?: string; + /** The plaintext password to give the user. Must be at least 8 characters long, and can't be in any list of hacked passwords. */ password?: string; + /** The first name to assign to the user. */ firstName?: string; + /** The last name to assign to the user. */ lastName?: string; - /** The locale of the user in BCP-47 format. */ + /** The locale of the user in BCP-47 format (e.g. `'en-US'`, `'fr-FR'`). */ locale?: string; + /** When set to `true`, all password checks are skipped. It is recommended to use this method only when migrating plaintext passwords to Clerk. Upon migration the user base should be prompted to pick stronger password. */ skipPasswordChecks?: boolean; + /** When set to `true`, password is not required anymore when creating the user and can be omitted. This is useful when you are trying to create a user that doesn't have a password, in an instance that is using passwords. **You cannot use this flag if password is the only way for a user to sign into your instance.** */ skipPasswordRequirement?: boolean; + /** When set to `true`, all legal checks are skipped. It is not recommended to skip legal checks unless you are migrating a user to Clerk. */ skipLegalChecks?: boolean; + /** A custom timestamp denoting when the user accepted legal requirements, specified in RFC3339 format (e.g. `'2012-10-20T07:15:20.902Z'`). */ legalAcceptedAt?: Date; + /** + * If TOTP is enabled on the instance, you can provide the secret to enable it on the newly created user without the need to reset it. Currently, the supported options are: + *
      + *
    • Period: 30 seconds
    • + *
    • Code length: 6 digits
    • + *
    • Algorithm: SHA1
    • + *
    + */ totpSecret?: string; + /** If backup codes are enabled on the instance, you can provide them to enable it on the newly created user without the need to reset them. You must provide the backup codes in plain format or the corresponding bcrypt digest. */ backupCodes?: string[]; + /** A custom timestamp denoting when the user signed up to the application, specified in RFC3339 format (e.g. `'2012-10-20T07:15:20.902Z'`). */ createdAt?: Date; - /** When set to `true`, the user is created already banned and cannot sign in. Requires the same plan support as the ban user endpoint. */ + /** When set to `true`, the user is created already banned and cannot sign in. */ banned?: boolean; - /** When set to `true`, the user is created already locked. Requires the user lockout feature to be enabled on the instance. */ + /** When set to `true`, the user is created already locked. Requires the [user lockout feature](https://clerk.com/docs/guides/secure/user-lockout#lock-a-user-programmatically) to be enabled on the instance. */ locked?: boolean; } & UserMetadataParams & (UserPasswordHashingParams | object); -type UpdateUserParams = { +/** @inline */ +export type UpdateUserParams = { /** The first name to assign to the user. */ firstName?: string; - - /** The last name of the user. */ + /** The last name to assign to the user. */ lastName?: string; - - /** The username to give to the user. It must be unique across your instance. */ + /** The username to assign to the user. Must be unique across your instance. */ username?: string; - - /** The plaintext password to give the user. Must be at least 8 characters long, and can not be in any list of hacked passwords. */ + /** The plaintext password to assign to the user. Must be at least 8 characters long, and can not be in any list of hacked passwords. */ password?: string; - - /** Set it to true if you're updating the user's password and want to skip any password policy settings check. This parameter can only be used when providing a password. */ + /** When set to `true`, all password checks are skipped. It is recommended to use this method only when migrating plaintext passwords to Clerk. Upon migration the user base should be prompted to pick stronger password. */ skipPasswordChecks?: boolean; - - /** Set to true to sign out the user from all their active sessions once their password is updated. This parameter can only be used when providing a password. */ + /** When set to `true`, the user is signed out from all their active sessions once their password is updated. */ signOutOfOtherSessions?: boolean; - - /** The ID of the email address to set as primary. It must be verified, and present on the current user. */ + /** The ID of the email address to set as primary. Must be verified and present on the given user. */ primaryEmailAddressID?: string; - - /** If set to true, the user will be notified that their primary email address has changed. By default, no notification is sent. */ + /** When set to `true`, the user is notified that their primary email address has changed. */ notifyPrimaryEmailAddressChanged?: boolean; - - /** The ID of the phone number to set as primary. It must be verified, and present on the current user. */ + /** The ID of the phone number to set as primary. Must be verified and present on the given user. */ primaryPhoneNumberID?: string; - - /** The ID of the web3 wallets to set as primary. It must be verified, and present on the current user. */ + /** The ID of the web3 wallets to set as primary. Must be verified and present on the given user. */ primaryWeb3WalletID?: string; - - /** The ID of the image to set as the user's profile image */ + /** The ID of the image to set as the user's profile image. */ profileImageID?: string; - /** - * In case TOTP is configured on the instance, you can provide the secret to enable it on the specific user without the need to reset it. - * Please note that currently the supported options are: - * - Period: 30 seconds - * - Code length: 6 digits - * - Algorithm: SHA1 + * In case TOTP is configured on the instance, you can provide the secret to enable it on the specific user without the need to reset it. Currently, the supported options are: + *
      + *
    • Period: 30 seconds
    • + *
    • Code length: 6 digits
    • + *
    • Algorithm: SHA1
    • + *
    */ totpSecret?: string; - - /** If Backup Codes are configured on the instance, you can provide them to enable it on the specific user without the need to reset them. You must provide the backup codes in plain format or the corresponding bcrypt digest. */ + /** If backup codes are configured on the instance, you can provide them to enable it on the specific user without the need to reset them. You must provide the backup codes in plain format or the corresponding bcrypt digest. */ backupCodes?: string[]; - - /** The ID of the user as used in your external systems or your previous authentication solution. Must be unique across your instance. */ + /** The ID of the user as used in your external systems or your previous authentication solution. Must be unique across your entire instance. */ externalId?: string; - - /** A custom timestamp denoting when the user signed up to the application, specified in RFC3339 format (e.g. 2012-10-20T07:15:20.902Z). */ + /** A custom timestamp denoting when the user signed up to the application, specified in RFC3339 format (e.g. `'2012-10-20T07:15:20.902Z'`). */ createdAt?: Date; - - /** When set to true all legal checks are skipped. It is not recommended to skip legal checks unless you are migrating a user to Clerk. */ + /** When set to `true`, all legal checks are skipped. It is not recommended to skip legal checks unless you are migrating a user to Clerk. */ skipLegalChecks?: boolean; - - /** A custom timestamp denoting when the user accepted legal requirements, specified in RFC3339 format (e.g. 2012-10-20T07:15:20.902Z). */ + /** A custom timestamp denoting when the user accepted legal requirements, specified in RFC3339 format (e.g. `'2012-10-20T07:15:20.902Z'`). */ legalAcceptedAt?: Date; - - /** The locale of the user in BCP-47 format. */ + /** The locale of the user in BCP-47 format (e.g. `'en-US'`). */ locale?: string; - - /** If true, the user can delete themselves with the Frontend API. */ + /** If `true`, the user can delete themselves with the Frontend API. */ deleteSelfEnabled?: boolean; - - /** If true, the user can create Organizations with the Frontend API. */ + /** If `true`, the user can create Organizations with the Frontend API. */ createOrganizationEnabled?: boolean; - - /** The maximum number of Organizations the user can create. 0 means unlimited. */ + /** The maximum number of Organizations the user can create. `0` means unlimited. */ createOrganizationsLimit?: number; - /** * Metadata visible to your Frontend and Backend APIs. * - * @deprecated Updating metadata via `updateUser` is deprecated. Use - * `updateUserMetadata` for partial updates (deep merge) or - * `replaceUserMetadata` for full replacement. + * @deprecated Updating metadata via `updateUser()` is deprecated. Use + * [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or + * [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. */ publicMetadata?: UserPublicMetadata; - /** * Metadata visible only to your Backend API. * - * @deprecated Updating metadata via `updateUser` is deprecated. Use - * `updateUserMetadata` for partial updates (deep merge) or - * `replaceUserMetadata` for full replacement. + * @deprecated Updating metadata via `updateUser()` is deprecated. Use + * [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or + * [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. */ privateMetadata?: UserPrivateMetadata; - /** * Metadata writeable from both the Frontend and Backend APIs. * - * @deprecated Updating metadata via `updateUser` is deprecated. Use - * `updateUserMetadata` for partial updates (deep merge) or - * `replaceUserMetadata` for full replacement. + * @deprecated Updating metadata via `updateUser()` is deprecated. Use + * [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or + * [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. */ unsafeMetadata?: UserUnsafeMetadata; } & (UserPasswordHashingParams | object); -type GetOrganizationMembershipListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type GetOrganizationMembershipListParams = ClerkPaginationRequest<{ + /** The ID of the user to get the list of Organization memberships for. */ userId: string; }>; -type GetOrganizationInvitationListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type GetOrganizationInvitationListParams = ClerkPaginationRequest<{ + /** The ID of the user to get the list of Organization invitations for. */ userId: string; + /** Filters the invitations by the provided status. */ status?: OrganizationInvitationStatus; }>; -type VerifyPasswordParams = { +/** @generateWithEmptyComment */ +export type VerifyPasswordParams = { + /** The ID of the user to verify the password for. */ userId: string; + /** The password to verify. */ password: string; }; -type VerifyTOTPParams = { +/** @generateWithEmptyComment */ +export type VerifyTOTPParams = { + /** The ID of the user to verify the TOTP for. */ userId: string; + /** The TOTP or backup code to verify. */ code: string; }; -type DeleteUserPasskeyParams = { +/** @generateWithEmptyComment */ +export type DeleteUserPasskeyParams = { + /** The ID of the user to delete the passkey for. */ userId: string; + /** The ID of the passkey identification to delete. */ passkeyIdentificationId: string; }; -type DeleteWeb3WalletParams = { +/** @generateWithEmptyComment */ +export type DeleteWeb3WalletParams = { + /** The ID of the user to delete the Web3 wallet for. */ userId: string; + /** The ID of the Web3 wallet identification to delete. */ web3WalletIdentificationId: string; }; -type DeleteUserExternalAccountParams = { +/** @generateWithEmptyComment */ +export type DeleteUserExternalAccountParams = { + /** The ID of the user to delete the external account for. */ userId: string; + /** The ID of the external account to delete. */ externalAccountId: string; }; -type SetPasswordCompromisedParams = { +/** @inline */ +export type SetPasswordCompromisedParams = { + /** Whether to revoke all sessions of the user. Defaults to `false`. */ revokeAllSessions?: boolean; }; -type ReplaceUserEmailAddressParams = { +export type ReplaceUserEmailAddressParams = { /** The new email address. Must adhere to the RFC 5322 specification for email address format. */ emailAddress: string; }; -type ReplaceUserPhoneNumberParams = { +export type ReplaceUserPhoneNumberParams = { /** The new phone number. Must adhere to the E.164 standard for phone number format. */ phoneNumber: string; }; @@ -287,6 +426,7 @@ type UserID = { userId: string; }; +/** @generateWithEmptyComment */ export class UserAPI extends AbstractAPI { /** * Retrieves the list of users in your instance. @@ -320,6 +460,18 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Creates a [`User`](https://clerk.com/docs/reference/backend/types/backend-user) in your instance. + * + * Your settings in the [Clerk Dashboard](https://dashboard.clerk.com) determine how you should setup your user model. Anything **Required** will need to be provided when creating a user. Trying to add a field that isn't enabled will result in an error. + * + * Any email address and phone number created using this method will be automatically verified. + * + * > [!CAUTION] + * > + * > This endpoint is [rate limited](/docs/guides/how-clerk-works/system-limits). For development instances, a rate limit rule of **100 requests per 10 seconds** is applied. + * > For production instances, that limit goes up to **1000 requests per 10 seconds**. + */ public async createUser(params: CreateUserParams) { return this.request({ method: 'POST', @@ -328,6 +480,10 @@ export class UserAPI extends AbstractAPI { }); } + /** Updates the given [`User`](https://clerk.com/docs/reference/backend/types/backend-user). + * @param userId - The ID of the user to update. + * @param params - The user attributes to update. + */ public async updateUser(userId: string, params: UpdateUserParams = {}) { this.requireId(userId); @@ -386,6 +542,12 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Updates the profile image for the given user. To remove the profile image, see [`deleteUserProfileImage()`](https://clerk.com/docs/reference/backend/user/delete-user-profile-image). + * @param userId - The ID of the user to update the profile image for. + * @param params - The file to set as the user's profile image. + * @returns The updated [`User`](https://clerk.com/docs/reference/backend/types/backend-user). + */ public async updateUserProfileImage(userId: string, params: { file: Blob | File }) { this.requireId(userId); @@ -399,6 +561,17 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Updates the metadata for the given user, by merging existing values with the provided parameters. + * + * A "deep" merge will be performed - "deep" means that any nested JSON objects will be merged as well. You can remove metadata keys at any level by setting their value to `null`. + * + * > [!TIP] + * > If you want to fully replace the existing metadata instead of merging, use [`replaceUserMetadata()`](/docs/reference/backend/user/replace-user-metadata). + * @param userId - The ID of the user to update. + * @param params - The metadata to update. + * @returns The updated [`User`](https://clerk.com/docs/reference/backend/types/backend-user). + */ public async updateUserMetadata(userId: string, params: UserMetadataParams) { this.requireId(userId); @@ -410,9 +583,18 @@ export class UserAPI extends AbstractAPI { } /** - * Replace a user's metadata. Supplied fields are overwritten in full; fields - * omitted from `params` are left unchanged. Prefer `updateUserMetadata` for - * partial updates with deep-merge semantics. + * Replaces the metadata associated with the specified user. Unlike [`updateUserMetadata()`](/docs/reference/backend/user/update-user-metadata), which deep-merges into the existing metadata, this method uses replace semantics: when a metadata field is provided, its previous value is overwritten in full with no merging at any level. + * + * The distinction is at two layers: + * - **Top-level field omission preserves the existing value.** Each top-level field (`publicMetadata`, `privateMetadata`, `unsafeMetadata`) is handled independently. If you don't include a field in the request, the stored value for that field is left untouched. + * - **The value inside a provided field is replaced in full.** When you do include a field, its previous content is discarded — any nested keys present before but absent in the new value are dropped. There is no merge. + * + * For the provided field, you can also send: + * - `{}` (empty object) to clear the field. + * - `null` to overwrite the field with a JSON `null` value. Prefer `{}` unless you specifically need a stored `null`. + * @param userId - The ID of the user to replace the metadata for. + * @param params - The metadata to replace. + * @returns The updated [`User`](https://clerk.com/docs/reference/backend/types/backend-user). */ public async replaceUserMetadata(userId: string, params: UserMetadataParams) { this.requireId(userId); @@ -424,6 +606,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Deletes the given [`User`](https://clerk.com/docs/reference/backend/types/backend-user). + * @param userId - The ID of the user to delete. + */ public async deleteUser(userId: string) { this.requireId(userId); return this.request({ @@ -452,6 +638,12 @@ export class UserAPI extends AbstractAPI { userId: string, provider: OAuthProvider, ): Promise>; + /** + * Gets the corresponding [OAuth access token](!oauth-access-token) for a user that has previously authenticated with the given OAuth provider. + * @param userId - The ID of the user to get the OAuth access tokens for. + * @param provider - The OAuth provider to get the access tokens for. If using a custom OAuth provider, prefix the provider name with `custom_` (e.g. `custom_google`). + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OauthAccessToken`](https://clerk.com/docs/reference/backend/types/backend-oauth-access-token) objects, and a `totalCount` property that indicates the total number of OAuth access tokens for the specified user and provider. + */ public async getUserOauthAccessToken(userId: string, provider: `oauth_${OAuthProvider}` | OAuthProvider) { this.requireId(userId); const hasPrefix = provider.startsWith('oauth_'); @@ -471,6 +663,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Disable all of a user's MFA methods (e.g. [OTP](!otp) sent via SMS, TOTP on their authenticator app) at once. + * @param userId - The ID of the user to disable MFA for. + */ public async disableUserMFA(userId: string) { this.requireId(userId); return this.request({ @@ -479,6 +675,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Gets a list of the given user's Organization memberships. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/organization-membership) objects, and a `totalCount` property that indicates the total number of Organization memberships for the user. + */ public async getOrganizationMembershipList(params: GetOrganizationMembershipListParams) { const { userId, limit, offset } = params; this.requireId(userId); @@ -490,6 +690,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Gets a list of the given user's Organization invitations. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/organization-invitation) objects, and a `totalCount` property that indicates the total number of Organization invitations for the user. + */ public async getOrganizationInvitationList(params: GetOrganizationInvitationListParams) { const { userId, ...queryParams } = params; this.requireId(userId); @@ -501,6 +705,7 @@ export class UserAPI extends AbstractAPI { }); } + /** Check that the user's password matches the supplied input. Useful for custom auth flows and re-verification. */ public async verifyPassword(params: VerifyPasswordParams) { const { userId, password } = params; this.requireId(userId); @@ -512,6 +717,7 @@ export class UserAPI extends AbstractAPI { }); } + /** Verify that the provided TOTP or backup code is valid for the user. Verifying a backup code will result it in being consumed (i.e. it will become invalid). Useful for custom auth flows and re-verification. */ public async verifyTOTP(params: VerifyTOTPParams) { const { userId, code } = params; this.requireId(userId); @@ -523,6 +729,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Marks the given [`User`](https://clerk.com/docs/reference/backend/types/backend-user) as banned, which means that all their sessions are revoked and they are not allowed to sign in again. + * @param userId - The ID of the user to ban. + */ public async banUser(userId: string) { this.requireId(userId); return this.request({ @@ -531,6 +741,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Removes the ban mark from the given [`User`](https://clerk.com/docs/reference/backend/types/backend-user), allowing them to sign in again. + * @param userId - The ID of the user to unban. + */ public async unbanUser(userId: string) { this.requireId(userId); return this.request({ @@ -539,6 +753,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Locks the given [`User`](https://clerk.com/docs/reference/backend/types/backend-user), which means that they are not allowed to sign in again until the lock expires or is manually unlocked. By default, lockout duration is 1 hour, but it can be configured in the application's [**Attack protection**](https://dashboard.clerk.com/~/protect/attack-protection) settings. See the [guide on user locks](https://clerk.com/docs/guides/secure/user-lockout). + * @param userId - The ID of the user to lock. + */ public async lockUser(userId: string) { this.requireId(userId); return this.request({ @@ -547,6 +765,9 @@ export class UserAPI extends AbstractAPI { }); } + /** Removes a sign-in lock from the given [`User`](https://clerk.com/docs/reference/backend/types/backend-user), allowing them to sign in again. See the [guide on user locks](https://clerk.com/docs/guides/secure/user-lockout). + * @param userId - The ID of the user to unlock. + */ public async unlockUser(userId: string) { this.requireId(userId); return this.request({ @@ -555,6 +776,11 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Deletes a user's profile image. + * @param userId - The ID of the user to delete the profile image for. + * @returns The updated [`User`](https://clerk.com/docs/reference/backend/types/backend-user). + */ public async deleteUserProfileImage(userId: string) { this.requireId(userId); return this.request({ @@ -563,6 +789,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Deletes the passkey identification for a given user and notifies them through email. + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/backend/types/backend-deleted-object-resource). + */ public async deleteUserPasskey(params: DeleteUserPasskeyParams) { this.requireId(params.userId); this.requireId(params.passkeyIdentificationId); @@ -572,6 +802,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Deletes a Web3 wallet identification for the given user. + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/backend/types/backend-deleted-object-resource). + */ public async deleteUserWeb3Wallet(params: DeleteWeb3WalletParams) { this.requireId(params.userId); this.requireId(params.web3WalletIdentificationId); @@ -581,6 +815,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Deletes an external account for the given user. + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/backend/types/backend-deleted-object-resource). + */ public async deleteUserExternalAccount(params: DeleteUserExternalAccountParams) { this.requireId(params.userId); this.requireId(params.externalAccountId); @@ -590,6 +828,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Deletes all backup codes for the given user. + * @param userId - The ID of the user to delete backup codes for. + */ public async deleteUserBackupCodes(userId: string) { this.requireId(userId); return this.request({ @@ -598,6 +840,10 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Deletes all of the TOTP secrets for the given user. + * @param userId - The ID of the user to delete the TOTP secrets for. + */ public async deleteUserTOTP(userId: string) { this.requireId(userId); return this.request({ @@ -606,6 +852,12 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Sets the given user's password as compromised. The user will be prompted to reset their password on their next sign-in. See the [guide on password protection and rules](/docs/guides/secure/password-protection-and-rules#reject-compromised-passwords) for more information. + * @param userId - The ID of the user to set the password as compromised for. + * @param params - Other parameters for the request. + * @returns The updated [`User`](https://clerk.com/docs/reference/backend/types/backend-user). + */ public async setPasswordCompromised( userId: string, params: SetPasswordCompromisedParams = { @@ -620,6 +872,11 @@ export class UserAPI extends AbstractAPI { }); } + /** + * Unsets the given user's password as compromised. The user will no longer be prompted to reset their password on their next sign-in. See the [guide on password protection and rules](/docs/guides/secure/password-protection-and-rules#reject-compromised-passwords) for more information. + * @param userId - The ID of the user to unset the password as compromised for. + * @returns The updated [`User`](https://clerk.com/docs/reference/backend/types/backend-user). + */ public async unsetPasswordCompromised(userId: string) { this.requireId(userId); return this.request({ diff --git a/packages/shared/src/types/pagination.ts b/packages/shared/src/types/pagination.ts index 0920ca247f9..eb955ba836d 100644 --- a/packages/shared/src/types/pagination.ts +++ b/packages/shared/src/types/pagination.ts @@ -6,11 +6,11 @@ */ export type ClerkPaginationRequest = { /** - * Maximum number of items returned per request. + * Maximum number of items returned per request. Must be an integer greater than zero and less than `501`. Can be used for paginating the results together with offset. Defaults to `10`. */ limit?: number; /** - * This is the starting point for your fetched results. + * Skip the first `offset` items when paginating. Needs to be an integer greater or equal to zero. To be used in conjunction with `limit`. Defaults to `0`. */ offset?: number; } & T; From daceb9d6709ee39df0e4ad65181bd9dcaf5f55cb Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:19:50 -0700 Subject: [PATCH 03/18] docs(backend,shared): Extract OrganizationAPI methods into per-method MDX pages - JSDoc all 24 public OrganizationAPI methods + their parameter types. - Register `organization-api` in `.typedoc/reference-objects.mjs` `BACKEND_API_CONFIG`. - Disambiguate the colliding `GetOrganization{Membership,Invitation}ListParams` exports (user-scoped on `UserApi`, org-scoped on `OrganizationApi`) via an explicit `export type { ... } from './UserApi'` in `endpoints/index.ts`. UserApi's variants remain the canonical public exports; OrganizationApi's exports stay reachable for direct imports and for typedoc. - `.typedoc/extract-methods.mjs`: - Resolve nested rows when a param type is `Array<{...}>` (e.g. `CreateBulkOrganizationInvitationParams`) by unwrapping the array element in `resolveDeclarationWithObjectMembers`. - Append `?` to nested `params.field` rows for optional child properties, matching the inline-`{ ... }` flatten behavior in `custom-theme.mjs`. Co-Authored-By: Claude Opus 4.7 (1M context) --- .typedoc/__tests__/file-structure.test.ts | 2 + .typedoc/extract-methods.mjs | 14 +- .typedoc/reference-objects.mjs | 4 + .../src/api/endpoints/OrganizationApi.ts | 342 +++++++++++++----- packages/backend/src/api/endpoints/index.ts | 5 + packages/backend/src/api/resources/Enums.ts | 5 +- packages/shared/src/types/organization.ts | 8 +- 7 files changed, 284 insertions(+), 96 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 1adb3482236..2949c6f2f60 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -48,6 +48,8 @@ describe('Typedoc output', () => { expect(nestedFolders).toMatchInlineSnapshot(` [ + "backend/organization-api", + "backend/organization-api/methods", "backend/user-api", "backend/user-api/methods", "react/legacy", diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 64398b981b4..725e73d6e12 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -806,6 +806,9 @@ function resolveDeclarationWithObjectMembers(t, project) { if (t.type === 'optional') { return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType, project); } + if (t.type === 'array') { + return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').ArrayType} */ (t).elementType, project); + } if (t.type === 'reflection') { const children = t.declaration?.children; return children?.length ? children : undefined; @@ -858,14 +861,15 @@ function resolveDeclarationWithObjectMembers(t, project) { } /** - * Build the name cell for a nominal-nested row. Uses `?.` when the parent param is optional (so `options?.foo` mirrors how it would be accessed at runtime) and `.` when required — same rule as `clerkParametersTable.flattenParams` in `custom-theme.mjs`. + * Build the name cell for a nominal-nested row. Uses `?.` when the parent param is optional (so `options?.foo` mirrors how it would be accessed at runtime) and `.` when required — same rule as `clerkParametersTable.flattenParams` in `custom-theme.mjs`. Appends `?` to the child name when the child property itself is optional, matching how the inline-flatten path renders `params.field?` via the standard parametersTable. * * @param {import('typedoc').ParameterReflection} parentParam - * @param {string} childName + * @param {import('typedoc').DeclarationReflection} child */ -function formatNestedParamNameColumn(parentParam, childName) { +function formatNestedParamNameColumn(parentParam, child) { const sep = parentParam.flags?.isOptional ? '?.' : '.'; - return `\`${parentParam.name}${sep}${childName}\``; + const childOptional = child.flags?.isOptional ? '?' : ''; + return `\`${parentParam.name}${sep}${child.name}${childOptional}\``; } /** @@ -923,7 +927,7 @@ function nestedParameterRowsFromDocumentedProperties(param, ctx) { for (const child of props) { const summary = child.comment?.summary; const typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; - const nestedNameCol = formatNestedParamNameColumn(param, child.name); + const nestedNameCol = formatNestedParamNameColumn(param, child); const nestedDescRaw = summary?.length ? displayPartsToString(summary).trim() || '—' : '—'; // Strip line breaks so multi-line `
      ` / paragraph descriptions don't shatter the markdown // table row (which must be a single line). Matches the treatment already applied to typeCell. diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 4db188b2df9..988a8668123 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -64,6 +64,10 @@ export const BACKEND_API_CONFIG = { symbol: 'UserAPI', declarationHint: 'api/endpoints/UserApi', }, + 'backend/organization-api/organization-api.mdx': { + symbol: 'OrganizationAPI', + declarationHint: 'api/endpoints/OrganizationApi', + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG} then {@link BACKEND_API_CONFIG}. */ diff --git a/packages/backend/src/api/endpoints/OrganizationApi.ts b/packages/backend/src/api/endpoints/OrganizationApi.ts index 1f95786a3bd..fa7b2ee35cf 100644 --- a/packages/backend/src/api/endpoints/OrganizationApi.ts +++ b/packages/backend/src/api/endpoints/OrganizationApi.ts @@ -17,232 +17,294 @@ import type { WithSign } from './util-types'; const basePath = '/organizations'; -type MetadataParams = { +/** @generateWithEmptyComment */ +export type MetadataParams = { + /** Metadata that can be read from the Frontend API and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. */ publicMetadata?: TPublic; + /** Metadata that can be read and set only from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}. */ privateMetadata?: TPrivate; }; -type GetOrganizationListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type GetOrganizationListParams = ClerkPaginationRequest<{ + /** Whether to include the number of members in the Organization. */ includeMembersCount?: boolean; + /** Filters Organizations by ID, name, or slug. Uses exact match for ID and partial match for name and slug. */ query?: string; + /** Filters Organizations in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`. */ orderBy?: WithSign<'name' | 'created_at' | 'members_count'>; + /** Filters Organizations by ID. Accepts up to 100 Organization IDs. */ organizationId?: string[]; }>; -type CreateParams = { +/** @generateWithEmptyComment */ +export type CreateParams = { + /** The name of the Organization. */ name: string; + /** The slug of the Organization. */ slug?: string; - /* The User id for the user creating the organization. The user will become an administrator for the organization. */ + /** The ID of the user creating the organization. The user will become an [admin](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions#default-roles) for the Organization. */ createdBy?: string; + /** The maximum number of memberships allowed in the Organization. `0` means unlimited. */ maxAllowedMemberships?: number; } & MetadataParams; -type GetOrganizationParams = ({ organizationId: string } | { slug: string }) & { +/** @generateWithEmptyComment */ +export type GetOrganizationParams = ( + | { + /** The ID of the Organization to get. */ + organizationId: string; + } + | { + /** The slug of the Organization to get. */ + slug: string; + } +) & { + /** Whether to include the number of members in the Organization. */ includeMembersCount?: boolean; }; -type UpdateParams = { +/** @inline */ +export type UpdateParams = { + /** The name to update the Organization with. */ name?: string; + /** The slug to update the Organization with. */ slug?: string; + /** Whether the Organization allows admins to delete users. */ adminDeleteEnabled?: boolean; + /** The maximum number of memberships allowed in the Organization. `0` means unlimited. */ maxAllowedMemberships?: number; - /** - * Metadata visible to your Frontend and Backend APIs. - * - * @deprecated Updating metadata via `updateOrganization` is deprecated. Use - * `updateOrganizationMetadata` for partial updates (deep merge) or - * `replaceOrganizationMetadata` for full replacement. + * @deprecated Updating metadata via [`updateOrganization()`](https://clerk.com/docs/reference/backend/organization/update-organization) is deprecated. Use [`updateOrganizationMetadata()`](https://clerk.com/docs/reference/backend/organization/update-organization-metadata) for partial updates (deep merge) or [`replaceOrganizationMetadata()`](https://clerk.com/docs/reference/backend/organization/replace-organization-metadata) for full replacement. */ publicMetadata?: OrganizationPublicMetadata; - /** - * Metadata visible only to your Backend API. - * - * @deprecated Updating metadata via `updateOrganization` is deprecated. Use - * `updateOrganizationMetadata` for partial updates (deep merge) or - * `replaceOrganizationMetadata` for full replacement. + * @deprecated Updating metadata via [`updateOrganization()`](https://clerk.com/docs/reference/backend/organization/update-organization) is deprecated. Use [`updateOrganizationMetadata()`](https://clerk.com/docs/reference/backend/organization/update-organization-metadata) for partial updates (deep merge) or [`replaceOrganizationMetadata()`](https://clerk.com/docs/reference/backend/organization/replace-organization-metadata) for full replacement. */ privateMetadata?: OrganizationPrivateMetadata; }; -type UpdateLogoParams = { +/** @inline */ +export type UpdateLogoParams = { + /** The file to upload as the logo. */ file: Blob | File; + /** The ID of the user uploading the logo. */ uploaderUserId?: string; }; -type UpdateMetadataParams = MetadataParams; +/** @inline */ +export type UpdateMetadataParams = MetadataParams; -type GetOrganizationMembershipListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type GetOrganizationMembershipListParams = ClerkPaginationRequest<{ + /** The ID of the Organization to get the list of memberships for. */ organizationId: string; - /** - * Sorts Organization memberships by phone_number, email_address, created_at, first_name, last_name or username. - * By prepending one of those values with + or -, we can choose to sort in ascending (ASC) or descending (DESC) order. + * Filters Organization memberships in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`. */ orderBy?: WithSign<'phone_number' | 'email_address' | 'created_at' | 'first_name' | 'last_name' | 'username'>; - /** - * Returns users with the user ids specified. For each user id, the `+` and `-` can be - * prepended to the id, which denote whether the respective user id should be included or - * excluded from the result set. Accepts up to 100 user ids. Any user ids not found are ignored. + * Filters Organization memberships by user ID. Accepts up to 100 user IDs. */ userId?: string[]; - - /* Returns users with the specified email addresses. Accepts up to 100 email addresses. Any email addresses not found are ignored. */ + /** Filters Organization memberships by email address. Accepts up to 100 email addresses. */ emailAddress?: string[]; - - /* Returns users with the specified phone numbers. Accepts up to 100 phone numbers. Any phone numbers not found are ignored. */ + /** Filters Organization memberships by phone number. Accepts up to 100 phone numbers. */ phoneNumber?: string[]; - - /* Returns users with the specified usernames. Accepts up to 100 usernames. Any usernames not found are ignored. */ + /** Filters Organization memberships by username. Accepts up to 100 usernames. */ username?: string[]; - - /* Returns users with the specified web3 wallet addresses. Accepts up to 100 web3 wallet addresses. Any web3 wallet addressed not found are ignored. */ + /** Filters Organization memberships by web3 wallet address. Accepts up to 100 web3 wallet addresses. */ web3Wallet?: string[]; - - /* Returns users with the specified Roles. Accepts up to 100 Roles. Any Roles not found are ignored. */ + /** Filters Organization memberships by Role. Accepts up to 100 Roles. */ role?: OrganizationMembershipRole[]; - /** - * Returns users that match the given query. - * For possible matches, we check the email addresses, phone numbers, usernames, web3 wallets, user ids, first and last names. - * The query value doesn't need to match the exact value you are looking for, it is capable of partial matches as well. + * Filters Organization memberships matching the given query across email addresses, phone numbers, usernames, Web3 wallet addresses, user IDs, first names, and last names. Partial matches supported. For example, `query=hello` will match a user with the email `HELLO@example.com`. */ query?: string; - /** - * Returns users with emails that match the given query, via case-insensitive partial match. - * For example, `email_address_query=ello` will match a user with the email `HELLO@example.com`. + * Filters Organization memberships by email address. Accepts up to 100 email addresses. Partial matches supported. For example, `emailAddressQuery=ello` will match a user with the email `HELLO@example.com`. */ emailAddressQuery?: string; /** - * Returns users with phone numbers that match the given query, via case-insensitive partial match. - * For example, `phone_number_query=555` will match a user with the phone number `+1555xxxxxxx`. + * Filters Organization memberships by phone number. Accepts up to 100 phone numbers. Partial matches supported. For example, `phoneNumberQuery=555` will match a user with the phone number `+1555xxxxxxx`. */ phoneNumberQuery?: string; - /** - * Returns users with usernames that match the given query, via case-insensitive partial match. - * For example, `username_query=CoolUser` will match a user with the username `SomeCoolUser`. + * Filters Organization memberships by username. Accepts up to 100 usernames. Partial matches supported. For example, `usernameQuery=CoolUser` will match a user with the username `SomeCoolUser`. */ usernameQuery?: string; - - /* Returns users with names that match the given query, via case-insensitive partial match. */ + /** Filters Organization memberships by name. Accepts up to 100 names. Partial matches supported. For example, `nameQuery=John Doe` will match a user with the name `John Doe`. */ nameQuery?: string; - /** - * Returns users whose last session activity was before the given date (with millisecond precision). - * Example: use 1700690400000 to retrieve users whose last session activity was before 2023-11-23. + * Filters Organization memberships by last session activity before the given date (with millisecond precision). For example, use `1700690400000` to get users whose last session activity was before 2023-11-23. */ lastActiveAtBefore?: number; /** - * Returns users whose last session activity was after the given date (with millisecond precision). - * Example: use 1700690400000 to retrieve users whose last session activity was after 2023-11-23. + * Filters Organization memberships by last session activity after the given date (with millisecond precision). For example, use `1700690400000` to get users whose last session activity was after 2023-11-23. */ lastActiveAtAfter?: number; - /** - * Returns users who have been created before the given date (with millisecond precision). - * Example: use 1730160000000 to retrieve users who have been created before 2024-10-29. + * Filters Organization memberships by creation date before the given date (with millisecond precision). For example, use `1730160000000` to get users who have been created before 2024-10-29. */ createdAtBefore?: number; - /** - * Returns users who have been created after the given date (with millisecond precision). - * Example: use 1730160000000 to retrieve users who have been created after 2024-10-29. + * Filters Organization memberships by creation date after the given date (with millisecond precision). For example, use `1730160000000` to get users who have been created after 2024-10-29. */ createdAtAfter?: number; }>; -type GetInstanceOrganizationMembershipListParams = ClerkPaginationRequest<{ +export type GetInstanceOrganizationMembershipListParams = ClerkPaginationRequest<{ /** - * Sorts Organization memberships by phone_number, email_address, created_at, first_name, last_name or username. - * By prepending one of those values with + or -, we can choose to sort in ascending (ASC) or descending (DESC) order. + * Filters Organization memberships in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`. */ orderBy?: WithSign<'phone_number' | 'email_address' | 'created_at' | 'first_name' | 'last_name' | 'username'>; }>; -type CreateOrganizationMembershipParams = { +/** @generateWithEmptyComment */ +export type CreateOrganizationMembershipParams = { + /** The ID of the Organization the user is being added to. */ organizationId: string; + /** The ID of the user to be added to the Organization. */ userId: string; + /** The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) to assign to the user. */ role: OrganizationMembershipRole; }; -type UpdateOrganizationMembershipParams = CreateOrganizationMembershipParams; +/** @generateWithEmptyComment */ +export type UpdateOrganizationMembershipParams = CreateOrganizationMembershipParams; -type UpdateOrganizationMembershipMetadataParams = { +/** @generateWithEmptyComment */ +export type UpdateOrganizationMembershipMetadataParams = { + /** The ID of the Organization the membership belongs to. */ organizationId: string; + /** The ID of the user the membership belongs to. */ userId: string; } & MetadataParams; -type DeleteOrganizationMembershipParams = { +/** @generateWithEmptyComment */ +export type DeleteOrganizationMembershipParams = { + /** The ID of the Organization to remove the user from. */ organizationId: string; + /** The ID of the user to remove from the Organization. */ userId: string; }; -type CreateOrganizationInvitationParams = { +/** @generateWithEmptyComment */ +export type CreateOrganizationInvitationParams = { + /** The ID of the Organization the user is being invited to. */ organizationId: string; + /** The email address of the user being invited. */ emailAddress: string; + /** The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) to assign to the user. */ role: OrganizationMembershipRole; + /** The number of days until the invitation expires. Defaults to `30`. */ expiresInDays?: number; + /** The ID of the user creating the invitation. */ inviterUserId?: string; + /** Metadata that can be read and set only from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}. */ privateMetadata?: OrganizationInvitationPrivateMetadata; + /** Metadata that can be read from the Frontend API and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. */ publicMetadata?: OrganizationInvitationPublicMetadata; + /** The full URL or path where the user will land after accepting the invitation. */ redirectUrl?: string; }; -type CreateBulkOrganizationInvitationParams = Array<{ +/** @inline */ +export type CreateBulkOrganizationInvitationParams = Array<{ + /** The email address of the user being invited. */ emailAddress: string; + /** The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) to assign to the user. */ role: OrganizationMembershipRole; + /** The number of days until the invitation expires. Defaults to `30`. */ expiresInDays?: number; + /** The ID of the user creating the invitation. */ inviterUserId?: string; + /** Metadata that can be read and set only from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}. */ privateMetadata?: OrganizationInvitationPrivateMetadata; + /** Metadata that can be read from the Frontend API and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. */ publicMetadata?: OrganizationInvitationPublicMetadata; + /** The full URL or path where the user will land after accepting the invitation. */ redirectUrl?: string; }>; -type GetOrganizationInvitationListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type GetOrganizationInvitationListParams = ClerkPaginationRequest<{ + /** The ID of the Organization to get the list of invitations for. */ organizationId: string; + /** Filters Organization invitations by status. Accepts up to 100 statuses. */ status?: OrganizationInvitationStatus[]; }>; -type GetOrganizationInvitationParams = { +/** @generateWithEmptyComment */ +export type GetOrganizationInvitationParams = { + /** The ID of the Organization to get the invitation for. */ organizationId: string; + /** The ID of the Organization invitation to get. */ invitationId: string; }; -type RevokeOrganizationInvitationParams = { +/** @generateWithEmptyComment */ +export type RevokeOrganizationInvitationParams = { + /** The ID of the Organization to revoke the invitation from. */ organizationId: string; + /** The ID of the Organization invitation to revoke. */ invitationId: string; + /** The ID of the user revoking the invitation. */ requestingUserId?: string; }; -type GetOrganizationDomainListParams = { +/** @generateWithEmptyComment */ +export type GetOrganizationDomainListParams = { + /** The ID of the Organization to get the list of domains for. */ organizationId: string; + /** Maximum number of items returned per request. Must be an integer greater than zero and less than `501`. Can be used for paginating the results together with offset. Defaults to `10`. */ limit?: number; + /** Skip the first `offset` items when paginating. Needs to be an integer greater or equal to zero. To be used in conjunction with `limit`. Defaults to `0`. */ offset?: number; }; -type CreateOrganizationDomainParams = { +/** @generateWithEmptyComment */ +export type CreateOrganizationDomainParams = { + /** The ID of the Organization to create the domain for. */ organizationId: string; + /** The name of the domain. */ name: string; + /** The enrollment mode that determines how matching users are added to the Organization. + * + *
        + *
      • `manual_invitation`: No automatic enrollment. Users with a matching email domain are not given any [invitation](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-invitations) or [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions); an admin must invite them manually.
      • + *
      • `automatic_invitation`: Users with a matching email domain automatically receive a pending [invitation](https://clerk.com/docs/reference/types/organization-invitation) (assigned the Organization's default role) which they can accept to join.
      • + *
      • `automatic_suggestion`: Users with a matching email domain automatically receive a [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions) to join, which they can request.
      • + *
      + */ enrollmentMode: OrganizationEnrollmentMode; + /** Whether the domain is verified. Defaults to `true`. */ verified?: boolean; }; -type UpdateOrganizationDomainParams = { +/** @generateWithEmptyComment */ +export type UpdateOrganizationDomainParams = { + /** The ID of the Organization to update the domain for. */ organizationId: string; + /** The ID of the domain to update. */ domainId: string; } & Partial; -type DeleteOrganizationDomainParams = { +/** @generateWithEmptyComment */ +export type DeleteOrganizationDomainParams = { + /** The ID of the Organization to delete the domain for. */ organizationId: string; + /** The ID of the domain to delete. */ domainId: string; }; +/** @generateWithEmptyComment */ export class OrganizationAPI extends AbstractAPI { + /** + * Gets the list of Organizations for the instance. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`Organization`](https://clerk.com/docs/reference/backend/types/backend-organization) objects and a `totalCount` property containing the total number of Organizations for the instance. + */ public async getOrganizationList(params?: GetOrganizationListParams) { return this.request>({ method: 'GET', @@ -251,6 +313,7 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** Creates an [`Organization`](https://clerk.com/docs/reference/backend/types/backend-organization). */ public async createOrganization(params: CreateParams) { return this.request({ method: 'POST', @@ -259,6 +322,7 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** Gets an [Organization](https://clerk.com/docs/reference/backend/types/backend-organization). */ public async getOrganization(params: GetOrganizationParams) { const { includeMembersCount } = params; const organizationIdOrSlug = 'organizationId' in params ? params.organizationId : params.slug; @@ -273,6 +337,12 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Updates an [Organization](https://clerk.com/docs/reference/backend/types/backend-organization). + * @param organizationId - The ID of the Organization to update. + * @param params - The parameters to update the Organization with. + * @returns The updated [Organization](https://clerk.com/docs/reference/backend/types/backend-organization). + */ public async updateOrganization(organizationId: string, params: UpdateParams) { this.requireId(organizationId); @@ -310,6 +380,12 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Updates the logo of the given Organization. + * @param organizationId - The ID of the Organization to update the logo for. + * @param params - The parameters to update the logo with. + * @returns The updated [`Organization`](https://clerk.com/docs/reference/backend/types/backend-organization). + */ public async updateOrganizationLogo(organizationId: string, params: UpdateLogoParams) { this.requireId(organizationId); @@ -326,6 +402,11 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Deletes the logo of the given Organization. + * @param organizationId - The ID of the Organization to delete the logo for. + * @returns The deleted [`Organization`](https://clerk.com/docs/reference/backend/types/backend-organization). + */ public async deleteOrganizationLogo(organizationId: string) { this.requireId(organizationId); @@ -335,6 +416,18 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Updates the metadata for the given Organization, by merging existing values with the provided parameters. + * + * A "deep" merge will be performed - "deep" means that any nested JSON objects will be merged as well. You can remove metadata keys at any level by setting their value to `null`. + * + * @param organizationId - The ID of the Organization to update the metadata for. + * @param params - The parameters to update the metadata with. + * @returns The updated [`Organization`](https://clerk.com/docs/reference/backend/types/backend-organization). + * + * > [!TIP] + * > If you want to fully replace the existing metadata instead of merging, use [`replaceOrganizationMetadata()`](https://clerk.com/docs/reference/backend/organization/replace-organization-metadata). + */ public async updateOrganizationMetadata(organizationId: string, params: UpdateMetadataParams) { this.requireId(organizationId); @@ -346,9 +439,18 @@ export class OrganizationAPI extends AbstractAPI { } /** - * Replace an organization's metadata. Supplied fields are overwritten in full; - * fields omitted from `params` are left unchanged. Prefer - * `updateOrganizationMetadata` for partial updates with deep-merge semantics. + * Replaces the metadata associated with the specified Organization. Unlike [`updateOrganizationMetadata()`](/docs/reference/backend/organization/update-organization-metadata), which deep-merges into the existing metadata, this method uses replace semantics: when a metadata field is provided, its previous value is overwritten in full with no merging at any level. + * + * The distinction is at two layers: + * - **Top-level field omission preserves the existing value.** Each top-level field (`publicMetadata`, `privateMetadata`, `unsafeMetadata`) is handled independently. If you don't include a field in the request, the stored value for that field is left untouched. + * - **The value inside a provided field is replaced in full.** When you do include a field, its previous content is discarded — any nested keys present before but absent in the new value are dropped. There is no merge. + * + * For the provided field, you can also send: + * - `{}` (empty object) to clear the field. + * - `null` to overwrite the field with a JSON `null` value. Prefer `{}` unless you specifically need a stored `null`. + * @param organizationId - The ID of the Organization to replace the metadata for. + * @param params - The metadata to replace. + * @returns The updated [`Organization`](https://clerk.com/docs/reference/backend/types/backend-organization). */ public async replaceOrganizationMetadata(organizationId: string, params: MetadataParams) { this.requireId(organizationId); @@ -360,6 +462,11 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Deletes the given Organization. + * @param organizationId - The ID of the Organization to delete. + * @returns The deleted [`Organization`](https://clerk.com/docs/reference/backend/types/backend-organization). + */ public async deleteOrganization(organizationId: string) { return this.request({ method: 'DELETE', @@ -367,6 +474,14 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Gets the list of Organization memberships for the specified Organization. + * + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership) objects and a `totalCount` property containing the total number of Organization memberships for the Organization. + * + * > [!TIP] + * > To get the list of Organization memberships **for your instance**, use [`getInstanceOrganizationMembershipList()`](/docs/reference/backend/organization/get-instance-organization-membership-list). + */ public async getOrganizationMembershipList(params: GetOrganizationMembershipListParams) { const { organizationId, ...queryParams } = params; this.requireId(organizationId); @@ -378,6 +493,14 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Gets the list of Organization memberships for the instance. + * + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership) objects and a `totalCount` property containing the total number of Organization memberships for the instance. + * + * > [!TIP] + * > To get the list of Organization memberships **for a specific Organization**, use [`getOrganizationMembershipList()`](/docs/reference/backend/organization/get-organization-membership-list). + */ public async getInstanceOrganizationMembershipList(params: GetInstanceOrganizationMembershipListParams) { return this.request>({ method: 'GET', @@ -386,6 +509,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Creates a membership to an Organization for a user directly (circumventing the need for an invitation). + * @returns The newly created [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership) object. + */ public async createOrganizationMembership(params: CreateOrganizationMembershipParams) { const { organizationId, ...bodyParams } = params; this.requireId(organizationId); @@ -397,6 +524,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Updates a user's [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership). + * @returns The updated [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership) object. + */ public async updateOrganizationMembership(params: UpdateOrganizationMembershipParams) { const { organizationId, userId, ...bodyParams } = params; this.requireId(organizationId); @@ -408,6 +539,13 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Updates the metadata for the given Organization membership, by merging existing values with the provided parameters. + * + * A "deep" merge will be performed - "deep" means that any nested JSON objects will be merged as well. You can remove metadata keys at any level by setting their value to `null`. + * + * @returns The updated [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership). + */ public async updateOrganizationMembershipMetadata(params: UpdateOrganizationMembershipMetadataParams) { const { organizationId, userId, ...bodyParams } = params; @@ -418,6 +556,12 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Removes a user from the given Organization. + * @param organizationId - The ID of the Organization to remove the user from. + * @param userId - The ID of the user to remove from the Organization. + * @returns The deleted [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership). + */ public async deleteOrganizationMembership(params: DeleteOrganizationMembershipParams) { const { organizationId, userId } = params; this.requireId(organizationId); @@ -428,6 +572,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Gets the list of Organization invitations for the specified Organization. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/backend-organization-invitation) objects and a `totalCount` property containing the total number of Organization invitations for the Organization. + */ public async getOrganizationInvitationList(params: GetOrganizationInvitationListParams) { const { organizationId, ...queryParams } = params; this.requireId(organizationId); @@ -439,6 +587,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Creates an invitation for a user to join an Organization. + * @returns The newly created [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/backend-organization-invitation) object. + */ public async createOrganizationInvitation(params: CreateOrganizationInvitationParams) { const { organizationId, ...bodyParams } = params; this.requireId(organizationId); @@ -450,6 +602,11 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** Creates multiple invitations for users to join an Organization. + * @param organizationId - The ID of the Organization to create the invitations for. + * @param params - The parameters to create the invitations with. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/backend-organization-invitation) objects and a `totalCount` property containing the total number of Organization invitations. + */ public async createOrganizationInvitationBulk( organizationId: string, params: CreateBulkOrganizationInvitationParams, @@ -463,6 +620,7 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** Gets an [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/backend-organization-invitation). */ public async getOrganizationInvitation(params: GetOrganizationInvitationParams) { const { organizationId, invitationId } = params; this.requireId(organizationId); @@ -474,6 +632,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Revokes an invitation from a user for the given Organization. + * @returns The revoked [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/backend-organization-invitation). + */ public async revokeOrganizationInvitation(params: RevokeOrganizationInvitationParams) { const { organizationId, invitationId, ...bodyParams } = params; this.requireId(organizationId); @@ -485,6 +647,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Gets the list of [Verified Domains](https://clerk.com/docs/guides/organizations/add-members/verified-domains) for the given Organization. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`OrganizationDomain`](https://clerk.com/docs/reference/backend/types/backend-organization-domain) objects and a `totalCount` property containing the total number of Verified Domains for the Organization. + */ public async getOrganizationDomainList(params: GetOrganizationDomainListParams) { const { organizationId, ...queryParams } = params; this.requireId(organizationId); @@ -496,6 +662,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Creates a new [Verified Domain](https://clerk.com/docs/guides/organizations/add-members/verified-domains) for the given Organization. By default, the domain is verified, but can be optionally set to unverified. + * @returns The newly created [`OrganizationDomain`](https://clerk.com/docs/reference/backend/types/backend-organization-domain) object. + */ public async createOrganizationDomain(params: CreateOrganizationDomainParams) { const { organizationId, ...bodyParams } = params; this.requireId(organizationId); @@ -510,6 +680,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Updates a [Verified Domain](https://clerk.com/docs/guides/organizations/add-members/verified-domains) for the given Organization. + * @returns The updated [`OrganizationDomain`](https://clerk.com/docs/reference/backend/types/backend-organization-domain) object. + */ public async updateOrganizationDomain(params: UpdateOrganizationDomainParams) { const { organizationId, domainId, ...bodyParams } = params; this.requireId(organizationId); @@ -522,6 +696,10 @@ export class OrganizationAPI extends AbstractAPI { }); } + /** + * Deletes a [Verified Domain](https://clerk.com/docs/guides/organizations/add-members/verified-domains) for the given Organization. + * @returns The deleted [`OrganizationDomain`](https://clerk.com/docs/reference/backend/types/backend-organization-domain) object. + */ public async deleteOrganizationDomain(params: DeleteOrganizationDomainParams) { const { organizationId, domainId } = params; this.requireId(organizationId); diff --git a/packages/backend/src/api/endpoints/index.ts b/packages/backend/src/api/endpoints/index.ts index ebbd1990b56..f5a33476e57 100644 --- a/packages/backend/src/api/endpoints/index.ts +++ b/packages/backend/src/api/endpoints/index.ts @@ -33,3 +33,8 @@ export * from './TestingTokenApi'; export * from './UserApi'; export * from './WaitlistEntryApi'; export * from './WebhookApi'; + +// Disambiguate names that exist in both UserApi (user-scoped) and OrganizationApi +// (org-scoped) — the UserApi versions remain the canonical public exports; the +// org-scoped variants stay reachable via direct import from './OrganizationApi'. +export type { GetOrganizationInvitationListParams, GetOrganizationMembershipListParams } from './UserApi'; diff --git a/packages/backend/src/api/resources/Enums.ts b/packages/backend/src/api/resources/Enums.ts index b2b60590cd3..0604553e24f 100644 --- a/packages/backend/src/api/resources/Enums.ts +++ b/packages/backend/src/api/resources/Enums.ts @@ -21,9 +21,7 @@ export type OAuthProvider = export type OAuthStrategy = `oauth_${OAuthProvider}`; -/** - * @inline - */ +/** @inline */ export type OrganizationInvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired'; export type OrganizationDomainVerificationStatus = 'unverified' | 'verified'; @@ -32,6 +30,7 @@ export type OrganizationDomainVerificationStrategy = 'email_code'; // only avail export type OrganizationEnrollmentMode = 'manual_invitation' | 'automatic_invitation' | 'automatic_suggestion'; +/** @inline */ export type OrganizationMembershipRole = OrganizationCustomRoleKey; export type SignInStatus = 'needs_identifier' | 'needs_factor_one' | 'needs_factor_two' | 'complete'; diff --git a/packages/shared/src/types/organization.ts b/packages/shared/src/types/organization.ts index e3e78b512b0..d504d736c54 100644 --- a/packages/shared/src/types/organization.ts +++ b/packages/shared/src/types/organization.ts @@ -22,18 +22,14 @@ import type { GetEnterpriseConnectionsParams } from './user'; declare global { /** - * If you want to provide custom types for the organization.publicMetadata object, - * simply redeclare this rule in the global namespace. - * Every Organization object will use the provided type. + * If you want to provide custom types for the `organization.publicMetadata` object, simply redeclare this rule in the global namespace. Every `Organization` object will use the provided type. */ interface OrganizationPublicMetadata { [k: string]: unknown; } /** - * If you want to provide custom types for the organization.privateMetadata object, - * simply redeclare this rule in the global namespace. - * Every Organization object will use the provided type. + * If you want to provide custom types for the `organization.privateMetadata` object, simply redeclare this rule in the global namespace. Every `Organization` object will use the provided type. */ interface OrganizationPrivateMetadata { [k: string]: unknown; From 9f65eee1453efd742082290984de174824df1235 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:37:59 -0700 Subject: [PATCH 04/18] fix broken organization API links --- .typedoc/custom-plugin.mjs | 4 ++++ packages/backend/src/api/endpoints/UserApi.ts | 10 +++++----- .../src/api/resources/OrganizationDomain.ts | 19 +++++++++++++++++++ .../backend/src/api/resources/Verification.ts | 5 +++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 869ba73a069..357551d7912 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -128,6 +128,10 @@ const LINK_REPLACEMENTS = [ ['session-task', '/docs/reference/types/session-task'], ['public-user-data', '/docs/reference/types/public-user-data'], ['session-status', '/docs/reference/types/session-status'], + [ + 'organization-domain-verification', + '/docs/reference/backend/types/backend-organization-domain#organization-domain-verification', + ], ]; /** diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index 975902c4f21..826774496ff 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -677,7 +677,7 @@ export class UserAPI extends AbstractAPI { /** * Gets a list of the given user's Organization memberships. - * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/organization-membership) objects, and a `totalCount` property that indicates the total number of Organization memberships for the user. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OrganizationMembership`](https://clerk.com/docs/reference/backend/types/backend-organization-membership) objects, and a `totalCount` property that indicates the total number of Organization memberships for the user. */ public async getOrganizationMembershipList(params: GetOrganizationMembershipListParams) { const { userId, limit, offset } = params; @@ -692,7 +692,7 @@ export class UserAPI extends AbstractAPI { /** * Gets a list of the given user's Organization invitations. - * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/organization-invitation) objects, and a `totalCount` property that indicates the total number of Organization invitations for the user. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OrganizationInvitation`](https://clerk.com/docs/reference/backend/types/backend-organization-invitation) objects, and a `totalCount` property that indicates the total number of Organization invitations for the user. */ public async getOrganizationInvitationList(params: GetOrganizationInvitationListParams) { const { userId, ...queryParams } = params; @@ -791,7 +791,7 @@ export class UserAPI extends AbstractAPI { /** * Deletes the passkey identification for a given user and notifies them through email. - * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/backend/types/backend-deleted-object-resource). + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource). */ public async deleteUserPasskey(params: DeleteUserPasskeyParams) { this.requireId(params.userId); @@ -804,7 +804,7 @@ export class UserAPI extends AbstractAPI { /** * Deletes a Web3 wallet identification for the given user. - * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/backend/types/backend-deleted-object-resource). + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource). */ public async deleteUserWeb3Wallet(params: DeleteWeb3WalletParams) { this.requireId(params.userId); @@ -817,7 +817,7 @@ export class UserAPI extends AbstractAPI { /** * Deletes an external account for the given user. - * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/backend/types/backend-deleted-object-resource). + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource). */ public async deleteUserExternalAccount(params: DeleteUserExternalAccountParams) { this.requireId(params.userId); diff --git a/packages/backend/src/api/resources/OrganizationDomain.ts b/packages/backend/src/api/resources/OrganizationDomain.ts index ee672a6926b..8401e2a1112 100644 --- a/packages/backend/src/api/resources/OrganizationDomain.ts +++ b/packages/backend/src/api/resources/OrganizationDomain.ts @@ -2,17 +2,36 @@ import type { OrganizationEnrollmentMode } from './Enums'; import type { OrganizationDomainJSON } from './JSON'; import { OrganizationDomainVerification } from './Verification'; +/** The `OrganizationDomain` object is the model around an Organization's [Verified Domain](https://clerk.com/docs/guides/organizations/add-members/verified-domains). */ export class OrganizationDomain { constructor( + /** The unique identifier of the domain. */ readonly id: string, + /** The ID of the Organization that the domain belongs to. */ readonly organizationId: string, + /** The name of the domain. */ readonly name: string, + /** + * The enrollment mode that determines how matching users are added to the Organization. + * + *
        + *
      • `manual_invitation`: No automatic enrollment. Users with a matching email domain are not given any [invitation](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-invitations) or [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions); an admin must invite them manually.
      • + *
      • `automatic_invitation`: Users with a matching email domain automatically receive a pending [invitation](https://clerk.com/docs/reference/types/organization-invitation) (assigned the Organization's default role) which they can accept to join.
      • + *
      • `automatic_suggestion`: Users with a matching email domain automatically receive a [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions) to join, which they can request.
      • + *
      + */ readonly enrollmentMode: OrganizationEnrollmentMode, + /** The verification details of the domain. */ readonly verification: OrganizationDomainVerification | null, + /** The total number of pending invitations for the domain. */ readonly totalPendingInvitations: number, + /** The total number of pending suggestions for the domain. */ readonly totalPendingSuggestions: number, + /** The date when the domain was created. */ readonly createdAt: number, + /** The date when the domain was last updated. */ readonly updatedAt: number, + /** The email address used to verify the domain. */ readonly affiliationEmailAddress: string | null, ) {} diff --git a/packages/backend/src/api/resources/Verification.ts b/packages/backend/src/api/resources/Verification.ts index 4da2489071e..d87b19a59d7 100644 --- a/packages/backend/src/api/resources/Verification.ts +++ b/packages/backend/src/api/resources/Verification.ts @@ -57,11 +57,16 @@ export class Verification { } } +/** @generateWithEmptyComment */ export class OrganizationDomainVerification { constructor( + /** The current status of the verification. */ readonly status: string, + /** The strategy used to verify the domain. */ readonly strategy: string, + /** The number of verification attempts that have been made. */ readonly attempts: number | null = null, + /** The date and time when the current verification attempt expires. */ readonly expireAt: number | null = null, ) {} From 6273c3f1695913fb9e53381fc243182dbb3319fa Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:05:21 -0700 Subject: [PATCH 05/18] fix(backend): enumerate OrganizationApi barrel exports to avoid name collision ESLint's `import/export` rule flagged the `export *` from both UserApi and OrganizationApi as a duplicate-export conflict on `GetOrganizationMembershipListParams` and `GetOrganizationInvitationListParams`, and the earlier explicit `export type { ... } from './UserApi'` disambiguator only added a third source of the names. Replace `export * from './OrganizationApi'` with an explicit enumeration that omits the two colliders, so only UserApi's user-scoped variants reach the public barrel. OrganizationApi's org-scoped variants stay exported from the file itself, reachable via direct import and resolvable by typedoc on the class methods. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/backend/src/api/endpoints/index.ts | 34 +++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/api/endpoints/index.ts b/packages/backend/src/api/endpoints/index.ts index f5a33476e57..fd10a65f428 100644 --- a/packages/backend/src/api/endpoints/index.ts +++ b/packages/backend/src/api/endpoints/index.ts @@ -17,7 +17,34 @@ export * from './MachineApi'; export * from './M2MTokenApi'; export * from './JwksApi'; export * from './JwtTemplatesApi'; -export * from './OrganizationApi'; +// `GetOrganizationMembershipListParams` and `GetOrganizationInvitationListParams` are defined on +// both `UserApi` (user-scoped) and `OrganizationApi` (org-scoped) with different shapes. +// UserApi's variants remain the canonical public exports through this barrel; OrganizationApi's +// variants are reachable via direct import from `./OrganizationApi`, and typedoc still resolves +// them locally on the class methods. +export { OrganizationAPI } from './OrganizationApi'; +export type { + CreateBulkOrganizationInvitationParams, + CreateOrganizationDomainParams, + CreateOrganizationInvitationParams, + CreateOrganizationMembershipParams, + CreateParams, + DeleteOrganizationDomainParams, + DeleteOrganizationMembershipParams, + GetInstanceOrganizationMembershipListParams, + GetOrganizationDomainListParams, + GetOrganizationInvitationParams, + GetOrganizationListParams, + GetOrganizationParams, + MetadataParams, + RevokeOrganizationInvitationParams, + UpdateLogoParams, + UpdateMetadataParams, + UpdateOrganizationDomainParams, + UpdateOrganizationMembershipMetadataParams, + UpdateOrganizationMembershipParams, + UpdateParams, +} from './OrganizationApi'; export * from './OrganizationPermissionApi'; export * from './OrganizationRoleApi'; export * from './OAuthApplicationsApi'; @@ -33,8 +60,3 @@ export * from './TestingTokenApi'; export * from './UserApi'; export * from './WaitlistEntryApi'; export * from './WebhookApi'; - -// Disambiguate names that exist in both UserApi (user-scoped) and OrganizationApi -// (org-scoped) — the UserApi versions remain the canonical public exports; the -// org-scoped variants stay reachable via direct import from './OrganizationApi'. -export type { GetOrganizationInvitationListParams, GetOrganizationMembershipListParams } from './UserApi'; From e178b2873b4069cc7d4d72bfd44f874bf2508e63 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 17 Jun 2026 14:27:25 -0600 Subject: [PATCH 06/18] docs review - organization --- packages/backend/src/api/endpoints/OrganizationApi.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/api/endpoints/OrganizationApi.ts b/packages/backend/src/api/endpoints/OrganizationApi.ts index fa7b2ee35cf..113c90227ad 100644 --- a/packages/backend/src/api/endpoints/OrganizationApi.ts +++ b/packages/backend/src/api/endpoints/OrganizationApi.ts @@ -43,7 +43,7 @@ export type CreateParams = { name: string; /** The slug of the Organization. */ slug?: string; - /** The ID of the user creating the organization. The user will become an [admin](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions#default-roles) for the Organization. */ + /** The ID of the user creating the Organization. The user will become an [admin](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions#default-roles) for the Organization. */ createdBy?: string; /** The maximum number of memberships allowed in the Organization. `0` means unlimited. */ maxAllowedMemberships?: number; @@ -154,6 +154,7 @@ export type GetOrganizationMembershipListParams = ClerkPaginationRequest<{ createdAtAfter?: number; }>; +/** @generateWithEmptyComment */ export type GetInstanceOrganizationMembershipListParams = ClerkPaginationRequest<{ /** * Filters Organization memberships in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`. From 231cddd9efff1b32496874e5e9c92d1f0789c54c Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:57:01 -0700 Subject: [PATCH 07/18] document the backend deleted-object --- packages/backend/src/api/endpoints/UserApi.ts | 6 +++--- packages/backend/src/api/resources/DeletedObject.ts | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index 826774496ff..5a02ae89754 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -791,7 +791,7 @@ export class UserAPI extends AbstractAPI { /** * Deletes the passkey identification for a given user and notifies them through email. - * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource). + * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object) object. */ public async deleteUserPasskey(params: DeleteUserPasskeyParams) { this.requireId(params.userId); @@ -804,7 +804,7 @@ export class UserAPI extends AbstractAPI { /** * Deletes a Web3 wallet identification for the given user. - * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource). + * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object) object. */ public async deleteUserWeb3Wallet(params: DeleteWeb3WalletParams) { this.requireId(params.userId); @@ -817,7 +817,7 @@ export class UserAPI extends AbstractAPI { /** * Deletes an external account for the given user. - * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource). + * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object) object. */ public async deleteUserExternalAccount(params: DeleteUserExternalAccountParams) { this.requireId(params.userId); diff --git a/packages/backend/src/api/resources/DeletedObject.ts b/packages/backend/src/api/resources/DeletedObject.ts index b89b8d85ec5..54858a97ee4 100644 --- a/packages/backend/src/api/resources/DeletedObject.ts +++ b/packages/backend/src/api/resources/DeletedObject.ts @@ -1,10 +1,17 @@ import type { DeletedObjectJSON } from './JSON'; +/** + * The `DeletedObject` object represents an item that has been deleted from the database. It is used to represent the result of a delete operation. + */ export class DeletedObject { constructor( + /** The type of object that has been deleted. */ readonly object: string, + /** The unique identifier for the deleted object. */ readonly id: string | null, + /** The URL-friendly identifier for the deleted object. */ readonly slug: string | null, + /** Whether the object has been deleted. */ readonly deleted: boolean, ) {} From 135ca40607acef4fe56becb237897895ec7a75bb Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 17 Jun 2026 14:06:04 -0700 Subject: [PATCH 08/18] fix(backend): enumerate typedoc endpoint entry points explicitly The `./src/api/endpoints/**/*.ts` glob in `packages/backend/typedoc.json` was silently expanding to zero files in CI (Linux + Node 24.15) while expanding correctly locally (macOS), producing `[warning] The glob ... did not match any files` and skipping backend extraction entirely. Replace the glob with an explicit file list to sidestep the environment-sensitive expansion. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/backend/typedoc.json | 39 ++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/backend/typedoc.json b/packages/backend/typedoc.json index ae6a83fa433..e93760481a6 100644 --- a/packages/backend/typedoc.json +++ b/packages/backend/typedoc.json @@ -9,6 +9,43 @@ "./src/tokens/authObjects.ts", "./src/api/resources/index.ts", "./src/api/resources/Deserializer.ts", - "./src/api/endpoints/**/*.ts" + "./src/api/endpoints/AbstractApi.ts", + "./src/api/endpoints/AccountlessApplicationsAPI.ts", + "./src/api/endpoints/ActorTokenApi.ts", + "./src/api/endpoints/AgentTaskApi.ts", + "./src/api/endpoints/AllowlistIdentifierApi.ts", + "./src/api/endpoints/APIKeysApi.ts", + "./src/api/endpoints/BetaFeaturesApi.ts", + "./src/api/endpoints/BillingApi.ts", + "./src/api/endpoints/BlocklistIdentifierApi.ts", + "./src/api/endpoints/ClientApi.ts", + "./src/api/endpoints/DomainApi.ts", + "./src/api/endpoints/EmailAddressApi.ts", + "./src/api/endpoints/EnterpriseConnectionApi.ts", + "./src/api/endpoints/IdPOAuthAccessTokenApi.ts", + "./src/api/endpoints/InstanceApi.ts", + "./src/api/endpoints/InvitationApi.ts", + "./src/api/endpoints/JwksApi.ts", + "./src/api/endpoints/JwtTemplatesApi.ts", + "./src/api/endpoints/M2MTokenApi.ts", + "./src/api/endpoints/MachineApi.ts", + "./src/api/endpoints/OAuthApplicationsApi.ts", + "./src/api/endpoints/OrganizationApi.ts", + "./src/api/endpoints/OrganizationPermissionApi.ts", + "./src/api/endpoints/OrganizationRoleApi.ts", + "./src/api/endpoints/PhoneNumberApi.ts", + "./src/api/endpoints/ProxyCheckApi.ts", + "./src/api/endpoints/RedirectUrlApi.ts", + "./src/api/endpoints/RoleSetApi.ts", + "./src/api/endpoints/SamlConnectionApi.ts", + "./src/api/endpoints/SessionApi.ts", + "./src/api/endpoints/SignInTokenApi.ts", + "./src/api/endpoints/SignUpApi.ts", + "./src/api/endpoints/TestingTokenApi.ts", + "./src/api/endpoints/UserApi.ts", + "./src/api/endpoints/WaitlistEntryApi.ts", + "./src/api/endpoints/WebhookApi.ts", + "./src/api/endpoints/index.ts", + "./src/api/endpoints/util-types.ts" ] } From 4da4276c69fd007593f44cc3ff4f90b4294e0b4c Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:41:05 -0700 Subject: [PATCH 09/18] Document Billing, Allowlist Identifier, and Domains APIs --- .typedoc/__tests__/file-structure.test.ts | 6 +++ .typedoc/reference-objects.mjs | 12 +++++ .../api/endpoints/AllowlistIdentifierApi.ts | 19 ++++++- .../backend/src/api/endpoints/BillingApi.ts | 35 ++++++++++--- .../backend/src/api/endpoints/DomainApi.ts | 51 +++++++++++-------- .../backend/src/api/resources/CommercePlan.ts | 4 +- .../src/api/resources/CommerceSubscription.ts | 20 ++++---- .../api/resources/CommerceSubscriptionItem.ts | 38 +++++++------- packages/backend/src/api/resources/Domain.ts | 9 ++++ 9 files changed, 134 insertions(+), 60 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 2949c6f2f60..ee62fd29c34 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -48,6 +48,12 @@ describe('Typedoc output', () => { expect(nestedFolders).toMatchInlineSnapshot(` [ + "backend/allowlist-identifier-api", + "backend/allowlist-identifier-api/methods", + "backend/billing-api", + "backend/billing-api/methods", + "backend/domain-api", + "backend/domain-api/methods", "backend/organization-api", "backend/organization-api/methods", "backend/user-api", diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 988a8668123..c5ae49cf4cc 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -68,6 +68,18 @@ export const BACKEND_API_CONFIG = { symbol: 'OrganizationAPI', declarationHint: 'api/endpoints/OrganizationApi', }, + 'backend/billing-api/billing-api.mdx': { + symbol: 'BillingAPI', + declarationHint: 'api/endpoints/BillingApi', + }, + 'backend/allowlist-identifier-api/allowlist-identifier-api.mdx': { + symbol: 'AllowlistIdentifierAPI', + declarationHint: 'api/endpoints/AllowlistIdentifierApi', + }, + 'backend/domain-api/domain-api.mdx': { + symbol: 'DomainAPI', + declarationHint: 'api/endpoints/DomainApi', + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG} then {@link BACKEND_API_CONFIG}. */ diff --git a/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts b/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts index 62611e92e8e..0931d07dfaf 100644 --- a/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts +++ b/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts @@ -8,12 +8,20 @@ import { AbstractAPI } from './AbstractApi'; const basePath = '/allowlist_identifiers'; -type AllowlistIdentifierCreateParams = { +/** @generateWithEmptyComment */ +export type AllowlistIdentifierCreateParams = { + /** The identifier to add to the allowlist. */ identifier: string; + /** Whether to notify the user that their identifier has been added to the allowlist. */ notify: boolean; }; +/** @generateWithEmptyComment */ export class AllowlistIdentifierAPI extends AbstractAPI { + /** + * Gets the list of allowlist identifiers for the instance. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`AllowlistIdentifier`](https://clerk.com/docs/reference/backend/types/backend-allowlist-identifier) objects and a `totalCount` property containing the total number of allowlist identifiers for the instance. + */ public async getAllowlistIdentifierList(params: ClerkPaginationRequest = {}) { return this.request>({ method: 'GET', @@ -22,6 +30,10 @@ export class AllowlistIdentifierAPI extends AbstractAPI { }); } + /** + * Creates a new allowlist identifier. + * @returns The created [`AllowlistIdentifier`](https://clerk.com/docs/reference/backend/types/backend-allowlist-identifier) object. + */ public async createAllowlistIdentifier(params: AllowlistIdentifierCreateParams) { return this.request({ method: 'POST', @@ -30,6 +42,11 @@ export class AllowlistIdentifierAPI extends AbstractAPI { }); } + /** + * Deletes an allowlist identifier. + * @param allowlistIdentifierId - The ID of the allowlist identifier to delete. + * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object) object. + */ public async deleteAllowlistIdentifier(allowlistIdentifierId: string) { this.requireId(allowlistIdentifierId); return this.request({ diff --git a/packages/backend/src/api/endpoints/BillingApi.ts b/packages/backend/src/api/endpoints/BillingApi.ts index 3561a7cc9f3..a20bb2bc0a8 100644 --- a/packages/backend/src/api/endpoints/BillingApi.ts +++ b/packages/backend/src/api/endpoints/BillingApi.ts @@ -11,31 +11,38 @@ const basePath = '/billing'; const organizationBasePath = '/organizations'; const userBasePath = '/users'; -type GetOrganizationListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type GetPlanListParams = ClerkPaginationRequest<{ + /** + * Filters plans by the type of payer. + */ payerType: 'org' | 'user'; }>; -type CancelSubscriptionItemParams = { +/** @inline */ +export type CancelSubscriptionItemParams = { /** - * If true, the subscription item will be canceled immediately. If false or undefined, the subscription item will be canceled at the end of the current billing period. - * @default undefined + * Whether the Subscription Item should be canceled immediately. If `false`, the Subscription Item will be canceled at the end of the current billing period. */ endNow?: boolean; }; -type ExtendSubscriptionItemFreeTrialParams = { +/** @inline */ +export type ExtendSubscriptionItemFreeTrialParams = { /** - * RFC3339 timestamp to extend the free trial to. - * Must be in the future and not more than 365 days from the current trial end. + * The date to extend the free trial to. Must be in the future and not more than 365 days from the current trial end date. */ extendTo: Date; }; +/** @generateWithEmptyComment */ export class BillingAPI extends AbstractAPI { /** + * Gets the list of Billing Plans for the instance. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`BillingPlan`](https://clerk.com/docs/reference/backend/types/billing-plan) objects and a `totalCount` property containing the total number of Billing Plans for the instance. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ - public async getPlanList(params?: GetOrganizationListParams) { + public async getPlanList(params?: GetPlanListParams) { return this.request>({ method: 'GET', path: joinPaths(basePath, 'plans'), @@ -44,6 +51,10 @@ export class BillingAPI extends AbstractAPI { } /** + * Cancels the given Subscription Item. + * @param subscriptionItemId - The ID of the Subscription Item to cancel. + * @param params - The parameters for the request. + * @returns The cancelled [`BillingSubscriptionItem`](https://clerk.com/docs/reference/backend/types/billing-subscription-item) object. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ public async cancelSubscriptionItem(subscriptionItemId: string, params?: CancelSubscriptionItemParams) { @@ -56,6 +67,10 @@ export class BillingAPI extends AbstractAPI { } /** + * Extends the free trial for the given Subscription Item. + * @param subscriptionItemId - The ID of the Subscription Item to extend the free trial for. + * @param params - The parameters for the request. + * @returns The updated [`BillingSubscriptionItem`](https://clerk.com/docs/reference/backend/types/billing-subscription-item) object. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ public async extendSubscriptionItemFreeTrial( @@ -71,6 +86,8 @@ export class BillingAPI extends AbstractAPI { } /** + * Gets the [`BillingSubscription`](https://clerk.com/docs/reference/backend/types/billing-subscription) for the given Organization. + * @param organizationId - The ID of the Organization to get the Billing Subscription for. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ public async getOrganizationBillingSubscription(organizationId: string) { @@ -82,6 +99,8 @@ export class BillingAPI extends AbstractAPI { } /** + * Gets the [`BillingSubscription`](https://clerk.com/docs/reference/backend/types/billing-subscription) for the given User. + * @param userId - The ID of the User to get the Billing Subscription for. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ public async getUserBillingSubscription(userId: string) { diff --git a/packages/backend/src/api/endpoints/DomainApi.ts b/packages/backend/src/api/endpoints/DomainApi.ts index 12872c6335d..da01715ae5a 100644 --- a/packages/backend/src/api/endpoints/DomainApi.ts +++ b/packages/backend/src/api/endpoints/DomainApi.ts @@ -6,35 +6,32 @@ import { AbstractAPI } from './AbstractApi'; const basePath = '/domains'; -type AddDomainParams = { +/** @generateWithEmptyComment */ +export type AddDomainParams = { /** - * The new domain name. For development instances, can contain the port, i.e myhostname:3000. For production instances, must be a valid FQDN, i.e mysite.com. Cannot contain protocol scheme. + * The new domain name. For development instances, can contain the port, e.g. `myhostname:3000`. For production instances, must be a valid FQDN, e.g. `mysite.com`. Cannot contain protocol scheme. */ name: string; - /** - * Marks the new domain as satellite. Only true is accepted at the moment. - */ + /** Whether the new domain is a satellite domain. Only `true` is accepted at the moment. */ is_satellite: boolean; - /** - * The full URL of the proxy which will forward requests to the Clerk Frontend API for this domain. Applicable only to production instances. - */ + /** The proxy URL for the domain. Applicable only to production instances. */ proxy_url?: string | null; }; -type UpdateDomainParams = Partial> & { - /** - * The ID of the domain that will be updated. - */ +/** @generateWithEmptyComment */ +export type UpdateDomainParams = Partial> & { + /** The ID of the domain that will be updated. */ domainId: string; - /** - * Whether this is a domain for a secondary app, meaning that any subdomain provided is significant - * and will be stored as part of the domain. This is useful for supporting multiple apps - * (one primary and multiple secondaries) on the same root domain (eTLD+1). - */ + /** Whether this is a domain for a secondary app, meaning that any subdomain provided is significant and will be stored as part of the domain. This is useful for supporting multiple apps (one primary and multiple secondaries) on the same root domain (eTLD+1). */ is_secondary?: boolean | null; }; +/** @generateWithEmptyComment */ export class DomainAPI extends AbstractAPI { + /** + * Gets the list of domains for the instance. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`Domain`](https://clerk.com/docs/reference/backend/types/domain) objects and a `totalCount` property containing the total number of domains for the instance. + */ public async list() { return this.request>({ method: 'GET', @@ -42,6 +39,10 @@ export class DomainAPI extends AbstractAPI { }); } + /** + * Adds a new domain to the instance. Useful in the case of multi-domain instances, allows adding [satellite domains](https://clerk.com/docs/guides/dashboard/dns-domains/satellite-domains) to an instance. + * @returns The created [`Domain`](https://clerk.com/docs/reference/backend/types/domain) object. + */ public async add(params: AddDomainParams) { return this.request({ method: 'POST', @@ -50,6 +51,12 @@ export class DomainAPI extends AbstractAPI { }); } + /** + * Updates a domain for the instance. Both primary and satellite domains can be updated. If you choose to use Clerk via proxy, use this endpoint to specify the `proxy_url`. Whenever you decide you'd rather switch to DNS setup for Clerk, simply set `proxy_url` to `null` for the domain. + * + * When you update a production instance's primary domain name, you have to make sure that you've completed all the necessary setup steps for DNS and emails to work. Expect downtime otherwise. Updating a primary domain's name will also update the instance's home origin, affecting the default application paths. + * @returns The updated [`Domain`](https://clerk.com/docs/reference/backend/types/domain) object. + */ public async update(params: UpdateDomainParams) { const { domainId, ...bodyParams } = params; @@ -63,15 +70,19 @@ export class DomainAPI extends AbstractAPI { } /** - * Deletes a satellite domain for the instance. - * It is currently not possible to delete the instance's primary domain. + * Deletes a satellite domain for the instance. It is currently not possible to delete the instance's primary domain. + * @param satelliteDomainId - The ID of the satellite domain to delete. + * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object). */ public async delete(satelliteDomainId: string) { return this.deleteDomain(satelliteDomainId); } /** - * @deprecated Use `delete` instead + * Deletes a satellite domain for the instance. + * @param satelliteDomainId - The ID of the satellite domain to delete. + * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object). + * @deprecated Use the [`delete()`](https://clerk.com/docs/reference/backend/domain/delete) method instead. */ public async deleteDomain(satelliteDomainId: string) { this.requireId(satelliteDomainId); diff --git a/packages/backend/src/api/resources/CommercePlan.ts b/packages/backend/src/api/resources/CommercePlan.ts index acfef8cf71b..c9eaec436bf 100644 --- a/packages/backend/src/api/resources/CommercePlan.ts +++ b/packages/backend/src/api/resources/CommercePlan.ts @@ -4,7 +4,7 @@ import { Feature } from './Feature'; import type { BillingPlanJSON } from './JSON'; /** - * The `BillingPlan` object is similar to the [`BillingPlanResource`](/docs/reference/types/billing-plan-resource) object as it holds information about a Plan, as well as methods for managing it. However, the `BillingPlan` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/plans) and is not directly accessible from the Frontend API. + * The `BillingPlan` object is similar to the [`BillingPlanResource`](https://clerk.com/docs/reference/types/billing-plan-resource) object as it holds information about a Plan, as well as methods for managing it. However, the `BillingPlan` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/plans) and is not directly accessible from the Frontend API. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ @@ -59,7 +59,7 @@ export class BillingPlan { */ readonly forPayerType: 'org' | 'user', /** - * The features the Plan offers. + * The [Features](https://clerk.com/docs/reference/backend/types/feature) the Plan offers. */ readonly features: Feature[], /** diff --git a/packages/backend/src/api/resources/CommerceSubscription.ts b/packages/backend/src/api/resources/CommerceSubscription.ts index 29758f165d8..6ceaa224655 100644 --- a/packages/backend/src/api/resources/CommerceSubscription.ts +++ b/packages/backend/src/api/resources/CommerceSubscription.ts @@ -4,46 +4,46 @@ import { BillingSubscriptionItem } from './CommerceSubscriptionItem'; import type { BillingSubscriptionJSON } from './JSON'; /** - * The `BillingSubscription` object is similar to the [`BillingSubscriptionResource`](/docs/reference/types/billing-subscription-resource) object as it holds information about a subscription, as well as methods for managing it. However, the `BillingSubscription` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/organizations/%7Borganization_id%7D/billing/subscription) and is not directly accessible from the Frontend API. + * The `BillingSubscription` object is similar to the [`BillingSubscriptionResource`](https://clerk.com/docs/reference/types/billing-subscription-resource) object as it holds information about a subscription, as well as methods for managing it. However, the `BillingSubscription` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/organizations/%7Borganization_id%7D/billing/subscription) and is not directly accessible from the Frontend API. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ export class BillingSubscription { constructor( /** - * The unique identifier for the billing subscription. + * The unique identifier for the Subscription. */ readonly id: string, /** - * The current status of the subscription. + * The current status of the Subscription. */ readonly status: BillingSubscriptionJSON['status'], /** - * The ID of the payer for this subscription. + * The ID of the payer for this Subscription. */ readonly payerId: string, /** - * Unix timestamp (milliseconds) of when the subscription was created. + * The Unix timestamp (milliseconds) of when the Subscription was created. */ readonly createdAt: number, /** - * Unix timestamp (milliseconds) of when the subscription was last updated. + * The Unix timestamp (milliseconds) of when the Subscription was last updated. */ readonly updatedAt: number, /** - * Unix timestamp (milliseconds) of when the subscription became active. + * The Unix timestamp (milliseconds) of when the Subscription became active. */ readonly activeAt: number | null, /** - * Unix timestamp (milliseconds) of when the subscription became past due. + * The Unix timestamp (milliseconds) of when the Subscription became past due. */ readonly pastDueAt: number | null, /** - * Array of subscription items in this subscription. + * All of the Subscription Items in this Subscription. */ readonly subscriptionItems: BillingSubscriptionItem[], /** - * Information about the next scheduled payment. + * Information about the next scheduled payment for this Subscription. If present, contains the amount of the next payment and the Unix timestamp (milliseconds) of when the next payment is scheduled. */ readonly nextPayment: { date: number; amount: BillingMoneyAmount } | null, /** diff --git a/packages/backend/src/api/resources/CommerceSubscriptionItem.ts b/packages/backend/src/api/resources/CommerceSubscriptionItem.ts index 1864bc73f9d..94f688c2436 100644 --- a/packages/backend/src/api/resources/CommerceSubscriptionItem.ts +++ b/packages/backend/src/api/resources/CommerceSubscriptionItem.ts @@ -4,30 +4,30 @@ import { BillingPlan } from './CommercePlan'; import type { BillingSubscriptionItemJSON } from './JSON'; /** - * The `BillingSubscriptionItem` object is similar to the [`BillingSubscriptionItemResource`](/docs/reference/types/billing-subscription-item-resource) object as it holds information about a subscription item, as well as methods for managing it. However, the `BillingSubscriptionItem` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/subscription_items) and is not directly accessible from the Frontend API. + * The `BillingSubscriptionItem` object is similar to the [`BillingSubscriptionItemResource`](https://clerk.com/docs/reference/types/billing-subscription-item-resource) object as it holds information about a subscription item, as well as methods for managing it. However, the `BillingSubscriptionItem` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/subscription_items) and is not directly accessible from the Frontend API. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ export class BillingSubscriptionItem { constructor( /** - * The unique identifier for the subscription item. + * The unique identifier for the Subscription Item. */ readonly id: string, /** - * The status of the subscription item. + * The status of the Subscription Item. */ readonly status: BillingSubscriptionItemJSON['status'], /** - * The Plan period for the subscription item. + * The period of the Plan associated with this Subscription Item. */ readonly planPeriod: 'month' | 'annual', /** - * Unix timestamp (milliseconds) of when the current period starts. + * The Unix timestamp (milliseconds) of when the current period starts. */ readonly periodStart: number, /** - * The next payment information. + * Information about the next scheduled payment for this Subscription Item. If present, contains the amount of the next payment and the Unix timestamp (milliseconds) of when the next payment is scheduled. */ readonly nextPayment: | { @@ -36,58 +36,58 @@ export class BillingSubscriptionItem { */ amount: number; /** - * Unix timestamp (milliseconds) of when the next payment is scheduled. + * The Unix timestamp (milliseconds) of when the next payment is scheduled. */ date: number; } | null | undefined, /** - * The current amount for the subscription item. + * The current amount for the Subscription Item. */ readonly amount: BillingMoneyAmount | undefined, /** - * The Plan associated with this subscription item. + * The Plan associated with this Subscription Item. */ readonly plan: BillingPlan | null, /** - * The Plan ID. + * The ID of the Plan associated with this Subscription Item. */ readonly planId: string | null, /** - * Unix timestamp (milliseconds) of when the subscription item was created. + * The Unix timestamp (milliseconds) of when the Subscription Item was created. */ readonly createdAt: number, /** - * Unix timestamp (milliseconds) of when the subscription item was last updated. + * The Unix timestamp (milliseconds) of when the Subscription Item was last updated. */ readonly updatedAt: number, /** - * Unix timestamp (milliseconds) of when the current period ends. + * The Unix timestamp (milliseconds) of when the current period ends. */ readonly periodEnd: number | null, /** - * Unix timestamp (milliseconds) of when the subscription item was canceled. + * The Unix timestamp (milliseconds) of when the Subscription Item was canceled. */ readonly canceledAt: number | null, /** - * Unix timestamp (milliseconds) of when the subscription item became past due. + * The Unix timestamp (milliseconds) of when the Subscription Item became past due. */ readonly pastDueAt: number | null, /** - * Unix timestamp (milliseconds) of when the subscription item ended. + * The Unix timestamp (milliseconds) of when the Subscription Item ended. */ readonly endedAt: number | null, /** - * The payer ID. + * The ID of the payer for this Subscription Item. */ readonly payerId: string | undefined, /** - * Whether this subscription item is currently in a free trial period. + * Whether this Subscription Item is currently in a free trial period. */ readonly isFreeTrial?: boolean, /** - * The lifetime amount paid for this subscription item. + * The lifetime amount paid for this Subscription Item. */ readonly lifetimePaid?: BillingMoneyAmount, ) {} diff --git a/packages/backend/src/api/resources/Domain.ts b/packages/backend/src/api/resources/Domain.ts index 684300c839b..e6a2dc78de5 100644 --- a/packages/backend/src/api/resources/Domain.ts +++ b/packages/backend/src/api/resources/Domain.ts @@ -1,15 +1,24 @@ import { CnameTarget } from './CnameTarget'; import type { DomainJSON } from './JSON'; +/** The `Domain` object represents a domain that is managed by the instance. */ export class Domain { constructor( + /** The unique identifier of the domain. */ readonly id: string, + /** The name of the domain. */ readonly name: string, + /** Whether the domain is a satellite domain. */ readonly isSatellite: boolean, + /** The Frontend API URL for the domain. */ readonly frontendApiUrl: string, + /** The development origin for the domain. */ readonly developmentOrigin: string, + /** The CNAME targets for the domain. */ readonly cnameTargets: CnameTarget[], + /** The [Account Portal](https://clerk.com/docs/guides/account-portal/overview) URL for the domain. */ readonly accountsPortalUrl?: string | null, + /** The [proxy URL](https://clerk.com/docs/guides/dashboard/dns-domains/proxy-fapi) for the domain. */ readonly proxyUrl?: string | null, ) {} From 64f24dd6aa79ca9ecf989bb4823a5687f4828b29 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 18 Jun 2026 08:09:42 -0600 Subject: [PATCH 10/18] docs review - user --- .typedoc/custom-plugin.mjs | 5 +++++ packages/backend/src/api/endpoints/UserApi.ts | 20 +++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 357551d7912..6f980c36181 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -73,6 +73,7 @@ const LINK_REPLACEMENTS = [ ['o-auth-consent-info', '/docs/reference/types/oauth-consent-info'], ['o-auth-consent-scope', '/docs/reference/types/oauth-consent-scope'], ['o-auth-strategy', '/docs/reference/types/sso#o-auth-strategy'], + ['o-auth-provider', '/docs/reference/types/sso#o-auth-provider'], ['session', '/docs/reference/backend/types/backend-session'], ['session-activity', '/docs/reference/backend/types/backend-session-activity'], ['organization', '/docs/reference/backend/types/backend-organization'], @@ -293,6 +294,10 @@ function getCatchAllReplacements() { pattern: /(?>; - public async getUserOauthAccessToken( - userId: string, - provider: OAuthProvider, - ): Promise>; /** * Gets the corresponding [OAuth access token](!oauth-access-token) for a user that has previously authenticated with the given OAuth provider. * @param userId - The ID of the user to get the OAuth access tokens for. * @param provider - The OAuth provider to get the access tokens for. If using a custom OAuth provider, prefix the provider name with `custom_` (e.g. `custom_google`). * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OauthAccessToken`](https://clerk.com/docs/reference/backend/types/backend-oauth-access-token) objects, and a `totalCount` property that indicates the total number of OAuth access tokens for the specified user and provider. */ + public async getUserOauthAccessToken( + userId: string, + provider: OAuthProvider, + ): Promise>; + /** @deprecated Use `getUserOauthAccessToken` without the `oauth_` provider prefix . */ + public async getUserOauthAccessToken( + userId: string, + provider: `oauth_${OAuthProvider}`, + ): Promise>; public async getUserOauthAccessToken(userId: string, provider: `oauth_${OAuthProvider}` | OAuthProvider) { this.requireId(userId); const hasPrefix = provider.startsWith('oauth_'); From 9cb19e57d61ee18ce927c3336ef60b42ece9b516 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 18 Jun 2026 12:42:59 -0600 Subject: [PATCH 11/18] docs review - domains, allowlist and billing --- packages/backend/src/api/endpoints/DomainApi.ts | 2 +- packages/backend/src/api/resources/AllowlistIdentifier.ts | 2 +- packages/backend/src/api/resources/CommercePlan.ts | 2 +- packages/backend/src/api/resources/CommerceSubscription.ts | 2 +- packages/backend/src/api/resources/CommerceSubscriptionItem.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/api/endpoints/DomainApi.ts b/packages/backend/src/api/endpoints/DomainApi.ts index da01715ae5a..cc13af045fa 100644 --- a/packages/backend/src/api/endpoints/DomainApi.ts +++ b/packages/backend/src/api/endpoints/DomainApi.ts @@ -82,7 +82,7 @@ export class DomainAPI extends AbstractAPI { * Deletes a satellite domain for the instance. * @param satelliteDomainId - The ID of the satellite domain to delete. * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object). - * @deprecated Use the [`delete()`](https://clerk.com/docs/reference/backend/domain/delete) method instead. + * @deprecated */ public async deleteDomain(satelliteDomainId: string) { this.requireId(satelliteDomainId); diff --git a/packages/backend/src/api/resources/AllowlistIdentifier.ts b/packages/backend/src/api/resources/AllowlistIdentifier.ts index 1b3bb00d704..f87a09566b9 100644 --- a/packages/backend/src/api/resources/AllowlistIdentifier.ts +++ b/packages/backend/src/api/resources/AllowlistIdentifier.ts @@ -2,7 +2,7 @@ import type { AllowlistIdentifierType } from './Enums'; import type { AllowlistIdentifierJSON } from './JSON'; /** - * The Backend `AllowlistIdentifier` object represents an identifier that has been added to the allowlist of your application. The Backend `AllowlistIdentifier` object is used in the [Backend API](https://clerk.com/docs/reference/backend-api/model/AllowlistIdentifier) and is not directly accessible from the Frontend API. + * The Backend `AllowlistIdentifier` object represents an identifier that has been added to the allowlist of your application. The Backend `AllowlistIdentifier` object is used in the [Backend API](https://clerk.com/docs/reference/backend-api/model/AllowlistIdentifier){{ target: '_blank' }} and is not directly accessible from the Frontend API. */ export class AllowlistIdentifier { constructor( diff --git a/packages/backend/src/api/resources/CommercePlan.ts b/packages/backend/src/api/resources/CommercePlan.ts index c9eaec436bf..058ff5760aa 100644 --- a/packages/backend/src/api/resources/CommercePlan.ts +++ b/packages/backend/src/api/resources/CommercePlan.ts @@ -4,7 +4,7 @@ import { Feature } from './Feature'; import type { BillingPlanJSON } from './JSON'; /** - * The `BillingPlan` object is similar to the [`BillingPlanResource`](https://clerk.com/docs/reference/types/billing-plan-resource) object as it holds information about a Plan, as well as methods for managing it. However, the `BillingPlan` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/plans) and is not directly accessible from the Frontend API. + * The `BillingPlan` object is similar to the [`BillingPlanResource`](https://clerk.com/docs/reference/types/billing-plan-resource) object as it holds information about a Plan, as well as methods for managing it. However, the `BillingPlan` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/plans){{ target: '_blank' }} and is not directly accessible from the Frontend API. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ diff --git a/packages/backend/src/api/resources/CommerceSubscription.ts b/packages/backend/src/api/resources/CommerceSubscription.ts index 6ceaa224655..732ad46fbb5 100644 --- a/packages/backend/src/api/resources/CommerceSubscription.ts +++ b/packages/backend/src/api/resources/CommerceSubscription.ts @@ -4,7 +4,7 @@ import { BillingSubscriptionItem } from './CommerceSubscriptionItem'; import type { BillingSubscriptionJSON } from './JSON'; /** - * The `BillingSubscription` object is similar to the [`BillingSubscriptionResource`](https://clerk.com/docs/reference/types/billing-subscription-resource) object as it holds information about a subscription, as well as methods for managing it. However, the `BillingSubscription` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/organizations/%7Borganization_id%7D/billing/subscription) and is not directly accessible from the Frontend API. + * The `BillingSubscription` object is similar to the [`BillingSubscriptionResource`](https://clerk.com/docs/reference/types/billing-subscription-resource) object as it holds information about a subscription, as well as methods for managing it. However, the `BillingSubscription` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/organizations/%7Borganization_id%7D/billing/subscription){{ target: '_blank' }} and is not directly accessible from the Frontend API. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ diff --git a/packages/backend/src/api/resources/CommerceSubscriptionItem.ts b/packages/backend/src/api/resources/CommerceSubscriptionItem.ts index 94f688c2436..b863892fe46 100644 --- a/packages/backend/src/api/resources/CommerceSubscriptionItem.ts +++ b/packages/backend/src/api/resources/CommerceSubscriptionItem.ts @@ -4,7 +4,7 @@ import { BillingPlan } from './CommercePlan'; import type { BillingSubscriptionItemJSON } from './JSON'; /** - * The `BillingSubscriptionItem` object is similar to the [`BillingSubscriptionItemResource`](https://clerk.com/docs/reference/types/billing-subscription-item-resource) object as it holds information about a subscription item, as well as methods for managing it. However, the `BillingSubscriptionItem` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/subscription_items) and is not directly accessible from the Frontend API. + * The `BillingSubscriptionItem` object is similar to the [`BillingSubscriptionItemResource`](https://clerk.com/docs/reference/types/billing-subscription-item-resource) object as it holds information about a subscription item, as well as methods for managing it. However, the `BillingSubscriptionItem` object is different in that it is used in the [Backend API](https://clerk.com/docs/reference/backend-api/tag/billing/GET/billing/subscription_items){{ target: '_blank' }} and is not directly accessible from the Frontend API. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ From 49556f84d16815fc653121386a0ee842d96c7712 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:30:07 -0700 Subject: [PATCH 12/18] apply Organization docs fixes --- .typedoc/custom-plugin.mjs | 4 + .typedoc/custom-theme.mjs | 85 ++++++++++++++- .typedoc/extract-methods.mjs | 102 +++++++++++++++++- .../src/api/endpoints/OrganizationApi.ts | 21 +--- packages/backend/src/api/resources/Enums.ts | 15 ++- .../src/api/resources/OrganizationDomain.ts | 2 +- .../backend/src/api/resources/Verification.ts | 2 +- 7 files changed, 199 insertions(+), 32 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 6f980c36181..4d5c1c69790 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -133,6 +133,10 @@ const LINK_REPLACEMENTS = [ 'organization-domain-verification', '/docs/reference/backend/types/backend-organization-domain#organization-domain-verification', ], + [ + 'create-organization-invitation-params', + '/docs/reference/backend/organization/create-organization-invitation#create-organization-invitation-params', + ], ]; /** diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index dc06bc65608..f353bdf7923 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -670,6 +670,74 @@ function renderPropertiesFormatTable(args) { : table(args.headers, args.rows, leftAlignHeadings); } +/** + * Resolve a property's type to the `@inline` class/interface declaration it ultimately points at, + * or `undefined` if the type isn't (or doesn't unwrap to) one. Unwraps `OptionalType` and a union + * arm — covers `T | null` / `T | undefined`. Used to decide whether to expand nested rows for a + * property whose type is rendered inline as `\{ … \}`. + * + * @param {import('typedoc').Type | undefined} t + */ +function getInlineClassOrInterfaceTarget(t) { + let bare = /** @type {import('typedoc').Type | undefined} */ (unwrapOptional(t)); + if (bare && bare.type === 'union') { + const u = /** @type {import('typedoc').UnionType} */ (bare); + bare = u.types.find(arm => { + if (arm.type !== 'reference') return false; + const refl = /** @type {import('typedoc').ReferenceType} */ (arm).reflection; + if (!refl || !isInlineModifierWithoutStandalonePage(refl)) return false; + return ( + /** @type {{ kindOf?: (k: number) => boolean }} */ (refl).kindOf?.(ReflectionKind.Class) || + /** @type {{ kindOf?: (k: number) => boolean }} */ (refl).kindOf?.(ReflectionKind.Interface) + ); + }); + } + if (!bare || bare.type !== 'reference') return undefined; + const decl = /** @type {import('typedoc').ReferenceType} */ (bare).reflection; + if (!decl) return undefined; + if (!isInlineModifierWithoutStandalonePage(decl)) return undefined; + const d = /** @type {import('typedoc').DeclarationReflection} */ (decl); + if (!d.kindOf(ReflectionKind.Class) && !d.kindOf(ReflectionKind.Interface)) return undefined; + return d; +} + +/** + * Append synthesized `parent.child` rows after each property whose type is an `@inline` class or + * interface (or `null | InlineClass`). Mirrors {@link appendUnionObjectChildPropertyRows} — the + * synthesized reflection inherits the child's `flags.isOptional` so the renderer appends `?` on + * its own, and uses `?.` as the separator when the parent is optional. + * + * @template {import('typedoc').DeclarationReflection} T + * @param {T[]} base + * @returns {T[]} + */ +function appendInlineObjectChildPropertyRows(base) { + /** @type {T[]} */ + const out = []; + for (const prop of base) { + out.push(prop); + if (prop.name.includes('.')) continue; + const target = getInlineClassOrInterfaceTarget(prop.type); + if (!target) continue; + const children = target.children?.filter(c => c.kindOf(ReflectionKind.Property)); + if (!children?.length) continue; + const sep = prop.flags?.isOptional ? '?.' : '.'; + for (const child of children) { + out.push( + /** @type {T} */ ( + /** @type {unknown} */ ({ + ...child, + name: `${prop.name}${sep}${child.name}`, + getFullName: () => prop.getFullName(), + getFriendlyFullName: () => prop.getFriendlyFullName(), + }) + ), + ); + } + } + return out; +} + /** * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` and then {@link appendUnionObjectChildPropertyRows} (union-object arm rows like `telemetry.*`). The default plugin skips flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. * @@ -1105,6 +1173,20 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { if (decl.kindOf(ReflectionKind.TypeAlias) && decl.type) { return removeLineBreaks(this.partials.someType(decl.type)); } + // Class or interface: there's no RHS to render, but the declaration's children describe the instance shape. Inline as a type-literal `\{ key: type; … \}` so use sites surface the same fields readers would see on the standalone page. Curly braces are escaped because MDX otherwise parses the literal as a JSX expression (and the object-literal bodies we emit aren't valid JS expressions). + if ((decl.kindOf(ReflectionKind.Class) || decl.kindOf(ReflectionKind.Interface)) && decl.children?.length) { + const props = decl.children.filter(c => c.kindOf(ReflectionKind.Property)); + if (props.length) { + const fields = props + .map(p => { + const sep = p.flags?.isOptional ? '?:' : ':'; + const typeMd = p.type ? this.partials.someType(p.type) : '`unknown`'; + return `${p.name}${sep} ${typeMd};`; + }) + .join(' '); + return removeLineBreaks(`\\{ ${fields} \\}`); + } + } return backTicks(decl.name); } return superPartials.referenceType.call(this, model); @@ -1128,7 +1210,8 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { prop => !isCallableInterfaceProperty(prop, this.helpers) && !prop.comment?.hasModifier('@extractMethods'), ) : model; - return superPartials.propertiesTable(filtered, options); + const expanded = appendInlineObjectChildPropertyRows(filtered); + return superPartials.propertiesTable(expanded, options); }, /** * Parameter tables: same as the stock partial except single-property inline object params are not expanded to nested rows. diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 725e73d6e12..63d32c02bd4 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -380,6 +380,49 @@ function substituteGenericParamRefsInType(t, map) { return t; } +/** + * Resolve a property's type to its declared default when it references a `TypeParameter` reflection. + * Used when a generic alias is inlined into an intersection (e.g. `CreateParams = { … } & MetadataParams` where the `& MetadataParams` arm gets inlined as a reflection, losing the named reference) so the property table surfaces the resolved default type instead of the open type-parameter name (e.g. `TPublic` → `OrganizationPublicMetadata`). + * + * @param {import('typedoc').Type | undefined} t + * @returns {import('typedoc').Type | undefined} + */ +function substituteTypeParameterDefaultsInType(t) { + if (!t) { + return t; + } + if (/** @type {{ type?: string }} */ (t).type === 'optional' && 'elementType' in t) { + const el = /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + const next = substituteTypeParameterDefaultsInType(el); + if (next && next !== el) { + return new OptionalType(/** @type {import('typedoc').SomeType} */ (/** @type {unknown} */ (next))); + } + return t; + } + if (t instanceof ReferenceType) { + const tp = /** @type {{ kindOf?: (k: number) => boolean, default?: import('typedoc').Type }} */ (t.reflection); + if (tp?.kindOf?.(ReflectionKind.TypeParameter) && tp.default) { + return tp.default; + } + } + return t; +} + +/** + * Clone each property and apply `substituteTypeParameterDefaultsInType` to its type — see comment on `substituteTypeParameterDefaultsInType` for the motivating case. + * + * @param {import('typedoc').DeclarationReflection[]} children + */ +function substituteTypeParameterDefaultsInChildren(children) { + return children.map(child => { + const next = substituteTypeParameterDefaultsInType(child.type); + if (next === child.type) { + return child; + } + return Object.assign(Object.create(Object.getPrototypeOf(child)), child, { type: next }); + }); +} + /** * @param {import('typedoc').SignatureReflection} sig * @param {Map | undefined} instantiationMap @@ -735,6 +778,38 @@ function pickBetterUnionPropertyCandidate(existing, candidate) { return candidateDoc > existingDoc ? candidate : existing; } +/** + * Collect the set of string-literal keys from a type used as the second argument to `Omit` or + * `Pick` — a single literal (`'organizationId'`) or a union of literals (`'a' | 'b'`). Returns + * `undefined` if the type isn't a literal/literal-union (e.g. a `keyof` reference we can't resolve + * here), in which case callers should fall through to the generic-instantiation path. + * + * @param {import('typedoc').Type | undefined} t + * @returns {Set | undefined} + */ +function collectLiteralStringKeys(t) { + if (!t) { + return undefined; + } + if (t.type === 'literal' && typeof (/** @type {{ value: unknown }} */ (t).value) === 'string') { + return new Set([/** @type {{ value: string }} */ (t).value]); + } + if (t.type === 'union') { + const u = /** @type {import('typedoc').UnionType} */ (t); + /** @type {Set} */ + const keys = new Set(); + for (const inner of u.types) { + const got = collectLiteralStringKeys(inner); + if (!got) { + return undefined; + } + for (const k of got) keys.add(k); + } + return keys.size ? keys : undefined; + } + return undefined; +} + /** * Filter each arm to `Property` reflections and dedupe by name, returning a single sorted list * (or `undefined` if every arm was empty). Used for intersection / union / generic-instantiation @@ -774,7 +849,8 @@ function mergePropertyArms(arms, options) { if (byName.size === 0) { return undefined; } - return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); + const merged = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); + return substituteTypeParameterDefaultsInChildren(merged); } /** @@ -815,6 +891,22 @@ function resolveDeclarationWithObjectMembers(t, project) { } if (t.type === 'reference') { const ref = /** @type {import('typedoc').ReferenceType} */ (t); + /** + * `Omit` / `Pick` — TypeScript built-in utilities. They have no project reflection + * to look up, and falling through to the generic-instantiation path below would merge K's + * properties (zero, since K is a literal type) without applying the filter — Omit/Pick would + * silently behave like an identity. Resolve `X` to its property list, then keep/drop by the + * literal-string keys in `K`. Without this, `Array>` shows no nested rows because + * `decl` is undefined. + */ + if ((ref.name === 'Omit' || ref.name === 'Pick') && (ref.typeArguments?.length ?? 0) === 2) { + const baseChildren = resolveDeclarationWithObjectMembers(ref.typeArguments[0], project); + const keys = collectLiteralStringKeys(ref.typeArguments[1]); + if (baseChildren?.length && keys) { + const keep = ref.name === 'Pick' ? c => keys.has(c.name) : c => !keys.has(c.name); + return baseChildren.filter(keep); + } + } let decl = ref.reflection && 'kind' in ref.reflection ? /** @type {import('typedoc').DeclarationReflection} */ (ref.reflection) @@ -839,7 +931,7 @@ function resolveDeclarationWithObjectMembers(t, project) { return mergePropertyArms([base, ...argArms]); } if (decl.children?.length) { - return decl.children; + return substituteTypeParameterDefaultsInChildren(decl.children); } if (decl.type) { return resolveDeclarationWithObjectMembers(decl.type, project); @@ -884,6 +976,12 @@ function parameterTypeLinksToStandaloneMdxPage(t, ctx) { if (!bare) { return false; } + // `Array`: the standalone page documents the element shape, not the array signature. + // Surface both — the Type column links to the element page, and nested `params.` rows + // flatten the element so readers don't have to navigate just to see field names. + if (bare.type === 'array') { + return false; + } if (bare.type === 'reference') { const ref = /** @type {import('typedoc').ReferenceType} */ (bare); if (isInlineModifierWithoutStandalonePage(ref.reflection)) { diff --git a/packages/backend/src/api/endpoints/OrganizationApi.ts b/packages/backend/src/api/endpoints/OrganizationApi.ts index 113c90227ad..e0b716cd95f 100644 --- a/packages/backend/src/api/endpoints/OrganizationApi.ts +++ b/packages/backend/src/api/endpoints/OrganizationApi.ts @@ -17,7 +17,7 @@ import type { WithSign } from './util-types'; const basePath = '/organizations'; -/** @generateWithEmptyComment */ +/** @inline */ export type MetadataParams = { /** Metadata that can be read from the Frontend API and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. */ publicMetadata?: TPublic; @@ -212,22 +212,7 @@ export type CreateOrganizationInvitationParams = { }; /** @inline */ -export type CreateBulkOrganizationInvitationParams = Array<{ - /** The email address of the user being invited. */ - emailAddress: string; - /** The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) to assign to the user. */ - role: OrganizationMembershipRole; - /** The number of days until the invitation expires. Defaults to `30`. */ - expiresInDays?: number; - /** The ID of the user creating the invitation. */ - inviterUserId?: string; - /** Metadata that can be read and set only from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}. */ - privateMetadata?: OrganizationInvitationPrivateMetadata; - /** Metadata that can be read from the Frontend API and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. */ - publicMetadata?: OrganizationInvitationPublicMetadata; - /** The full URL or path where the user will land after accepting the invitation. */ - redirectUrl?: string; -}>; +export type CreateBulkOrganizationInvitationParams = Array>; /** @generateWithEmptyComment */ export type GetOrganizationInvitationListParams = ClerkPaginationRequest<{ @@ -274,7 +259,7 @@ export type CreateOrganizationDomainParams = { /** The enrollment mode that determines how matching users are added to the Organization. * *
        - *
      • `manual_invitation`: No automatic enrollment. Users with a matching email domain are not given any [invitation](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-invitations) or [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions); an admin must invite them manually.
      • + *
      • `manual_invitation`: No automatic enrollment. Users with a matching email domain are not given any [invitation](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-invitations) or [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions); an [admin](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions#default-roles) must invite them manually.
      • *
      • `automatic_invitation`: Users with a matching email domain automatically receive a pending [invitation](https://clerk.com/docs/reference/types/organization-invitation) (assigned the Organization's default role) which they can accept to join.
      • *
      • `automatic_suggestion`: Users with a matching email domain automatically receive a [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions) to join, which they can request.
      • *
      diff --git a/packages/backend/src/api/resources/Enums.ts b/packages/backend/src/api/resources/Enums.ts index 0604553e24f..3a5c883e425 100644 --- a/packages/backend/src/api/resources/Enums.ts +++ b/packages/backend/src/api/resources/Enums.ts @@ -24,10 +24,13 @@ export type OAuthStrategy = `oauth_${OAuthProvider}`; /** @inline */ export type OrganizationInvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired'; +/** @inline */ export type OrganizationDomainVerificationStatus = 'unverified' | 'verified'; +/** @inline */ export type OrganizationDomainVerificationStrategy = 'email_code'; // only available value for now +/** @inline */ export type OrganizationEnrollmentMode = 'manual_invitation' | 'automatic_invitation' | 'automatic_suggestion'; /** @inline */ @@ -56,17 +59,11 @@ export const ActorTokenStatus = { } as const; export type ActorTokenStatus = (typeof ActorTokenStatus)[keyof typeof ActorTokenStatus]; -/** - * @inline - */ +/** @inline */ export type AllowlistIdentifierType = 'email_address' | 'phone_number' | 'web3_wallet'; -/** - * @inline - */ +/** @inline */ export type BlocklistIdentifierType = AllowlistIdentifierType; -/** - * @inline - */ +/** @inline */ export type WaitlistEntryStatus = 'pending' | 'invited' | 'completed' | 'rejected'; diff --git a/packages/backend/src/api/resources/OrganizationDomain.ts b/packages/backend/src/api/resources/OrganizationDomain.ts index 8401e2a1112..eeda091950b 100644 --- a/packages/backend/src/api/resources/OrganizationDomain.ts +++ b/packages/backend/src/api/resources/OrganizationDomain.ts @@ -15,7 +15,7 @@ export class OrganizationDomain { * The enrollment mode that determines how matching users are added to the Organization. * *
        - *
      • `manual_invitation`: No automatic enrollment. Users with a matching email domain are not given any [invitation](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-invitations) or [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions); an admin must invite them manually.
      • + *
      • `manual_invitation`: No automatic enrollment. Users with a matching email domain are not given any [invitation](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-invitations) or [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions); an [admin](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions#default-roles) must invite them manually.
      • *
      • `automatic_invitation`: Users with a matching email domain automatically receive a pending [invitation](https://clerk.com/docs/reference/types/organization-invitation) (assigned the Organization's default role) which they can accept to join.
      • *
      • `automatic_suggestion`: Users with a matching email domain automatically receive a [suggestion](https://clerk.com/docs/guides/organizations/add-members/verified-domains#automatic-suggestions) to join, which they can request.
      • *
      diff --git a/packages/backend/src/api/resources/Verification.ts b/packages/backend/src/api/resources/Verification.ts index d87b19a59d7..6dd0e750c4b 100644 --- a/packages/backend/src/api/resources/Verification.ts +++ b/packages/backend/src/api/resources/Verification.ts @@ -57,7 +57,7 @@ export class Verification { } } -/** @generateWithEmptyComment */ +/** @inline */ export class OrganizationDomainVerification { constructor( /** The current status of the verification. */ From c385aa930f7f67d65702e50b9c5842dac3918ef6 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:33:09 -0700 Subject: [PATCH 13/18] custom_google --> oauth_custom_google --- packages/backend/src/api/endpoints/UserApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index 9fb6d1111f4..a12e3e75a6a 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -632,7 +632,7 @@ export class UserAPI extends AbstractAPI { /** * Gets the corresponding [OAuth access token](!oauth-access-token) for a user that has previously authenticated with the given OAuth provider. * @param userId - The ID of the user to get the OAuth access tokens for. - * @param provider - The OAuth provider to get the access tokens for. If using a custom OAuth provider, prefix the provider name with `custom_` (e.g. `custom_google`). + * @param provider - The OAuth provider to get the access tokens for. If using a custom OAuth provider, prefix the provider name with `custom_` (e.g. `oauth_custom_google`). * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property than contains an array of [`OauthAccessToken`](https://clerk.com/docs/reference/backend/types/backend-oauth-access-token) objects, and a `totalCount` property that indicates the total number of OAuth access tokens for the specified user and provider. */ public async getUserOauthAccessToken( From 7007c436343dcd2e459a04e896e1c7a5c97b872e Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:37:40 -0700 Subject: [PATCH 14/18] update CreateAllowlistIdentifierParams --- packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts b/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts index 0931d07dfaf..c548626fd40 100644 --- a/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts +++ b/packages/backend/src/api/endpoints/AllowlistIdentifierApi.ts @@ -10,9 +10,9 @@ const basePath = '/allowlist_identifiers'; /** @generateWithEmptyComment */ export type AllowlistIdentifierCreateParams = { - /** The identifier to add to the allowlist. */ + /** The identifier to add to the allowlist. Can be an email address, a domain in wildcard email format (e.g. `*@example.com`), a phone number in international E.164 format (e.g. `+15555555555`), or a Web3 wallet address. */ identifier: string; - /** Whether to notify the user that their identifier has been added to the allowlist. */ + /** Whether to notify the user that their identifier has been added to the allowlist. Notifies the user if the `identifier` is an email address or phone number. Defaults to `true`. */ notify: boolean; }; From 002391aa97bd2ae3dcf2db5108677ad5c6a000db Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:44:24 -0700 Subject: [PATCH 15/18] clean up comment formatting --- .typedoc/custom-theme.mjs | 10 ++-------- .typedoc/extract-methods.mjs | 15 +++------------ 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index f353bdf7923..d7dfcb04209 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -671,10 +671,7 @@ function renderPropertiesFormatTable(args) { } /** - * Resolve a property's type to the `@inline` class/interface declaration it ultimately points at, - * or `undefined` if the type isn't (or doesn't unwrap to) one. Unwraps `OptionalType` and a union - * arm — covers `T | null` / `T | undefined`. Used to decide whether to expand nested rows for a - * property whose type is rendered inline as `\{ … \}`. + * Resolve a property's type to the `@inline` class/interface declaration it ultimately points at, or `undefined` if the type isn't (or doesn't unwrap to) one. Unwraps `OptionalType` and a union arm — covers `T | null` / `T | undefined`. Used to decide whether to expand nested rows for a property whose type is rendered inline as `\{ … \}`. * * @param {import('typedoc').Type | undefined} t */ @@ -702,10 +699,7 @@ function getInlineClassOrInterfaceTarget(t) { } /** - * Append synthesized `parent.child` rows after each property whose type is an `@inline` class or - * interface (or `null | InlineClass`). Mirrors {@link appendUnionObjectChildPropertyRows} — the - * synthesized reflection inherits the child's `flags.isOptional` so the renderer appends `?` on - * its own, and uses `?.` as the separator when the parent is optional. + * Append synthesized `parent.child` rows after each property whose type is an `@inline` class or interface (or `null | InlineClass`). Mirrors {@link appendUnionObjectChildPropertyRows} — the synthesized reflection inherits the child's `flags.isOptional` so the renderer appends `?` on its own, and uses `?.` as the separator when the parent is optional. * * @template {import('typedoc').DeclarationReflection} T * @param {T[]} base diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 63d32c02bd4..ea089142434 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -779,10 +779,7 @@ function pickBetterUnionPropertyCandidate(existing, candidate) { } /** - * Collect the set of string-literal keys from a type used as the second argument to `Omit` or - * `Pick` — a single literal (`'organizationId'`) or a union of literals (`'a' | 'b'`). Returns - * `undefined` if the type isn't a literal/literal-union (e.g. a `keyof` reference we can't resolve - * here), in which case callers should fall through to the generic-instantiation path. + * Collect the set of string-literal keys from a type used as the second argument to `Omit` or `Pick` — a single literal (`'organizationId'`) or a union of literals (`'a' | 'b'`). Returns `undefined` if the type isn't a literal/literal-union (e.g. a `keyof` reference we can't resolve here), in which case callers should fall through to the generic-instantiation path. * * @param {import('typedoc').Type | undefined} t * @returns {Set | undefined} @@ -892,12 +889,7 @@ function resolveDeclarationWithObjectMembers(t, project) { if (t.type === 'reference') { const ref = /** @type {import('typedoc').ReferenceType} */ (t); /** - * `Omit` / `Pick` — TypeScript built-in utilities. They have no project reflection - * to look up, and falling through to the generic-instantiation path below would merge K's - * properties (zero, since K is a literal type) without applying the filter — Omit/Pick would - * silently behave like an identity. Resolve `X` to its property list, then keep/drop by the - * literal-string keys in `K`. Without this, `Array>` shows no nested rows because - * `decl` is undefined. + * `Omit` / `Pick` — TypeScript built-in utilities. They have no project reflection to look up, and falling through to the generic-instantiation path below would merge K's properties (zero, since K is a literal type) without applying the filter — Omit/Pick would silently behave like an identity. Resolve `X` to its property list, then keep/drop by the literal-string keys in `K`. Without this, `Array>` shows no nested rows because `decl` is undefined. */ if ((ref.name === 'Omit' || ref.name === 'Pick') && (ref.typeArguments?.length ?? 0) === 2) { const baseChildren = resolveDeclarationWithObjectMembers(ref.typeArguments[0], project); @@ -977,8 +969,7 @@ function parameterTypeLinksToStandaloneMdxPage(t, ctx) { return false; } // `Array`: the standalone page documents the element shape, not the array signature. - // Surface both — the Type column links to the element page, and nested `params.` rows - // flatten the element so readers don't have to navigate just to see field names. + // Surface both — the Type column links to the element page, and nested `params.` rows flatten the element so readers don't have to navigate just to see field names. if (bare.type === 'array') { return false; } From f89143b12ac5afc6e9d55a71b0309f49272fd822 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 18 Jun 2026 14:03:50 -0700 Subject: [PATCH 16/18] fix deprecation tag missing from params table; add back deprecation text to deleteDomain --- .typedoc/extract-methods.mjs | 18 ++++++++++++++++-- .../backend/src/api/endpoints/DomainApi.ts | 2 +- packages/backend/src/api/endpoints/UserApi.ts | 18 +++--------------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index ea089142434..731606fcd42 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -100,6 +100,21 @@ function codeDisplayPartToMarkdown(text) { return `\`${text}\``; } +/** + * Build the description cell for a nested `param.field` row: summary text plus a leading `**Deprecated.**` marker (with the `@deprecated` tag's body) when that tag is present. Matches what typedoc-plugin-markdown's `parseParams` emits for inline `{ … }` params — fields documented only via `@deprecated` (no summary) otherwise rendered as `—` here, hiding important guidance. + * Returns `'—'` when both summary and `@deprecated` are empty. + * + * @param {import('typedoc').Comment | undefined} comment + */ +function renderNestedRowDescription(comment) { + const summaryText = comment?.summary?.length ? displayPartsToString(comment.summary).trim() : ''; + const deprecatedTag = comment?.blockTags?.find(t => t.tag === '@deprecated'); + const deprecatedText = deprecatedTag ? displayPartsToString(deprecatedTag.content).trim() : ''; + const deprecatedMd = deprecatedTag ? `**Deprecated.**${deprecatedText ? ` ${deprecatedText}` : ''}` : ''; + const combined = [summaryText, deprecatedMd].filter(Boolean).join(' '); + return combined || '—'; +} + /** * @param {import('typedoc').CommentDisplayPart[] | undefined} parts */ @@ -1014,10 +1029,9 @@ function nestedParameterRowsFromDocumentedProperties(param, ctx) { /** @type {string[]} */ const rows = []; for (const child of props) { - const summary = child.comment?.summary; const typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; const nestedNameCol = formatNestedParamNameColumn(param, child); - const nestedDescRaw = summary?.length ? displayPartsToString(summary).trim() || '—' : '—'; + const nestedDescRaw = renderNestedRowDescription(child.comment); // Strip line breaks so multi-line `
        ` / paragraph descriptions don't shatter the markdown // table row (which must be a single line). Matches the treatment already applied to typeCell. const nestedDesc = removeLineBreaksForTableCell(nestedDescRaw) ?? '—'; diff --git a/packages/backend/src/api/endpoints/DomainApi.ts b/packages/backend/src/api/endpoints/DomainApi.ts index cc13af045fa..bdd61b47995 100644 --- a/packages/backend/src/api/endpoints/DomainApi.ts +++ b/packages/backend/src/api/endpoints/DomainApi.ts @@ -82,7 +82,7 @@ export class DomainAPI extends AbstractAPI { * Deletes a satellite domain for the instance. * @param satelliteDomainId - The ID of the satellite domain to delete. * @returns The [`DeletedObject`](https://clerk.com/docs/reference/backend/types/deleted-object). - * @deprecated + * @deprecated Use `delete()` instead. */ public async deleteDomain(satelliteDomainId: string) { this.requireId(satelliteDomainId); diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index a12e3e75a6a..1206801646f 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -327,27 +327,15 @@ export type UpdateUserParams = { /** The maximum number of Organizations the user can create. `0` means unlimited. */ createOrganizationsLimit?: number; /** - * Metadata visible to your Frontend and Backend APIs. - * - * @deprecated Updating metadata via `updateUser()` is deprecated. Use - * [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or - * [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. + * @deprecated Updating metadata via `updateUser()` is deprecated. Use [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. */ publicMetadata?: UserPublicMetadata; /** - * Metadata visible only to your Backend API. - * - * @deprecated Updating metadata via `updateUser()` is deprecated. Use - * [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or - * [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. + * @deprecated Updating metadata via `updateUser()` is deprecated. Use [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. */ privateMetadata?: UserPrivateMetadata; /** - * Metadata writeable from both the Frontend and Backend APIs. - * - * @deprecated Updating metadata via `updateUser()` is deprecated. Use - * [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or - * [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. + * @deprecated Updating metadata via `updateUser()` is deprecated. Use [`updateUserMetadata()`](https://clerk.com/docs/reference/backend/user/update-user-metadata) for partial updates (deep merge) or [`replaceUserMetadata()`](https://clerk.com/docs/reference/backend/user/replace-user-metadata) for full replacement. */ unsafeMetadata?: UserUnsafeMetadata; } & (UserPasswordHashingParams | object); From 88e40b857ae68edc46c6312a6d642e0718e27962 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 18 Jun 2026 14:23:34 -0700 Subject: [PATCH 17/18] remove organization-domain-resource from custom plugin --- .typedoc/custom-plugin.mjs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 4d5c1c69790..9d516a916f5 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -129,10 +129,6 @@ const LINK_REPLACEMENTS = [ ['session-task', '/docs/reference/types/session-task'], ['public-user-data', '/docs/reference/types/public-user-data'], ['session-status', '/docs/reference/types/session-status'], - [ - 'organization-domain-verification', - '/docs/reference/backend/types/backend-organization-domain#organization-domain-verification', - ], [ 'create-organization-invitation-params', '/docs/reference/backend/organization/create-organization-invitation#create-organization-invitation-params', From 6127d230986f97a50e3d1c04eedb6ae6173a3ca9 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 18 Jun 2026 16:59:46 -0700 Subject: [PATCH 18/18] Document Session, Client, Invitation API --- .typedoc/__tests__/file-structure.test.ts | 6 ++ .typedoc/reference-objects.mjs | 12 ++++ .../backend/src/api/endpoints/ClientApi.ts | 13 ++++ .../src/api/endpoints/InvitationApi.ts | 54 +++++++++++---- .../backend/src/api/endpoints/SessionApi.ts | 65 +++++++++++++++---- packages/backend/src/api/resources/Enums.ts | 4 +- 6 files changed, 127 insertions(+), 27 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index ee62fd29c34..49c3511ade4 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -52,10 +52,16 @@ describe('Typedoc output', () => { "backend/allowlist-identifier-api/methods", "backend/billing-api", "backend/billing-api/methods", + "backend/client-api", + "backend/client-api/methods", "backend/domain-api", "backend/domain-api/methods", + "backend/invitation-api", + "backend/invitation-api/methods", "backend/organization-api", "backend/organization-api/methods", + "backend/session-api", + "backend/session-api/methods", "backend/user-api", "backend/user-api/methods", "react/legacy", diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index c5ae49cf4cc..cc86fd340f7 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -80,6 +80,18 @@ export const BACKEND_API_CONFIG = { symbol: 'DomainAPI', declarationHint: 'api/endpoints/DomainApi', }, + 'backend/session-api/session-api.mdx': { + symbol: 'SessionAPI', + declarationHint: 'api/endpoints/SessionApi', + }, + 'backend/client-api/client-api.mdx': { + symbol: 'ClientAPI', + declarationHint: 'api/endpoints/ClientApi', + }, + 'backend/invitation-api/invitation-api.mdx': { + symbol: 'InvitationAPI', + declarationHint: 'api/endpoints/InvitationApi', + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG} then {@link BACKEND_API_CONFIG}. */ diff --git a/packages/backend/src/api/endpoints/ClientApi.ts b/packages/backend/src/api/endpoints/ClientApi.ts index 13d313cf449..3d9633d162c 100644 --- a/packages/backend/src/api/endpoints/ClientApi.ts +++ b/packages/backend/src/api/endpoints/ClientApi.ts @@ -12,7 +12,11 @@ type GetHandshakePayloadParams = { nonce: string; }; +/** @generateWithEmptyComment */ export class ClientAPI extends AbstractAPI { + /** + * @deprecated This method is deprecated and will be removed in a future version. + */ public async getClientList(params: ClerkPaginationRequest = {}) { return this.request>({ method: 'GET', @@ -21,6 +25,10 @@ export class ClientAPI extends AbstractAPI { }); } + /** + * Gets the given [`Client`](https://clerk.com/docs/reference/backend/types/backend-client). The clients are returned sorted by creation date, with the newest clients appearing first. + * @param clientId - The ID of the client to get. + */ public async getClient(clientId: string) { this.requireId(clientId); return this.request({ @@ -29,6 +37,11 @@ export class ClientAPI extends AbstractAPI { }); } + /** + * Verifies the client in the given token. + * @param token - The token to verify. + * @returns The verified [`Client`](https://clerk.com/docs/reference/backend/types/backend-client). + */ public verifyClient(token: string) { return this.request({ method: 'POST', diff --git a/packages/backend/src/api/endpoints/InvitationApi.ts b/packages/backend/src/api/endpoints/InvitationApi.ts index ade0e5b6671..e30b0ce3a2f 100644 --- a/packages/backend/src/api/endpoints/InvitationApi.ts +++ b/packages/backend/src/api/endpoints/InvitationApi.ts @@ -9,30 +9,34 @@ import type { WithSign } from './util-types'; const basePath = '/invitations'; -type TemplateSlug = 'invitation' | 'waitlist_invitation'; +/** @inline */ +export type TemplateSlug = 'invitation' | 'waitlist_invitation'; -type CreateParams = { +/** @generateWithEmptyComment */ +export type CreateParams = { + /** The email address of the user to invite. */ emailAddress: string; + /** The number of days until the invitation expires. Defaults to `30`. */ expiresInDays?: number; + /** Whether an invitation should be created if there is already an existing invitation for this email address, or if the email address already exists in the application. Defaults to `false`. */ ignoreExisting?: boolean; + /** Whether an email invitation should be sent to the given email address. Defaults to `true`. */ notify?: boolean; + /** Metadata that can be read and set only from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}. Once the user accepts the invitation and signs up, these metadata will end up in the user's public metadata ([`User.publicMetadata`](https://clerk.com/docs/reference/backend/types/backend-user)). */ publicMetadata?: UserPublicMetadata; + /** The full URL or path where the user will land after accepting the invitation. See the [custom flow guide for handling application invitations](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations). */ redirectUrl?: string; + /** The template slug to use for the invitation. Defaults to `invitation`. */ templateSlug?: TemplateSlug; }; -type CreateBulkParams = Array; +/** @generateWithEmptyComment */ +export type CreateBulkParams = Array; -type GetInvitationListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type GetInvitationListParams = ClerkPaginationRequest<{ /** - * Orders the returned invitations by a specific field and direction. - * - * Use a leading '-' for descending order, or no sign/'+' for ascending. - * - * Supported fields: - * - 'created_at' — when the invitation was created - * - 'email_address' — recipient email address - * - 'expires_at' — when the invitation expires + * Filters the invitations in a particular order. Prefix a value with `+` to sort in ascending order, or `-` to sort in descending order. Defaults to `-created_at`. * * @example * ```ts @@ -70,7 +74,12 @@ type GetInvitationListParams = ClerkPaginationRequest<{ query?: string; }>; +/** @generateWithEmptyComment */ export class InvitationAPI extends AbstractAPI { + /** + * Gets a list of non-revoked invitations for the instance, sorted by descending creation date. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`Invitation`](https://clerk.com/docs/reference/backend/types/backend-invitation) objects and a `totalCount` property containing the total number of invitations. + */ public async getInvitationList(params: GetInvitationListParams = {}) { return this.request>({ method: 'GET', @@ -79,6 +88,12 @@ export class InvitationAPI extends AbstractAPI { }); } + /** + * Creates a new invitation for the given email address, and sends the invitation email. + * + * If an email address has already been invited or already exists in your application, trying to create a new invitation will return an error. To bypass this error and create a new invitation anyways, set `ignoreExisting` to `true`. + * @returns The newly created [`Invitation`](https://clerk.com/docs/reference/backend/types/backend-invitation). + */ public async createInvitation(params: CreateParams) { return this.request({ method: 'POST', @@ -87,6 +102,12 @@ export class InvitationAPI extends AbstractAPI { }); } + /** + * Creates multiple invitations for the given email addresses, and sends the invitation emails. + * + * If an email address has already been invited or already exists in your application, trying to create a new invitation will return an error. To bypass this error and create a new invitation anyways, set `ignoreExisting` to `true`. + * @returns An array of each created [`Invitation`](https://clerk.com/docs/reference/backend/types/backend-invitation) object. + */ public async createInvitationBulk(params: CreateBulkParams) { return this.request({ method: 'POST', @@ -95,6 +116,15 @@ export class InvitationAPI extends AbstractAPI { }); } + /** + * Revokes the given invitation. + * + * Revoking an an invitation makes the invitation email link unusable. However, it doesn't prevent the user from signing up if they follow the sign up flow. + * + * Only active (i.e. non-revoked) invitations can be revoked. + * @param invitationId - The ID of the invitation to revoke. + * @returns The revoked [`Invitation`](https://clerk.com/docs/reference/backend/types/backend-invitation). + */ public async revokeInvitation(invitationId: string) { this.requireId(invitationId); return this.request({ diff --git a/packages/backend/src/api/endpoints/SessionApi.ts b/packages/backend/src/api/endpoints/SessionApi.ts index 170408ea5ac..ae4351b70ae 100644 --- a/packages/backend/src/api/endpoints/SessionApi.ts +++ b/packages/backend/src/api/endpoints/SessionApi.ts @@ -9,27 +9,46 @@ import { AbstractAPI } from './AbstractApi'; const basePath = '/sessions'; -type SessionListParams = ClerkPaginationRequest<{ +/** @generateWithEmptyComment */ +export type SessionListParams = ClerkPaginationRequest<{ + /** The ID of the client to get sessions for. */ clientId?: string; + /** The ID of the user to get sessions for. */ userId?: string; + /** The status of the sessions to get. */ status?: SessionStatus; }>; -type RefreshTokenParams = { +/** @inline */ +export type RefreshTokenParams = { + /** The expired token to refresh. */ expired_token: string; + /** The refresh token to refresh. */ refresh_token: string; + /** The origin of the request. */ request_origin: string; + /** The originating IP address of the request. */ request_originating_ip?: string; + /** The headers of the request. */ request_headers?: Record; + /** Whether to use suffixed cookies. */ suffixed_cookies?: boolean; + /** The format of the token to refresh. */ format?: 'token' | 'cookie'; }; -type CreateSessionParams = { +/** @generateWithEmptyComment */ +export type CreateSessionParams = { + /** The ID of the user to create a session for. */ userId: string; }; +/** @generateWithEmptyComment */ export class SessionAPI extends AbstractAPI { + /** + * Gets a list of sessions for either the specified client or user. Requires either `clientId` or `userId` to be provided. + * @returns A [`PaginatedResourceResponse`](https://clerk.com/docs/reference/backend/types/paginated-resource-response) object with a `data` property containing an array of [`Session`](https://clerk.com/docs/reference/backend/types/backend-session) objects and a `totalCount` property containing the total number of sessions. + */ public async getSessionList(params: SessionListParams = {}) { return this.request>({ method: 'GET', @@ -38,6 +57,10 @@ export class SessionAPI extends AbstractAPI { }); } + /** + * Gets the given [`Session`](https://clerk.com/docs/reference/backend/types/backend-session). + * @param sessionId - The ID of the session to get. + */ public async getSession(sessionId: string) { this.requireId(sessionId); return this.request({ @@ -46,6 +69,10 @@ export class SessionAPI extends AbstractAPI { }); } + /** + * Creates a new session for the given user. + * @returns The created [`Session`](https://clerk.com/docs/reference/backend/types/backend-session). + */ public async createSession(params: CreateSessionParams) { return this.request({ method: 'POST', @@ -54,6 +81,11 @@ export class SessionAPI extends AbstractAPI { }); } + /** + * Revokes the given session. The user will be signed out from the client the session is associated with. + * @param sessionId - The ID of the session to revoke. + * @returns The revoked [`Session`](https://clerk.com/docs/reference/backend/types/backend-session). + */ public async revokeSession(sessionId: string) { this.requireId(sessionId); return this.request({ @@ -72,16 +104,13 @@ export class SessionAPI extends AbstractAPI { } /** - * Gets a session token or generates a JWT using a specified template. - * - * @param sessionId - The ID of the session for which to generate the token - * @param template - The name of the JWT template configured in the Clerk Dashboard. - * @param expiresInSeconds - The expiration time for the token in seconds. - * If not provided, uses the default expiration. + * Gets a session token or generates a JWT using a specified template that is defined in the [**JWT templates**](https://dashboard.clerk.com/~/jwt-templates) page in the Clerk Dashboard. * - * @returns A promise that resolves to the generated token + * @param sessionId - The ID of the session to get the token for. + * @param template - The name of the JWT template configured in the Clerk Dashboard to generate a new token from. + * @param expiresInSeconds - The expiration time for the token in seconds. If not provided, uses the default expiration. * - * @throws {Error} When sessionId is invalid or empty + * @returns The generated token. */ public async getToken(sessionId: string, template?: string, expiresInSeconds?: number) { this.requireId(sessionId); @@ -102,7 +131,19 @@ export class SessionAPI extends AbstractAPI { return this.request(requestOptions); } - public async refreshSession(sessionId: string, params: RefreshTokenParams & { format: 'token' }): Promise; + /** + * Refreshes the given session. + * @param sessionId - The ID of the session to refresh. + * @param params - The parameters to refresh the session. + * @returns The refreshed token. + */ + public async refreshSession( + sessionId: string, + params: RefreshTokenParams & { + /** The format of the token to refresh. */ + format: 'token'; + }, + ): Promise; public async refreshSession(sessionId: string, params: RefreshTokenParams & { format: 'cookie' }): Promise; public async refreshSession(sessionId: string, params: RefreshTokenParams): Promise; public async refreshSession(sessionId: string, params: RefreshTokenParams): Promise { diff --git a/packages/backend/src/api/resources/Enums.ts b/packages/backend/src/api/resources/Enums.ts index 3a5c883e425..019bc3458a7 100644 --- a/packages/backend/src/api/resources/Enums.ts +++ b/packages/backend/src/api/resources/Enums.ts @@ -40,9 +40,7 @@ export type SignInStatus = 'needs_identifier' | 'needs_factor_one' | 'needs_fact export type SignUpVerificationNextAction = 'needs_prepare' | 'needs_attempt' | ''; -/** - * @inline - */ +/** @inline */ export type InvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired'; export const DomainsEnrollmentModes = {