diff --git a/scripts/mintlify-post-processing/appended-articles.json b/scripts/mintlify-post-processing/appended-articles.json
index 100bcc4..969e10d 100644
--- a/scripts/mintlify-post-processing/appended-articles.json
+++ b/scripts/mintlify-post-processing/appended-articles.json
@@ -1,4 +1,7 @@
{
"interfaces/EntitiesModule": "interfaces/EntityHandler",
- "type-aliases/integrations": "interfaces/CoreIntegrations"
+ "type-aliases/integrations": [
+ "interfaces/CoreIntegrations",
+ "interfaces/CustomIntegrationsModule"
+ ]
}
diff --git a/scripts/mintlify-post-processing/copy-to-local-docs.js b/scripts/mintlify-post-processing/copy-to-local-docs.js
index 9fc2c10..ec29357 100644
--- a/scripts/mintlify-post-processing/copy-to-local-docs.js
+++ b/scripts/mintlify-post-processing/copy-to-local-docs.js
@@ -248,6 +248,12 @@ function main() {
console.log(`Copying docs to ${sdkDocsTarget}...`);
fs.cpSync(DOCS_SOURCE_PATH, sdkDocsTarget, { recursive: true });
+ // Remove README.mdx - it's not used in the docs navigation
+ const readmePath = path.join(sdkDocsTarget, "README.mdx");
+ if (fs.existsSync(readmePath)) {
+ fs.rmSync(readmePath, { force: true });
+ }
+
// Scan the sdk-docs directory
const sdkFiles = scanSdkDocs(sdkDocsTarget);
console.debug(`SDK files: ${JSON.stringify(sdkFiles, null, 2)}`);
diff --git a/scripts/mintlify-post-processing/file-processing/file-processing.js b/scripts/mintlify-post-processing/file-processing/file-processing.js
index b14b347..67f7cbd 100755
--- a/scripts/mintlify-post-processing/file-processing/file-processing.js
+++ b/scripts/mintlify-post-processing/file-processing/file-processing.js
@@ -2,7 +2,7 @@
/**
* Post-processing script for TypeDoc-generated MDX files
- *
+ *
* TypeDoc now emits .mdx files directly, so this script:
* 1. Processes links to make them Mintlify-compatible
* 2. Removes files for linked types that should be suppressed
@@ -94,6 +94,15 @@ function processLinksInFile(filePath) {
let content = fs.readFileSync(filePath, "utf-8");
let modified = false;
+ // Rename "Type Declaration" to "Type Declarations" (TypeDoc outputs singular)
+ if (content.includes("## Type Declaration\n")) {
+ content = content.replace(
+ "## Type Declaration\n",
+ "## Type Declarations\n"
+ );
+ modified = true;
+ }
+
// Remove undesirable lines like "> **IntegrationsModule** = `object` & `object`"
// This typically appears in type alias files using intersection types
const typeDefinitionRegex = /^> \*\*\w+\*\* = `object` & `object`\s*$/m;
@@ -113,21 +122,10 @@ function processLinksInFile(filePath) {
\\[\`packageName\`: \`string\`\\]: [\`IntegrationPackage\`](IntegrationPackage)
Access to additional integration packages.
-
-### Example
-
-
-
-\`\`\`typescript Access additional packages
-// Access a custom integration package
-base44.integrations.MyCustomPackage.MyFunction({ param: 'value' });
-\`\`\`
-
-
`;
- // Append it before the "Type Declaration" or "Core" section if possible, or just at the end before methods if any
+ // Append it before the "Type Declarations" or "Core" section if possible, or just at the end before methods if any
// Finding a good insertion point
- const typeDeclarationIndex = content.indexOf("## Type Declaration");
+ const typeDeclarationIndex = content.indexOf("## Type Declarations");
if (typeDeclarationIndex !== -1) {
content =
content.slice(0, typeDeclarationIndex) +
@@ -136,7 +134,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' });
content.slice(typeDeclarationIndex);
modified = true;
} else {
- // If no Type Declaration, maybe append after the main description?
+ // If no Type Declarations, maybe append after the main description?
// Look for the first horizontal rule or similar
const firstHR = content.indexOf("***", 10); // skip first few chars
if (firstHR !== -1) {
@@ -149,7 +147,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' });
}
}
}
-
+
// Remove .md and .mdx extensions from markdown links
// This handles both relative and absolute paths
// Regex breakdown:
@@ -162,7 +160,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' });
let newContent = content.replace(
linkRegex,
(match, linkText, linkPath, ext) => {
- modified = true;
+ modified = true;
// Check if the link points to a renamed module
const pathParts = linkPath.split("/");
@@ -179,7 +177,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' });
// Handle relative links that might be missing context (basic cleanup)
// e.g. if linkPath is just "entities" but it should be relative
- return `[${linkText}](${linkPath})`;
+ return `[${linkText}](${linkPath})`;
}
);
@@ -187,12 +185,12 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' });
// or if the above regex missed them (though it matches .mdx?)
// The regex requires .md or .mdx extension. If links are already extensionless, this won't run.
// But TypeDoc usually outputs links with extensions.
-
+
if (modified) {
fs.writeFileSync(filePath, newContent, "utf-8");
return true;
}
-
+
return false;
}
@@ -272,18 +270,18 @@ function scanDocsContent() {
};
const sections = ["functions", "interfaces", "classes", "type-aliases"];
-
+
for (const section of sections) {
const sectionDir = path.join(CONTENT_DIR, section);
if (!fs.existsSync(sectionDir)) continue;
-
+
const files = fs.readdirSync(sectionDir);
const mdxFiles = files
.filter((file) => file.endsWith(".mdx"))
.map((file) => path.basename(file, ".mdx"))
.sort()
.map((fileName) => `content/${section}/${fileName}`);
-
+
const key = section === "type-aliases" ? "typeAliases" : section;
result[key] = mdxFiles;
}
@@ -298,7 +296,7 @@ function getGroupName(section, categoryMap) {
if (categoryMap[section]) {
return categoryMap[section];
}
-
+
return section;
}
@@ -314,30 +312,30 @@ function generateDocsJson(docsContent) {
// If file doesn't exist or can't be read, return empty object
console.error(`Error: Category map file not found: ${CATEGORY_MAP_PATH}`);
}
-
+
const groups = [];
-
+
if (docsContent.functions.length > 0 && categoryMap.functions) {
groups.push({
group: getGroupName("functions", categoryMap),
pages: docsContent.functions,
});
}
-
+
if (docsContent.interfaces.length > 0 && categoryMap.interfaces) {
groups.push({
group: getGroupName("interfaces", categoryMap),
pages: docsContent.interfaces,
});
}
-
+
if (docsContent.classes.length > 0 && categoryMap.classes) {
groups.push({
group: getGroupName("classes", categoryMap),
pages: docsContent.classes,
});
}
-
+
if (docsContent.typeAliases.length > 0 && categoryMap["type-aliases"]) {
// Merge into existing group if name matches
const groupName = getGroupName("type-aliases", categoryMap);
@@ -348,13 +346,13 @@ function generateDocsJson(docsContent) {
existingGroup.pages.push(...docsContent.typeAliases);
existingGroup.pages.sort(); // Sort combined pages alphabetically
} else {
- groups.push({
+ groups.push({
group: groupName,
- pages: docsContent.typeAliases,
- });
+ pages: docsContent.typeAliases,
+ });
}
}
-
+
// Find or create SDK Reference tab
let sdkTab = template.navigation.tabs.find(
(tab) => tab.tab === "SDK Reference"
@@ -363,9 +361,9 @@ function generateDocsJson(docsContent) {
sdkTab = { tab: "SDK Reference", groups: [] };
template.navigation.tabs.push(sdkTab);
}
-
+
sdkTab.groups = groups;
-
+
const docsJsonPath = path.join(DOCS_DIR, "docs.json");
fs.writeFileSync(
docsJsonPath,
@@ -405,10 +403,10 @@ function isTypeDocPath(relativePath) {
*/
function processAllFiles(dir, linkedTypeNames, exposedTypeNames) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
-
+
for (const entry of entries) {
const entryPath = path.join(dir, entry.name);
-
+
if (entry.isDirectory()) {
processAllFiles(entryPath, linkedTypeNames, exposedTypeNames);
} else if (
@@ -433,7 +431,7 @@ function processAllFiles(dir, linkedTypeNames, exposedTypeNames) {
exposedTypeNames.has(originalName) ||
exposedTypeNames.has(fileName) ||
isRenamedModule;
-
+
// Remove any type doc files that are not explicitly exposed
if (isTypeDoc && !isExposedType) {
fs.unlinkSync(entryPath);
@@ -683,7 +681,7 @@ function applyAppendedArticles(appendedArticles) {
console.warn(
`Warning: Appended article not found: ${appendKey} (checked content/ and docs/ roots)`
);
- continue;
+ continue;
}
}
}
@@ -692,7 +690,7 @@ function applyAppendedArticles(appendedArticles) {
const { section, headings } = prepareAppendedSection(appendPath);
combinedSections += `\n\n${section}`;
if (PANELS_ENABLED && collectedHeadings) {
- collectedHeadings.push(...headings);
+ collectedHeadings.push(...headings);
}
try {
@@ -763,34 +761,197 @@ function applyHeadingDemotion(dir) {
}
}
+/**
+ * Link type names in the Type Declarations section to their corresponding ## headings on the page
+ */
+function linkTypeDeclarationToSections(content) {
+ // Find all ## headings on the page (these are potential link targets)
+ const sectionHeadingRegex = /^## (\w+)\s*$/gm;
+ const sectionNames = new Set();
+ let match;
+ while ((match = sectionHeadingRegex.exec(content)) !== null) {
+ sectionNames.add(match[1]);
+ }
+
+ if (sectionNames.size === 0) {
+ return { content, modified: false };
+ }
+
+ // Find the Type Declarations section
+ const typeDeclarationStart = content.indexOf("## Type Declarations");
+ if (typeDeclarationStart === -1) {
+ return { content, modified: false };
+ }
+
+ // Find the end of Type Declarations section (next ## heading or end of file)
+ const afterTypeDeclaration = content.slice(
+ typeDeclarationStart + "## Type Declarations".length
+ );
+ const nextSectionMatch = afterTypeDeclaration.match(/\n## /);
+ const typeDeclarationEnd = nextSectionMatch
+ ? typeDeclarationStart +
+ "## Type Declarations".length +
+ nextSectionMatch.index
+ : content.length;
+
+ const beforeSection = content.slice(0, typeDeclarationStart);
+ const typeDeclarationSection = content.slice(
+ typeDeclarationStart,
+ typeDeclarationEnd
+ );
+ const afterSection = content.slice(typeDeclarationEnd);
+
+ // In the Type Declarations section, find type names in backticks on blockquote lines
+ // Pattern: > **propertyName**: `TypeName`
+ let modified = false;
+ const updatedSection = typeDeclarationSection.replace(
+ /^(>\s*\*\*\w+\*\*:\s*)`(\w+)`/gm,
+ (match, prefix, typeName) => {
+ if (sectionNames.has(typeName)) {
+ modified = true;
+ const anchor = typeName.toLowerCase();
+ return `${prefix}[\`${typeName}\`](#${anchor})`;
+ }
+ return match;
+ }
+ );
+
+ if (!modified) {
+ return { content, modified: false };
+ }
+
+ return {
+ content: beforeSection + updatedSection + afterSection,
+ modified: true,
+ };
+}
+
+/**
+ * Apply type declarations linking to all files
+ */
+function applyTypeDeclarationLinking(dir) {
+ if (!fs.existsSync(dir)) return;
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ for (const entry of entries) {
+ const entryPath = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ applyTypeDeclarationLinking(entryPath);
+ } else if (
+ entry.isFile() &&
+ (entry.name.endsWith(".mdx") || entry.name.endsWith(".md"))
+ ) {
+ const content = fs.readFileSync(entryPath, "utf-8");
+ const { content: updated, modified } =
+ linkTypeDeclarationToSections(content);
+ if (modified) {
+ fs.writeFileSync(entryPath, updated, "utf-8");
+ console.log(
+ `Linked type declarations: ${path.relative(DOCS_DIR, entryPath)}`
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Remove links to types that are not in the exposed types list.
+ * Converts [TypeName](path) or [`TypeName`](path) to just TypeName or `TypeName`.
+ */
+function removeNonExposedTypeLinks(content, exposedTypeNames) {
+ // Match markdown links where the link text is a type name (with or without backticks)
+ // Pattern: [TypeName](some/path/TypeName) or [`TypeName`](some/path/TypeName)
+ const linkRegex = /\[(`?)([A-Z][A-Za-z0-9]*)\1\]\(([^)]+)\)/g;
+
+ let modified = false;
+ const updatedContent = content.replace(
+ linkRegex,
+ (match, backtick, typeName, linkPath) => {
+ // Check if this looks like a type doc link (path ends with the type name)
+ const pathEnd = linkPath
+ .split("/")
+ .pop()
+ .replace(/\.mdx?$/, "");
+
+ // If the link path ends with a type name that's NOT exposed, remove the link
+ if (pathEnd === typeName && !exposedTypeNames.has(typeName)) {
+ modified = true;
+ // Keep the type name with backticks if it had them, otherwise plain
+ return backtick ? `\`${typeName}\`` : typeName;
+ }
+
+ return match;
+ }
+ );
+
+ return { content: updatedContent, modified };
+}
+
+/**
+ * Apply non-exposed type link removal to all files
+ */
+function applyNonExposedTypeLinkRemoval(dir, exposedTypeNames) {
+ if (!fs.existsSync(dir)) return;
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ for (const entry of entries) {
+ const entryPath = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ applyNonExposedTypeLinkRemoval(entryPath, exposedTypeNames);
+ } else if (
+ entry.isFile() &&
+ (entry.name.endsWith(".mdx") || entry.name.endsWith(".md"))
+ ) {
+ const content = fs.readFileSync(entryPath, "utf-8");
+ const { content: updated, modified } = removeNonExposedTypeLinks(
+ content,
+ exposedTypeNames
+ );
+ if (modified) {
+ fs.writeFileSync(entryPath, updated, "utf-8");
+ console.log(
+ `Removed non-exposed type links: ${path.relative(
+ DOCS_DIR,
+ entryPath
+ )}`
+ );
+ }
+ }
+ }
+}
+
/**
* Main function
*/
function main() {
console.log("Processing TypeDoc MDX files for Mintlify...\n");
-
+
if (!fs.existsSync(DOCS_DIR)) {
console.error(`Error: Documentation directory not found: ${DOCS_DIR}`);
console.error('Please run "npm run docs:generate" first.');
process.exit(1);
}
-
+
// Get list of linked types to suppress
const linkedTypeNames = getLinkedTypeNames();
const exposedTypeNames = getTypesToExpose();
// First, perform module renames (EntitiesModule -> entities, etc.)
performModuleRenames(DOCS_DIR);
-
+
// Process all files (remove suppressed ones and fix links)
- processAllFiles(DOCS_DIR, linkedTypeNames, exposedTypeNames);
+ processAllFiles(DOCS_DIR, linkedTypeNames, exposedTypeNames);
// Append configured articles
const appendedArticles = loadAppendedArticlesConfig();
applyAppendedArticles(appendedArticles);
applyHeadingDemotion(DOCS_DIR);
-
+
+ // Link type names in Type Declarations sections to their corresponding headings
+ applyTypeDeclarationLinking(DOCS_DIR);
+
+ // Remove links to types that aren't exposed (would 404)
+ applyNonExposedTypeLinkRemoval(DOCS_DIR, exposedTypeNames);
+
// Clean up the linked types file
try {
if (fs.existsSync(LINKED_TYPES_FILE)) {
@@ -799,14 +960,14 @@ function main() {
} catch (e) {
// Ignore errors
}
-
+
// Scan content and generate docs.json
const docsContent = scanDocsContent();
generateDocsJson(docsContent);
-
+
// Copy styling.css
copyStylingCss();
-
+
console.log(`\n✓ Post-processing complete!`);
console.log(` Documentation directory: ${DOCS_DIR}`);
}
diff --git a/scripts/mintlify-post-processing/push-to-docs-repo.js b/scripts/mintlify-post-processing/push-to-docs-repo.js
index 60f06ba..c1143c6 100644
--- a/scripts/mintlify-post-processing/push-to-docs-repo.js
+++ b/scripts/mintlify-post-processing/push-to-docs-repo.js
@@ -220,6 +220,11 @@ function main() {
recursive: true,
});
+ // Remove README.mdx - it's not used in the docs navigation
+ fs.rmSync(path.join(tempRepoDir, "sdk-docs", "README.mdx"), {
+ force: true,
+ });
+
// Scan the sdk-docs directory
const sdkDocsDir = path.join(tempRepoDir, "sdk-docs");
const sdkFiles = scanSdkDocs(sdkDocsDir);
diff --git a/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js b/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js
index 5c3958d..3e6d9b1 100644
--- a/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js
+++ b/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js
@@ -462,14 +462,29 @@ function extractPropertiesFromMarkdownFile(linkedTypeInfo, context) {
*/
function parsePropertiesFromTypeFile(content) {
const properties = [];
- const lines = content.split("\n");
+
+ // Strip YAML frontmatter if present
+ let contentWithoutFrontmatter = content;
+ if (content.startsWith("---")) {
+ const endIndex = content.indexOf("\n---", 3);
+ if (endIndex !== -1) {
+ contentWithoutFrontmatter = content.slice(endIndex + 4).trimStart();
+ }
+ }
+
+ // Strip leading horizontal rule (***) that TypeDoc adds after frontmatter
+ if (contentWithoutFrontmatter.startsWith("***")) {
+ contentWithoutFrontmatter = contentWithoutFrontmatter.slice(3).trimStart();
+ }
+
+ const lines = contentWithoutFrontmatter.split("\n");
// Collect intro description until Properties section
const introLines = [];
let descriptionCaptured = false;
- // Extract Indexable section if present
- const indexSignature = parseIndexableSection(content);
+ // Extract Indexable section if present (use content without frontmatter)
+ const indexSignature = parseIndexableSection(contentWithoutFrontmatter);
// Find the Properties section
let inPropertiesSection = false;
diff --git a/scripts/mintlify-post-processing/types-to-expose.json b/scripts/mintlify-post-processing/types-to-expose.json
index 94bd563..49e790d 100644
--- a/scripts/mintlify-post-processing/types-to-expose.json
+++ b/scripts/mintlify-post-processing/types-to-expose.json
@@ -3,6 +3,7 @@
"AppLogsModule",
"AuthModule",
"ConnectorsModule",
+ "CustomIntegrationsModule",
"EntitiesModule",
"EntityHandler",
"FunctionsModule",
diff --git a/src/modules/auth.types.ts b/src/modules/auth.types.ts
index fe349a9..8795693 100644
--- a/src/modules/auth.types.ts
+++ b/src/modules/auth.types.ts
@@ -204,6 +204,7 @@ export interface AuthModule {
* // Login with GitHub and redirect to dashboard
* base44.auth.loginWithProvider('microsoft', '/dashboard');
* ```
+ * @internal
*/
loginWithProvider(provider: string, fromUrl?: string): void;
diff --git a/src/modules/connectors.types.ts b/src/modules/connectors.types.ts
index a9a486b..3a5a774 100644
--- a/src/modules/connectors.types.ts
+++ b/src/modules/connectors.types.ts
@@ -13,9 +13,7 @@ export interface ConnectorAccessTokenResponse {
/**
* Connectors module for managing OAuth tokens for external services.
*
- * This module allows you to retrieve OAuth access tokens for external services
- * that the app has connected to. Use these tokens to make API
- * calls to external services.
+ * This module allows you to retrieve OAuth access tokens for external services that the app has connected to. Connectors are app-scoped. When an app builder connects an integration like Google Calendar or Slack, all users of the app share that same connection.
*
* Unlike the integrations module that provides pre-built functions, connectors give you
* raw OAuth tokens so you can call external service APIs directly with full control over
@@ -28,9 +26,9 @@ export interface ConnectorsModule {
/**
* Retrieves an OAuth access token for a specific external integration type.
*
- * Returns the OAuth token string for an external service that the app
- * has connected to. You can then use this token to make authenticated API calls
- * to that external service.
+ * Returns the OAuth token string for an external service that an app builder
+ * has connected to. This token represents the connected app builder's account
+ * and can be used to make authenticated API calls to that external service on behalf of the app.
*
* @param integrationType - The type of integration, such as `'googlecalendar'`, `'slack'`, or `'github'`.
* @returns Promise resolving to the access token string.
diff --git a/src/modules/custom-integrations.types.ts b/src/modules/custom-integrations.types.ts
index 04c8fa5..959169f 100644
--- a/src/modules/custom-integrations.types.ts
+++ b/src/modules/custom-integrations.types.ts
@@ -9,7 +9,7 @@ export interface CustomIntegrationCallParams {
payload?: Record;
/**
- * Path parameters to substitute in the URL (e.g., `{ owner: "user", repo: "repo" }`).
+ * Path parameters to substitute in the URL. For example, `{ owner: "user", repo: "repo" }`.
*/
pathParams?: Record;
@@ -17,12 +17,6 @@ export interface CustomIntegrationCallParams {
* Query string parameters to append to the URL.
*/
queryParams?: Record;
-
- /**
- * Additional headers to send with this specific request.
- * These are merged with the integration's configured headers.
- */
- headers?: Record;
}
/**
@@ -48,61 +42,17 @@ export interface CustomIntegrationCallResponse {
}
/**
- * Module for calling custom workspace-level API integrations.
- *
- * Custom integrations allow workspace administrators to connect any external API
- * by importing an OpenAPI specification. Apps in the workspace can then call
- * these integrations using this module.
- *
- * Unlike the built-in integrations (like `Core`), custom integrations:
- * - Are defined per-workspace by importing OpenAPI specs
- * - Use a slug-based identifier instead of package names
- * - Proxy requests through Base44's backend (credentials never exposed to frontend)
- *
- * @example
- * ```typescript
- * // Call a custom GitHub integration
- * const response = await base44.integrations.custom.call(
- * "github", // integration slug (defined by workspace admin)
- * "get:/repos/{owner}/{repo}/issues", // endpoint in method:path format
- * {
- * pathParams: { owner: "myorg", repo: "myrepo" },
- * queryParams: { state: "open", per_page: 100 }
- * }
- * );
+ * Module for calling custom pre-configured API integrations.
*
- * if (response.success) {
- * console.log("Issues:", response.data);
- * } else {
- * console.error("API returned error:", response.status_code);
- * }
- * ```
- *
- * @example
- * ```typescript
- * // Call with request body payload
- * const response = await base44.integrations.custom.call(
- * "github",
- * "post:/repos/{owner}/{repo}/issues",
- * {
- * pathParams: { owner: "myorg", repo: "myrepo" },
- * payload: {
- * title: "Bug report",
- * body: "Something is broken",
- * labels: ["bug"]
- * }
- * }
- * );
- * ```
- * @internal
+ * Custom integrations allow workspace administrators to connect any external API by importing an OpenAPI specification. Apps in the workspace can then call these integrations using this module.
*/
export interface CustomIntegrationsModule {
/**
* Call a custom integration endpoint.
*
- * @param slug - The integration's unique identifier (slug), as defined by the workspace admin.
- * @param operationId - The endpoint identifier in method:path format (e.g., "get:/repos/{owner}/{repo}/issues", "get:/users/{username}").
- * @param params - Optional parameters including payload, pathParams, queryParams, and headers.
+ * @param slug - The integration's unique identifier, as defined by the workspace admin.
+ * @param operationId - The endpoint in `method:path` format. For example, `"get:/contacts"`, or `"post:/users/{id}"`. The method is the HTTP verb in lowercase and the path matches the OpenAPI specification.
+ * @param params - Optional parameters including payload, pathParams, and queryParams.
* @returns Promise resolving to the integration call response.
*
* @throws {Error} If slug is not provided.
@@ -110,6 +60,36 @@ export interface CustomIntegrationsModule {
* @throws {Base44Error} If the integration or operation is not found (404).
* @throws {Base44Error} If the external API call fails (502).
* @throws {Base44Error} If the request times out (504).
+ *
+ * @example
+ * ```typescript
+ * // Call a custom CRM integration
+ * const response = await base44.integrations.custom.call(
+ * "my-crm",
+ * "get:/contacts",
+ * { queryParams: { limit: 10 } }
+ * );
+ *
+ * if (response.success) {
+ * console.log("Contacts:", response.data);
+ * }
+ * ```
+ *
+ * @example
+ * ```typescript
+ * // Call with path params and request body
+ * const response = await base44.integrations.custom.call(
+ * "github",
+ * "post:/repos/{owner}/{repo}/issues",
+ * {
+ * pathParams: { owner: "myorg", repo: "myrepo" },
+ * payload: {
+ * title: "Bug report",
+ * body: "Something is broken"
+ * }
+ * }
+ * );
+ * ```
*/
call(
slug: string,
diff --git a/src/modules/integrations.types.ts b/src/modules/integrations.types.ts
index d109778..6696fa7 100644
--- a/src/modules/integrations.types.ts
+++ b/src/modules/integrations.types.ts
@@ -351,22 +351,28 @@ export interface CoreIntegrations {
}
/**
- * Integrations module for calling integration endpoints.
+ * Integrations module for calling integration methods.
*
- * This module provides access to integration endpoints for interacting with external
- * services. Integrations are organized into packages. Base44 provides built-in integrations
- * in the `Core` package.
+ * This module provides access to integration methods for interacting with external services. Unlike the connectors module that gives you raw OAuth tokens, integrations provide pre-built functions that Base44 executes on your behalf.
*
- * Unlike the connectors module that gives you raw OAuth tokens, integrations provide
- * pre-built functions that Base44 executes on your behalf.
+ * There are two types of integrations:
*
- * Integration endpoints are accessed dynamically using the pattern:
- * `base44.integrations.PackageName.EndpointName(params)`
+ * - **Built-in integrations** (`Core`): Pre-built functions provided by Base44 for common tasks such as AI-powered text generation, image creation, file uploads, and email. Access core integration methods using:
+ * ```
+ * base44.integrations.Core.FunctionName(params)
+ * ```
+ *
+ * - **Custom integrations** (`custom`): Pre-configured external APIs. Custom integration calls are proxied through Base44's backend, so credentials are never exposed to the frontend. Access custom integration methods using:
+ * ```
+ * base44.integrations.custom.call(slug, operationId, params)
+ * ```
+ *
+ * To call a custom integration, it must be pre-configured by a workspace administrator who imports an OpenAPI specification.
*
* This module is available to use with a client in all authentication modes:
*
- * - **Anonymous or User authentication** (`base44.integrations`): Integration endpoints are invoked with the current user's permissions. Anonymous users invoke endpoints without authentication, while authenticated users invoke endpoints with their authentication context.
- * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration endpoints are invoked with elevated admin-level permissions. The endpoints execute with admin authentication context.
+ * - **Anonymous or User authentication** (`base44.integrations`): Integration methods are invoked with the current user's permissions. Anonymous users invoke methods without authentication, while authenticated users invoke methods with their authentication context.
+ * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration methods are invoked with elevated admin-level permissions. The methods execute with admin authentication context.
*/
export type IntegrationsModule = {
/**
@@ -375,23 +381,7 @@ export type IntegrationsModule = {
Core: CoreIntegrations;
/**
- * Custom integrations module for calling workspace-level API integrations.
- *
- * Allows calling external APIs that workspace admins have configured
- * by importing OpenAPI specifications.
- *
- * @example
- * ```typescript
- * const response = await base44.integrations.custom.call(
- * "github", // integration slug
- * "get:/repos/{owner}/{repo}/issues", // endpoint in method:path format
- * {
- * pathParams: { owner: "myorg", repo: "myrepo" },
- * queryParams: { state: "open" }
- * }
- * );
- * ```
- * @internal
+ * Custom integrations module for calling pre-configured external APIs.
*/
custom: CustomIntegrationsModule;
} & {