From 181f871924d31b674f682faaaad3211523f50698 Mon Sep 17 00:00:00 2001 From: Saif Date: Thu, 13 Aug 2026 16:53:01 +0530 Subject: [PATCH 1/8] prototype(docs): product guide sidebars for cookbooks and how-tos Split cookbooks into AgentKit and Auth for SaaS shelves as plain Starlight docs (no starlight-blog). Each product gets a dedicated guides topic with Cookbooks and How-to collapsibles, entered from secondary nav rather than the journey sidebars. --- astro.config.mjs | 11 - package.json | 1 - pnpm-lock.yaml | 133 ----- project-docs/COOKBOOKS.md | 458 +++--------------- .../litellm-agentkit-inbox-triage-0.svg | 0 .../litellm-agentkit-inbox-triage-1.svg | 0 scripts/generate-llms-index.js | 7 +- src/configs/llms.config.ts | 5 +- src/configs/redirects.config.ts | 56 ++- src/configs/secondary-nav.config.ts | 20 +- src/configs/sidebar.config.ts | 66 ++- src/content.config.ts | 4 +- .../cookbooks/apify-actor-per-user-oauth.mdx | 9 - .../build-voice-assistant-1000-tools.mdx | 10 - .../crewai-agentkit-email-triage.mdx | 13 - .../cookbooks/daily-briefing-agent.mdx | 9 - .../fastrouter-agentkit-tool-calling.mdx | 11 +- src/content/docs/agentkit/cookbooks/index.mdx | 12 + .../cookbooks/langsmith-tracing-agentkit.mdx | 9 - .../litellm-agentkit-inbox-triage.mdx | 9 - .../livekit-agentkit-voice-tool-calling.mdx | 9 - .../cookbooks/mastra-agentkit.mdx | 9 - .../cookbooks/render-github-pr-summarizer.mdx | 9 - .../schedule-meeting-and-draft-email.mdx | 9 - ...set-up-agentkit-with-your-coding-agent.mdx | 11 - src/content/docs/agentkit/examples/crewai.mdx | 2 +- src/content/docs/agentkit/how-to/index.mdx | 12 + ...ct-connected-accounts-in-the-dashboard.mdx | 56 +++ .../authenticate/m2m/api-auth-quickstart.mdx | 6 +- src/content/docs/cookbooks.mdx | 24 + .../docs/dev-kit/build-with-ai/index.mdx | 2 +- .../add-enterprise-sso-nextjs-authjs.mdx | 12 - .../add-hosted-auth-nextjs-app-router.mdx | 11 - .../building-custom-org-switcher.mdx | 8 - .../cookbooks/implement-nextjs-auth.mdx | 8 - src/content/docs/saaskit/cookbooks/index.mdx | 12 + .../java-spring-boot-jwt-timeout.mdx | 8 - .../cookbooks/m2m-jwks-and-oauth-scopes.mdx | 8 - .../migrate-from-auth0-to-scalekit.mdx | 12 - .../cookbooks/scim-seat-limit-enforcement.mdx | 11 - .../search-scalekit-docs-in-your-ide.mdx | 10 - .../sync-b2b-billing-with-chargebee.mdx | 24 +- .../dashboard => saaskit/how-to}/billing.mdx | 0 .../how-to}/environments.mdx | 2 +- src/content/docs/saaskit/how-to/index.mdx | 12 + .../how-to}/manage-team-members.mdx | 0 src/styles/custom.css | 27 -- 47 files changed, 343 insertions(+), 814 deletions(-) rename public/d2/docs/{ => agentkit}/cookbooks/litellm-agentkit-inbox-triage-0.svg (100%) rename public/d2/docs/{ => agentkit}/cookbooks/litellm-agentkit-inbox-triage-1.svg (100%) rename src/content/docs/{ => agentkit}/cookbooks/apify-actor-per-user-oauth.mdx (97%) rename src/content/docs/{ => agentkit}/cookbooks/build-voice-assistant-1000-tools.mdx (97%) rename src/content/docs/{ => agentkit}/cookbooks/crewai-agentkit-email-triage.mdx (96%) rename src/content/docs/{ => agentkit}/cookbooks/daily-briefing-agent.mdx (97%) rename src/content/docs/{ => agentkit}/cookbooks/fastrouter-agentkit-tool-calling.mdx (95%) create mode 100644 src/content/docs/agentkit/cookbooks/index.mdx rename src/content/docs/{ => agentkit}/cookbooks/langsmith-tracing-agentkit.mdx (95%) rename src/content/docs/{ => agentkit}/cookbooks/litellm-agentkit-inbox-triage.mdx (96%) rename src/content/docs/{ => agentkit}/cookbooks/livekit-agentkit-voice-tool-calling.mdx (96%) rename src/content/docs/{ => agentkit}/cookbooks/mastra-agentkit.mdx (96%) rename src/content/docs/{ => agentkit}/cookbooks/render-github-pr-summarizer.mdx (98%) rename src/content/docs/{ => agentkit}/cookbooks/schedule-meeting-and-draft-email.mdx (96%) rename src/content/docs/{ => agentkit}/cookbooks/set-up-agentkit-with-your-coding-agent.mdx (90%) create mode 100644 src/content/docs/agentkit/how-to/index.mdx create mode 100644 src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx create mode 100644 src/content/docs/cookbooks.mdx rename src/content/docs/{ => saaskit}/cookbooks/add-enterprise-sso-nextjs-authjs.mdx (95%) rename src/content/docs/{ => saaskit}/cookbooks/add-hosted-auth-nextjs-app-router.mdx (96%) rename src/content/docs/{ => saaskit}/cookbooks/building-custom-org-switcher.mdx (95%) rename src/content/docs/{ => saaskit}/cookbooks/implement-nextjs-auth.mdx (96%) create mode 100644 src/content/docs/saaskit/cookbooks/index.mdx rename src/content/docs/{ => saaskit}/cookbooks/java-spring-boot-jwt-timeout.mdx (91%) rename src/content/docs/{ => saaskit}/cookbooks/m2m-jwks-and-oauth-scopes.mdx (93%) rename src/content/docs/{ => saaskit}/cookbooks/migrate-from-auth0-to-scalekit.mdx (96%) rename src/content/docs/{ => saaskit}/cookbooks/scim-seat-limit-enforcement.mdx (97%) rename src/content/docs/{ => saaskit}/cookbooks/search-scalekit-docs-in-your-ide.mdx (95%) rename src/content/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee.mdx (96%) rename src/content/docs/{dev-kit/guides/dashboard => saaskit/how-to}/billing.mdx (100%) rename src/content/docs/{dev-kit/guides/dashboard => saaskit/how-to}/environments.mdx (98%) create mode 100644 src/content/docs/saaskit/how-to/index.mdx rename src/content/docs/{dev-kit/guides/dashboard => saaskit/how-to}/manage-team-members.mdx (100%) diff --git a/astro.config.mjs b/astro.config.mjs index 4e59a3eb3..e509138a2 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -16,7 +16,6 @@ import starlightThemeNova from 'starlight-theme-nova' import starlightVideos from 'starlight-videos' import starlightLinksValidator from 'starlight-links-validator' import starlightLlmsTxt from 'starlight-llms-txt' -import starlightBlog from 'starlight-blog' import { sidebar as sidebarConfig, topics, exclude } from './src/configs/sidebar.config' import { redirects } from './src/configs/redirects.config' import { llmsConfig } from './src/configs/llms.config.ts' @@ -140,14 +139,6 @@ export default defineConfig({ }, // No baseUrl — prevents llms.txt generation (already handled by starlight-llms-txt) }), - starlightBlog({ - prefix: 'cookbooks', - rss: false, - metrics: { - readingTime: true, - words: 'total', - }, - }), ], head: [ { @@ -453,8 +444,6 @@ export default defineConfig({ }, optimizeDeps: { include: ['vue'], - // starlight-blog uses Astro/Starlight virtual modules that should not be pre-bundled. - exclude: ['starlight-blog'], }, // Provide a safe fallback for libraries that reference the CommonJS // global `__dirname` (e.g. canvaskit-wasm used by astro-og-canvas). diff --git a/package.json b/package.json index ac0d848ea..95dcd57d1 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "react": "^19.2.5", "react-dom": "^19.2.5", "sharp": "^0.35.3", - "starlight-blog": "^0.26.1", "starlight-image-zoom": "^0.14.2", "starlight-links-validator": "^0.24.1", "starlight-llms-txt": "^0.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97f2225d4..a26f59cce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,9 +107,6 @@ importers: sharp: specifier: ^0.35.3 version: 0.35.3(@types/node@25.6.0) - starlight-blog: - specifier: ^0.26.1 - version: 0.26.1(@astrojs/starlight@0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3))(typescript@6.0.3))(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3)) starlight-image-zoom: specifier: ^0.14.2 version: 0.14.2(@astrojs/starlight@0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3))(typescript@6.0.3)) @@ -405,9 +402,6 @@ packages: react: ^17.0.2 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 - '@astrojs/rss@4.0.19': - resolution: {integrity: sha512-e+z5wYeYtffQdHQO8c2tkSd2JEBdAuRXJV4ZEU5IxkYeE6e39woDd7nw1PH1Kk2tEYNCYuKdylnnbhGmt61awA==} - '@astrojs/sitemap@3.7.2': resolution: {integrity: sha512-PqkzkcZTb5ICiyIR8VoKbIAP/laNRXi5tw616N1Ckk+40oNB8Can1AzVV56lrbC5GKSZFCyJYUVYqVivMisvpA==} @@ -1912,9 +1906,6 @@ packages: engines: {node: '>=18.14.0'} hasBin: true - '@nodable/entities@1.1.0': - resolution: {integrity: sha512-bidpxmTBP0pOsxULw6XlxzQpTgrAGLDHGBK/JuWhPDL6ZV0GZ/PmN9CA9do6e+A9lYI6qx6ikJUtJYRxup141g==} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -3623,10 +3614,6 @@ packages: peerDependencies: astro: ^5.0.0 || ^6.0.0-alpha - astro-remote@0.3.4: - resolution: {integrity: sha512-jL5skNQLA0YBc1R3bVGXyHew3FqGqsT7AgLzWAVeTLzFkwVMUYvs4/lKJSmS7ygcF1GnHnoKG6++8GL9VtWwGQ==} - engines: {node: '>=18.14.1'} - astro-theme-toggle@0.8.1: resolution: {integrity: sha512-cUQDnMpRrGwWQ0C6dj1x3y3e3ULlikvoc6Tb6FkIQkL1Ropyd6jQ/IZ9GdhSogWEVpNodPckOKeItIh89EDwkg==} @@ -4671,13 +4658,6 @@ packages: fast-wrap-ansi@0.1.6: resolution: {integrity: sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w==} - fast-xml-builder@1.1.4: - resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} - - fast-xml-parser@5.6.0: - resolution: {integrity: sha512-5G+uaEBbOm9M4dgMOV3K/rBzfUNGqGqoUTaYJM3hBwM8t71w07gxLQZoTsjkY8FtfjabqgQHEkeIySBDYeBmJw==} - hasBin: true - fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} @@ -5806,31 +5786,6 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - marked-footnote@1.4.0: - resolution: {integrity: sha512-fZTxAhI1TcLEs5UOjCfYfTHpyKGaWQevbxaGTEA68B51l7i87SctPFtHETYqPkEN0ka5opvy4Dy1l/yXVC+hmg==} - peerDependencies: - marked: '>=7.0.0' - - marked-plaintify@1.1.1: - resolution: {integrity: sha512-r3kMKArhfo2H3lD4ctFq/OJTzM0uNvXHh7FBTI1hMDpf4Ac1djjtq4g8NfTBWMxWLmaEz3KL1jCkLygik3gExA==} - peerDependencies: - marked: '>=13.0.0' - - marked-smartypants@1.1.12: - resolution: {integrity: sha512-Z0QL2GpihbSeG5aaCrQxMEoqvngMftF/gq1SrdlCnbecUSrX3HYgPtCZzCW+OyNe2ideQqaFdxfGryqQX1MBDA==} - peerDependencies: - marked: '>=4 <19' - - marked@12.0.2: - resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==} - engines: {node: '>= 18'} - hasBin: true - - marked@17.0.6: - resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==} - engines: {node: '>= 20'} - hasBin: true - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -6472,10 +6427,6 @@ packages: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-expression-matcher@1.5.0: - resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} - engines: {node: '>=14.0.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -7261,10 +7212,6 @@ packages: resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} engines: {node: '>=20'} - smartypants@0.2.2: - resolution: {integrity: sha512-TzobUYoEft/xBtb2voRPryAUIvYguG0V7Tt3de79I1WfXgCwelqVsGuZSnu3GFGRZhXR90AeEYIM+icuB/S06Q==} - hasBin: true - smol-toml@1.6.1: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} @@ -7322,12 +7269,6 @@ packages: stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - starlight-blog@0.26.1: - resolution: {integrity: sha512-c2aLtkVTNFKHDJrh1DwlxBj+VyOrLqJeHmGkKKHwL6oowUoaqUiC/vyOZLXNmZL1kcYI0hgHDw1t4G/lkg8mZg==} - engines: {node: '>=22.12.0'} - peerDependencies: - '@astrojs/starlight': '>=0.38.0' - starlight-image-zoom@0.14.2: resolution: {integrity: sha512-YBvE724gFMiVjObGtXmfIDi3zAxVBsvGukeRXOiWLNnESFOBZFfO9DpC/reHohrCxFrO+5ot0XR8EimbE0LpsA==} engines: {node: '>=22.12.0'} @@ -7492,9 +7433,6 @@ packages: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} - strnum@2.2.3: - resolution: {integrity: sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==} - stubborn-fs@2.0.0: resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} @@ -8787,12 +8725,6 @@ snapshots: - tsx - yaml - '@astrojs/rss@4.0.19': - dependencies: - fast-xml-parser: 5.6.0 - piccolore: 0.1.3 - zod: 4.3.6 - '@astrojs/sitemap@3.7.2': dependencies: sitemap: 9.0.1 @@ -10597,8 +10529,6 @@ snapshots: - rollup - supports-color - '@nodable/entities@1.1.0': {} - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -12675,14 +12605,6 @@ snapshots: deterministic-object-hash: 2.0.2 entities: 7.0.1 - astro-remote@0.3.4: - dependencies: - entities: 4.5.0 - marked: 12.0.2 - marked-footnote: 1.4.0(marked@12.0.2) - marked-smartypants: 1.1.12(marked@12.0.2) - ultrahtml: 1.6.0 - astro-theme-toggle@0.8.1: {} astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3): @@ -13886,17 +13808,6 @@ snapshots: dependencies: fast-string-width: 1.1.0 - fast-xml-builder@1.1.4: - dependencies: - path-expression-matcher: 1.5.0 - - fast-xml-parser@5.6.0: - dependencies: - '@nodable/entities': 1.1.0 - fast-xml-builder: 1.1.4 - path-expression-matcher: 1.5.0 - strnum: 2.2.3 - fastest-levenshtein@1.0.16: {} fastify-plugin@6.0.0: {} @@ -15151,23 +15062,6 @@ snapshots: markdown-table@3.0.4: {} - marked-footnote@1.4.0(marked@12.0.2): - dependencies: - marked: 12.0.2 - - marked-plaintify@1.1.1(marked@17.0.6): - dependencies: - marked: 17.0.6 - - marked-smartypants@1.1.12(marked@12.0.2): - dependencies: - marked: 12.0.2 - smartypants: 0.2.2 - - marked@12.0.2: {} - - marked@17.0.6: {} - math-intrinsics@1.1.0: {} maxstache-stream@1.0.4: @@ -16232,8 +16126,6 @@ snapshots: path-exists@5.0.0: {} - path-expression-matcher@1.5.0: {} - path-key@3.1.1: {} path-key@4.0.0: {} @@ -17240,8 +17132,6 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - smartypants@0.2.2: {} - smol-toml@1.6.1: {} sonic-boom@4.2.1: @@ -17291,27 +17181,6 @@ snapshots: stackframe@1.3.4: {} - starlight-blog@0.26.1(@astrojs/starlight@0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3))(typescript@6.0.3))(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3)): - dependencies: - '@astrojs/markdown-remark': 7.2.1 - '@astrojs/mdx': 5.0.4(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3)) - '@astrojs/rss': 4.0.19 - '@astrojs/starlight': 0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3))(typescript@6.0.3) - astro-remote: 0.3.4 - github-slugger: 2.0.0 - hast-util-from-html: 2.0.3 - hast-util-to-html: 9.0.5 - hast-util-to-string: 3.0.1 - marked: 17.0.6 - marked-plaintify: 1.1.1(marked@17.0.6) - mdast-util-mdx-expression: 2.0.1 - unist-util-is: 6.0.1 - unist-util-remove: 4.0.0 - unist-util-visit: 5.1.0 - transitivePeerDependencies: - - astro - - supports-color - starlight-image-zoom@0.14.2(@astrojs/starlight@0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3))(typescript@6.0.3)): dependencies: '@astrojs/starlight': 0.41.3(@astrojs/markdown-remark@7.2.1)(astro@7.1.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@netlify/blobs@10.7.9)(@types/node@25.6.0)(jiti@2.7.0)(rollup@4.60.1)(terser@5.46.1)(yaml@2.8.3))(typescript@6.0.3) @@ -17532,8 +17401,6 @@ snapshots: strip-json-comments@2.0.1: {} - strnum@2.2.3: {} - stubborn-fs@2.0.0: dependencies: stubborn-utils: 1.0.2 diff --git a/project-docs/COOKBOOKS.md b/project-docs/COOKBOOKS.md index 13490d056..7bc68d067 100644 --- a/project-docs/COOKBOOKS.md +++ b/project-docs/COOKBOOKS.md @@ -1,445 +1,109 @@ # Cookbook authoring guide -Cookbooks live at `src/content/docs/cookbooks/` and are published at `/cookbooks/`. They are powered by the `starlight-blog` plugin (configured with `prefix: 'cookbooks'` in `astro.config.mjs`). +Cookbooks are plain Starlight docs pages under product shelves: -This document covers how to author, review, and validate a cookbook entry. +| Product | Path | URL | +| ------------- | -------------------------------------- | ---------------------- | +| AgentKit | `src/content/docs/agentkit/cookbooks/` | `/agentkit/cookbooks/` | +| Auth for SaaS | `src/content/docs/saaskit/cookbooks/` | `/saaskit/cookbooks/` | ---- +How-to guides (short dashboard answers) live beside them: -## What a cookbook is +| Product | Path | URL | +| ------------- | ----------------------------------- | ------------------- | +| AgentKit | `src/content/docs/agentkit/how-to/` | `/agentkit/how-to/` | +| Auth for SaaS | `src/content/docs/saaskit/how-to/` | `/saaskit/how-to/` | -A cookbook is a practical, developer-focused guide that solves one specific real-world problem. It is not a feature announcement, a product tour, or a reference page. +## How readers find them -The best cookbooks share knowledge, not features. They are useful even for developers who have not yet adopted Scalekit. Each cookbook should be independently useful — a developer should be able to land on one recipe, solve their problem, and continue without reading anything else. +Each product has a **dedicated guides sidebar** (not the journey rail): -**Cookbooks are not:** +- **AgentKit** secondary nav → **Guides** → left rail shows **Cookbooks** and **How-to** collapsibles +- **Auth for SaaS** secondary nav → Developer Resources → **Cookbooks** / **How-to** → same pattern -- "How Scalekit works" explanations (those belong in concept pages) -- Step-by-step quickstarts for first-time setup (those belong in guides) -- API reference documentation +New topic IDs in `src/configs/sidebar.config.ts`: -**Cookbooks are:** - -- Recipes for solving a specific implementation problem -- Pattern guides developers can adapt to their own projects -- Deep dives into real tradeoffs, gotchas, and working code - -### Two layers of content - -Every cookbook belongs to one of two layers: - -**Layer 1 — Orientation**: Mental models, architecture maps, prerequisites, recommended learning paths. Explains _why_ a pattern matters before showing how to implement it. Use this layer when the reader needs context before they can act. - -**Layer 2 — Recipes**: Concrete implementation tasks with working code, expected outcomes, failure modes, and production notes. Use this layer when the reader knows what they want to build and just needs the how. - -Most cookbooks are Layer 2. A cookbook may contain both layers — a brief orientation section followed by one or more recipes. - ---- +- `agentkit-guides` +- `saaskit-guides` -## The P.A.T. framework +Each uses `autogenerate` on the product directory. Drop a new `.mdx` file in the folder and it appears in the collapsible. -Every cookbook must be structured around three layers: +A cross-product hub remains at `/cookbooks/` (`src/content/docs/cookbooks.mdx`). -1. **Problem** — Start from the real developer pain, workflow, or use case. What is hard or broken without this recipe? -2. **Angle** — Give a clear point of view on the solution. Why this approach? What makes it better than the alternatives? -3. **Teach** — Teach step by step with code, explanations, expected outcomes, common mistakes, and extensions. - -Apply P.A.T. to the cookbook as a whole and to each major section. - ---- - -## File structure - -Create a new `.mdx` file in `src/content/docs/cookbooks/`: - -``` -src/content/docs/cookbooks/.mdx -``` - -Filename rules: - -- Lowercase, hyphen-separated: `implement-nextjs-auth.mdx` -- Descriptive of the task, not the product: prefer `building-custom-org-switcher` over `scalekit-org-switcher` -- Name files like search queries a developer would type: `handle-token-refresh-long-running-agents.mdx`, `pass-user-context-to-tools.mdx`, `debug-failed-oauth-flows.mdx` +## What a cookbook is -**Title naming rules:** +A cookbook is a practical, developer-focused guide that solves one specific real-world problem. It is not a feature announcement, a product tour, or a reference page. -Name the cookbook like a concrete developer task. Prefer titles that expose the action, mechanism, or target outcome. +**Cookbooks are:** -| Bad | Better | -| ------------------------------------ | ----------------------------------------------------- | -| "Authentication concepts for agents" | "Set up agent auth in JavaScript" | -| "Scalekit org switcher" | "Build a custom organization switcher" | -| "Token handling" | "Handle token refresh in long-running agent sessions" | -| "User identity in agents" | "Pass user identity from your app to an agent safely" | +- Recipes for solving a specific implementation problem +- Pattern guides developers can adapt to their own projects +- Deep dives into real tradeoffs, gotchas, and working code -Prefer title patterns: "How to…", "Build…", "Handle…", "Debug…", "Pass…", "Validate…", "Set up…" +**Cookbooks are not:** ---- +- "How Scalekit works" explanations (concept pages) +- First-time setup quickstarts (journey guides) +- API reference documentation +- Short dashboard answers (those belong in **How-to**) -## Required frontmatter +## Frontmatter -The frontmatter schema is enforced by `starlight-blog`. Use this exact structure — field names and types must match. +Use normal Starlight docs frontmatter. Blog-only fields (`date`, `excerpt`, `featured`, `authors`, `tags`, `cover`) are not used. ```yaml --- -title: 'Verb-first title describing what the developer builds (≤60 chars)' -description: 'One sentence: the problem solved and the outcome (≤160 chars)' -date: YYYY-MM-DD -tags: ['tag-one', 'tag-two'] -excerpt: > - 2–3 sentence teaser that states the core problem, the approach, and why it matters. - This appears on the cookbooks index page. -featured: false -# Optional: include a cover image for featured cookbooks -cover: - alt: 'Descriptive alt text for the cover image' - image: ../../../../assets/blog/covers/.jpg -authors: - - name: 'Author Name' - title: 'Role' - url: 'https://linkedin.com/in/...' - picture: '/images/blog/authors/.jpg' +title: 'Build a Mastra agent with Scalekit AgentKit tools' +description: 'Give a Mastra agent access to Gmail and 200+ connectors through Scalekit AgentKit.' +sidebar: + label: 'Mastra AgentKit' +tableOfContents: true --- ``` **Field rules:** -| Field | Required | Notes | -| ------------------- | -------- | ------------------------------------------------------------------------------- | -| `title` | Yes | ≤60 chars, verb-first, sentence case | -| `description` | Yes | ≤160 chars, states problem and outcome | -| `date` | Yes | `YYYY-MM-DD` format | -| `tags` | Yes | Array of strings; use vocabulary below | -| `excerpt` | Yes | 2–3 sentences; appears on `/cookbooks/` index | -| `featured` | Yes | `true` or `false`; featured cookbooks appear prominently | -| `cover` | No | Include only when `featured: true`; image path is relative to the cookbook file | -| `authors` | Yes | Array; at least one author required | -| `authors[].name` | Yes | Full name | -| `authors[].title` | Yes | Role or job title | -| `authors[].url` | Yes | LinkedIn or personal URL | -| `authors[].picture` | Yes | Path under `/images/blog/authors/`; file must exist in `public/` | - -**Cover image path:** relative to the `.mdx` file. For `src/content/docs/cookbooks/.mdx`, the path to `src/assets/blog/covers/.jpg` is `../../../../assets/blog/covers/.jpg`. - -**Real examples from existing cookbooks:** - -```yaml -# implement-nextjs-auth.mdx -title: 'Implementing Passwordless Auth in Next.js 15' -description: "Add magic link and OTP authentication to your Next.js application using Scalekit's headless API." -date: 2025-02-19 -tags: ['Full stack auth', 'Next.js'] -featured: false -``` - -```yaml -# building-custom-org-switcher.mdx (featured, with cover) -title: 'Building a Custom Organization Switcher' -description: 'Learn how to build your own organization switcher UI for complete control over multi-tenant user experiences.' -date: 2025-01-21 -tags: ['Full stack auth'] -featured: true -cover: - alt: 'Modern desk setup with laptop and workspace accessories' - image: ../../../../assets/blog/covers/custom-org-switcher.jpg -``` +| Field | Required | Notes | +| ----------------- | -------- | ------------------------------------ | +| `title` | Yes | ≤60 chars, verb-first, sentence case | +| `description` | Yes | ≤160 chars, problem and outcome | +| `sidebar.label` | Yes | Short left-rail label (1–5 words) | +| `tableOfContents` | Optional | Default true for long recipes | -**Tag vocabulary** (use existing tags for consistency): +## Create a cookbook -- Product area: `Full stack auth`, `SSO`, `SCIM`, `Agent auth`, `M2M` -- Framework/language: `Next.js`, `Node.js`, `Python`, `Go`, `Java`, `Spring Boot` -- Pattern: `JWT`, `OAuth`, `Webhooks`, `API keys` - ---- +1. Choose the product shelf (`agentkit/cookbooks` or `saaskit/cookbooks`). +2. Create `src/content/docs//cookbooks/.mdx`. +3. Write the recipe body (problem, steps, working code, failure modes). +4. If the page replaces an old `/cookbooks/` URL, add a redirect in `src/configs/redirects.config.ts`. +5. Run a local build or `pnpm start` and confirm the page appears under the product **Guides** / **Cookbooks** sidebar. ## Content structure -Follow this order. Every section is required unless marked optional. - ### Opening (no heading) -2–4 sentences immediately after the frontmatter. State: - -- The concrete developer context (framework, pattern, use case) -- Why this is hard without the recipe -- What the cookbook teaches - -Do not start with "In this guide" or "Welcome to". Start with the problem or the context. +2–4 sentences: concrete context, why it is hard without the recipe, what the reader will build. ### The problem -Use heading `## The problem`. Describe the specific pain points the developer faces. Use a bulleted list with **bolded pain point** — explanation format. Be concrete. Name the exact failure modes, error types, or architectural mismatches. - -**Combined opening**: When the audience and pain points are tightly coupled, you can merge the opening paragraph, problem bullets, and audience callout into a single unnumbered intro block — no separate `## The problem` or `## Who needs this` headings needed. Use this when the cookbook is focused enough that every reader faces the same problem. The combined block should still cover: context, concrete pain points, and a one- or two-line scope statement ("this is for X; if you're doing Y, see Z"). - -### Who needs this (optional but recommended) - -Use heading `## Who needs this`. Two short lists: +Heading `## The problem`. Bulleted pain points with **bold** lead-ins. -- Checkmark bullets for the intended audience -- X bullets for who should look elsewhere +### Who needs this (optional) -This reduces support noise and increases relevance for the right reader. Omit this section when audience and scope are already clear from the combined opening. +Two short lists: for you if / not for you if. -### The solution - -Use heading `## The solution`. Before any code: explain the approach in 2–4 sentences. Name the specific APIs, methods, or patterns you will use and why they fit the problem. - -### Implementation - -Use heading `## Implementation`. Break into numbered H3 subsections (`### 1. Step name`). Each step should: - -- Have a clear, task-oriented title -- Include working code (see code standards below) -- Explain what the code does and why — not just paste it -- Note security implications where relevant - -### Testing and verification (optional but recommended) - -Use heading `## Testing`. Show the developer how to verify the implementation works. Include expected output, curl commands, test assertions, or screenshots where appropriate. - -### Common mistakes - -Use heading `## Common mistakes`. Bulleted list of the 3–5 most frequent errors, each with: - -- The symptom or error message -- The cause -- The fix - -### Production notes (optional but recommended) - -Use heading `## Production notes`. Capture what changes between a working prototype and a production deployment. Include: - -- Failure modes specific to production scale or load -- Security hardening steps not covered in the main implementation -- Monitoring and observability hooks -- When not to use this pattern - -### Next steps - -Use heading `## Next steps`. 3–5 links or actions the developer can take to go deeper or extend the recipe. - ---- +### Procedure -## Code standards +Use `` for ordered work. Multi-language SDK samples use `` with Node.js, Python, Go, and Java when the 90% rule applies. -All code in cookbooks must follow the project-wide standards from `CLAUDE.md`. Key rules for cookbooks: +### Verify / next steps -- **Multi-language**: Use `` with all four SDK languages (Node.js, Python, Go, Java) for at least 90% of SDK-related code blocks. -- **SDK variable names**: `scalekit` (Node.js), `scalekit_client` (Python), `scalekitClient` (Go/Java) — non-negotiable. -- **Working code**: Examples must be runnable or clearly marked as illustrative pseudocode. -- **No hardcoded secrets**: Always use environment variables. Add a comment explaining why. -- **Error handling**: Show the failure path alongside the happy path. - ---- - -## Authoring checklist - -Use this before submitting a cookbook for review. - -### Content - -- [ ] Title is verb-first, task-focused, ≤60 characters -- [ ] Description states the problem and outcome, ≤160 characters -- [ ] Excerpt is 2–3 sentences, no hype or filler -- [ ] Opening paragraph states context, difficulty, and what reader will learn -- [ ] P.A.T. structure is present: problem → angle → teach -- [ ] "The problem" section uses concrete pain points, not vague descriptions -- [ ] Solution is explained before code, not just introduced by it -- [ ] Each implementation step has a task-oriented heading -- [ ] Common mistakes section exists - -### Code - -- [ ] All code uses correct SDK variable names (`scalekit`, `scalekit_client`, `scalekitClient`) -- [ ] SDK examples cover all four languages (90% rule) -- [ ] No hardcoded secrets -- [ ] Error paths are shown -- [ ] Code compiles or is clearly marked pseudocode - -### Writing - -- [ ] Sentence case for all headings -- [ ] No "just", "simply", "obviously", "we're excited" -- [ ] Active voice throughout -- [ ] Second person ("you") for instructions -- [ ] Present tense ("this method returns" not "will return") -- [ ] Technical terms defined on first use -- [ ] Links use descriptive text (not "click here") - -### Frontmatter - -- [ ] `title`, `description`, `date`, `tags`, `excerpt`, `authors` all present -- [ ] Tags match existing vocabulary -- [ ] Author photo exists at the specified path - ---- - -## Review checklist - -Use this when reviewing a cookbook authored by someone else. - -### P.A.T. pass - -- [ ] Can you identify the exact problem in the first two paragraphs? -- [ ] Is the angle (why this approach) stated, not implied? -- [ ] Does the teach section teach — or just show code? - -### Accuracy pass - -- [ ] Code examples have been tested against the current SDK -- [ ] API method names match current SDK docs -- [ ] Environment variable names are consistent with other cookbooks -- [ ] No steps assume undocumented behavior - -### Audience pass - -- [ ] Would a developer new to Scalekit understand this? -- [ ] Are prerequisites explicitly stated? -- [ ] Does it avoid assuming reader knows internal Scalekit architecture? - -### Quality pass - -- [ ] Does every section earn its place? Remove anything that doesn't add value. -- [ ] Are the "Common mistakes" real mistakes, or invented edge cases? -- [ ] Does the cookbook feel like it was written by someone who solved this problem, or like it was written to explain a feature? - ---- - -## Prompt for AI-assisted authoring - -When using an AI assistant to draft or rewrite a cookbook, use the following prompt. Provide your rough draft after the prompt. - -```text -You are a senior technical content strategist and developer education writer. - -PRIMARY DIRECTIVE - -Transform my rough draft into a task-oriented developer cookbook for src/content/docs/cookbooks/. - -This cookbook must: -- help developers accomplish a specific, concrete implementation task, -- teach through example-backed, implementation-oriented content, -- separate any orientation context from hands-on recipes, -- use task-first naming throughout, -- and be independently useful — a developer should land on this, solve the problem, and move on. - -Apply the P.A.T. framework to the whole cookbook and to each major section: -1. Problem — Start from the real developer pain point -2. Angle — Give a clear point of view on why this approach -3. Teach — Teach step by step with code, explanations, outcomes, and failure modes - -STRUCTURE DIRECTIVE - -Build the cookbook in up to three layers (use only the layers the content actually needs): - -1. Orientation (if needed) - - What problem this cookbook solves - - Who it is for and who should look elsewhere - - Mental model or architecture overview - - Recommended reading path - -2. Core concept (if the implementation pattern needs explaining first) - - Why this pattern exists - - Tradeoffs vs alternatives - - Security boundaries - -3. Recipes (required) - For each recipe, include: - - Title (task-oriented, sounds like a search query) - - The problem this solves - - When to use it - - Inputs / prerequisites - - Step-by-step implementation - - Code walkthrough (not just code dump) - - Expected outcome - - What could go wrong - - Production notes - - Related recipes - -NAMING RULES - -Name recipes and titles like concrete developer tasks: -- "Set up agent auth locally" -- "Pass user context through an auth layer" -- "Validate identity before tool execution" -- "Handle token refresh for long-running agent sessions" -- "Debug failed OAuth flows in Python" - -Avoid abstract theme titles like "Authentication concepts" or "Token handling." - -REPOSITORY REFERENCE - -For agent auth cookbooks, use as source of truth: -https://github.com/scalekit-developers/agent-auth-examples -(README.md, AGENTS.md, javascript/*, python/*) - -Extract reusable patterns from the repo — don't just summarize files. -Call out where JavaScript and Python implementations meaningfully differ. - -WRITING RULES - -- Share knowledge, not features -- Prefer one language as the primary walkthrough when clarity matters; show the other language only where the implementation differs -- No hype, no vague claims, no marketing copy -- Useful even for someone who has not yet adopted Scalekit -- Optimize for bookmarking: each recipe should stand alone - -SCALEKIT CODE CONVENTIONS - -- SDK variable names (non-negotiable): scalekit (Node.js), scalekit_client (Python), scalekitClient (Go/Java) -- Use for multi-language blocks -- No hardcoded secrets; use environment variables with a comment explaining why -- Show error paths alongside the happy path - -OUTPUT FORMAT - -Return in this order: - -1. Positioning - - 3 title options (verb-first, task-focused, ≤60 chars each) - - Recommended title + one-sentence promise - - Target audience - -2. Proposed structure - - Table of contents - - One-line purpose for each section - -3. Rewritten draft - - Full MDX with correct frontmatter (title, description, date, tags, excerpt, featured, authors — cover only if featured: true) - - P.A.T. structure throughout - - Multi-language code tabs where applicable - - Production notes and failure modes for each recipe - -4. Editorial notes - - What you changed and why - - Weak spots in the original draft - - Missing material to add - - Where real test output or additional examples would strengthen it - -Quality bar: the final result should feel like a practitioner wrote it from experience — something a developer would bookmark and return to. - ---- - -Audience context (fill in before pasting): -- Who is this for: -- Desired outcome: -- Constraints or scope limits: - ---- - -Here is my first draft: - -[PASTE DRAFT HERE] -``` - ---- +How the reader knows it worked; links to related journey docs or how-tos. -## Publishing +## Diagrams (d2) -Cookbooks are automatically listed at `/cookbooks/` once the `.mdx` file is committed and built. No sidebar configuration is needed — `starlight-blog` handles discovery and pagination. +If you add a `d2` code fence, generate and commit the SVG under `public/d2/docs//cookbooks/` after a local build (Netlify does not run `d2`). -To feature a cookbook on the index page, set `featured: true` in frontmatter. +## How-to guides -Authors appear on the cookbook detail page. Author photos should be placed in `public/images/blog/authors/` before the cookbook is published. +How-tos are short, single-task dashboard pages in `*/how-to/`. Prefer imperative titles ("Manage environments"). Keep them out of the product journey sidebars — they belong only in the guides topic. diff --git a/public/d2/docs/cookbooks/litellm-agentkit-inbox-triage-0.svg b/public/d2/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage-0.svg similarity index 100% rename from public/d2/docs/cookbooks/litellm-agentkit-inbox-triage-0.svg rename to public/d2/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage-0.svg diff --git a/public/d2/docs/cookbooks/litellm-agentkit-inbox-triage-1.svg b/public/d2/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage-1.svg similarity index 100% rename from public/d2/docs/cookbooks/litellm-agentkit-inbox-triage-1.svg rename to public/d2/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage-1.svg diff --git a/scripts/generate-llms-index.js b/scripts/generate-llms-index.js index ad3f7d6a1..8a99856a6 100644 --- a/scripts/generate-llms-index.js +++ b/scripts/generate-llms-index.js @@ -202,7 +202,12 @@ const OTHER_SECTIONS = [ }, { heading: 'Cookbooks & Examples', - match: (p) => p.startsWith('/cookbooks/') || p.startsWith('/resources/'), + match: (p) => + p.startsWith('/agentkit/cookbooks/') || + p.startsWith('/saaskit/cookbooks/') || + p.startsWith('/agentkit/how-to/') || + p.startsWith('/saaskit/how-to/') || + p.startsWith('/resources/'), }, { heading: 'Developer Kit & AI-Assisted Development', diff --git a/src/configs/llms.config.ts b/src/configs/llms.config.ts index 048fe13fa..705a434d2 100644 --- a/src/configs/llms.config.ts +++ b/src/configs/llms.config.ts @@ -63,7 +63,7 @@ Start with the Quickstart Collection, then follow the developer's question to th label: 'AgentKit', description: 'Complete AgentKit documentation with connectors, frameworks, and tool calling for AI agents', - paths: ['agentkit/**', 'dev-kit/ai-assisted-development/**', 'cookbooks/**'], + paths: ['agentkit/**', 'dev-kit/ai-assisted-development/**'], }, { label: 'AgentKit Frameworks', @@ -139,7 +139,8 @@ Start with the Quickstart Collection, then follow the developer's question to th '**/overview', // All overview pages '**/quickstart', // All quickstart guides 'agentkit/examples/**', // Framework examples (high value for agent queries) - 'cookbooks/**', // Practical cookbooks + 'agentkit/cookbooks/**', // Practical AgentKit cookbooks + 'saaskit/cookbooks/**', // Practical Auth for SaaS cookbooks 'fsa/data-modelling', // Critical data modeling guide 'authenticate/set-up-scalekit', // Initial setup 'authenticate/fsa/complete-login', // Core FSA flow diff --git a/src/configs/redirects.config.ts b/src/configs/redirects.config.ts index 49ae1c5ee..d6e4466f2 100644 --- a/src/configs/redirects.config.ts +++ b/src/configs/redirects.config.ts @@ -216,9 +216,10 @@ export const redirects = { // Coding agent guides moved from product quickstarts to /dev-kit/build-with-ai/ // Note: With trailingSlash: 'ignore', single redirect without slash handles both /path and /path/ variants - '/agentkit/start-agentkit-coding-agents': '/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/agentkit/start-agentkit-coding-agents': + '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', '/agent-auth/start-agent-auth-coding-agents': - '/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', '/authenticate/fsa/start-fsa-coding-agents': '/dev-kit/build-with-ai/full-stack-auth/', '/authenticate/mcp/start-mcp-auth-coding-agents': '/dev-kit/build-with-ai/mcp-auth/', '/authenticate/sso/start-sso-coding-agents': '/dev-kit/build-with-ai/sso/', @@ -226,12 +227,13 @@ export const redirects = { // Build with AI moved from /build-with-ai/ to /dev-kit/build-with-ai/ // Agent Auth variant now lives in cookbooks - '/agentkit/build-with-ai': '/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/agentkit/build-with-ai': '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', '/build-with-ai': '/dev-kit/build-with-ai/', '/build-with-ai/full-stack-auth': '/dev-kit/build-with-ai/full-stack-auth/', - '/build-with-ai/agent-auth': '/cookbooks/set-up-agentkit-with-your-coding-agent/', - '/dev-kit/build-with-ai/agentkit': '/cookbooks/set-up-agentkit-with-your-coding-agent/', - '/dev-kit/build-with-ai/agent-auth': '/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/build-with-ai/agent-auth': '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/dev-kit/build-with-ai/agentkit': '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/dev-kit/build-with-ai/agent-auth': + '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', '/build-with-ai/mcp-auth': '/dev-kit/build-with-ai/mcp-auth/', '/build-with-ai/sso': '/dev-kit/build-with-ai/sso/', '/build-with-ai/scim': '/dev-kit/build-with-ai/scim/', @@ -466,4 +468,46 @@ export const redirects = { '/sdks/go/reference': '/saaskit/sdks/go/', '/sdks/java': '/saaskit/sdks/java/', '/sdks/java/reference': '/saaskit/sdks/java/', + + // ============================================================================= + // COOKBOOK REDIRECTS (per-product shelves) + // ============================================================================= + '/cookbooks/apify-actor-per-user-oauth': '/agentkit/cookbooks/apify-actor-per-user-oauth/', + '/cookbooks/build-voice-assistant-1000-tools': + '/agentkit/cookbooks/build-voice-assistant-1000-tools/', + '/cookbooks/crewai-agentkit-email-triage': '/agentkit/cookbooks/crewai-agentkit-email-triage/', + '/cookbooks/daily-briefing-agent': '/agentkit/cookbooks/daily-briefing-agent/', + '/cookbooks/fastrouter-agentkit-tool-calling': + '/agentkit/cookbooks/fastrouter-agentkit-tool-calling/', + '/cookbooks/langsmith-tracing-agentkit': '/agentkit/cookbooks/langsmith-tracing-agentkit/', + '/cookbooks/litellm-agentkit-inbox-triage': '/agentkit/cookbooks/litellm-agentkit-inbox-triage/', + '/cookbooks/livekit-agentkit-voice-tool-calling': + '/agentkit/cookbooks/livekit-agentkit-voice-tool-calling/', + '/cookbooks/mastra-agentkit': '/agentkit/cookbooks/mastra-agentkit/', + '/cookbooks/render-github-pr-summarizer': '/agentkit/cookbooks/render-github-pr-summarizer/', + '/cookbooks/schedule-meeting-and-draft-email': + '/agentkit/cookbooks/schedule-meeting-and-draft-email/', + '/cookbooks/set-up-agentkit-with-your-coding-agent': + '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/cookbooks/add-enterprise-sso-nextjs-authjs': + '/saaskit/cookbooks/add-enterprise-sso-nextjs-authjs/', + '/cookbooks/add-hosted-auth-nextjs-app-router': + '/saaskit/cookbooks/add-hosted-auth-nextjs-app-router/', + '/cookbooks/building-custom-org-switcher': '/saaskit/cookbooks/building-custom-org-switcher/', + '/cookbooks/implement-nextjs-auth': '/saaskit/cookbooks/implement-nextjs-auth/', + '/cookbooks/java-spring-boot-jwt-timeout': '/saaskit/cookbooks/java-spring-boot-jwt-timeout/', + '/cookbooks/m2m-jwks-and-oauth-scopes': '/saaskit/cookbooks/m2m-jwks-and-oauth-scopes/', + '/cookbooks/migrate-from-auth0-to-scalekit': '/saaskit/cookbooks/migrate-from-auth0-to-scalekit/', + '/cookbooks/scim-seat-limit-enforcement': '/saaskit/cookbooks/scim-seat-limit-enforcement/', + '/cookbooks/search-scalekit-docs-in-your-ide': + '/saaskit/cookbooks/search-scalekit-docs-in-your-ide/', + '/cookbooks/sync-b2b-billing-with-chargebee': + '/saaskit/cookbooks/sync-b2b-billing-with-chargebee/', + + // ============================================================================= + // WORKSPACE HOW-TO REDIRECTS + // ============================================================================= + '/dev-kit/guides/dashboard/billing': '/saaskit/how-to/billing/', + '/dev-kit/guides/dashboard/environments': '/saaskit/how-to/environments/', + '/dev-kit/guides/dashboard/manage-team-members': '/saaskit/how-to/manage-team-members/', } diff --git a/src/configs/secondary-nav.config.ts b/src/configs/secondary-nav.config.ts index 1be051f06..36a67775c 100644 --- a/src/configs/secondary-nav.config.ts +++ b/src/configs/secondary-nav.config.ts @@ -75,6 +75,13 @@ const agentKitItems: NavItem[] = [ label: 'Enterprise Deployment', iconComponent: IconSolarServerPathOutline, }, + { + // Opens the dedicated AgentKit guides sidebar (Cookbooks + How-to collapsibles) + id: 'agentkit-guides', + href: '/agentkit/cookbooks/', + label: 'Guides', + iconComponent: IconLucideBookOpenText, + }, ] const saasKitItems: NavItem[] = [ @@ -175,14 +182,23 @@ const saasKitItems: NavItem[] = [ columnGroup: 'right', }, { - id: 'cookbooks', - href: '/cookbooks/', + id: 'saaskit-cookbooks', + href: '/saaskit/cookbooks/', label: 'Developer Resources', dropdownLabel: 'Cookbooks', iconComponent: IconLucideBookOpenText, description: 'Implement common patterns with step-by-step recipes', columnGroup: 'right', }, + { + id: 'saaskit-how-to', + href: '/saaskit/how-to/', + label: 'Developer Resources', + dropdownLabel: 'How-to guides', + iconComponent: IconLucideBookOpenText, + description: 'Short answers to dashboard, workspace, and account tasks', + columnGroup: 'right', + }, { id: 'code-samples', href: '/resources/code-samples/', diff --git a/src/configs/sidebar.config.ts b/src/configs/sidebar.config.ts index 58c368f0b..ba178c441 100644 --- a/src/configs/sidebar.config.ts +++ b/src/configs/sidebar.config.ts @@ -233,6 +233,43 @@ export const sidebar = [ }, ], }, + // Product guide shelves — dedicated sidebars entered from secondary nav, not journey rails + { + label: 'AgentKit guides', + id: 'agentkit-guides', + link: '/agentkit/cookbooks/', + icon: 'open-book', + items: [ + { + label: 'Cookbooks', + collapsed: false, + items: [{ autogenerate: { directory: 'agentkit/cookbooks' } }], + }, + { + label: 'How-to', + collapsed: false, + items: [{ autogenerate: { directory: 'agentkit/how-to' } }], + }, + ], + }, + { + label: 'Auth for SaaS guides', + id: 'saaskit-guides', + link: '/saaskit/cookbooks/', + icon: 'open-book', + items: [ + { + label: 'Cookbooks', + collapsed: false, + items: [{ autogenerate: { directory: 'saaskit/cookbooks' } }], + }, + { + label: 'How-to', + collapsed: false, + items: [{ autogenerate: { directory: 'saaskit/how-to' } }], + }, + ], + }, { label: 'Developer Kit', id: 'dev-kit', @@ -622,6 +659,20 @@ export const topics = { // Agent connectors (dedicated connectors sidebar — must come before connect) 'agent-connectors': ['/agentkit/connectors/**/*'], + // Product guide shelves (before connect catch-all and resources) + 'agentkit-guides': [ + '/agentkit/cookbooks', + '/agentkit/cookbooks/**/*', + '/agentkit/how-to', + '/agentkit/how-to/**/*', + ], + 'saaskit-guides': [ + '/saaskit/cookbooks', + '/saaskit/cookbooks/**/*', + '/saaskit/how-to', + '/saaskit/how-to/**/*', + ], + // Product SDK sidebars (before connect catch-all) 'agentkit-sdks': ['/agentkit/sdks/**/*'], 'saaskit-sdks': ['/saaskit/sdks/**/*', '/sdks', '/sdks/', '/sdks/expo/**/*', '/sdks/ios/**/*'], @@ -641,8 +692,7 @@ export const topics = { '/guides/**/*', '/browse/**/*', '/reference/**/*', - '/cookbooks', - '/cookbooks/**/*', + '/cookbooks', // Cross-product hub only '/**/*', // Catch-all: anything not matched above defaults here ], @@ -725,6 +775,16 @@ export const sidebarToSecondaryNav: Record = { // Agent connectors sidebar → AgentKit Connectors tab 'agent-connectors': 'agentkit-connectors', + // Product guide shelves → secondary nav entries + 'agentkit-guides': 'agentkit-guides', + 'saaskit-guides': { + default: 'saaskit-cookbooks', + pathOverrides: { + '/saaskit/cookbooks': 'saaskit-cookbooks', + '/saaskit/how-to': 'saaskit-how-to', + }, + }, + // AgentKit sidebar → AgentKit tabs connect: { default: 'agentkit-quickstart', @@ -762,7 +822,7 @@ export const sidebarToSecondaryNav: Record = { '/authenticate/interceptors': 'workflows', '/reference/interceptors': 'workflows', '/reference/admin-portal': 'workflows', - '/cookbooks': 'cookbooks', + '/cookbooks': 'saaskit-cookbooks', }, }, diff --git a/src/content.config.ts b/src/content.config.ts index c2e78caa6..bd796f73c 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -5,15 +5,13 @@ import { topicSchema } from 'starlight-sidebar-topics/schema' import { videosSchema } from 'starlight-videos/schemas' import { githubReleasesLoader } from 'astro-loader-github-releases' import { githubFilesLoader } from './loaders/github-files-loader' -import { blogSchema } from 'starlight-blog/schema' export const collections = { docs: defineCollection({ loader: docsLoader(), schema: docsSchema({ extend: (context) => - blogSchema(context) - .merge(topicSchema) + topicSchema .merge(videosSchema) .merge(z.object({ overviewTitle: z.string().optional() })) .merge( diff --git a/src/content/docs/cookbooks/apify-actor-per-user-oauth.mdx b/src/content/docs/agentkit/cookbooks/apify-actor-per-user-oauth.mdx similarity index 97% rename from src/content/docs/cookbooks/apify-actor-per-user-oauth.mdx rename to src/content/docs/agentkit/cookbooks/apify-actor-per-user-oauth.mdx index f46b74098..67a63e45c 100644 --- a/src/content/docs/cookbooks/apify-actor-per-user-oauth.mdx +++ b/src/content/docs/agentkit/cookbooks/apify-actor-per-user-oauth.mdx @@ -1,17 +1,8 @@ --- title: 'Apify Actor with per-user OAuth via Scalekit' description: 'Build an Apify Actor that uses Scalekit Agent Auth so each user connects their OAuth accounts, keyed by Apify userId.' -date: 2026-04-21 sidebar: label: 'Apify Actor per-user OAuth' -excerpt: > - Apify Actors run in isolated containers with no persistent session — there is no concept of "who is logged in." This recipe shows how to use Apify's built-in user identity as the key into Scalekit's OAuth token vault, so each user who runs your Actor connects their own third-party accounts, and tokens survive across runs without any user-managed input fields. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/build-voice-assistant-1000-tools.mdx b/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx similarity index 97% rename from src/content/docs/cookbooks/build-voice-assistant-1000-tools.mdx rename to src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx index 74f293f42..8735e3f0f 100644 --- a/src/content/docs/cookbooks/build-voice-assistant-1000-tools.mdx +++ b/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx @@ -1,19 +1,9 @@ --- title: 'Build a Vapi voice assistant with Scalekit' description: 'Use Vapi + Scalekit Virtual MCP for voice assistants to securely access any tool from large catalogs.' -date: 2026-07-01 -tags: ['Agent auth', 'Voice', 'MCP', 'Vapi'] sidebar: label: 'Vapi + Scalekit assistant' tableOfContents: true -excerpt: > - Voice assistants are powerful for natural interaction but hit walls with auth and tool volume. This cookbook shows how to use Scalekit's Virtual MCP so a Vapi assistant can safely discover and call any tool the user is authorized for — without token bloat or per-tool OAuth code. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/crewai-agentkit-email-triage.mdx b/src/content/docs/agentkit/cookbooks/crewai-agentkit-email-triage.mdx similarity index 96% rename from src/content/docs/cookbooks/crewai-agentkit-email-triage.mdx rename to src/content/docs/agentkit/cookbooks/crewai-agentkit-email-triage.mdx index 4ad4b82bc..2a6a1c14c 100644 --- a/src/content/docs/cookbooks/crewai-agentkit-email-triage.mdx +++ b/src/content/docs/agentkit/cookbooks/crewai-agentkit-email-triage.mdx @@ -1,22 +1,9 @@ --- title: 'Build a multi-agent email triage crew with CrewAI' description: 'Use CrewAI multi-agent orchestration with Scalekit-authenticated Gmail tools to scan, classify, and draft replies to emails.' -date: 2026-07-12 sidebar: label: 'CrewAI email triage' tableOfContents: true -excerpt: > - CrewAI lets you split complex workflows across specialized agents, but - each agent still needs authenticated access to user tools like Gmail. This - cookbook shows how to wire Scalekit OAuth into a CrewAI crew via MCP, - then build a three-agent pipeline that scans, classifies, and drafts - replies to a user's unread emails. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/daily-briefing-agent.mdx b/src/content/docs/agentkit/cookbooks/daily-briefing-agent.mdx similarity index 97% rename from src/content/docs/cookbooks/daily-briefing-agent.mdx rename to src/content/docs/agentkit/cookbooks/daily-briefing-agent.mdx index f4738cff9..ce89d6d10 100644 --- a/src/content/docs/cookbooks/daily-briefing-agent.mdx +++ b/src/content/docs/agentkit/cookbooks/daily-briefing-agent.mdx @@ -1,17 +1,8 @@ --- title: 'Build a daily briefing agent with Vercel AI SDK and Scalekit Agent Auth' description: 'Connect a TypeScript or Python agent via Vercel AI SDK and Scalekit AgentKit to Google Calendar and Gmail with authenticated tool calls.' -date: 2026-03-27 sidebar: label: 'Daily briefing agent' -excerpt: > - Connecting an agent to two external APIs means handling two separate OAuth tokens, two authorization flows, and two different error surfaces. This recipe shows how Scalekit manages the OAuth lifecycle for both connectors and how you call Calendar and Gmail through built-in tools — without talking to provider APIs yourself. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/fastrouter-agentkit-tool-calling.mdx b/src/content/docs/agentkit/cookbooks/fastrouter-agentkit-tool-calling.mdx similarity index 95% rename from src/content/docs/cookbooks/fastrouter-agentkit-tool-calling.mdx rename to src/content/docs/agentkit/cookbooks/fastrouter-agentkit-tool-calling.mdx index 939fbcae4..50314e2ec 100644 --- a/src/content/docs/cookbooks/fastrouter-agentkit-tool-calling.mdx +++ b/src/content/docs/agentkit/cookbooks/fastrouter-agentkit-tool-calling.mdx @@ -1,18 +1,9 @@ --- title: 'FastRouter + Scalekit tool calling' description: 'Build a Node.js agent that routes LLM calls through FastRouter and uses Scalekit for per-user OAuth tools.' -date: 2026-05-26 sidebar: label: 'Tool calling with FastRouter' tableOfContents: true -excerpt: > - Connect FastRouter's OpenAI-compatible API to per-user OAuth tools via Scalekit. The agent discovers available tools, runs an agentic loop through FastRouter, and executes each tool call via Scalekit — no per-integration OAuth code required. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components'; @@ -350,4 +341,4 @@ const { tools } = await scalekit.tools.listScopedTools('user_123', { - **[Scalekit overview](/agentkit/connections)** — Understand connected accounts, tool discovery, and tool execution in depth. - **[AgentKit connections](/agentkit/connectors)** — Set up Gmail, GitHub, Slack, and other connections. - **[OpenAI example](/agentkit/examples/openai)** — See the same tool-calling pattern with OpenAI directly. -- **[LiteLLM inbox triage cookbook](/cookbooks/litellm-agentkit-inbox-triage)** — A more complex multi-connection agent with a web approval interface. +- **[LiteLLM inbox triage cookbook](/agentkit/cookbooks/litellm-agentkit-inbox-triage)** — A more complex multi-connection agent with a web approval interface. diff --git a/src/content/docs/agentkit/cookbooks/index.mdx b/src/content/docs/agentkit/cookbooks/index.mdx new file mode 100644 index 000000000..e33459b81 --- /dev/null +++ b/src/content/docs/agentkit/cookbooks/index.mdx @@ -0,0 +1,12 @@ +--- +title: 'AgentKit cookbooks' +description: 'Step-by-step recipes for connecting agents to tools and third-party APIs with Scalekit AgentKit.' +sidebar: + label: 'Overview' + order: 1 +tableOfContents: false +--- + +AgentKit cookbooks are self-contained recipes. Pick a task, follow it end to end, and adapt the code. + +Browse the full list in the left sidebar under **Cookbooks**. For short dashboard answers, open **How-to**. diff --git a/src/content/docs/cookbooks/langsmith-tracing-agentkit.mdx b/src/content/docs/agentkit/cookbooks/langsmith-tracing-agentkit.mdx similarity index 95% rename from src/content/docs/cookbooks/langsmith-tracing-agentkit.mdx rename to src/content/docs/agentkit/cookbooks/langsmith-tracing-agentkit.mdx index 76631fbbe..a0c8a0211 100644 --- a/src/content/docs/cookbooks/langsmith-tracing-agentkit.mdx +++ b/src/content/docs/agentkit/cookbooks/langsmith-tracing-agentkit.mdx @@ -1,18 +1,9 @@ --- title: 'Trace AgentKit tool calls in LangSmith' description: 'Add LangSmith observability to a LangChain agent that uses Scalekit AgentKit tools for Gmail, Slack, GitHub, and 200+ connectors.' -date: 2026-05-12 sidebar: label: 'LangSmith tracing' tableOfContents: true -excerpt: > - Scalekit AgentKit returns native LangChain StructuredTool objects. Enable LangSmith tracing and every tool call — Gmail fetches, Slack messages, GitHub searches — appears as a traced span automatically. This recipe walks through setup, a working agent, and verifying traces in the LangSmith dashboard. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/litellm-agentkit-inbox-triage.mdx b/src/content/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage.mdx similarity index 96% rename from src/content/docs/cookbooks/litellm-agentkit-inbox-triage.mdx rename to src/content/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage.mdx index 5c752cd60..69dfa108b 100644 --- a/src/content/docs/cookbooks/litellm-agentkit-inbox-triage.mdx +++ b/src/content/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage.mdx @@ -1,18 +1,9 @@ --- title: 'Triage a Gmail inbox with AgentKit and the LiteLLM gateway' description: 'Node.js inbox triage agent: classify Gmail threads, route to GitHub repos, draft issues and replies via LiteLLM, and approve before any side effects.' -date: 2026-05-06 sidebar: label: 'Inbox triage + LiteLLM' tableOfContents: true -excerpt: > - Poll Gmail with AgentKit tools, send each new thread through a multi-stage LiteLLM pipeline with per-stage models, notify Slack, then approve filing a GitHub issue and sending a reply from a localhost dashboard. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/livekit-agentkit-voice-tool-calling.mdx b/src/content/docs/agentkit/cookbooks/livekit-agentkit-voice-tool-calling.mdx similarity index 96% rename from src/content/docs/cookbooks/livekit-agentkit-voice-tool-calling.mdx rename to src/content/docs/agentkit/cookbooks/livekit-agentkit-voice-tool-calling.mdx index e59be1060..a597db031 100644 --- a/src/content/docs/cookbooks/livekit-agentkit-voice-tool-calling.mdx +++ b/src/content/docs/agentkit/cookbooks/livekit-agentkit-voice-tool-calling.mdx @@ -1,18 +1,9 @@ --- title: 'Build a LiveKit voice agent with Scalekit AgentKit tools' description: 'Give a LiveKit voice agent secure access to Google Calendar and 200+ AgentKit connectors — no token ever reaches the browser or the LLM.' -date: 2026-07-02 sidebar: label: 'LiveKit voice agent' tableOfContents: true -excerpt: > - A voice agent that checks a calendar or sends an email needs an OAuth token for that API — and LiveKit's Agents framework has no opinion on where that token comes from. This cookbook wires Scalekit AgentKit into a LiveKit Node agent using direct tool calls, carrying the user's identity through LiveKit's own dispatch metadata so the token never reaches the browser or the LLM. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/mastra-agentkit.mdx b/src/content/docs/agentkit/cookbooks/mastra-agentkit.mdx similarity index 96% rename from src/content/docs/cookbooks/mastra-agentkit.mdx rename to src/content/docs/agentkit/cookbooks/mastra-agentkit.mdx index 2f4eea223..067ca2d1d 100644 --- a/src/content/docs/cookbooks/mastra-agentkit.mdx +++ b/src/content/docs/agentkit/cookbooks/mastra-agentkit.mdx @@ -1,18 +1,9 @@ --- title: 'Build a Mastra agent with Scalekit AgentKit tools' description: 'Give a Mastra agent access to Gmail and 200+ connectors through Scalekit AgentKit — zero manual OAuth handling.' -date: 2026-05-19 sidebar: label: 'Mastra AgentKit' tableOfContents: true -excerpt: > - Mastra agents need tools. Each third-party API — Gmail, Slack, Calendar — means another OAuth flow, another token store, another refresh cycle. This recipe connects a Mastra agent to Scalekit AgentKit tools using the Node SDK, with automatic authorization and token refresh. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/render-github-pr-summarizer.mdx b/src/content/docs/agentkit/cookbooks/render-github-pr-summarizer.mdx similarity index 98% rename from src/content/docs/cookbooks/render-github-pr-summarizer.mdx rename to src/content/docs/agentkit/cookbooks/render-github-pr-summarizer.mdx index 6a986d660..e60ecec03 100644 --- a/src/content/docs/cookbooks/render-github-pr-summarizer.mdx +++ b/src/content/docs/agentkit/cookbooks/render-github-pr-summarizer.mdx @@ -1,18 +1,9 @@ --- title: 'Build a multi-user GitHub PR summarizer agent' description: 'Build a GitHub PR summarizer that binds each connected GitHub account to a secure browser session instead of trusting a client-supplied user ID.' -date: 2026-04-11 sidebar: label: 'GitHub PR summarizer' tableOfContents: true -excerpt: > - Build a GitHub PR summarizer that ranks the most-discussed pull requests in a repository and writes plain-language summaries. The secure version of this recipe binds each GitHub connection to a server-side session and never accepts a user ID from the browser. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/schedule-meeting-and-draft-email.mdx b/src/content/docs/agentkit/cookbooks/schedule-meeting-and-draft-email.mdx similarity index 96% rename from src/content/docs/cookbooks/schedule-meeting-and-draft-email.mdx rename to src/content/docs/agentkit/cookbooks/schedule-meeting-and-draft-email.mdx index f57e1399e..22d4a73a9 100644 --- a/src/content/docs/cookbooks/schedule-meeting-and-draft-email.mdx +++ b/src/content/docs/agentkit/cookbooks/schedule-meeting-and-draft-email.mdx @@ -1,15 +1,6 @@ --- title: 'Build an agent that books meetings and drafts emails' description: 'Connect a Python agent to Google Calendar and Gmail via Scalekit to find free slots, book meetings, and draft follow-up emails.' -date: 2026-03-06 -excerpt: > - Building a scheduling agent means coordinating authentication to two separate tools — Google Calendar and Gmail — then chaining their outputs in one workflow. Without managed OAuth, each connector requires its own token lifecycle and error-handling logic. This recipe shows how Scalekit handles auth per connector so your agent can focus on finding a free slot, creating the event, and drafting the confirmation. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Steps, TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/set-up-agentkit-with-your-coding-agent.mdx b/src/content/docs/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent.mdx similarity index 90% rename from src/content/docs/cookbooks/set-up-agentkit-with-your-coding-agent.mdx rename to src/content/docs/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent.mdx index de866ca13..c31507b28 100644 --- a/src/content/docs/cookbooks/set-up-agentkit-with-your-coding-agent.mdx +++ b/src/content/docs/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent.mdx @@ -1,19 +1,8 @@ --- title: 'Set up AgentKit with your coding agent' description: 'Add Scalekit Agent Auth to your codebase using Claude Code, Codex, GitHub Copilot CLI, Cursor, or any of 40+ coding agents.' -date: 2026-04-15 sidebar: label: 'Set up AgentKit with coding agents' -excerpt: > - Install the authstack plugin into your coding agent and paste one - prompt. The agent scaffolds OAuth handling, token management, and connected - account logic so you can start writing agent logic immediately. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { TabItem, Aside } from '@astrojs/starlight/components' diff --git a/src/content/docs/agentkit/examples/crewai.mdx b/src/content/docs/agentkit/examples/crewai.mdx index 67a049735..1ba67b606 100644 --- a/src/content/docs/agentkit/examples/crewai.mdx +++ b/src/content/docs/agentkit/examples/crewai.mdx @@ -113,7 +113,7 @@ with MCPServerAdapter({ ## Multi-agent crew -CrewAI's real strength is multi-agent orchestration. For a full example that splits email triage across three specialized agents (scanner, prioritizer, drafter), see the [CrewAI email triage cookbook](/cookbooks/crewai-agentkit-email-triage/). +CrewAI's real strength is multi-agent orchestration. For a full example that splits email triage across three specialized agents (scanner, prioritizer, drafter), see the [CrewAI email triage cookbook](/agentkit/cookbooks/crewai-agentkit-email-triage/). ## Get the MCP server URL diff --git a/src/content/docs/agentkit/how-to/index.mdx b/src/content/docs/agentkit/how-to/index.mdx new file mode 100644 index 000000000..125561766 --- /dev/null +++ b/src/content/docs/agentkit/how-to/index.mdx @@ -0,0 +1,12 @@ +--- +title: 'AgentKit how-to guides' +description: 'Short answers for AgentKit dashboard and workspace tasks.' +sidebar: + label: 'Overview' + order: 1 +tableOfContents: false +--- + +How-to guides answer one dashboard or workspace question at a time. They are not full implementation journeys. + +Browse the list in the left sidebar under **How-to**. For longer build recipes, open **Cookbooks**. diff --git a/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx b/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx new file mode 100644 index 000000000..aecebc086 --- /dev/null +++ b/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx @@ -0,0 +1,56 @@ +--- +title: 'Inspect a user connection in the dashboard' +description: 'Find a specific connected account in the Scalekit dashboard and read its state when an agent fails a tool call.' +sidebar: + label: 'Inspect a user connection' +tableOfContents: true +--- + +import { Aside } from '@astrojs/starlight/components' + +When an agent suddenly cannot reach a user's Gmail, Calendar, or GitHub, the cause is almost always +the state of that user's **connected account** rather than your code. The dashboard shows that state +directly, which is faster than adding logging and redeploying. + +## Find the connection + +Go to **Dashboard > Connections** and select the connection the agent uses — the name you pass as +`connection_name` in your code, such as `github-connect`. + +Each connection lists the connected accounts created against it. One row exists per user identifier +you have authorized. + + + +## Read the state + +The row's status is the diagnosis. Only `ACTIVE` accounts can serve tool calls. + +| State | What it means | What to do | +| --- | --- | --- | +| `ACTIVE` | Credentials are valid and tool calls will work | Look elsewhere — the connection is healthy | +| `EXPIRED` | The access token expired | Send the user a fresh authorization link | +| `PENDING_AUTH` | The user never finished authenticating, or re-authentication is in progress | Send or re-send the authorization link | +| `PENDING_VERIFICATION` | OAuth finished but identity verification has not | Have the user complete verification | +| `DISCONNECTED` | The account was disconnected manually | Send a fresh authorization link | + +For what each state means in code, see +[Manage connected accounts](/agentkit/connected-accounts/). For the failure modes behind each one, +see [Troubleshoot connection errors](/agentkit/authentication/troubleshooting/). + +## Recover a broken connection + +Every non-`ACTIVE` state is fixed the same way: generate a new authorization link and send it to the +user. The link opens a Scalekit-hosted page that adapts to the connection's auth type, so you do not +branch on connector type in your own code. + +Once the user completes it, the row returns to `ACTIVE` and tool calls resume. + +## Check the connection itself + +If every account on a connection is failing rather than one, the problem is the connection, not the +users. Confirm on the connection's own page that its credentials are still valid — a rotated or +expired OAuth client on the provider side takes down every account beneath it at once. diff --git a/src/content/docs/authenticate/m2m/api-auth-quickstart.mdx b/src/content/docs/authenticate/m2m/api-auth-quickstart.mdx index 2a86ff306..885c08f61 100644 --- a/src/content/docs/authenticate/m2m/api-auth-quickstart.mdx +++ b/src/content/docs/authenticate/m2m/api-auth-quickstart.mdx @@ -28,7 +28,7 @@ import InstallSDK from '@components/templates/_installsdk.mdx'; APIs let your customers, partners, and external systems interact with your application and its data. You need authentication to ensure only authorized clients can consume your APIs. Scalekit helps you add OAuth 2.0-based client-credentials authentication to your API endpoints. -If you are new to JWT-based API authentication, read the cookbook **[M2M JWT verification with JWKS and OAuth scopes](/cookbooks/m2m-jwks-and-oauth-scopes/)** for foundational context before following the steps below. +If you are new to JWT-based API authentication, read the cookbook **[M2M JWT verification with JWKS and OAuth scopes](/saaskit/cookbooks/m2m-jwks-and-oauth-scopes/)** for foundational context before following the steps below. Here's how it works: @@ -294,7 +294,7 @@ Your App -> API Client: 7. Returns the protected resource - 1. **Retrieve the public key:** Fetch the appropriate public key from your Scalekit environment's [JSON Web Key Set (JWKS)](/cookbooks/m2m-jwks-and-oauth-scopes/#jwks-and-scalekit-keys) at `https:///keys`. Use the `kid` (Key ID) from the JWT header to identify the correct key. Cache the key according to standard JWKS practices. + 1. **Retrieve the public key:** Fetch the appropriate public key from your Scalekit environment's [JSON Web Key Set (JWKS)](/saaskit/cookbooks/m2m-jwks-and-oauth-scopes/#jwks-and-scalekit-keys) at `https:///keys`. Use the `kid` (Key ID) from the JWT header to identify the correct key. Cache the key according to standard JWKS practices. @@ -369,7 +369,7 @@ Your App -> API Client: 7. Returns the protected resource 5. ## Register API client's scopes - [OAuth scopes](/cookbooks/m2m-jwks-and-oauth-scopes/#oauth-scopes-for-machine-clients) are embedded in the access token and validated server-side using the Scalekit SDK. This ensures that API clients only access resources they're authorized for, adding an extra layer of security. + [OAuth scopes](/saaskit/cookbooks/m2m-jwks-and-oauth-scopes/#oauth-scopes-for-machine-clients) are embedded in the access token and validated server-side using the Scalekit SDK. This ensures that API clients only access resources they're authorized for, adding an extra layer of security. For example, you might create an API client for a customer's deployment service with scopes like `deploy:applications` and `read:deployments`. diff --git a/src/content/docs/cookbooks.mdx b/src/content/docs/cookbooks.mdx new file mode 100644 index 000000000..3a926c93b --- /dev/null +++ b/src/content/docs/cookbooks.mdx @@ -0,0 +1,24 @@ +--- +title: 'Cookbooks' +description: 'Step-by-step recipes for building with Scalekit, split by product.' +sidebar: + label: 'Cookbooks' +tableOfContents: false +--- + +import { CardGrid, LinkCard } from '@astrojs/starlight/components' + +Cookbooks are self-contained recipes. Each product keeps its own shelf, reached from that product’s secondary nav **Guides** or **Cookbooks** entry. + + + + + diff --git a/src/content/docs/dev-kit/build-with-ai/index.mdx b/src/content/docs/dev-kit/build-with-ai/index.mdx index 0e72ca08b..a558a9ea0 100644 --- a/src/content/docs/dev-kit/build-with-ai/index.mdx +++ b/src/content/docs/dev-kit/build-with-ai/index.mdx @@ -44,7 +44,7 @@ The CLI installs the authstack plugin (with AgentKit and SaaSKit skills) for Cla - Every enterprise customer wants their own IdP — Okta, Azure AD, Google Workspace. - Handling SAML handshakes and per-tenant routing before you've shipped a feature is - exhausting. This cookbook shows how to delegate that complexity to Scalekit and use - Auth.js to own the session layer, so enterprise SSO is an afternoon, not a sprint. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/add-hosted-auth-nextjs-app-router.mdx b/src/content/docs/saaskit/cookbooks/add-hosted-auth-nextjs-app-router.mdx similarity index 96% rename from src/content/docs/cookbooks/add-hosted-auth-nextjs-app-router.mdx rename to src/content/docs/saaskit/cookbooks/add-hosted-auth-nextjs-app-router.mdx index c16373ad3..9a66f983c 100644 --- a/src/content/docs/cookbooks/add-hosted-auth-nextjs-app-router.mdx +++ b/src/content/docs/saaskit/cookbooks/add-hosted-auth-nextjs-app-router.mdx @@ -1,17 +1,6 @@ --- title: 'Add Scalekit hosted auth to a Next.js app' description: 'Wire Scalekit hosted login into the Next.js App Router with server-side sessions, transparent token refresh, and logout.' -date: 2026-06-28 -tags: ['Full stack auth', 'Next.js'] -excerpt: > - The Next.js App Router runs auth on the server, but Scalekit's framework-agnostic guides assume Express-style request and response objects. - This cookbook maps the full hosted-login flow — redirect, code exchange, session cookies, refresh, and logout — onto Route Handlers, Server Components, and middleware. -featured: false -authors: - - name: 'Hashirr Lukmahn' - title: 'Software Engineer' - url: 'https://www.linkedin.com/in/hashirr-lukmahn/' - picture: '/images/blog/authors/hashirr-lukmahn.jpg' --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/building-custom-org-switcher.mdx b/src/content/docs/saaskit/cookbooks/building-custom-org-switcher.mdx similarity index 95% rename from src/content/docs/cookbooks/building-custom-org-switcher.mdx rename to src/content/docs/saaskit/cookbooks/building-custom-org-switcher.mdx index 16aa4abc7..52e1770cf 100644 --- a/src/content/docs/cookbooks/building-custom-org-switcher.mdx +++ b/src/content/docs/saaskit/cookbooks/building-custom-org-switcher.mdx @@ -1,14 +1,6 @@ --- title: 'Building a Custom Organization Switcher' description: 'Learn how to build your own organization switcher UI for complete control over multi-tenant user experiences.' -date: 2025-01-21 -excerpt: When users belong to multiple organizations, the default Scalekit organization switcher handles most use cases. However, some applications require deeper integration—a custom switcher embedded directly in your app's navigation, or a specialized UI that matches your design system. -featured: false -authors: - - name: 'Hashirr Lukmahn' - title: 'Software Engineer' - url: 'https://www.linkedin.com/in/hashirr-lukmahn/' - picture: '/images/blog/authors/hashirr-lukmahn.jpg' --- import { TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/implement-nextjs-auth.mdx b/src/content/docs/saaskit/cookbooks/implement-nextjs-auth.mdx similarity index 96% rename from src/content/docs/cookbooks/implement-nextjs-auth.mdx rename to src/content/docs/saaskit/cookbooks/implement-nextjs-auth.mdx index 8b0f813b3..44207f9ed 100644 --- a/src/content/docs/cookbooks/implement-nextjs-auth.mdx +++ b/src/content/docs/saaskit/cookbooks/implement-nextjs-auth.mdx @@ -1,14 +1,6 @@ --- title: 'Implement passwordless auth in Next.js 15' description: 'Add magic link and OTP authentication to your Next.js application using Scalekit''s headless API.' -date: 2025-02-19 -excerpt: Next.js 15's App Router requires server-first authentication. This guide shows you how to implement magic link + OTP passwordless login using Scalekit's headless API, keeping all security logic server-side while maintaining full control over your UI. -featured: false -authors: - - name: 'Hashirr Lukmahn' - title: 'Software Engineer' - url: 'https://www.linkedin.com/in/hashirr-lukmahn/' - picture: '/images/blog/authors/hashirr-lukmahn.jpg' --- import { TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/index.mdx b/src/content/docs/saaskit/cookbooks/index.mdx new file mode 100644 index 000000000..dd4321fbc --- /dev/null +++ b/src/content/docs/saaskit/cookbooks/index.mdx @@ -0,0 +1,12 @@ +--- +title: 'Auth for SaaS cookbooks' +description: 'Step-by-step recipes for login, SSO, SCIM, API auth, and related Auth for SaaS patterns.' +sidebar: + label: 'Overview' + order: 1 +tableOfContents: false +--- + +Auth for SaaS cookbooks are self-contained recipes. Pick a task, follow it end to end, and adapt the code. + +Browse the full list in the left sidebar under **Cookbooks**. For short dashboard and workspace answers, open **How-to**. diff --git a/src/content/docs/cookbooks/java-spring-boot-jwt-timeout.mdx b/src/content/docs/saaskit/cookbooks/java-spring-boot-jwt-timeout.mdx similarity index 91% rename from src/content/docs/cookbooks/java-spring-boot-jwt-timeout.mdx rename to src/content/docs/saaskit/cookbooks/java-spring-boot-jwt-timeout.mdx index 815443d17..b9736407b 100644 --- a/src/content/docs/cookbooks/java-spring-boot-jwt-timeout.mdx +++ b/src/content/docs/saaskit/cookbooks/java-spring-boot-jwt-timeout.mdx @@ -1,14 +1,6 @@ --- title: 'Configuring JWT Validation Timeouts in Spring Boot 4.0+' description: 'Fix connection timeout errors when validating Scalekit JWT tokens in Spring Boot 4.0.0 and later versions.' -date: 2025-02-19 -excerpt: Spring Boot 4.0.0 introduced stricter timeout defaults for JWT validation that can cause connection failures when verifying Scalekit tokens. This guide shows you how to configure appropriate timeouts for production environments. -featured: false -authors: - - name: 'Hashirr Lukmahn' - title: 'Software Engineer' - url: 'https://www.linkedin.com/in/hashirr-lukmahn/' - picture: '/images/blog/authors/hashirr-lukmahn.jpg' --- diff --git a/src/content/docs/cookbooks/m2m-jwks-and-oauth-scopes.mdx b/src/content/docs/saaskit/cookbooks/m2m-jwks-and-oauth-scopes.mdx similarity index 93% rename from src/content/docs/cookbooks/m2m-jwks-and-oauth-scopes.mdx rename to src/content/docs/saaskit/cookbooks/m2m-jwks-and-oauth-scopes.mdx index 5f078826e..11408d327 100644 --- a/src/content/docs/cookbooks/m2m-jwks-and-oauth-scopes.mdx +++ b/src/content/docs/saaskit/cookbooks/m2m-jwks-and-oauth-scopes.mdx @@ -1,14 +1,6 @@ --- title: 'M2M JWT verification with JWKS and OAuth scopes' description: 'How JSON Web Key Sets work with Scalekit, how to use the /keys endpoint to verify machine-to-machine tokens, and how OAuth scopes map to JWT claims for authorization.' -date: 2026-04-15 -excerpt: Machine-to-machine access tokens are JWTs signed by Scalekit. Your API verifies signatures using the published JWKS at /keys, then authorizes requests using the scopes embedded in each token. This cookbook ties those ideas together end to end. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/migrate-from-auth0-to-scalekit.mdx b/src/content/docs/saaskit/cookbooks/migrate-from-auth0-to-scalekit.mdx similarity index 96% rename from src/content/docs/cookbooks/migrate-from-auth0-to-scalekit.mdx rename to src/content/docs/saaskit/cookbooks/migrate-from-auth0-to-scalekit.mdx index dff88fecd..c3b5247f9 100644 --- a/src/content/docs/cookbooks/migrate-from-auth0-to-scalekit.mdx +++ b/src/content/docs/saaskit/cookbooks/migrate-from-auth0-to-scalekit.mdx @@ -1,18 +1,6 @@ --- title: 'Migrate from Auth0 to Scalekit' description: 'Move users, organizations, and enterprise SSO off Auth0 to Scalekit Full Stack Auth with a safe, incremental cutover.' -date: 2026-07-05 -tags: ['Full stack auth', 'Migration', 'SSO'] -excerpt: > - Switching a B2B app off Auth0 means moving users, organizations, and enterprise SSO - connections without locking anyone out. This recipe maps the Auth0 data model to Scalekit, - imports it with the SDK, and cuts over incrementally behind a feature flag. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/scim-seat-limit-enforcement.mdx b/src/content/docs/saaskit/cookbooks/scim-seat-limit-enforcement.mdx similarity index 97% rename from src/content/docs/cookbooks/scim-seat-limit-enforcement.mdx rename to src/content/docs/saaskit/cookbooks/scim-seat-limit-enforcement.mdx index 149060896..90c18f6e8 100644 --- a/src/content/docs/cookbooks/scim-seat-limit-enforcement.mdx +++ b/src/content/docs/saaskit/cookbooks/scim-seat-limit-enforcement.mdx @@ -1,20 +1,9 @@ --- title: 'Enforce seat limits with SCIM provisioning' description: 'Block over-quota user creation and alert admins when SCIM pushes users beyond your plan seat limit.' -date: 2026-04-14 sidebar: label: 'Enforce seat limits' tableOfContents: true -excerpt: > - When your customer's identity provider pushes more users than your plan allows, SCIM won't stop it. - This cookbook shows how to track provisioned users per organization, block creation when the quota - is hit, and notify admins — using two webhook events and a counter in your database. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside, Tabs, TabItem } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/search-scalekit-docs-in-your-ide.mdx b/src/content/docs/saaskit/cookbooks/search-scalekit-docs-in-your-ide.mdx similarity index 95% rename from src/content/docs/cookbooks/search-scalekit-docs-in-your-ide.mdx rename to src/content/docs/saaskit/cookbooks/search-scalekit-docs-in-your-ide.mdx index fc650982b..7b9d7e65c 100644 --- a/src/content/docs/cookbooks/search-scalekit-docs-in-your-ide.mdx +++ b/src/content/docs/saaskit/cookbooks/search-scalekit-docs-in-your-ide.mdx @@ -3,16 +3,6 @@ title: 'Search Scalekit docs with ref.tools' description: 'Configure ref.tools MCP to search Scalekit documentation directly from Cursor, Claude Code, or Windsurf without leaving your IDE.' sidebar: label: 'Search Scalekit docs in IDE' -date: 2026-03-26 -excerpt: > - Switching to a browser to look up Scalekit docs breaks your coding flow. - This cookbook shows you how to configure the ref.tools MCP server so your - AI coding assistant can search Scalekit documentation inline — in Cursor, - Claude Code, Windsurf, or any MCP-compatible client. -featured: false -authors: - - name: 'Saif' - title: 'Developer' --- import { Aside, Steps } from '@astrojs/starlight/components'; diff --git a/src/content/docs/cookbooks/sync-b2b-billing-with-chargebee.mdx b/src/content/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee.mdx similarity index 96% rename from src/content/docs/cookbooks/sync-b2b-billing-with-chargebee.mdx rename to src/content/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee.mdx index ff8a5001a..697bad80b 100644 --- a/src/content/docs/cookbooks/sync-b2b-billing-with-chargebee.mdx +++ b/src/content/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee.mdx @@ -1,21 +1,9 @@ --- title: 'Sync B2B billing with Scalekit and Chargebee' description: 'Map Scalekit organizations to Chargebee customers, run hosted checkout, and keep subscription state in sync via webhooks.' -date: 2026-06-15 sidebar: label: 'Chargebee B2B billing' tableOfContents: true -excerpt: > - B2B billing breaks when your auth org model and Chargebee customer records drift apart. - This cookbook shows how to use the Scalekit organization ID as the billing reference, - provision Chargebee customers on org creation, run hosted checkout, and reconcile - subscription state from Chargebee webhooks. -featured: false -authors: - - name: 'Saif' - title: 'Developer Advocate' - url: 'https://www.linkedin.com/in/saif-shines/' - picture: '/images/blog/authors/saif.png' --- import { Aside } from '@astrojs/starlight/components'; @@ -55,7 +43,7 @@ You **don't** need this if: Treat the Scalekit **organization ID** as the single billing reference for the tenant. Scalekit authenticates the user and org, your app owns the mapping and local subscription cache, and Chargebee owns catalog, checkout, and billing state. -![Architecture: User logs in via Scalekit, app provisions Chargebee customers and hosted checkout, Chargebee returns checkout success and subscription webhooks, app stores org mapping and subscription cache in local DB](@/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/architecture.png) +![Architecture: User logs in via Scalekit, app provisions Chargebee customers and hosted checkout, Chargebee returns checkout success and subscription webhooks, app stores org mapping and subscription cache in local DB](@/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/architecture.png) The integration has four seams: @@ -288,7 +276,7 @@ Return `2xx` after accepting the event. Scalekit retries on non-2xx responses. T In the reference app dashboard, steps 1–3 show **Done** and the org is linked to a Chargebee customer before you open billing: -![Dashboard after sign-in: organization linked to Chargebee customer, subscribe step current](@/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/02-dashboard-org-linked.png) +![Dashboard after sign-in: organization linked to Chargebee customer, subscribe step current](@/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/02-dashboard-org-linked.png) ## Step 6: Read the organization ID from the session @@ -414,11 +402,11 @@ The `future` row gives your app a stable ID to reconcile against before Chargebe In the demo, the billing page lists plans scoped to the session org. **Subscribe** calls your create route, then redirects to Chargebee hosted pages: -![Billing page: Growth plan with Subscribe, integration journey on subscribe step](@/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/03-billing-choose-plan.png) +![Billing page: Growth plan with Subscribe, integration journey on subscribe step](@/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/03-billing-choose-plan.png) -![Chargebee hosted cart: Growth Plan monthly with 14-day free trial](@/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/04-chargebee-cart.png) +![Chargebee hosted cart: Growth Plan monthly with 14-day free trial](@/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/04-chargebee-cart.png) -![Chargebee hosted checkout: account details, payment method, and order summary](@/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/05-chargebee-checkout.png) +![Chargebee hosted checkout: account details, payment method, and order summary](@/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/05-chargebee-checkout.png) ## Step 9: Configure Chargebee webhooks @@ -491,7 +479,7 @@ export async function POST(req: NextRequest) { After a successful checkout (and webhook or eager sync), the billing UI shows the live plan status — for example **In Trial** — and the journey marks subscribe and webhook sync as **Done**: -![Billing after checkout: In Trial subscription and integration journey complete through webhook sync](@/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/06-billing-subscription-active.png) +![Billing after checkout: In Trial subscription and integration journey complete through webhook sync](@/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/06-billing-subscription-active.png) ## Step 10: Eager-sync on checkout redirect diff --git a/src/content/docs/dev-kit/guides/dashboard/billing.mdx b/src/content/docs/saaskit/how-to/billing.mdx similarity index 100% rename from src/content/docs/dev-kit/guides/dashboard/billing.mdx rename to src/content/docs/saaskit/how-to/billing.mdx diff --git a/src/content/docs/dev-kit/guides/dashboard/environments.mdx b/src/content/docs/saaskit/how-to/environments.mdx similarity index 98% rename from src/content/docs/dev-kit/guides/dashboard/environments.mdx rename to src/content/docs/saaskit/how-to/environments.mdx index eeb30a5d5..451d514ca 100644 --- a/src/content/docs/dev-kit/guides/dashboard/environments.mdx +++ b/src/content/docs/saaskit/how-to/environments.mdx @@ -69,7 +69,7 @@ EU data residency is a **$99/month add-on per production environment**, billed s Other per-environment add-ons, such as **Customization Pro** (custom domain, branding removal, custom email templates), are also billed per region. Running Customization Pro on both a US and an EU production environment bills the add-on twice. -Add a payment method under **Workspace Settings → Billing** in the EU workspace so the add-on and usage-based charges succeed. See [Billing and usage](/dev-kit/guides/dashboard/billing/) for payment methods and plan details. +Add a payment method under **Workspace Settings → Billing** in the EU workspace so the add-on and usage-based charges succeed. See [Billing and usage](/saaskit/how-to/billing/) for payment methods and plan details. ## Access environment settings diff --git a/src/content/docs/saaskit/how-to/index.mdx b/src/content/docs/saaskit/how-to/index.mdx new file mode 100644 index 000000000..d4cf53f8b --- /dev/null +++ b/src/content/docs/saaskit/how-to/index.mdx @@ -0,0 +1,12 @@ +--- +title: 'Auth for SaaS how-to guides' +description: 'Short answers for Auth for SaaS dashboard, workspace, and account tasks.' +sidebar: + label: 'Overview' + order: 1 +tableOfContents: false +--- + +How-to guides answer one dashboard or workspace question at a time. They are not full implementation journeys. + +Browse the list in the left sidebar under **How-to**. For longer build recipes, open **Cookbooks**. diff --git a/src/content/docs/dev-kit/guides/dashboard/manage-team-members.mdx b/src/content/docs/saaskit/how-to/manage-team-members.mdx similarity index 100% rename from src/content/docs/dev-kit/guides/dashboard/manage-team-members.mdx rename to src/content/docs/saaskit/how-to/manage-team-members.mdx diff --git a/src/styles/custom.css b/src/styles/custom.css index 0031c6e84..2f265d774 100644 --- a/src/styles/custom.css +++ b/src/styles/custom.css @@ -615,33 +615,6 @@ UNLAYERED OVERRIDES - Must be outside @layer to beat third-party unlayered rules ============================================================================= */ -/* Fix: starlight-blog injects a global rule that hides .right-sidebar-panel and - mobile-starlight-toc on all pages (not just blog pages) when data-has-toc is set. - The rule [data-has-toc] :is(.right-sidebar-panel, ...) has specificity (0,2,0). - This rule uses html selector for specificity (0,2,1) to win without !important. */ -@media (min-width: 72rem) { - html[data-has-toc] .right-sidebar-panel { - display: block; - } -} - -/* Fix: starlight-blog's Blog.astro has :global(.content-panel:first-of-type) { display: none } - which leaks to ALL pages via Vite's shared CSS chunking (because starlight-blog's - MarkdownContent override is rendered on every page, pulling in the CSS bundle). - Blog pages are identifiable by .posts (blog index) or .post-footer (blog post) elements. - Specificity (0,2,1) beats Blog.astro's (0,1,0). */ -main:not(:has(.posts, .post-footer)) .content-panel:first-of-type { - display: block; -} - -/* Fix: starlight-blog's Blog.astro leaks :global(.content-panel:nth-of-type(2)) { border-top: none } - to ALL pages via Vite's shared CSS chunking. - Non-blog pages need the hairline border between title and content panels. - Specificity (0,2,1) beats Blog.astro's (0,1,0). */ -main:not(:has(.posts, .post-footer)) .content-panel:nth-of-type(2) { - border-top: 1px solid var(--sl-color-hairline); -} - /* Readable article column for `.sl-container` (unlayered so it wins over starlight-theme-nova; do not use 100%). */ :root, ::backdrop { From 20f2ad41073a27d48a02d62f96e7bfd6c9e71796 Mon Sep 17 00:00:00 2001 From: Saif Date: Thu, 13 Aug 2026 17:26:43 +0530 Subject: [PATCH 2/8] fix(dev): stop Netlify edge proxy from breaking /auth/refresh Local Netlify edge middleware mishandles empty-body POSTs and threw unhandled TypeError: fetch failed on every page load's session probe. Exclude /auth/* and /api/* from edge functions and send an explicit JSON body on refresh POSTs. --- netlify/edge-functions/serve-markdown.ts | 3 +++ netlify/edge-functions/track-agents.ts | 4 ++++ src/components/auth/AuthCTA.astro | 2 ++ src/components/auth/AuthValue.astro | 7 ++++++- src/utils/auth/session-client.ts | 2 ++ 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/netlify/edge-functions/serve-markdown.ts b/netlify/edge-functions/serve-markdown.ts index 8d8df0594..de509dfae 100644 --- a/netlify/edge-functions/serve-markdown.ts +++ b/netlify/edge-functions/serve-markdown.ts @@ -165,6 +165,9 @@ export const config: Config = { '/images/*', '/fonts/*', '/favicon*', + // Auth/API: not HTML→markdown; also avoids local edge empty-body POST proxy bugs. + '/auth/*', + '/api/*', '/*.js', '/*.css', '/*.json', diff --git a/netlify/edge-functions/track-agents.ts b/netlify/edge-functions/track-agents.ts index 44eedaf83..8565fea55 100644 --- a/netlify/edge-functions/track-agents.ts +++ b/netlify/edge-functions/track-agents.ts @@ -38,6 +38,10 @@ export const config = { '/fonts/*', '/favicon*', '/og/*', + // Auth API routes: local Netlify edge proxy mishandles empty-body POSTs + // (e.g. /auth/refresh) and surfaces TypeError: fetch failed as unhandled rejections. + '/auth/*', + '/api/*', '/*.js', '/*.css', '/*.png', diff --git a/src/components/auth/AuthCTA.astro b/src/components/auth/AuthCTA.astro index 570927b8d..f9414d981 100644 --- a/src/components/auth/AuthCTA.astro +++ b/src/components/auth/AuthCTA.astro @@ -119,6 +119,8 @@ const loginHref = '/auth/login' const refreshResponse = await fetch('/auth/refresh', { method: 'POST', credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: '{}', }) if (refreshResponse.ok) { response = await fetch('/auth/session', { credentials: 'include' }) diff --git a/src/components/auth/AuthValue.astro b/src/components/auth/AuthValue.astro index 35fafa3f5..0e3fbe4a5 100644 --- a/src/components/auth/AuthValue.astro +++ b/src/components/auth/AuthValue.astro @@ -47,7 +47,12 @@ const { path = '' } = Astro.props // Handle 401 by attempting token refresh if (response.status === 401) { - const refreshResponse = await fetch('/auth/refresh', { credentials: 'include' }) + const refreshResponse = await fetch('/auth/refresh', { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: '{}', + }) if (!refreshResponse.ok) { clearCache() diff --git a/src/utils/auth/session-client.ts b/src/utils/auth/session-client.ts index e6d5529a3..d56e2debc 100644 --- a/src/utils/auth/session-client.ts +++ b/src/utils/auth/session-client.ts @@ -57,6 +57,8 @@ export const getSession = async (): Promise => { const refreshResponse = await fetch('/auth/refresh', { method: 'POST', credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: '{}', }) if (refreshResponse.ok) { From 4b244f19361165bf32d4eb124e07e63ee3d0737a Mon Sep 17 00:00:00 2001 From: Saif Date: Thu, 13 Aug 2026 18:57:37 +0530 Subject: [PATCH 3/8] fix(docs): resolve review findings on cookbook and how-to shelves Move cookbook image assets to match the product-scoped page paths. The Chargebee cookbook already pointed at @/assets/docs/saaskit/cookbooks/ while the files still sat under the retired cookbooks/ namespace, which failed the production build with ImageNotFound. Local dev never caught it because it compiles pages only on visit. - Move chargebee assets to src/assets/docs/saaskit/cookbooks/ and voice assistant assets to src/assets/docs/agentkit/cookbooks/, repointing the two voice-assistant references that were still on the old path - Exclude the cross-product hub /cookbooks/ from sidebar topics and give it the splash template, so it no longer inherits the Auth for SaaS journey rail or lights up a product nav pill; link it from both product shelves - Redirect the routes starlight-blog used to generate: /cookbooks/tags/*, /cookbooks/authors/*, and pagination pages 2-5 - Rewrite the connected-account how-to to stop duplicating the status table and per-status remedies already in the AgentKit troubleshooting guide; it now covers dashboard navigation and delegates the rest - Correct the AgentKit how-to overview, which promised workspace tasks it does not ship, and cross-link the shared workspace guides from both how-to overviews - Keep /api/* tracked in track-agents: agents fetch the OpenAPI specs under it, and only /auth/refresh hit the empty-body POST proxy bug - Add saaskit/cookbooks and saaskit/how-to to the SaaSKit and Integration Guides llms.txt sets, which lost their cookbook paths in the move - Move the orphaned scim-simulator page into dev-kit/tools/ beside the other testing utilities, retiring the now-empty dev-kit/guides/ tree - Restore the P.A.T. framework, content-layer model, and naming rules to COOKBOOKS.md, and document the image and redirect rules for future moves --- netlify/edge-functions/serve-markdown.ts | 5 +- netlify/edge-functions/track-agents.ts | 5 +- project-docs/COOKBOOKS.md | 59 +++++++++++++++++- .../tool-registration-scalekit.png | Bin .../voice-assistant/vmcp-scalekit.png | Bin .../02-dashboard-org-linked.png | Bin .../03-billing-choose-plan.png | Bin .../04-chargebee-cart.png | Bin .../05-chargebee-checkout.png | Bin .../06-billing-subscription-active.png | Bin .../sync-b2b-billing-with-chargebee/README.md | 0 .../architecture.png | Bin src/configs/llms.config.ts | 3 + src/configs/redirects.config.ts | 16 +++++ src/configs/sidebar.config.ts | 6 +- .../build-voice-assistant-1000-tools.mdx | 4 +- src/content/docs/agentkit/cookbooks/index.mdx | 4 ++ src/content/docs/agentkit/how-to/index.mdx | 11 +++- ...ct-connected-accounts-in-the-dashboard.mdx | 43 ++++++------- src/content/docs/cookbooks.mdx | 3 +- .../testing => tools}/scim-simulator.mdx | 0 src/content/docs/saaskit/cookbooks/index.mdx | 4 ++ src/content/docs/saaskit/how-to/index.mdx | 4 ++ 23 files changed, 130 insertions(+), 37 deletions(-) rename src/assets/docs/{ => agentkit}/cookbooks/voice-assistant/tool-registration-scalekit.png (100%) rename src/assets/docs/{ => agentkit}/cookbooks/voice-assistant/vmcp-scalekit.png (100%) rename src/assets/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee/02-dashboard-org-linked.png (100%) rename src/assets/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee/03-billing-choose-plan.png (100%) rename src/assets/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee/04-chargebee-cart.png (100%) rename src/assets/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee/05-chargebee-checkout.png (100%) rename src/assets/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee/06-billing-subscription-active.png (100%) rename src/assets/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee/README.md (100%) rename src/assets/docs/{ => saaskit}/cookbooks/sync-b2b-billing-with-chargebee/architecture.png (100%) rename src/content/docs/dev-kit/{guides/testing => tools}/scim-simulator.mdx (100%) diff --git a/netlify/edge-functions/serve-markdown.ts b/netlify/edge-functions/serve-markdown.ts index de509dfae..9c89ce026 100644 --- a/netlify/edge-functions/serve-markdown.ts +++ b/netlify/edge-functions/serve-markdown.ts @@ -165,9 +165,10 @@ export const config: Config = { '/images/*', '/fonts/*', '/favicon*', - // Auth/API: not HTML→markdown; also avoids local edge empty-body POST proxy bugs. + // Auth routes serve JSON, never HTML→markdown, and the local edge proxy + // mishandles their empty-body POSTs. `/api/*` needs no entry — the handler + // only rewrites `text/html` responses, so spec files pass straight through. '/auth/*', - '/api/*', '/*.js', '/*.css', '/*.json', diff --git a/netlify/edge-functions/track-agents.ts b/netlify/edge-functions/track-agents.ts index 8565fea55..c9f8afc76 100644 --- a/netlify/edge-functions/track-agents.ts +++ b/netlify/edge-functions/track-agents.ts @@ -38,10 +38,11 @@ export const config = { '/fonts/*', '/favicon*', '/og/*', - // Auth API routes: local Netlify edge proxy mishandles empty-body POSTs + // Auth routes only: the local Netlify edge proxy mishandles empty-body POSTs // (e.g. /auth/refresh) and surfaces TypeError: fetch failed as unhandled rejections. + // `/api/*` stays tracked — agents fetch the OpenAPI specs under it, which is + // exactly the traffic this function exists to measure. '/auth/*', - '/api/*', '/*.js', '/*.css', '/*.png', diff --git a/project-docs/COOKBOOKS.md b/project-docs/COOKBOOKS.md index 7bc68d067..fbf58a465 100644 --- a/project-docs/COOKBOOKS.md +++ b/project-docs/COOKBOOKS.md @@ -28,12 +28,17 @@ New topic IDs in `src/configs/sidebar.config.ts`: Each uses `autogenerate` on the product directory. Drop a new `.mdx` file in the folder and it appears in the collapsible. -A cross-product hub remains at `/cookbooks/` (`src/content/docs/cookbooks.mdx`). +A cross-product hub remains at `/cookbooks/` (`src/content/docs/cookbooks.mdx`). It belongs to no +single product, so it is listed in `exclude` in `src/configs/sidebar.config.ts` — that keeps it from +inheriting a product journey rail or lighting up a product nav pill. Both product overview pages +link to it, and it links back to both shelves. ## What a cookbook is A cookbook is a practical, developer-focused guide that solves one specific real-world problem. It is not a feature announcement, a product tour, or a reference page. +The best cookbooks share knowledge, not features. They are useful even for developers who have not yet adopted Scalekit. Each cookbook should be independently useful — a developer should be able to land on one recipe, solve their problem, and continue without reading anything else. + **Cookbooks are:** - Recipes for solving a specific implementation problem @@ -47,6 +52,47 @@ A cookbook is a practical, developer-focused guide that solves one specific real - API reference documentation - Short dashboard answers (those belong in **How-to**) +### Two layers of content + +Every cookbook belongs to one of two layers: + +**Layer 1 — Orientation**: Mental models, architecture maps, prerequisites, recommended learning paths. Explains _why_ a pattern matters before showing how to implement it. Use this layer when the reader needs context before they can act. + +**Layer 2 — Recipes**: Concrete implementation tasks with working code, expected outcomes, failure modes, and production notes. Use this layer when the reader knows what they want to build and just needs the how. + +Most cookbooks are Layer 2. A cookbook may contain both layers — a brief orientation section followed by one or more recipes. + +## The P.A.T. framework + +Every cookbook must be structured around three layers: + +1. **Problem** — Start from the real developer pain, workflow, or use case. What is hard or broken without this recipe? +2. **Angle** — Give a clear point of view on the solution. Why this approach? What makes it better than the alternatives? +3. **Teach** — Teach step by step with code, explanations, expected outcomes, common mistakes, and extensions. + +Apply P.A.T. to the cookbook as a whole and to each major section. + +## Naming + +Filename rules: + +- Lowercase, hyphen-separated: `implement-nextjs-auth.mdx` +- Descriptive of the task, not the product: prefer `building-custom-org-switcher` over `scalekit-org-switcher` +- Name files like search queries a developer would type: `handle-token-refresh-long-running-agents.mdx`, `pass-user-context-to-tools.mdx`, `debug-failed-oauth-flows.mdx` + +**Title naming rules:** + +Name the cookbook like a concrete developer task. Prefer titles that expose the action, mechanism, or target outcome. + +| Bad | Better | +| ------------------------------------ | ----------------------------------------------------- | +| "Authentication concepts for agents" | "Set up agent auth in JavaScript" | +| "Scalekit org switcher" | "Build a custom organization switcher" | +| "Token handling" | "Handle token refresh in long-running agent sessions" | +| "User identity in agents" | "Pass user identity from your app to an agent safely" | + +Prefer title patterns: "How to…", "Build…", "Handle…", "Debug…", "Pass…", "Validate…", "Set up…" + ## Frontmatter Use normal Starlight docs frontmatter. Blog-only fields (`date`, `excerpt`, `featured`, `authors`, `tags`, `cover`) are not used. @@ -75,9 +121,18 @@ tableOfContents: true 1. Choose the product shelf (`agentkit/cookbooks` or `saaskit/cookbooks`). 2. Create `src/content/docs//cookbooks/.mdx`. 3. Write the recipe body (problem, steps, working code, failure modes). -4. If the page replaces an old `/cookbooks/` URL, add a redirect in `src/configs/redirects.config.ts`. +4. Put images under `src/assets/docs//cookbooks//` and reference them as + `@/assets/docs//cookbooks//.png`. 5. Run a local build or `pnpm start` and confirm the page appears under the product **Guides** / **Cookbooks** sidebar. +**Redirects:** a brand-new cookbook needs none. Only a page whose published URL changes needs an +entry in `src/configs/redirects.config.ts`, and it must be listed one slug at a time — a +`/cookbooks/*` splat cannot work, because the old flat namespace now splits across two products. + +**Images:** moving a cookbook means moving its assets in the same commit. A stale `@/assets/…` path +is a hard build failure (`[ImageNotFound]`), and `pnpm start` will not surface it because dev +compiles pages only when you visit them. Run a full `pnpm build` before pushing a move. + ## Content structure ### Opening (no heading) diff --git a/src/assets/docs/cookbooks/voice-assistant/tool-registration-scalekit.png b/src/assets/docs/agentkit/cookbooks/voice-assistant/tool-registration-scalekit.png similarity index 100% rename from src/assets/docs/cookbooks/voice-assistant/tool-registration-scalekit.png rename to src/assets/docs/agentkit/cookbooks/voice-assistant/tool-registration-scalekit.png diff --git a/src/assets/docs/cookbooks/voice-assistant/vmcp-scalekit.png b/src/assets/docs/agentkit/cookbooks/voice-assistant/vmcp-scalekit.png similarity index 100% rename from src/assets/docs/cookbooks/voice-assistant/vmcp-scalekit.png rename to src/assets/docs/agentkit/cookbooks/voice-assistant/vmcp-scalekit.png diff --git a/src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/02-dashboard-org-linked.png b/src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/02-dashboard-org-linked.png similarity index 100% rename from src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/02-dashboard-org-linked.png rename to src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/02-dashboard-org-linked.png diff --git a/src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/03-billing-choose-plan.png b/src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/03-billing-choose-plan.png similarity index 100% rename from src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/03-billing-choose-plan.png rename to src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/03-billing-choose-plan.png diff --git a/src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/04-chargebee-cart.png b/src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/04-chargebee-cart.png similarity index 100% rename from src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/04-chargebee-cart.png rename to src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/04-chargebee-cart.png diff --git a/src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/05-chargebee-checkout.png b/src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/05-chargebee-checkout.png similarity index 100% rename from src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/05-chargebee-checkout.png rename to src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/05-chargebee-checkout.png diff --git a/src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/06-billing-subscription-active.png b/src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/06-billing-subscription-active.png similarity index 100% rename from src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/06-billing-subscription-active.png rename to src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/06-billing-subscription-active.png diff --git a/src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/README.md b/src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/README.md similarity index 100% rename from src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/README.md rename to src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/README.md diff --git a/src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/architecture.png b/src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/architecture.png similarity index 100% rename from src/assets/docs/cookbooks/sync-b2b-billing-with-chargebee/architecture.png rename to src/assets/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee/architecture.png diff --git a/src/configs/llms.config.ts b/src/configs/llms.config.ts index 705a434d2..91f02e9ae 100644 --- a/src/configs/llms.config.ts +++ b/src/configs/llms.config.ts @@ -57,6 +57,8 @@ Start with the Quickstart Collection, then follow the developer's question to th 'directory/scim/**', 'guides/user-auth/**', 'guides/user-management/**', + 'saaskit/cookbooks/**', + 'saaskit/how-to/**', ], }, { @@ -122,6 +124,7 @@ Start with the Quickstart Collection, then follow the developer's question to th 'guides/integrations/index', 'guides/integrations/*/index', 'guides/dashboard/**', + 'saaskit/how-to/**', // Workspace/dashboard how-tos moved here from dev-kit/guides/dashboard 'dev-kit/api-collections/**', ], }, diff --git a/src/configs/redirects.config.ts b/src/configs/redirects.config.ts index d6e4466f2..21245a8bd 100644 --- a/src/configs/redirects.config.ts +++ b/src/configs/redirects.config.ts @@ -504,10 +504,26 @@ export const redirects = { '/cookbooks/sync-b2b-billing-with-chargebee': '/saaskit/cookbooks/sync-b2b-billing-with-chargebee/', + // Routes that `starlight-blog` generated and nothing replaces. It injected + // `/[...prefix]/tags/[tag]`, `/[...prefix]/authors/[author]`, and a paginated + // `/[...prefix]/[...page]` (default 5 posts per page, so pages 2–5 existed for + // 22 cookbooks). Collapse all of them onto the hub instead of serving 404s. + // Listed after the slug redirects above; these patterns cannot shadow them. + '/cookbooks/tags/*': '/cookbooks/', + '/cookbooks/authors/*': '/cookbooks/', + '/cookbooks/2': '/cookbooks/', + '/cookbooks/3': '/cookbooks/', + '/cookbooks/4': '/cookbooks/', + '/cookbooks/5': '/cookbooks/', + // ============================================================================= // WORKSPACE HOW-TO REDIRECTS // ============================================================================= '/dev-kit/guides/dashboard/billing': '/saaskit/how-to/billing/', '/dev-kit/guides/dashboard/environments': '/saaskit/how-to/environments/', '/dev-kit/guides/dashboard/manage-team-members': '/saaskit/how-to/manage-team-members/', + + // Moving the dashboard how-tos out left `dev-kit/guides/` holding one orphaned + // page. It now sits with the other testing utilities. + '/dev-kit/guides/testing/scim-simulator': '/dev-kit/tools/scim-simulator/', } diff --git a/src/configs/sidebar.config.ts b/src/configs/sidebar.config.ts index ba178c441..f12ce93d5 100644 --- a/src/configs/sidebar.config.ts +++ b/src/configs/sidebar.config.ts @@ -301,6 +301,7 @@ export const sidebar = [ items: [ 'dev-kit/tools/scalekit-dryrun', 'dev-kit/tools/sso-simulator', + 'dev-kit/tools/scim-simulator', 'dev-kit/tools/use-scalekit-credentials', ], }, @@ -635,6 +636,9 @@ export const exclude = [ '/blog', '/404', // Error page '/apis/**/*', // REST API reference has Scalar-powered navigation + // Cross-product cookbook hub: belongs to no single product, so it must not + // inherit the Auth for SaaS journey rail or light up a product nav pill. + '/cookbooks', ] /** @@ -692,7 +696,6 @@ export const topics = { '/guides/**/*', '/browse/**/*', '/reference/**/*', - '/cookbooks', // Cross-product hub only '/**/*', // Catch-all: anything not matched above defaults here ], @@ -822,7 +825,6 @@ export const sidebarToSecondaryNav: Record = { '/authenticate/interceptors': 'workflows', '/reference/interceptors': 'workflows', '/reference/admin-portal': 'workflows', - '/cookbooks': 'saaskit-cookbooks', }, }, diff --git a/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx b/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx index 8735e3f0f..3abcb3db9 100644 --- a/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx +++ b/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx @@ -149,7 +149,7 @@ Key differences from a naive "give the LLM every tool" approach: 4. Save. Copy the **config ID** (e.g. `cfg_...`) and the generated **mcp_server_url**. -![Creating a Virtual MCP in the Scalekit dashboard](@/assets/docs/cookbooks/voice-assistant/vmcp-scalekit.png) +![Creating a Virtual MCP in the Scalekit dashboard](@/assets/docs/agentkit/cookbooks/voice-assistant/vmcp-scalekit.png) The screenshot above shows the Scalekit dashboard flow for creating the scoped Virtual MCP. @@ -183,7 +183,7 @@ You have two main options in Vapi, depending on whether you want dynamic discove 4. Attach the MCP tool to the assistant. 5. Update the system prompt to tell the model when and how to use tools (example in the demo repo). -![Registering the MCP tool in the Vapi dashboard](@/assets/docs/cookbooks/voice-assistant/tool-registration-scalekit.png) +![Registering the MCP tool in the Vapi dashboard](@/assets/docs/agentkit/cookbooks/voice-assistant/tool-registration-scalekit.png) The screenshot above illustrates where to configure the server URL and add the Authorization HTTP header in Vapi's MCP tool form. diff --git a/src/content/docs/agentkit/cookbooks/index.mdx b/src/content/docs/agentkit/cookbooks/index.mdx index e33459b81..4f5a550cb 100644 --- a/src/content/docs/agentkit/cookbooks/index.mdx +++ b/src/content/docs/agentkit/cookbooks/index.mdx @@ -10,3 +10,7 @@ tableOfContents: false AgentKit cookbooks are self-contained recipes. Pick a task, follow it end to end, and adapt the code. Browse the full list in the left sidebar under **Cookbooks**. For short dashboard answers, open **How-to**. + +Recipes for user login, enterprise SSO, and SCIM live in the +[Auth for SaaS cookbooks](/saaskit/cookbooks/). Both shelves are listed on the +[cookbook hub](/cookbooks/). diff --git a/src/content/docs/agentkit/how-to/index.mdx b/src/content/docs/agentkit/how-to/index.mdx index 125561766..da39f503e 100644 --- a/src/content/docs/agentkit/how-to/index.mdx +++ b/src/content/docs/agentkit/how-to/index.mdx @@ -1,12 +1,19 @@ --- title: 'AgentKit how-to guides' -description: 'Short answers for AgentKit dashboard and workspace tasks.' +description: 'Short answers for AgentKit dashboard tasks such as inspecting a user connection.' sidebar: label: 'Overview' order: 1 tableOfContents: false --- -How-to guides answer one dashboard or workspace question at a time. They are not full implementation journeys. +How-to guides answer one dashboard question at a time. They are not full implementation journeys. Browse the list in the left sidebar under **How-to**. For longer build recipes, open **Cookbooks**. + +Workspace and account tasks apply to every Scalekit product and are documented once, under Auth for +SaaS: + +- [Billing and usage](/saaskit/how-to/billing/) +- [Environments and data residency](/saaskit/how-to/environments/) +- [Manage team members](/saaskit/how-to/manage-team-members/) diff --git a/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx b/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx index aecebc086..7d775b927 100644 --- a/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx +++ b/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx @@ -1,6 +1,6 @@ --- title: 'Inspect a user connection in the dashboard' -description: 'Find a specific connected account in the Scalekit dashboard and read its state when an agent fails a tool call.' +description: 'Find a specific connected account in the Scalekit dashboard and read its state without adding logging to your agent.' sidebar: label: 'Inspect a user connection' tableOfContents: true @@ -12,6 +12,9 @@ When an agent suddenly cannot reach a user's Gmail, Calendar, or GitHub, the cau the state of that user's **connected account** rather than your code. The dashboard shows that state directly, which is faster than adding logging and redeploying. +This guide covers only where to look. For what each state means, and the code that fixes it, follow +the links to [Troubleshoot connection errors](/agentkit/authentication/troubleshooting/). + ## Find the connection Go to **Dashboard > Connections** and select the connection the agent uses — the name you pass as @@ -25,32 +28,24 @@ you have authorized. GitHub has two connected accounts, and they can be in different states. -## Read the state - -The row's status is the diagnosis. Only `ACTIVE` accounts can serve tool calls. - -| State | What it means | What to do | -| --- | --- | --- | -| `ACTIVE` | Credentials are valid and tool calls will work | Look elsewhere — the connection is healthy | -| `EXPIRED` | The access token expired | Send the user a fresh authorization link | -| `PENDING_AUTH` | The user never finished authenticating, or re-authentication is in progress | Send or re-send the authorization link | -| `PENDING_VERIFICATION` | OAuth finished but identity verification has not | Have the user complete verification | -| `DISCONNECTED` | The account was disconnected manually | Send a fresh authorization link | +## Read the row -For what each state means in code, see -[Manage connected accounts](/agentkit/connected-accounts/). For the failure modes behind each one, -see [Troubleshoot connection errors](/agentkit/authentication/troubleshooting/). +The row's status is the diagnosis. Only `ACTIVE` accounts can serve tool calls; every other status +names the exact step the user or your app still owes. -## Recover a broken connection +The dashboard reports the same value your code reads from `account.status`, so the row and the SDK +never disagree. Use the dashboard for a one-off answer and the SDK when you need the check in code. -Every non-`ACTIVE` state is fixed the same way: generate a new authorization link and send it to the -user. The link opens a Scalekit-hosted page that adapts to the connection's auth type, so you do not -branch on connector type in your own code. +For the full status list and the fix for each one, see +[Start with diagnostics](/agentkit/authentication/troubleshooting/#start-with-diagnostics). For the +webhook that pushes these changes to your app instead, see +[Detect when re-authentication is needed](/agentkit/connected-accounts/#detect-when-re-authentication-is-needed). -Once the user completes it, the row returns to `ACTIVE` and tool calls resume. +## Narrow one user versus every user -## Check the connection itself +The row count is the useful signal the SDK will not give you in a single call: -If every account on a connection is failing rather than one, the problem is the connection, not the -users. Confirm on the connection's own page that its credentials are still valid — a rotated or -expired OAuth client on the provider side takes down every account beneath it at once. +- **One row is failing** — that user needs to re-authorize. Send a fresh authorization link. +- **Every row is failing** — the connection is at fault, not the users. Open the connection's own + settings and confirm its credentials. A rotated or expired OAuth client on the provider side takes + down every account beneath it at once. diff --git a/src/content/docs/cookbooks.mdx b/src/content/docs/cookbooks.mdx index 3a926c93b..629353822 100644 --- a/src/content/docs/cookbooks.mdx +++ b/src/content/docs/cookbooks.mdx @@ -1,6 +1,7 @@ --- title: 'Cookbooks' description: 'Step-by-step recipes for building with Scalekit, split by product.' +template: splash sidebar: label: 'Cookbooks' tableOfContents: false @@ -8,7 +9,7 @@ tableOfContents: false import { CardGrid, LinkCard } from '@astrojs/starlight/components' -Cookbooks are self-contained recipes. Each product keeps its own shelf, reached from that product’s secondary nav **Guides** or **Cookbooks** entry. +Cookbooks are self-contained recipes. Each product keeps its own shelf, reached from that product's secondary nav **Guides** or **Cookbooks** entry. Date: Thu, 13 Aug 2026 19:18:07 +0530 Subject: [PATCH 4/8] feat(nav): top-level Guides for both products Move Auth for SaaS cookbooks and how-tos out of Developer Resources into a secondary nav Guides item that matches AgentKit and opens the dedicated guides sidebar. --- project-docs/COOKBOOKS.md | 2 +- src/configs/secondary-nav.config.ts | 25 +++++++------------------ src/configs/sidebar.config.ts | 10 ++-------- src/content/docs/cookbooks.mdx | 2 +- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/project-docs/COOKBOOKS.md b/project-docs/COOKBOOKS.md index fbf58a465..64397e649 100644 --- a/project-docs/COOKBOOKS.md +++ b/project-docs/COOKBOOKS.md @@ -19,7 +19,7 @@ How-to guides (short dashboard answers) live beside them: Each product has a **dedicated guides sidebar** (not the journey rail): - **AgentKit** secondary nav → **Guides** → left rail shows **Cookbooks** and **How-to** collapsibles -- **Auth for SaaS** secondary nav → Developer Resources → **Cookbooks** / **How-to** → same pattern +- **Auth for SaaS** secondary nav → **Guides** → same pattern (not under Developer Resources) New topic IDs in `src/configs/sidebar.config.ts`: diff --git a/src/configs/secondary-nav.config.ts b/src/configs/secondary-nav.config.ts index 36a67775c..1e3feb06b 100644 --- a/src/configs/secondary-nav.config.ts +++ b/src/configs/secondary-nav.config.ts @@ -128,6 +128,13 @@ const saasKitItems: NavItem[] = [ label: 'Enterprise Deployment', iconComponent: IconSolarServerPathOutline, }, + { + // Opens the dedicated Auth for SaaS guides sidebar (Cookbooks + How-to collapsibles) + id: 'saaskit-guides', + href: '/saaskit/cookbooks/', + label: 'Guides', + iconComponent: IconLucideBookOpenText, + }, { id: 'developer-resources', href: '#developer-resources', @@ -181,24 +188,6 @@ const saasKitItems: NavItem[] = [ description: 'Automate user lifecycle and auth events with webhooks', columnGroup: 'right', }, - { - id: 'saaskit-cookbooks', - href: '/saaskit/cookbooks/', - label: 'Developer Resources', - dropdownLabel: 'Cookbooks', - iconComponent: IconLucideBookOpenText, - description: 'Implement common patterns with step-by-step recipes', - columnGroup: 'right', - }, - { - id: 'saaskit-how-to', - href: '/saaskit/how-to/', - label: 'Developer Resources', - dropdownLabel: 'How-to guides', - iconComponent: IconLucideBookOpenText, - description: 'Short answers to dashboard, workspace, and account tasks', - columnGroup: 'right', - }, { id: 'code-samples', href: '/resources/code-samples/', diff --git a/src/configs/sidebar.config.ts b/src/configs/sidebar.config.ts index f12ce93d5..5ef6195e7 100644 --- a/src/configs/sidebar.config.ts +++ b/src/configs/sidebar.config.ts @@ -778,15 +778,9 @@ export const sidebarToSecondaryNav: Record = { // Agent connectors sidebar → AgentKit Connectors tab 'agent-connectors': 'agentkit-connectors', - // Product guide shelves → secondary nav entries + // Product guide shelves → top-level Guides secondary nav (not Developer Resources) 'agentkit-guides': 'agentkit-guides', - 'saaskit-guides': { - default: 'saaskit-cookbooks', - pathOverrides: { - '/saaskit/cookbooks': 'saaskit-cookbooks', - '/saaskit/how-to': 'saaskit-how-to', - }, - }, + 'saaskit-guides': 'saaskit-guides', // AgentKit sidebar → AgentKit tabs connect: { diff --git a/src/content/docs/cookbooks.mdx b/src/content/docs/cookbooks.mdx index 629353822..cd3fea887 100644 --- a/src/content/docs/cookbooks.mdx +++ b/src/content/docs/cookbooks.mdx @@ -9,7 +9,7 @@ tableOfContents: false import { CardGrid, LinkCard } from '@astrojs/starlight/components' -Cookbooks are self-contained recipes. Each product keeps its own shelf, reached from that product's secondary nav **Guides** or **Cookbooks** entry. +Cookbooks are self-contained recipes. Each product keeps its own shelf, reached from that product's secondary nav **Guides** entry. Date: Thu, 13 Aug 2026 19:34:42 +0530 Subject: [PATCH 5/8] fix(nav): put AgentKit Enterprise Deployment last in secondary nav Guides sits with SDKs/APIs; Enterprise Deployment stays at the end of the bar. --- src/configs/secondary-nav.config.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/configs/secondary-nav.config.ts b/src/configs/secondary-nav.config.ts index 1e3feb06b..8eea0435e 100644 --- a/src/configs/secondary-nav.config.ts +++ b/src/configs/secondary-nav.config.ts @@ -69,12 +69,6 @@ const agentKitItems: NavItem[] = [ label: 'APIs', iconComponent: IconApi, }, - { - id: 'enterprise-deployment', - href: '/self-hosted/overview/', - label: 'Enterprise Deployment', - iconComponent: IconSolarServerPathOutline, - }, { // Opens the dedicated AgentKit guides sidebar (Cookbooks + How-to collapsibles) id: 'agentkit-guides', @@ -82,6 +76,12 @@ const agentKitItems: NavItem[] = [ label: 'Guides', iconComponent: IconLucideBookOpenText, }, + { + id: 'enterprise-deployment', + href: '/self-hosted/overview/', + label: 'Enterprise Deployment', + iconComponent: IconSolarServerPathOutline, + }, ] const saasKitItems: NavItem[] = [ From 7b59c35b6b203da8d669364f199cdb4a023e100d Mon Sep 17 00:00:00 2001 From: Saif Date: Thu, 13 Aug 2026 19:35:53 +0530 Subject: [PATCH 6/8] fix(nav): Auth for SaaS order Guides, Developer Resources, Enterprise Secondary nav ends with Guides, then Developer Resources dropdown, then Enterprise Deployment last. --- src/configs/secondary-nav.config.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/configs/secondary-nav.config.ts b/src/configs/secondary-nav.config.ts index 8eea0435e..08bc7f59a 100644 --- a/src/configs/secondary-nav.config.ts +++ b/src/configs/secondary-nav.config.ts @@ -122,12 +122,6 @@ const saasKitItems: NavItem[] = [ label: 'APIs', iconComponent: IconApi, }, - { - id: 'enterprise-deployment', - href: '/self-hosted/overview/', - label: 'Enterprise Deployment', - iconComponent: IconSolarServerPathOutline, - }, { // Opens the dedicated Auth for SaaS guides sidebar (Cookbooks + How-to collapsibles) id: 'saaskit-guides', @@ -198,6 +192,12 @@ const saasKitItems: NavItem[] = [ }, ], }, + { + id: 'enterprise-deployment', + href: '/self-hosted/overview/', + label: 'Enterprise Deployment', + iconComponent: IconSolarServerPathOutline, + }, ] export const secondaryNavConfig: Record<'agentkit' | 'saaskit', NavItem[]> = { From ab4ae3307818ae4f5c7bde615077b817f7229160 Mon Sep 17 00:00:00 2001 From: Saif Date: Fri, 14 Aug 2026 15:40:43 +0530 Subject: [PATCH 7/8] feat(docs): share how-tos and add dashboard role recipes Rename secondary-nav Guides to Cookbooks, keep How-to open, and serve the same dashboard how-tos from /how-to in both product shelves. Add the first workspace-role, account, and development-domain recipes from the live dashboard. --- astro.config.mjs | 2 + project-docs/COOKBOOKS.md | 38 +++--- scripts/generate-llms-index.js | 1 + src/components/SecondaryNav.astro | 14 +- .../overrides/HeaderProductToggle.astro | 37 ++++-- src/configs/llms.config.ts | 6 +- src/configs/redirects.config.ts | 21 ++- src/configs/secondary-nav.config.ts | 12 +- src/configs/self-hosted.ts | 30 ++++- src/configs/sidebar.config.ts | 28 ++-- .../cookbooks/apify-actor-per-user-oauth.mdx | 3 +- .../build-voice-assistant-1000-tools.mdx | 3 +- .../crewai-agentkit-email-triage.mdx | 1 + .../cookbooks/daily-briefing-agent.mdx | 1 + .../fastrouter-agentkit-tool-calling.mdx | 3 +- src/content/docs/agentkit/cookbooks/index.mdx | 16 --- .../cookbooks/langsmith-tracing-agentkit.mdx | 3 +- .../litellm-agentkit-inbox-triage.mdx | 3 +- .../livekit-agentkit-voice-tool-calling.mdx | 1 + .../agentkit/cookbooks/mastra-agentkit.mdx | 3 +- .../cookbooks/render-github-pr-summarizer.mdx | 1 + .../schedule-meeting-and-draft-email.mdx | 3 + ...set-up-agentkit-with-your-coding-agent.mdx | 3 +- src/content/docs/agentkit/how-to/index.mdx | 19 --- ...ct-connected-accounts-in-the-dashboard.mdx | 3 +- src/content/docs/cookbooks.mdx | 8 +- .../docs/{saaskit => }/how-to/billing.mdx | 1 + .../how-to/configure-environment-roles.mdx | 94 +++++++++++++ .../how-to/custom-domain-on-development.mdx | 95 +++++++++++++ .../how-to/define-custom-dashboard-roles.mdx | 125 ++++++++++++++++++ .../docs/how-to/delete-your-account.mdx | 87 ++++++++++++ .../{saaskit => }/how-to/environments.mdx | 3 +- .../how-to/manage-team-members.mdx | 1 + .../docs/how-to/set-up-workspace-roles.mdx | 95 +++++++++++++ .../add-enterprise-sso-nextjs-authjs.mdx | 3 + .../add-hosted-auth-nextjs-app-router.mdx | 3 + .../building-custom-org-switcher.mdx | 3 + .../cookbooks/implement-nextjs-auth.mdx | 3 + src/content/docs/saaskit/cookbooks/index.mdx | 16 --- .../java-spring-boot-jwt-timeout.mdx | 3 + .../cookbooks/m2m-jwks-and-oauth-scopes.mdx | 3 + .../migrate-from-auth0-to-scalekit.mdx | 3 + .../cookbooks/scim-seat-limit-enforcement.mdx | 1 + .../search-scalekit-docs-in-your-ide.mdx | 3 +- .../sync-b2b-billing-with-chargebee.mdx | 3 +- src/content/docs/saaskit/how-to/index.mdx | 16 --- src/integrations/assign-how-to-topic.ts | 19 +++ src/middleware.ts | 18 +++ src/middleware/assign-how-to-topic.ts | 30 +++++ src/utils/secondary-nav-utils.ts | 34 +++-- 50 files changed, 771 insertions(+), 154 deletions(-) delete mode 100644 src/content/docs/agentkit/cookbooks/index.mdx delete mode 100644 src/content/docs/agentkit/how-to/index.mdx rename src/content/docs/{saaskit => }/how-to/billing.mdx (99%) create mode 100644 src/content/docs/how-to/configure-environment-roles.mdx create mode 100644 src/content/docs/how-to/custom-domain-on-development.mdx create mode 100644 src/content/docs/how-to/define-custom-dashboard-roles.mdx create mode 100644 src/content/docs/how-to/delete-your-account.mdx rename src/content/docs/{saaskit => }/how-to/environments.mdx (98%) rename src/content/docs/{saaskit => }/how-to/manage-team-members.mdx (99%) create mode 100644 src/content/docs/how-to/set-up-workspace-roles.mdx delete mode 100644 src/content/docs/saaskit/cookbooks/index.mdx delete mode 100644 src/content/docs/saaskit/how-to/index.mdx create mode 100644 src/integrations/assign-how-to-topic.ts create mode 100644 src/middleware/assign-how-to-topic.ts diff --git a/astro.config.mjs b/astro.config.mjs index e509138a2..9fdee73ce 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -28,6 +28,7 @@ import Icons from 'unplugin-icons/vite' import netlify from '@astrojs/netlify' import openapiToMarkdown from './src/integrations/openapi-markdown' import { injectAgentHeader } from './src/integrations/inject-agent-header.ts' +import assignHowToTopic from './src/integrations/assign-how-to-topic' // https://astro.build/config export default defineConfig({ @@ -106,6 +107,7 @@ export default defineConfig({ starlightImageZoom({ showCaptions: true, }), + assignHowToTopic(), starlightSidebarTopics(sidebarConfig, { topics, exclude }), starlightDocSearch({ appId: '7554BDRAJD', diff --git a/project-docs/COOKBOOKS.md b/project-docs/COOKBOOKS.md index 64397e649..0a5c88466 100644 --- a/project-docs/COOKBOOKS.md +++ b/project-docs/COOKBOOKS.md @@ -2,24 +2,28 @@ Cookbooks are plain Starlight docs pages under product shelves: -| Product | Path | URL | -| ------------- | -------------------------------------- | ---------------------- | -| AgentKit | `src/content/docs/agentkit/cookbooks/` | `/agentkit/cookbooks/` | -| Auth for SaaS | `src/content/docs/saaskit/cookbooks/` | `/saaskit/cookbooks/` | +| Product | Path | URL | +| ------------- | -------------------------------------- | ----------------------------- | +| AgentKit | `src/content/docs/agentkit/cookbooks/` | `/agentkit/cookbooks//` | +| Auth for SaaS | `src/content/docs/saaskit/cookbooks/` | `/saaskit/cookbooks//` | How-to guides (short dashboard answers) live beside them: -| Product | Path | URL | -| ------------- | ----------------------------------- | ------------------- | -| AgentKit | `src/content/docs/agentkit/how-to/` | `/agentkit/how-to/` | -| Auth for SaaS | `src/content/docs/saaskit/how-to/` | `/saaskit/how-to/` | +| Scope | Path | URL | +| --------------------------- | ----------------------------------- | -------------------------- | +| Shared (both product rails) | `src/content/docs/how-to/` | `/how-to//` | +| AgentKit only | `src/content/docs/agentkit/how-to/` | `/agentkit/how-to//` | + +Shared how-tos appear in both Cookbooks sidebars from one file. Product chrome on `/how-to/**` uses the same session/cookie as Enterprise Deployment. + +Do not add an `index.mdx` overview unless the shelf is long enough that a hub page helps. The secondary nav lands on the first recipe. ## How readers find them -Each product has a **dedicated guides sidebar** (not the journey rail): +Each product has a **dedicated cookbooks sidebar** (not the journey rail): -- **AgentKit** secondary nav → **Guides** → left rail shows **Cookbooks** and **How-to** collapsibles -- **Auth for SaaS** secondary nav → **Guides** → same pattern (not under Developer Resources) +- **AgentKit** secondary nav → **Cookbooks** → left rail shows **Cookbooks** and **How-to** collapsibles +- **Auth for SaaS** secondary nav → **Cookbooks** → same pattern (not under Developer Resources) New topic IDs in `src/configs/sidebar.config.ts`: @@ -30,8 +34,8 @@ Each uses `autogenerate` on the product directory. Drop a new `.mdx` file in the A cross-product hub remains at `/cookbooks/` (`src/content/docs/cookbooks.mdx`). It belongs to no single product, so it is listed in `exclude` in `src/configs/sidebar.config.ts` — that keeps it from -inheriting a product journey rail or lighting up a product nav pill. Both product overview pages -link to it, and it links back to both shelves. +inheriting a product journey rail or lighting up a product nav pill. The hub links to the first +recipe on each shelf. ## What a cookbook is @@ -102,7 +106,8 @@ Use normal Starlight docs frontmatter. Blog-only fields (`date`, `excerpt`, `fea title: 'Build a Mastra agent with Scalekit AgentKit tools' description: 'Give a Mastra agent access to Gmail and 200+ connectors through Scalekit AgentKit.' sidebar: - label: 'Mastra AgentKit' + label: 'Build a Mastra agent' + order: 3 tableOfContents: true --- ``` @@ -113,7 +118,8 @@ tableOfContents: true | ----------------- | -------- | ------------------------------------ | | `title` | Yes | ≤60 chars, verb-first, sentence case | | `description` | Yes | ≤160 chars, problem and outcome | -| `sidebar.label` | Yes | Short left-rail label (1–5 words) | +| `sidebar.label` | Yes | Short left-rail label (1–3 words) | +| `sidebar.order` | Yes | Explicit order in the product shelf | | `tableOfContents` | Optional | Default true for long recipes | ## Create a cookbook @@ -123,7 +129,7 @@ tableOfContents: true 3. Write the recipe body (problem, steps, working code, failure modes). 4. Put images under `src/assets/docs//cookbooks//` and reference them as `@/assets/docs//cookbooks//.png`. -5. Run a local build or `pnpm start` and confirm the page appears under the product **Guides** / **Cookbooks** sidebar. +5. Run a local build or `pnpm start` and confirm the page appears under the product **Cookbooks** sidebar. **Redirects:** a brand-new cookbook needs none. Only a page whose published URL changes needs an entry in `src/configs/redirects.config.ts`, and it must be listed one slug at a time — a diff --git a/scripts/generate-llms-index.js b/scripts/generate-llms-index.js index 8a99856a6..853c5a2c0 100644 --- a/scripts/generate-llms-index.js +++ b/scripts/generate-llms-index.js @@ -206,6 +206,7 @@ const OTHER_SECTIONS = [ p.startsWith('/agentkit/cookbooks/') || p.startsWith('/saaskit/cookbooks/') || p.startsWith('/agentkit/how-to/') || + p.startsWith('/how-to/') || p.startsWith('/saaskit/how-to/') || p.startsWith('/resources/'), }, diff --git a/src/components/SecondaryNav.astro b/src/components/SecondaryNav.astro index bc870690f..df8baa8b8 100644 --- a/src/components/SecondaryNav.astro +++ b/src/components/SecondaryNav.astro @@ -3,7 +3,7 @@ import { secondaryNavConfig, IconLucideCheck } from '../configs/secondary-nav.co import { getActiveProduct, isCurrentPage, - isSelfHostedPath, + isSharedProductPath, getDisplayLabel, type SecondaryNavProps, } from '../utils/secondary-nav-utils' @@ -16,7 +16,7 @@ const { entry } = Astro.props satisfies SecondaryNavProps const searchParams = Astro.url.searchParams const activeProduct = getActiveProduct(Astro.url.pathname, entry?.data?.topic, searchParams) -const isSharedPath = isSelfHostedPath(Astro.url.pathname) +const isSharedPath = isSharedProductPath(Astro.url.pathname) // Shared self-hosted pages: render both product rows so the client can swap without // a full rebuild. SSR shows AgentKit (cold default). Unambiguous pages render one row. @@ -992,6 +992,15 @@ function groupNavItems(items: NavItem[]): NavItem[][] { if (row.getAttribute('data-product') === product) row.removeAttribute('hidden') else row.setAttribute('hidden', '') }) + if (location.pathname === '/how-to' || location.pathname.startsWith('/how-to/')) { + document.querySelectorAll('.secondary-nav-product .nav-item').forEach((el) => { + el.classList.toggle( + 'current', + el.closest('[data-product]')?.getAttribute('data-product') === product && + el.textContent.trim() === 'Cookbooks', + ) + }) + } } function initSecondaryNav() { @@ -1014,6 +1023,7 @@ function groupNavItems(items: NavItem[]): NavItem[][] { try { sessionStorage.setItem(storageKey, product) } catch {} + document.cookie = `${storageKey}=${product}; path=/; SameSite=Lax` }, { signal, capture: true }, ) diff --git a/src/components/overrides/HeaderProductToggle.astro b/src/components/overrides/HeaderProductToggle.astro index ab3a74b70..01b9d72ce 100644 --- a/src/components/overrides/HeaderProductToggle.astro +++ b/src/components/overrides/HeaderProductToggle.astro @@ -2,7 +2,7 @@ import { getActiveSecondaryNavId, getActiveProduct, - isSelfHostedPath, + isSharedProductPath, type SecondaryNavProps, } from '../../utils/secondary-nav-utils' import { PRODUCT_STORAGE_KEY } from '../../configs/self-hosted' @@ -18,7 +18,7 @@ const activeProduct = getActiveProduct(Astro.url.pathname, entry?.data?.topic, s // Self-hosted routes are shared; product is resolved (cold default AgentKit or // ?product=). Client restores SaaS from sessionStorage when needed. Never show // a third "Self Hosted" product option in the picker. -const isSharedPath = isSelfHostedPath(Astro.url.pathname) +const isSharedPath = isSharedProductPath(Astro.url.pathname) // activeId !== null means the page is in the doc system with a known nav context. // Shared items (rest-apis, sdks, etc.) live in SaasKit nav, so activeProduct covers them. @@ -135,6 +135,16 @@ const productLinks = [ return phrase?.getAttribute('data-storage-key') || 'sk-active-product' } + function persistProduct(product: string) { + const storageKey = getStorageKey() + try { + sessionStorage.setItem(storageKey, product) + } catch { + /* private mode */ + } + document.cookie = `${storageKey}=${product}; path=/; SameSite=Lax` + } + /** * Resolve product for the current page: ?product= wins, then path signals, * then session memory on shared paths, then SSR default baked into the DOM. @@ -165,11 +175,7 @@ const productLinks = [ } if (knownProduct) { - try { - sessionStorage.setItem(storageKey, knownProduct) - } catch { - /* private mode */ - } + persistProduct(knownProduct) } if (isShared) { @@ -231,6 +237,17 @@ const productLinks = [ }) document.dispatchEvent(new CustomEvent('sk:product-context', { detail: { product } })) + tagSharedHowToLinks(product) + } + + /** Autogenerate cannot add ?product=; stamp it so the next /how-to/** render keeps this rail. */ + function tagSharedHowToLinks(product: string) { + document.querySelectorAll('a[href^="/how-to/"]').forEach((anchor) => { + if (!(anchor instanceof HTMLAnchorElement)) return + const url = new URL(anchor.href, location.origin) + url.searchParams.set('product', product) + anchor.setAttribute('href', `${url.pathname}${url.search}`) + }) } function restoreProductContext() { @@ -345,11 +362,7 @@ const productLinks = [ const storageValue = option.getAttribute('data-storage-value') if (storageValue === 'agentkit' || storageValue === 'saaskit') { - try { - sessionStorage.setItem(getStorageKey(), storageValue) - } catch { - /* private mode */ - } + persistProduct(storageValue) applyProductChrome(storageValue) } diff --git a/src/configs/llms.config.ts b/src/configs/llms.config.ts index 91f02e9ae..5caf831f3 100644 --- a/src/configs/llms.config.ts +++ b/src/configs/llms.config.ts @@ -58,7 +58,7 @@ Start with the Quickstart Collection, then follow the developer's question to th 'guides/user-auth/**', 'guides/user-management/**', 'saaskit/cookbooks/**', - 'saaskit/how-to/**', + 'how-to/**', ], }, { @@ -124,7 +124,7 @@ Start with the Quickstart Collection, then follow the developer's question to th 'guides/integrations/index', 'guides/integrations/*/index', 'guides/dashboard/**', - 'saaskit/how-to/**', // Workspace/dashboard how-tos moved here from dev-kit/guides/dashboard + 'how-to/**', // Workspace/dashboard how-tos (shared by both products) 'dev-kit/api-collections/**', ], }, @@ -143,7 +143,7 @@ Start with the Quickstart Collection, then follow the developer's question to th '**/quickstart', // All quickstart guides 'agentkit/examples/**', // Framework examples (high value for agent queries) 'agentkit/cookbooks/**', // Practical AgentKit cookbooks - 'saaskit/cookbooks/**', // Practical Auth for SaaS cookbooks + 'saaskit/cookbooks/**', // Practical SaaSKit cookbooks 'fsa/data-modelling', // Critical data modeling guide 'authenticate/set-up-scalekit', // Initial setup 'authenticate/fsa/complete-login', // Core FSA flow diff --git a/src/configs/redirects.config.ts b/src/configs/redirects.config.ts index 21245a8bd..5ca7775a8 100644 --- a/src/configs/redirects.config.ts +++ b/src/configs/redirects.config.ts @@ -516,12 +516,27 @@ export const redirects = { '/cookbooks/4': '/cookbooks/', '/cookbooks/5': '/cookbooks/', + // Product shelf indexes removed — lists are short enough to land on the first page + '/agentkit/cookbooks': '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/agentkit/cookbooks/': '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', + '/agentkit/how-to': '/agentkit/how-to/inspect-connected-accounts-in-the-dashboard/', + '/agentkit/how-to/': '/agentkit/how-to/inspect-connected-accounts-in-the-dashboard/', + '/saaskit/cookbooks': '/saaskit/cookbooks/add-hosted-auth-nextjs-app-router/', + '/saaskit/cookbooks/': '/saaskit/cookbooks/add-hosted-auth-nextjs-app-router/', + '/how-to': '/how-to/environments/', + '/how-to/': '/how-to/environments/', + // ============================================================================= // WORKSPACE HOW-TO REDIRECTS // ============================================================================= - '/dev-kit/guides/dashboard/billing': '/saaskit/how-to/billing/', - '/dev-kit/guides/dashboard/environments': '/saaskit/how-to/environments/', - '/dev-kit/guides/dashboard/manage-team-members': '/saaskit/how-to/manage-team-members/', + '/saaskit/how-to': '/how-to/environments/', + '/saaskit/how-to/': '/how-to/environments/', + '/saaskit/how-to/billing': '/how-to/billing/', + '/saaskit/how-to/environments': '/how-to/environments/', + '/saaskit/how-to/manage-team-members': '/how-to/manage-team-members/', + '/dev-kit/guides/dashboard/billing': '/how-to/billing/', + '/dev-kit/guides/dashboard/environments': '/how-to/environments/', + '/dev-kit/guides/dashboard/manage-team-members': '/how-to/manage-team-members/', // Moving the dashboard how-tos out left `dev-kit/guides/` holding one orphaned // page. It now sits with the other testing utilities. diff --git a/src/configs/secondary-nav.config.ts b/src/configs/secondary-nav.config.ts index 08bc7f59a..16003b070 100644 --- a/src/configs/secondary-nav.config.ts +++ b/src/configs/secondary-nav.config.ts @@ -70,10 +70,10 @@ const agentKitItems: NavItem[] = [ iconComponent: IconApi, }, { - // Opens the dedicated AgentKit guides sidebar (Cookbooks + How-to collapsibles) + // Opens the dedicated AgentKit cookbooks sidebar (Cookbooks + How-to collapsibles) id: 'agentkit-guides', - href: '/agentkit/cookbooks/', - label: 'Guides', + href: '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', + label: 'Cookbooks', iconComponent: IconLucideBookOpenText, }, { @@ -123,10 +123,10 @@ const saasKitItems: NavItem[] = [ iconComponent: IconApi, }, { - // Opens the dedicated Auth for SaaS guides sidebar (Cookbooks + How-to collapsibles) + // Opens the dedicated SaaSKit cookbooks sidebar (Cookbooks + How-to collapsibles) id: 'saaskit-guides', - href: '/saaskit/cookbooks/', - label: 'Guides', + href: '/saaskit/cookbooks/add-hosted-auth-nextjs-app-router/', + label: 'Cookbooks', iconComponent: IconLucideBookOpenText, }, { diff --git a/src/configs/self-hosted.ts b/src/configs/self-hosted.ts index 41a2f6f41..02c1bd589 100644 --- a/src/configs/self-hosted.ts +++ b/src/configs/self-hosted.ts @@ -1,15 +1,39 @@ /** - * Self-hosted deployment is shared by AgentKit and Auth for SaaS. - * Product chrome is resolved client-side via sessionStorage when the URL - * does not encode a product (see HeaderProductToggle + SecondaryNav). + * Product chrome for routes that both AgentKit and Auth for SaaS share. + * The URL does not encode a product. Header + secondary nav resolve it from + * ?product=, then sessionStorage / the sk-active-product cookie, then a cold default. + * See HeaderProductToggle + SecondaryNav. */ export const PRODUCT_STORAGE_KEY = 'sk-active-product' /** Cold load default for /self-hosted/** when no query param or session memory exists. */ export const SELF_HOSTED_COLD_DEFAULT_PRODUCT = 'agentkit' as const +/** + * Workspace how-tos (/how-to/**) are shared. Cold default is Auth for SaaS because + * these pages are workspace/dashboard tasks that historically lived under SaaS. + */ +export const SHARED_HOW_TO_COLD_DEFAULT_PRODUCT = 'saaskit' as const + export type DocsProduct = 'agentkit' | 'saaskit' export function isDocsProduct(value: string | null | undefined): value is DocsProduct { return value === 'agentkit' || value === 'saaskit' } + +export function isSelfHostedPath(pathname: string): boolean { + return pathname.startsWith('/self-hosted/') +} + +export function isSharedHowToPath(pathname: string): boolean { + return pathname === '/how-to' || pathname === '/how-to/' || pathname.startsWith('/how-to/') +} + +/** Routes that keep product chrome from session/cookie rather than from the path. */ +export function isSharedProductPath(pathname: string): boolean { + return isSelfHostedPath(pathname) || isSharedHowToPath(pathname) +} + +export function guidesTopicForProduct(product: DocsProduct): 'agentkit-guides' | 'saaskit-guides' { + return product === 'agentkit' ? 'agentkit-guides' : 'saaskit-guides' +} diff --git a/src/configs/sidebar.config.ts b/src/configs/sidebar.config.ts index 5ef6195e7..8485263ed 100644 --- a/src/configs/sidebar.config.ts +++ b/src/configs/sidebar.config.ts @@ -233,11 +233,11 @@ export const sidebar = [ }, ], }, - // Product guide shelves — dedicated sidebars entered from secondary nav, not journey rails + // Product cookbook shelves — dedicated sidebars entered from secondary nav, not journey rails { - label: 'AgentKit guides', + label: 'AgentKit cookbooks', id: 'agentkit-guides', - link: '/agentkit/cookbooks/', + link: '/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent/', icon: 'open-book', items: [ { @@ -248,14 +248,17 @@ export const sidebar = [ { label: 'How-to', collapsed: false, - items: [{ autogenerate: { directory: 'agentkit/how-to' } }], + items: [ + { autogenerate: { directory: 'agentkit/how-to' } }, + { autogenerate: { directory: 'how-to' } }, + ], }, ], }, { - label: 'Auth for SaaS guides', + label: 'SaaSKit cookbooks', id: 'saaskit-guides', - link: '/saaskit/cookbooks/', + link: '/saaskit/cookbooks/add-hosted-auth-nextjs-app-router/', icon: 'open-book', items: [ { @@ -266,7 +269,7 @@ export const sidebar = [ { label: 'How-to', collapsed: false, - items: [{ autogenerate: { directory: 'saaskit/how-to' } }], + items: [{ autogenerate: { directory: 'how-to' } }], }, ], }, @@ -669,13 +672,10 @@ export const topics = { '/agentkit/cookbooks/**/*', '/agentkit/how-to', '/agentkit/how-to/**/*', + '/how-to', + '/how-to/**/*', ], - 'saaskit-guides': [ - '/saaskit/cookbooks', - '/saaskit/cookbooks/**/*', - '/saaskit/how-to', - '/saaskit/how-to/**/*', - ], + 'saaskit-guides': ['/saaskit/cookbooks', '/saaskit/cookbooks/**/*', '/how-to', '/how-to/**/*'], // Product SDK sidebars (before connect catch-all) 'agentkit-sdks': ['/agentkit/sdks/**/*'], @@ -778,7 +778,7 @@ export const sidebarToSecondaryNav: Record = { // Agent connectors sidebar → AgentKit Connectors tab 'agent-connectors': 'agentkit-connectors', - // Product guide shelves → top-level Guides secondary nav (not Developer Resources) + // Product cookbook shelves → top-level Cookbooks secondary nav (not Developer Resources) 'agentkit-guides': 'agentkit-guides', 'saaskit-guides': 'saaskit-guides', diff --git a/src/content/docs/agentkit/cookbooks/apify-actor-per-user-oauth.mdx b/src/content/docs/agentkit/cookbooks/apify-actor-per-user-oauth.mdx index 67a63e45c..f68ff927e 100644 --- a/src/content/docs/agentkit/cookbooks/apify-actor-per-user-oauth.mdx +++ b/src/content/docs/agentkit/cookbooks/apify-actor-per-user-oauth.mdx @@ -2,7 +2,8 @@ title: 'Apify Actor with per-user OAuth via Scalekit' description: 'Build an Apify Actor that uses Scalekit Agent Auth so each user connects their OAuth accounts, keyed by Apify userId.' sidebar: - label: 'Apify Actor per-user OAuth' + label: 'Apify per-user OAuth' + order: 12 --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx b/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx index 3abcb3db9..a0279c0bc 100644 --- a/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx +++ b/src/content/docs/agentkit/cookbooks/build-voice-assistant-1000-tools.mdx @@ -2,7 +2,8 @@ title: 'Build a Vapi voice assistant with Scalekit' description: 'Use Vapi + Scalekit Virtual MCP for voice assistants to securely access any tool from large catalogs.' sidebar: - label: 'Vapi + Scalekit assistant' + label: 'Vapi voice assistant' + order: 10 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/crewai-agentkit-email-triage.mdx b/src/content/docs/agentkit/cookbooks/crewai-agentkit-email-triage.mdx index 2a6a1c14c..f0a1e95d0 100644 --- a/src/content/docs/agentkit/cookbooks/crewai-agentkit-email-triage.mdx +++ b/src/content/docs/agentkit/cookbooks/crewai-agentkit-email-triage.mdx @@ -3,6 +3,7 @@ title: 'Build a multi-agent email triage crew with CrewAI' description: 'Use CrewAI multi-agent orchestration with Scalekit-authenticated Gmail tools to scan, classify, and draft replies to emails.' sidebar: label: 'CrewAI email triage' + order: 5 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/daily-briefing-agent.mdx b/src/content/docs/agentkit/cookbooks/daily-briefing-agent.mdx index ce89d6d10..b442309ad 100644 --- a/src/content/docs/agentkit/cookbooks/daily-briefing-agent.mdx +++ b/src/content/docs/agentkit/cookbooks/daily-briefing-agent.mdx @@ -3,6 +3,7 @@ title: 'Build a daily briefing agent with Vercel AI SDK and Scalekit Agent Auth' description: 'Connect a TypeScript or Python agent via Vercel AI SDK and Scalekit AgentKit to Google Calendar and Gmail with authenticated tool calls.' sidebar: label: 'Daily briefing agent' + order: 7 --- import { TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/agentkit/cookbooks/fastrouter-agentkit-tool-calling.mdx b/src/content/docs/agentkit/cookbooks/fastrouter-agentkit-tool-calling.mdx index 50314e2ec..ed4f31cb1 100644 --- a/src/content/docs/agentkit/cookbooks/fastrouter-agentkit-tool-calling.mdx +++ b/src/content/docs/agentkit/cookbooks/fastrouter-agentkit-tool-calling.mdx @@ -2,7 +2,8 @@ title: 'FastRouter + Scalekit tool calling' description: 'Build a Node.js agent that routes LLM calls through FastRouter and uses Scalekit for per-user OAuth tools.' sidebar: - label: 'Tool calling with FastRouter' + label: 'FastRouter tools' + order: 4 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/index.mdx b/src/content/docs/agentkit/cookbooks/index.mdx deleted file mode 100644 index 4f5a550cb..000000000 --- a/src/content/docs/agentkit/cookbooks/index.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: 'AgentKit cookbooks' -description: 'Step-by-step recipes for connecting agents to tools and third-party APIs with Scalekit AgentKit.' -sidebar: - label: 'Overview' - order: 1 -tableOfContents: false ---- - -AgentKit cookbooks are self-contained recipes. Pick a task, follow it end to end, and adapt the code. - -Browse the full list in the left sidebar under **Cookbooks**. For short dashboard answers, open **How-to**. - -Recipes for user login, enterprise SSO, and SCIM live in the -[Auth for SaaS cookbooks](/saaskit/cookbooks/). Both shelves are listed on the -[cookbook hub](/cookbooks/). diff --git a/src/content/docs/agentkit/cookbooks/langsmith-tracing-agentkit.mdx b/src/content/docs/agentkit/cookbooks/langsmith-tracing-agentkit.mdx index a0c8a0211..3cc665d04 100644 --- a/src/content/docs/agentkit/cookbooks/langsmith-tracing-agentkit.mdx +++ b/src/content/docs/agentkit/cookbooks/langsmith-tracing-agentkit.mdx @@ -2,7 +2,8 @@ title: 'Trace AgentKit tool calls in LangSmith' description: 'Add LangSmith observability to a LangChain agent that uses Scalekit AgentKit tools for Gmail, Slack, GitHub, and 200+ connectors.' sidebar: - label: 'LangSmith tracing' + label: 'Trace calls in LangSmith' + order: 13 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage.mdx b/src/content/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage.mdx index 69dfa108b..b02aa3392 100644 --- a/src/content/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage.mdx +++ b/src/content/docs/agentkit/cookbooks/litellm-agentkit-inbox-triage.mdx @@ -2,7 +2,8 @@ title: 'Triage a Gmail inbox with AgentKit and the LiteLLM gateway' description: 'Node.js inbox triage agent: classify Gmail threads, route to GitHub repos, draft issues and replies via LiteLLM, and approve before any side effects.' sidebar: - label: 'Inbox triage + LiteLLM' + label: 'LiteLLM inbox triage' + order: 6 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/livekit-agentkit-voice-tool-calling.mdx b/src/content/docs/agentkit/cookbooks/livekit-agentkit-voice-tool-calling.mdx index a597db031..8641d81db 100644 --- a/src/content/docs/agentkit/cookbooks/livekit-agentkit-voice-tool-calling.mdx +++ b/src/content/docs/agentkit/cookbooks/livekit-agentkit-voice-tool-calling.mdx @@ -3,6 +3,7 @@ title: 'Build a LiveKit voice agent with Scalekit AgentKit tools' description: 'Give a LiveKit voice agent secure access to Google Calendar and 200+ AgentKit connectors — no token ever reaches the browser or the LLM.' sidebar: label: 'LiveKit voice agent' + order: 11 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/mastra-agentkit.mdx b/src/content/docs/agentkit/cookbooks/mastra-agentkit.mdx index 067ca2d1d..1d8018d19 100644 --- a/src/content/docs/agentkit/cookbooks/mastra-agentkit.mdx +++ b/src/content/docs/agentkit/cookbooks/mastra-agentkit.mdx @@ -2,7 +2,8 @@ title: 'Build a Mastra agent with Scalekit AgentKit tools' description: 'Give a Mastra agent access to Gmail and 200+ connectors through Scalekit AgentKit — zero manual OAuth handling.' sidebar: - label: 'Mastra AgentKit' + label: 'Build a Mastra agent' + order: 3 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/render-github-pr-summarizer.mdx b/src/content/docs/agentkit/cookbooks/render-github-pr-summarizer.mdx index e60ecec03..924ecaef8 100644 --- a/src/content/docs/agentkit/cookbooks/render-github-pr-summarizer.mdx +++ b/src/content/docs/agentkit/cookbooks/render-github-pr-summarizer.mdx @@ -3,6 +3,7 @@ title: 'Build a multi-user GitHub PR summarizer agent' description: 'Build a GitHub PR summarizer that binds each connected GitHub account to a secure browser session instead of trusting a client-supplied user ID.' sidebar: label: 'GitHub PR summarizer' + order: 9 tableOfContents: true --- diff --git a/src/content/docs/agentkit/cookbooks/schedule-meeting-and-draft-email.mdx b/src/content/docs/agentkit/cookbooks/schedule-meeting-and-draft-email.mdx index 22d4a73a9..e31862d21 100644 --- a/src/content/docs/agentkit/cookbooks/schedule-meeting-and-draft-email.mdx +++ b/src/content/docs/agentkit/cookbooks/schedule-meeting-and-draft-email.mdx @@ -1,6 +1,9 @@ --- title: 'Build an agent that books meetings and drafts emails' description: 'Connect a Python agent to Google Calendar and Gmail via Scalekit to find free slots, book meetings, and draft follow-up emails.' +sidebar: + label: 'Book meetings, draft email' + order: 8 --- import { Steps, TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent.mdx b/src/content/docs/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent.mdx index c31507b28..a556954a0 100644 --- a/src/content/docs/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent.mdx +++ b/src/content/docs/agentkit/cookbooks/set-up-agentkit-with-your-coding-agent.mdx @@ -2,7 +2,8 @@ title: 'Set up AgentKit with your coding agent' description: 'Add Scalekit Agent Auth to your codebase using Claude Code, Codex, GitHub Copilot CLI, Cursor, or any of 40+ coding agents.' sidebar: - label: 'Set up AgentKit with coding agents' + label: 'Set up with agents' + order: 2 --- import { TabItem, Aside } from '@astrojs/starlight/components' diff --git a/src/content/docs/agentkit/how-to/index.mdx b/src/content/docs/agentkit/how-to/index.mdx deleted file mode 100644 index da39f503e..000000000 --- a/src/content/docs/agentkit/how-to/index.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: 'AgentKit how-to guides' -description: 'Short answers for AgentKit dashboard tasks such as inspecting a user connection.' -sidebar: - label: 'Overview' - order: 1 -tableOfContents: false ---- - -How-to guides answer one dashboard question at a time. They are not full implementation journeys. - -Browse the list in the left sidebar under **How-to**. For longer build recipes, open **Cookbooks**. - -Workspace and account tasks apply to every Scalekit product and are documented once, under Auth for -SaaS: - -- [Billing and usage](/saaskit/how-to/billing/) -- [Environments and data residency](/saaskit/how-to/environments/) -- [Manage team members](/saaskit/how-to/manage-team-members/) diff --git a/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx b/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx index 7d775b927..8b5b710e5 100644 --- a/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx +++ b/src/content/docs/agentkit/how-to/inspect-connected-accounts-in-the-dashboard.mdx @@ -2,7 +2,8 @@ title: 'Inspect a user connection in the dashboard' description: 'Find a specific connected account in the Scalekit dashboard and read its state without adding logging to your agent.' sidebar: - label: 'Inspect a user connection' + label: 'Inspect a connection' + order: 2 tableOfContents: true --- diff --git a/src/content/docs/cookbooks.mdx b/src/content/docs/cookbooks.mdx index cd3fea887..696e04951 100644 --- a/src/content/docs/cookbooks.mdx +++ b/src/content/docs/cookbooks.mdx @@ -9,17 +9,17 @@ tableOfContents: false import { CardGrid, LinkCard } from '@astrojs/starlight/components' -Cookbooks are self-contained recipes. Each product keeps its own shelf, reached from that product's secondary nav **Guides** entry. +Cookbooks are self-contained recipes. Each product keeps its own shelf, reached from that product's secondary nav **Cookbooks** entry. diff --git a/src/content/docs/saaskit/how-to/billing.mdx b/src/content/docs/how-to/billing.mdx similarity index 99% rename from src/content/docs/saaskit/how-to/billing.mdx rename to src/content/docs/how-to/billing.mdx index 2df3e7eba..2c95f473a 100644 --- a/src/content/docs/saaskit/how-to/billing.mdx +++ b/src/content/docs/how-to/billing.mdx @@ -4,6 +4,7 @@ description: View your current plan, manage payment methods, and monitor your Sc tableOfContents: true sidebar: label: Billing and usage + order: 8 head: - tag: style content: | diff --git a/src/content/docs/how-to/configure-environment-roles.mdx b/src/content/docs/how-to/configure-environment-roles.mdx new file mode 100644 index 000000000..422f9204e --- /dev/null +++ b/src/content/docs/how-to/configure-environment-roles.mdx @@ -0,0 +1,94 @@ +--- +title: 'Configure roles per environment' +description: 'Override a member’s dashboard role on Development or Production so they cannot change the wrong environment.' +tableOfContents: true +sidebar: + label: 'Environment roles' + order: 3 +head: + - tag: style + content: | + .sl-markdown-content h2 { font-size: var(--sl-text-xl); } + .sl-markdown-content h3 { font-size: var(--sl-text-lg); } +--- + +import { Steps, Aside } from '@astrojs/starlight/components' + +A workspace role is the default for every environment. An environment override replaces that default on one Development or Production environment. + +Use an override when someone should edit Development but not Production. Create the role first in [Define custom dashboard roles](/how-to/define-custom-dashboard-roles/). Environment list and types are in [Manage environments](/how-to/environments/). + +## Before you start + +- You can open **Team Members** and **Edit role** +- You hold `environment_access:write` to set or reset overrides +- The member already has a workspace role +- You know which environment they should use: **Development** or **Production** + + + +1. ## Open Edit role + + In the header, click the **workspace name** and choose **Team Members**. + + Open the member's row menu and choose **Edit role**. + +2. ## Confirm the workspace default + + **Workspace role** is the default dashboard role. It applies across every environment unless you override it below. + + Leave this set to the role they should have on most environments. + +3. ## Override one environment + + In **Environment access**, each row shows **Environment**, **Effective role**, and **Source**. + + | Source | Meaning | + | ------ | ------- | + | Workspace Default | Uses the workspace role above | + | Overridden | This environment uses a different dashboard role | + + Choose **Set override** on the environment. Select the dashboard role for that environment, or **No access** to hide it. + + Repeat for each environment that should differ from the default. + + + +4. ## Save + + Click **Save**. + + Tell the teammate which environment to open. They switch with the environment name in the header (right of the workspace name). + + + +## Reset an override + +In **Environment access**, choose **Reset to Default** on that environment. + +The row source returns to **Workspace Default**. The member uses the workspace role again on that environment. + +## Verify + +1. Sign in as the member +2. Switch to the granted environment and confirm settings load +3. Switch to an environment set to **No access** and confirm it is blocked or hidden +4. Re-open **Edit role** and confirm **Source** is **Overridden** on the environments you changed + +## Common questions + +
+Do application roles follow the same per-environment switch? + +No. This page is about Scalekit dashboard access. Application roles for your product's users are not scoped here. + +
+ +
+Why is Staging not in the list? + +Scalekit environment types are **Development** and **Production**. Create extra environments of either type from **Workspace > Environments** if you need a separate test stack. + +
diff --git a/src/content/docs/how-to/custom-domain-on-development.mdx b/src/content/docs/how-to/custom-domain-on-development.mdx new file mode 100644 index 000000000..e076d629e --- /dev/null +++ b/src/content/docs/how-to/custom-domain-on-development.mdx @@ -0,0 +1,95 @@ +--- +title: 'Add a custom domain on development' +description: 'Development environments use the assigned Scalekit URL. Custom domains require Production and Customization Pro.' +tableOfContents: true +sidebar: + label: 'Dev custom domain' + order: 5 +head: + - tag: style + content: | + .sl-markdown-content h2 { font-size: var(--sl-text-xl); } + .sl-markdown-content h3 { font-size: var(--sl-text-lg); } +--- + +import { Steps, Aside } from '@astrojs/starlight/components' + +A Development environment cannot use a branded custom domain. **Configure Custom Domain** stays disabled, with the hint: custom domains are available on **Production** after you upgrade to the **Customization Pro** add-on. + +Use the assigned Development environment URL for local work. When you need `auth.yourapp.com`, switch to Production and follow [Branded custom domains](/guides/custom-domain/). + +## Before you start + +- You can open the environment (**Development** or **Production**) from the header switcher +- For a branded hostname, you can edit DNS for a domain you own +- Customization Pro is billed at **$99/month** and includes custom domains, watermark removal, custom email provider, and custom email templates + + + + + +1. ## Open the Development environment + + In the header, click the **environment name** (right of the workspace name). + + Choose the Development environment you use for local work. + +2. ## Open Custom Domain + + In the left nav, under **Customize**, click **Custom Domain**. + + The page shows **Custom Domain Add-on**, the **Environment URL**, and a **Live** badge. + +3. ## Copy the Development environment URL + + Copy the **Environment URL** (for example `scalekit-xxxxx-dev.scalekit.cloud`). + + Use that host in Development redirect URLs, SDK `environmentUrl`, and local tests. See [Manage environments](/how-to/environments/). + + **Configure Custom Domain** is disabled on Development. Hover it to read the Production + Customization Pro requirement. + +4. ## Configure a branded domain on Production + + Switch the header to a **Production** environment. + + Open **Customize > Custom Domain** and click **Configure Custom Domain**. + + If Customization Pro is not on this environment, Scalekit opens the **Customization Pro Add-on $99/month** dialog. Click **Upgrade**, then continue with the CNAME steps in [Branded custom domains](/guides/custom-domain/). + + + +## Verify + +**Development** + +1. Open the **Environment URL** in a browser +2. Confirm the hosted login or admin page loads over HTTPS +3. Confirm Development redirect URLs use that host, not a production hostname + +**Production (after upgrade)** + +1. **Configure Custom Domain** is enabled +2. DNS verifies and the branded host serves HTTPS +3. Production redirect URLs use the branded host + + + +## Common questions + +
+Can I attach auth.dev.yourapp.com to Development? + +Not through **Custom Domain**. That page only enables CNAME on Production with Customization Pro. Keep Development on the assigned `*-dev.scalekit.cloud` URL. + +
+ +
+Can I reuse the production hostname in Development? + +No. Use the Development environment URL so cookies and redirect URLs never collide with Production. + +
diff --git a/src/content/docs/how-to/define-custom-dashboard-roles.mdx b/src/content/docs/how-to/define-custom-dashboard-roles.mdx new file mode 100644 index 000000000..d58d32625 --- /dev/null +++ b/src/content/docs/how-to/define-custom-dashboard-roles.mdx @@ -0,0 +1,125 @@ +--- +title: 'Define custom dashboard roles' +description: 'Create a custom dashboard role and pick which Scalekit dashboard permissions it grants.' +tableOfContents: true +sidebar: + label: 'Custom dashboard roles' + order: 2 +head: + - tag: style + content: | + .sl-markdown-content h2 { font-size: var(--sl-text-xl); } + .sl-markdown-content h3 { font-size: var(--sl-text-lg); } +--- + +import { Steps, Aside } from '@astrojs/starlight/components' + +Custom dashboard roles grant access to specific Scalekit dashboard areas. Use one when **Admin** is too broad and a fixed role such as **Member** or **Developer** is too narrow. + +These permissions apply to the dashboard, not to users in your application. Application roles stay in [Create roles and permissions](/authenticate/authz/create-roles-permissions/). + +## Before you start + +- **Roles** appears under **Workspace** in workspace settings, between **Team Members** and **Billing** +- You hold `dashboard_roles:write` (and you can only grant permissions you already have) +- You know which dashboard areas the job must open + + + +## Permission groups + +The role drawer groups dashboard permissions. Typical groups: + +| Area | What it gates | +| ---- | ------------- | +| Workspace | Workspace name and settings | +| Members | **Team Members** invites and removals | +| Dashboard roles | Creating and editing dashboard roles | +| Environment access | Per-environment role overrides | +| Billing | Plan, invoices, and payment method | +| Environments | Creating and renaming environments | +| Environment settings | Auth methods, session policy, and related env settings | +| Branding, emails, custom domain | **Customize** pages | +| Applications and API credentials | **Applications** and client secrets | +| Organizations, users, SSO, SCIM | Customer-tenant configuration | +| Webhooks, interceptors, logs | Developer tooling | + +Permissions marked **Sensitive** cover irreversible actions, live credentials, or the ability to grant access to others. + + + +1. ## Open Roles + + In the header, click the **workspace name** and open workspace settings. + + In the workspace left nav, click **Roles**. + + The table lists **Name**, **Permissions**, **Description**, and **Type**. **Fixed** roles ship with Scalekit. **Custom** roles are ones you created. + +2. ## Create a role + + Click **Create role**. + + Enter a **Role name** (50 characters or fewer). Name it after the job, not the person — for example **Billing Owner** or **Support**. + + Add a **Description** so the next Admin knows when to assign it. + +3. ## Start from a preset (optional) + + In **Start from**, pick a preset if one matches the job: + + | Preset | Use when | + | ------ | -------- | + | Member floor | Baseline read access | + | Developer | Apps, credentials, and env configuration | + | Auditor | Read-only review | + | Support | Helping customers without billing or role admin | + | Billing Owner | Plan and invoices | + | Designer | Branding and emails | + + A preset is a starting point. You can change any permission after you pick one. + +4. ## Choose permissions + + Enable only the permissions this job needs. Use **Filter permissions**, **Select all** on a group, or **Clear all**. + + + + + +5. ## Save and assign + + Click **Save changes**. + + Open **Team Members**, choose **Edit role** on the member, select the new role under **Workspace role**, and click **Save**. See [Set up workspace roles](/how-to/set-up-workspace-roles/). + + + +## Verify + +1. Open **Roles** and confirm the new row shows **Custom** and the permission count +2. Assign the role to a test member +3. Sign in as that member and confirm allowed pages load +4. Confirm a denied page shows an access error instead of the setting + +## Common questions + +
+Can I delete a custom role? + +Open the role's **Role actions** menu and choose **Delete**. You cannot delete a role that is still assigned. Reassign those members first. Fixed roles cannot be deleted. + +
+ +
+Does this role apply inside my application? + +No. Dashboard roles only gate `app.scalekit.com`. Application roles for your users live in left-nav **Roles & Permissions** and in [Create roles and permissions](/authenticate/authz/create-roles-permissions/). + +
diff --git a/src/content/docs/how-to/delete-your-account.mdx b/src/content/docs/how-to/delete-your-account.mdx new file mode 100644 index 000000000..e7b758075 --- /dev/null +++ b/src/content/docs/how-to/delete-your-account.mdx @@ -0,0 +1,87 @@ +--- +title: 'Delete your Scalekit account' +description: 'Remove your Scalekit dashboard login. The dashboard has no self-serve delete-account control.' +tableOfContents: true +sidebar: + label: 'Delete your account' + order: 4 +head: + - tag: style + content: | + .sl-markdown-content h2 { font-size: var(--sl-text-xl); } + .sl-markdown-content h3 { font-size: var(--sl-text-lg); } +--- + +import { Steps, Aside } from '@astrojs/starlight/components' + +The Scalekit dashboard does not include a **Delete account** action. Profile settings only edit your name and passkeys. Avatar menu options are **Profile Settings** and **Logout**. + +To remove your login and workspace data, email Scalekit. To leave one workspace and keep the login, ask an Admin to remove you. + +This page is about **your** Scalekit dashboard login. To delete users inside your application, use [Delete users and organizations](/authenticate/manage-users-orgs/delete-users-and-organizations/). + +## Before you start + +- You are signed in to the account you want removed +- You have exported anything you still need: client IDs, redirect URLs, connection configs +- Another Admin can keep the workspace if you should not remain a member + + + + + +1. ## Decide what you need removed + + | Goal | What to do | + | ---- | ---------- | + | Leave one workspace | Ask an Admin to **Remove Member** on **Team Members** | + | Sign out only | Open your avatar menu (initials in the header) and choose **Logout** | + | Delete the Scalekit login | Email support (next step) | + +2. ## Export workspace data you still need + + Copy environment URLs, client credentials, and redirect URLs from each environment you use. After the login is deleted you cannot open the dashboard. + +3. ## Email Scalekit support + + Write to [support@scalekit.com](mailto:support@scalekit.com) from the same address as the account. + + Include: + + - The email on the account + - The workspace name + - Whether you also want the workspace deleted (only if you are the last Admin) + + Support confirms when the login is gone. + + + +## Verify + +- Signing in with the same email fails or starts a new signup +- Teammates no longer see you on **Workspace > Team Members** +- If you asked to delete the workspace, the workspace URL no longer loads + +## Common questions + +
+I only want to leave one workspace + +Ask an Admin to open **Team Members**, open your row menu, and choose **Remove Member**. That removes this workspace only. Your Scalekit login can still join other workspaces. + +
+ +
+Can I delete the account from Profile? + +No. **Profile** (avatar menu → **Profile Settings**, or **My account > Profile**) lets you edit first name, last name, and passkeys. It does not delete the account. + +
+ +
+I need a workspace deleted but I am not the last Admin + +Ask an Admin to remove the workspace, or include that request when you write to [support@scalekit.com](mailto:support@scalekit.com). +
diff --git a/src/content/docs/saaskit/how-to/environments.mdx b/src/content/docs/how-to/environments.mdx similarity index 98% rename from src/content/docs/saaskit/how-to/environments.mdx rename to src/content/docs/how-to/environments.mdx index 451d514ca..f02b96008 100644 --- a/src/content/docs/saaskit/how-to/environments.mdx +++ b/src/content/docs/how-to/environments.mdx @@ -4,6 +4,7 @@ description: Configure development, staging, and production environments, and ch tableOfContents: true sidebar: label: Manage environments + order: 6 head: - tag: style content: | @@ -69,7 +70,7 @@ EU data residency is a **$99/month add-on per production environment**, billed s Other per-environment add-ons, such as **Customization Pro** (custom domain, branding removal, custom email templates), are also billed per region. Running Customization Pro on both a US and an EU production environment bills the add-on twice. -Add a payment method under **Workspace Settings → Billing** in the EU workspace so the add-on and usage-based charges succeed. See [Billing and usage](/saaskit/how-to/billing/) for payment methods and plan details. +Add a payment method under **Workspace Settings → Billing** in the EU workspace so the add-on and usage-based charges succeed. See [Billing and usage](/how-to/billing/) for payment methods and plan details. ## Access environment settings diff --git a/src/content/docs/saaskit/how-to/manage-team-members.mdx b/src/content/docs/how-to/manage-team-members.mdx similarity index 99% rename from src/content/docs/saaskit/how-to/manage-team-members.mdx rename to src/content/docs/how-to/manage-team-members.mdx index 3a27881bc..b27b970e4 100644 --- a/src/content/docs/saaskit/how-to/manage-team-members.mdx +++ b/src/content/docs/how-to/manage-team-members.mdx @@ -4,6 +4,7 @@ description: Invite team members to your Scalekit organization and manage their tableOfContents: true sidebar: label: Manage team members + order: 7 head: - tag: style content: | diff --git a/src/content/docs/how-to/set-up-workspace-roles.mdx b/src/content/docs/how-to/set-up-workspace-roles.mdx new file mode 100644 index 000000000..3ba31345a --- /dev/null +++ b/src/content/docs/how-to/set-up-workspace-roles.mdx @@ -0,0 +1,95 @@ +--- +title: 'Set up workspace roles' +description: 'Invite teammates to your Scalekit workspace and assign the dashboard role they need.' +tableOfContents: true +sidebar: + label: 'Workspace roles' + order: 1 +head: + - tag: style + content: | + .sl-markdown-content h2 { font-size: var(--sl-text-xl); } + .sl-markdown-content h3 { font-size: var(--sl-text-lg); } +--- + +import { Steps, Aside } from '@astrojs/starlight/components' + +Workspace roles control who can open the Scalekit dashboard for your workspace. They do not control users inside your application. + +Invite teammates from **Team Members**. Assign a dashboard role when more than one role exists. For a role you define yourself, see [Define custom dashboard roles](/how-to/define-custom-dashboard-roles/). + +## Before you start + +- You can open **Team Members** +- You have the person's email +- You know whether they need full workspace access or a narrower dashboard role + + + +1. ## Open Team Members + + In the dashboard header, click the **workspace name** (left of the environment switcher). + + Choose **Team Members**. + + The page lists name, email, role, status, and created date. Status values are **Active**, **Pending Invite**, and **Invite Expired**. + +2. ## Invite the teammate + + Click **Invite**. + + Enter their email. + + In **Role**, choose the dashboard role they should get. If the workspace only has **Admin**, that field is fixed to Admin. + +3. ## Send the invite + + Click **Invite**. + + Scalekit emails them a join link. They sign in or create a Scalekit login to accept. + + Until they accept, the row shows **Pending Invite**. After the link expires, the row shows **Invite Expired**. + +4. ## Change an existing member's role + + On the member row, open the menu and choose **Edit role**. + + Under **Workspace role**, select the default dashboard role. That role applies in every environment unless you override it. See [Configure roles per environment](/how-to/configure-environment-roles/). + + Click **Save**. + + + + + +## Verify + +1. Open **Team Members** +2. Confirm the person appears with the role you chose +3. Confirm status is **Pending Invite** (new) or **Active** (accepted) +4. Ask them to sign in and open a page their role should allow + +## Common questions + +
+Where is Team Members in the dashboard? + +Click the **workspace name** in the header, not the environment name and not left-nav **Roles & Permissions**. Workspace settings use their own left nav: **General**, **Environments**, **Team Members**, **Billing**, and **Profile**. + +
+ +
+What is the difference between workspace roles and Roles & Permissions? + +Workspace roles gate the Scalekit dashboard. Left-nav **Roles & Permissions** (under **Configure**) is application RBAC for users in your product. Configure those in [Create roles and permissions](/authenticate/authz/create-roles-permissions/). + +
+ +
+How do I remove someone? + +Open the row menu and choose **Remove Member**. They lose dashboard access to this workspace immediately. Their Scalekit login remains; it is not an account deletion. See [Delete your Scalekit account](/how-to/delete-your-account/). + +
diff --git a/src/content/docs/saaskit/cookbooks/add-enterprise-sso-nextjs-authjs.mdx b/src/content/docs/saaskit/cookbooks/add-enterprise-sso-nextjs-authjs.mdx index 85a021f47..6763d1d15 100644 --- a/src/content/docs/saaskit/cookbooks/add-enterprise-sso-nextjs-authjs.mdx +++ b/src/content/docs/saaskit/cookbooks/add-enterprise-sso-nextjs-authjs.mdx @@ -1,6 +1,9 @@ --- title: 'Add Enterprise SSO to Next.js with Auth.js' description: 'Wire Scalekit''s OIDC interface into Auth.js to ship per-tenant enterprise SSO in Next.js without touching SAML or IdP-specific code.' +sidebar: + label: 'SSO with Auth.js' + order: 5 --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/add-hosted-auth-nextjs-app-router.mdx b/src/content/docs/saaskit/cookbooks/add-hosted-auth-nextjs-app-router.mdx index 9a66f983c..3c194bfaa 100644 --- a/src/content/docs/saaskit/cookbooks/add-hosted-auth-nextjs-app-router.mdx +++ b/src/content/docs/saaskit/cookbooks/add-hosted-auth-nextjs-app-router.mdx @@ -1,6 +1,9 @@ --- title: 'Add Scalekit hosted auth to a Next.js app' description: 'Wire Scalekit hosted login into the Next.js App Router with server-side sessions, transparent token refresh, and logout.' +sidebar: + label: 'Hosted auth in Next.js' + order: 2 --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/building-custom-org-switcher.mdx b/src/content/docs/saaskit/cookbooks/building-custom-org-switcher.mdx index 52e1770cf..cf70d20c0 100644 --- a/src/content/docs/saaskit/cookbooks/building-custom-org-switcher.mdx +++ b/src/content/docs/saaskit/cookbooks/building-custom-org-switcher.mdx @@ -1,6 +1,9 @@ --- title: 'Building a Custom Organization Switcher' description: 'Learn how to build your own organization switcher UI for complete control over multi-tenant user experiences.' +sidebar: + label: 'Custom org switcher' + order: 4 --- import { TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/implement-nextjs-auth.mdx b/src/content/docs/saaskit/cookbooks/implement-nextjs-auth.mdx index 44207f9ed..9965cd9f2 100644 --- a/src/content/docs/saaskit/cookbooks/implement-nextjs-auth.mdx +++ b/src/content/docs/saaskit/cookbooks/implement-nextjs-auth.mdx @@ -1,6 +1,9 @@ --- title: 'Implement passwordless auth in Next.js 15' description: 'Add magic link and OTP authentication to your Next.js application using Scalekit''s headless API.' +sidebar: + label: 'Passwordless in Next.js' + order: 3 --- import { TabItem, Tabs } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/index.mdx b/src/content/docs/saaskit/cookbooks/index.mdx deleted file mode 100644 index 9a324674f..000000000 --- a/src/content/docs/saaskit/cookbooks/index.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: 'Auth for SaaS cookbooks' -description: 'Step-by-step recipes for login, SSO, SCIM, API auth, and related Auth for SaaS patterns.' -sidebar: - label: 'Overview' - order: 1 -tableOfContents: false ---- - -Auth for SaaS cookbooks are self-contained recipes. Pick a task, follow it end to end, and adapt the code. - -Browse the full list in the left sidebar under **Cookbooks**. For short dashboard and workspace answers, open **How-to**. - -Recipes for connecting agents to third-party tools live in the -[AgentKit cookbooks](/agentkit/cookbooks/). Both shelves are listed on the -[cookbook hub](/cookbooks/). diff --git a/src/content/docs/saaskit/cookbooks/java-spring-boot-jwt-timeout.mdx b/src/content/docs/saaskit/cookbooks/java-spring-boot-jwt-timeout.mdx index b9736407b..d6ac1393b 100644 --- a/src/content/docs/saaskit/cookbooks/java-spring-boot-jwt-timeout.mdx +++ b/src/content/docs/saaskit/cookbooks/java-spring-boot-jwt-timeout.mdx @@ -1,6 +1,9 @@ --- title: 'Configuring JWT Validation Timeouts in Spring Boot 4.0+' description: 'Fix connection timeout errors when validating Scalekit JWT tokens in Spring Boot 4.0.0 and later versions.' +sidebar: + label: 'Spring Boot JWT timeout' + order: 7 --- diff --git a/src/content/docs/saaskit/cookbooks/m2m-jwks-and-oauth-scopes.mdx b/src/content/docs/saaskit/cookbooks/m2m-jwks-and-oauth-scopes.mdx index 11408d327..f646cd3a0 100644 --- a/src/content/docs/saaskit/cookbooks/m2m-jwks-and-oauth-scopes.mdx +++ b/src/content/docs/saaskit/cookbooks/m2m-jwks-and-oauth-scopes.mdx @@ -1,6 +1,9 @@ --- title: 'M2M JWT verification with JWKS and OAuth scopes' description: 'How JSON Web Key Sets work with Scalekit, how to use the /keys endpoint to verify machine-to-machine tokens, and how OAuth scopes map to JWT claims for authorization.' +sidebar: + label: 'M2M JWT and scopes' + order: 6 --- import { Aside } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/migrate-from-auth0-to-scalekit.mdx b/src/content/docs/saaskit/cookbooks/migrate-from-auth0-to-scalekit.mdx index c3b5247f9..ea1461512 100644 --- a/src/content/docs/saaskit/cookbooks/migrate-from-auth0-to-scalekit.mdx +++ b/src/content/docs/saaskit/cookbooks/migrate-from-auth0-to-scalekit.mdx @@ -1,6 +1,9 @@ --- title: 'Migrate from Auth0 to Scalekit' description: 'Move users, organizations, and enterprise SSO off Auth0 to Scalekit Full Stack Auth with a safe, incremental cutover.' +sidebar: + label: 'Migrate from Auth0' + order: 10 --- import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/scim-seat-limit-enforcement.mdx b/src/content/docs/saaskit/cookbooks/scim-seat-limit-enforcement.mdx index 90c18f6e8..7e1254c6c 100644 --- a/src/content/docs/saaskit/cookbooks/scim-seat-limit-enforcement.mdx +++ b/src/content/docs/saaskit/cookbooks/scim-seat-limit-enforcement.mdx @@ -3,6 +3,7 @@ title: 'Enforce seat limits with SCIM provisioning' description: 'Block over-quota user creation and alert admins when SCIM pushes users beyond your plan seat limit.' sidebar: label: 'Enforce seat limits' + order: 8 tableOfContents: true --- diff --git a/src/content/docs/saaskit/cookbooks/search-scalekit-docs-in-your-ide.mdx b/src/content/docs/saaskit/cookbooks/search-scalekit-docs-in-your-ide.mdx index 7b9d7e65c..b8af18e10 100644 --- a/src/content/docs/saaskit/cookbooks/search-scalekit-docs-in-your-ide.mdx +++ b/src/content/docs/saaskit/cookbooks/search-scalekit-docs-in-your-ide.mdx @@ -2,7 +2,8 @@ title: 'Search Scalekit docs with ref.tools' description: 'Configure ref.tools MCP to search Scalekit documentation directly from Cursor, Claude Code, or Windsurf without leaving your IDE.' sidebar: - label: 'Search Scalekit docs in IDE' + label: 'Search docs in IDE' + order: 11 --- import { Aside, Steps } from '@astrojs/starlight/components'; diff --git a/src/content/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee.mdx b/src/content/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee.mdx index 697bad80b..81cfcf2ca 100644 --- a/src/content/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee.mdx +++ b/src/content/docs/saaskit/cookbooks/sync-b2b-billing-with-chargebee.mdx @@ -2,7 +2,8 @@ title: 'Sync B2B billing with Scalekit and Chargebee' description: 'Map Scalekit organizations to Chargebee customers, run hosted checkout, and keep subscription state in sync via webhooks.' sidebar: - label: 'Chargebee B2B billing' + label: 'Sync Chargebee billing' + order: 9 tableOfContents: true --- diff --git a/src/content/docs/saaskit/how-to/index.mdx b/src/content/docs/saaskit/how-to/index.mdx deleted file mode 100644 index f930e4f8e..000000000 --- a/src/content/docs/saaskit/how-to/index.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: 'Auth for SaaS how-to guides' -description: 'Short answers for Auth for SaaS dashboard, workspace, and account tasks.' -sidebar: - label: 'Overview' - order: 1 -tableOfContents: false ---- - -How-to guides answer one dashboard or workspace question at a time. They are not full implementation journeys. - -Browse the list in the left sidebar under **How-to**. For longer build recipes, open **Cookbooks**. - -The workspace and account guides here — billing, environments, and team members — apply to every -Scalekit product, including AgentKit. For AgentKit-specific dashboard answers, see the -[AgentKit how-to guides](/agentkit/how-to/). diff --git a/src/integrations/assign-how-to-topic.ts b/src/integrations/assign-how-to-topic.ts new file mode 100644 index 000000000..c6dc1ecf6 --- /dev/null +++ b/src/integrations/assign-how-to-topic.ts @@ -0,0 +1,19 @@ +import type { StarlightPlugin } from '@astrojs/starlight/types' + +/** + * Runs before starlight-sidebar-topics so /how-to/** pages get the Cookbooks + * sidebar for the visitor's product (?product= or sk-active-product cookie). + */ +export default function assignHowToTopicPlugin(): StarlightPlugin { + return { + name: 'assign-how-to-topic', + hooks: { + 'config:setup'({ addRouteMiddleware }) { + addRouteMiddleware({ + entrypoint: './src/middleware/assign-how-to-topic.ts', + order: 'pre', + }) + }, + }, + } +} diff --git a/src/middleware.ts b/src/middleware.ts index 48e8fa13d..974603972 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,5 +1,12 @@ import { defineMiddleware } from 'astro:middleware' import { verifyJwt } from '@/utils/auth/jwt' +import { + PRODUCT_STORAGE_KEY, + SHARED_HOW_TO_COLD_DEFAULT_PRODUCT, + isDocsProduct, + isSharedHowToPath, + type DocsProduct, +} from '@/configs/self-hosted' const PUBLIC_PATH_PREFIXES = [ '/auth/', @@ -26,6 +33,17 @@ function isProtectedPath(pathname: string): boolean { export const onRequest = defineMiddleware(async (context, next) => { const { pathname } = context.url + if (isSharedHowToPath(pathname)) { + const fromQuery = context.url.searchParams.get('product') + const fromCookie = context.cookies.get(PRODUCT_STORAGE_KEY)?.value + const product: DocsProduct = isDocsProduct(fromQuery) + ? fromQuery + : isDocsProduct(fromCookie) + ? fromCookie + : SHARED_HOW_TO_COLD_DEFAULT_PRODUCT + ;(context.locals as { skHowToProduct?: DocsProduct }).skHowToProduct = product + } + // Only allow access to public paths without authentication if (isPublicPath(pathname)) { return next() diff --git a/src/middleware/assign-how-to-topic.ts b/src/middleware/assign-how-to-topic.ts new file mode 100644 index 000000000..0231fec6f --- /dev/null +++ b/src/middleware/assign-how-to-topic.ts @@ -0,0 +1,30 @@ +import { defineRouteMiddleware } from '@astrojs/starlight/route-data' +import { + SHARED_HOW_TO_COLD_DEFAULT_PRODUCT, + guidesTopicForProduct, + isDocsProduct, + isSharedHowToPath, + type DocsProduct, +} from '../configs/self-hosted' + +export const onRequest = defineRouteMiddleware((context) => { + const pathname = context.url.pathname + if (!isSharedHowToPath(pathname)) return + + // Prefer product resolved in src/middleware.ts (has the real request URL + cookies + // when the route is server-rendered). On prerendered builds this falls back. + const fromLocals = (context.locals as { skHowToProduct?: DocsProduct }).skHowToProduct + const product: DocsProduct = isDocsProduct(fromLocals) + ? fromLocals + : SHARED_HOW_TO_COLD_DEFAULT_PRODUCT + + const topic = guidesTopicForProduct(product) + const data = context.locals.starlightRoute.entry.data as { topic?: string } + try { + data.topic = topic + } catch { + Object.assign(context.locals.starlightRoute.entry, { + data: { ...context.locals.starlightRoute.entry.data, topic }, + }) + } +}) diff --git a/src/utils/secondary-nav-utils.ts b/src/utils/secondary-nav-utils.ts index 4033d9422..dec8ea57f 100644 --- a/src/utils/secondary-nav-utils.ts +++ b/src/utils/secondary-nav-utils.ts @@ -2,26 +2,24 @@ import { sidebar, sidebarToSecondaryNav, type SecondaryNavMapping } from '../con import { buildPathToSidebarMap, getSidebarIdForPath } from '../configs/sidebar-utils' import { SELF_HOSTED_COLD_DEFAULT_PRODUCT, + SHARED_HOW_TO_COLD_DEFAULT_PRODUCT, + guidesTopicForProduct, isDocsProduct, + isSelfHostedPath, + isSharedHowToPath, + isSharedProductPath, type DocsProduct, } from '../configs/self-hosted' import { isHashOnly, normalizePath } from './path-matching' import type { NavItem } from '../configs/secondary-nav.config' import { IconLucideLayoutGrid } from './icon-map' -/** - * Self-hosted deployment docs are shared by both products. Header and secondary - * nav keep the visitor's product (AgentKit or Auth for SaaS) — never a third - * "Self Hosted" product option. - */ -export function isSelfHostedPath(pathname: string): boolean { - return pathname.startsWith('/self-hosted/') -} +export { isSelfHostedPath, isSharedHowToPath, isSharedProductPath } /** * Determines which product is active based on the current page context. - * Frontmatter topic and path take precedence; shared self-hosted routes use - * ?product= then a cold default (sessionStorage is applied client-side only). + * Frontmatter topic and path take precedence; shared routes use ?product= + * then a cold default (sessionStorage / cookie applied client-side). */ export function getActiveProduct( pathname: string, @@ -31,12 +29,14 @@ export function getActiveProduct( const productParam = searchParams?.get('product') if (isDocsProduct(productParam)) return productParam - if (topic === 'connect') return 'agentkit' + if (topic === 'connect' || topic === 'agentkit-guides') return 'agentkit' + if (topic === 'saaskit-guides') return 'saaskit' if (pathname.startsWith('/agentkit/')) return 'agentkit' - // Shared self-hosted docs: server-side cold default is AgentKit. Client restores - // SaaS from sessionStorage when the visitor arrived from Auth for SaaS. + // Shared routes: server-side cold default. Client restores the other product + // from sessionStorage when the visitor arrived from that product. if (isSelfHostedPath(pathname)) return SELF_HOSTED_COLD_DEFAULT_PRODUCT + if (isSharedHowToPath(pathname)) return SHARED_HOW_TO_COLD_DEFAULT_PRODUCT return 'saaskit' } @@ -96,6 +96,14 @@ export function getActiveSecondaryNavId( return 'saaskit-user-management' } + // Shared workspace how-tos: product (query / topic set by middleware) picks + // which Cookbooks tab to highlight. Do this before the path map, which would + // pin /how-to/** to whichever sidebar listed the folder first. + if (isSharedHowToPath(pathname)) { + const product = getActiveProduct(pathname, entry?.data?.topic, searchParams) + return guidesTopicForProduct(product) + } + // 1. First check explicit topic from page frontmatter if (entry?.data?.topic) { const mapping = sidebarToSecondaryNav[entry.data.topic] From e32ac6273857eb970d0c72cfc736ab4438a735f0 Mon Sep 17 00:00:00 2001 From: Saif Date: Fri, 14 Aug 2026 16:03:55 +0530 Subject: [PATCH 8/8] fix(docs): keep how-to hashes and stop exporting secrets Preserve URL fragments when stamping ?product= on /how-to/ links. Document assignHowToTopic. Tell readers not to copy client secrets before deleting an account. --- astro.config.mjs | 1 + src/components/overrides/HeaderProductToggle.astro | 2 +- src/content/docs/how-to/delete-your-account.mdx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/astro.config.mjs b/astro.config.mjs index 9fdee73ce..7747a60ac 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -107,6 +107,7 @@ export default defineConfig({ starlightImageZoom({ showCaptions: true, }), + // Shared /how-to/** pages have no product folder; assign a topic so Starlight can pick a Cookbooks rail. assignHowToTopic(), starlightSidebarTopics(sidebarConfig, { topics, exclude }), starlightDocSearch({ diff --git a/src/components/overrides/HeaderProductToggle.astro b/src/components/overrides/HeaderProductToggle.astro index 01b9d72ce..6f420f4b1 100644 --- a/src/components/overrides/HeaderProductToggle.astro +++ b/src/components/overrides/HeaderProductToggle.astro @@ -246,7 +246,7 @@ const productLinks = [ if (!(anchor instanceof HTMLAnchorElement)) return const url = new URL(anchor.href, location.origin) url.searchParams.set('product', product) - anchor.setAttribute('href', `${url.pathname}${url.search}`) + anchor.setAttribute('href', `${url.pathname}${url.search}${url.hash}`) }) } diff --git a/src/content/docs/how-to/delete-your-account.mdx b/src/content/docs/how-to/delete-your-account.mdx index e7b758075..449346ac6 100644 --- a/src/content/docs/how-to/delete-your-account.mdx +++ b/src/content/docs/how-to/delete-your-account.mdx @@ -42,7 +42,7 @@ This page is about **your** Scalekit dashboard login. To delete users inside you 2. ## Export workspace data you still need - Copy environment URLs, client credentials, and redirect URLs from each environment you use. After the login is deleted you cannot open the dashboard. + Record environment URLs, client IDs, and redirect URLs from each environment you use. Do not export client secrets. After the login is deleted you cannot open the dashboard. 3. ## Email Scalekit support