@@ -125,6 +214,7 @@ To grant a permission to a user, use the `grantPermission` method on the `Server
```tsx
const team = await hexclaveServerApp.getTeam('teamId');
const user = await hexclaveServerApp.getUser();
+if (!team || !user) throw new Error("Team or user not found");
await user.grantPermission(team, 'read');
```
@@ -135,6 +225,7 @@ To revoke a permission from a user, use the `revokePermission` method on the `Se
```tsx
const team = await hexclaveServerApp.getTeam('teamId');
const user = await hexclaveServerApp.getUser();
+if (!team || !user) throw new Error("Team or user not found");
await user.revokePermission(team, 'read');
```
@@ -144,17 +235,17 @@ Project permissions are global permissions that apply to a user across the entir
### Creating a Project Permission
-To create a new project permission, navigate to the `Project Permissions` section of the Stack dashboard. Similar to team permissions, you can select other permissions that the new permission will contain, creating a hierarchical structure.
+To create a new project permission, navigate to **RBAC -> Project Permissions** in the Hexclave dashboard. Similar to team permissions, you can set an ID, add a description, and select other project permissions that the new permission contains.
### Checking if a User has a Project Permission
-To check whether a user has a specific project permission, use the `getPermission` method or the `usePermission` hook. Here's an example:
+To check whether a user has a specific project permission, use `hasPermission`, `getPermission`, or the `usePermission` hook. Here's an example:
```tsx title="Check user permission on the client"
"use client";
- import { useUser } from "@hexclave/next"; // replace `next` with the correct framework SDK package
+ import { useUser } from "@hexclave/next";
export function CheckGlobalPermission() {
const user = useUser({ or: 'redirect' });
@@ -170,7 +261,7 @@ To check whether a user has a specific project permission, use the `getPermissio
```tsx title="Check user permission on the server"
- import { hexclaveServerApp } from "@/stack/server";
+ import { hexclaveServerApp } from "@/hexclave/server";
export default async function CheckGlobalPermission() {
const user = await hexclaveServerApp.getUser({ or: 'redirect' });
@@ -186,15 +277,32 @@ To check whether a user has a specific project permission, use the `getPermissio
+For authorization logic, prefer a server-side boolean check:
+
+```tsx title="app/admin/page.tsx"
+import { hexclaveServerApp } from "@/hexclave/server";
+
+export default async function AdminPage() {
+ const user = await hexclaveServerApp.getUser({ or: "redirect" });
+ const canAccessAdmin = await user.hasPermission("access_admin_dashboard");
+
+ if (!canAccessAdmin) {
+ return
Access denied
;
+ }
+
+ return
Admin dashboard
;
+}
+```
+
### Listing All Project Permissions
-To get a list of all global permissions a user has, use the `listPermissions` method or the `usePermissions` hook:
+To get a list of all global permissions a user has, use the `listPermissions` method or the `usePermissions` hook. Pass `{ recursive: false }` if you only want direct grants:
```tsx title="List global permissions on the client"
"use client";
- import { useUser } from "@hexclave/next"; // replace `next` with the correct framework SDK package
+ import { useUser } from "@hexclave/next";
export function DisplayGlobalPermissions() {
const user = useUser({ or: 'redirect' });
@@ -212,7 +320,7 @@ To get a list of all global permissions a user has, use the `listPermissions` me
```tsx title="List global permissions on the server"
- import { hexclaveServerApp } from "@/stack/server";
+ import { hexclaveServerApp } from "@/hexclave/server";
export default async function DisplayGlobalPermissions() {
const user = await hexclaveServerApp.getUser({ or: 'redirect' });
@@ -230,12 +338,19 @@ To get a list of all global permissions a user has, use the `listPermissions` me
+If you only want direct project permission grants, pass `{ recursive: false }`:
+
+```tsx
+const directPermissions = await user.listPermissions({ recursive: false });
+```
+
### Granting a Project Permission
To grant a global permission to a user, use the `grantPermission` method:
```tsx
const user = await hexclaveServerApp.getUser();
+if (!user) throw new Error("User not found");
await user.grantPermission('access_admin_dashboard');
```
@@ -245,7 +360,25 @@ To revoke a global permission from a user, use the `revokePermission` method:
```tsx
const user = await hexclaveServerApp.getUser();
+if (!user) throw new Error("User not found");
await user.revokePermission('access_admin_dashboard');
```
-By following these guidelines, you can efficiently manage and verify both team and user permissions within your application.
+## Direct vs. inherited permissions
+
+A permission can be present in two ways:
+
+- **Direct** - The user was explicitly granted that permission.
+- **Inherited** - The user was granted a permission that contains it, directly or recursively.
+
+The dashboard definition tables show direct containment only. The SDK can return recursive or direct-only lists:
+
+```tsx
+// Includes inherited permissions
+const allPermissions = await user.listPermissions(team);
+
+// Direct assignments only
+const directPermissions = await user.listPermissions(team, { recursive: false });
+```
+
+For checks like `hasPermission` and `getPermission`, Hexclave resolves contained permissions recursively so roles work as expected.
diff --git a/docs-mintlify/guides/going-further/hosted-vs-handler.mdx b/docs-mintlify/guides/going-further/hosted-vs-handler.mdx
new file mode 100644
index 000000000..3919d8a6c
--- /dev/null
+++ b/docs-mintlify/guides/going-further/hosted-vs-handler.mdx
@@ -0,0 +1,82 @@
+---
+title: "Hosted Components vs. Handler"
+description: "Choose where Hexclave auth pages live — hosted by Hexclave, or on your own domain with HexclaveHandler."
+sidebarTitle: "Hosted vs. Handler"
+---
+
+Hexclave can render sign-in, sign-up, password reset, and the other auth pages in two ways. You pick the mode with the SDK `urls` option.
+
+| Mode | Config | Where pages live | Best for |
+| --- | --- | --- | --- |
+| **Hosted components** (recommended) | `urls: { default: { type: "hosted" } }` | Hexclave-hosted URLs for your project | New projects, least maintenance |
+| **Own handler** | `urls: { default: { type: "handler-component" } }` plus `
` | Routes on your domain (typically `/handler/...`) | Same-domain auth UI, frameworks that need a local catch-all |
+
+You can also point individual keys (`signIn`, `accountSettings`, …) at a custom path or mix hosted and handler targets. See [Setup](/guides/getting-started/setup) for framework-specific wiring.
+
+## Prefer hosted components
+
+For new projects, set:
+
+```ts
+export const hexclaveClientApp = new HexclaveClientApp({
+ tokenStore: "cookie", // or "nextjs-cookie" on Next.js
+ urls: {
+ default: {
+ type: "hosted",
+ },
+ },
+});
+```
+
+With hosted components:
+
+- Users land on Hexclave-hosted auth pages that stay up to date automatically.
+- You do **not** need a `/handler/[...]` catch-all in your app for the default sign-in flow.
+- Redirect helpers such as `redirectToSignIn()` send people to those hosted pages, then back to your app.
+
+This is the path Setup and the CLI onboarding flow recommend.
+
+## Own handler on your domain
+
+Use a local handler when you want auth UI on your own origin (same cookies / branding constraints, or an older integration).
+
+1. Point `urls.default` at the handler component (this is also the SDK default if you omit `urls.default`):
+
+```ts
+urls: {
+ default: {
+ type: "handler-component",
+ },
+},
+```
+
+2. Mount the catch-all route your framework SDK documents — for Next.js:
+
+```tsx title="app/handler/[...hexclave]/page.tsx"
+import { HexclaveHandler } from "@hexclave/next";
+
+export default function Handler() {
+ return
;
+}
+```
+
+Auth URLs then look like `/handler/sign-in`, `/handler/sign-up`, and so on on **your** domain. The handler component is only available in some framework SDKs; hosted works everywhere the client SDK can redirect.
+
+
+ Older docs and reminders may say `type: "handler"`. The current target is `{ type: "handler-component" }`. Prefer `type: "hosted"` for new work.
+
+
+## Mixing and custom pages
+
+You do not have to pick one mode for every page. Examples:
+
+- Keep `default: { type: "hosted" }`, but set `accountSettings: "/settings"` (or `{ type: "custom", url: "/settings", version: 0 }`) for a page you own.
+- Keep most pages on the handler, but send `signIn: { type: "hosted" }` if you want only sign-in hosted.
+
+Whenever you add a custom auth page, update the matching `urls` key and any post-auth redirects (`afterSignIn`, `afterSignUp`, `afterSignOut`, `home`). Those keys are the source of truth for `redirectToSignIn()` and related helpers — if they still point at defaults after you customize routes, users can hit extra redirects or land on the wrong page.
+
+## Related
+
+- [Setup](/guides/getting-started/setup) — framework setup, including the hosted `urls` default.
+- [Authentication overview](/guides/apps/authentication/overview) — what the Authentication app covers.
+- [Local vs. Cloud Dashboard](/guides/going-further/local-vs-cloud-dashboard) — development environment vs cloud project.
diff --git a/docs-mintlify/guides/other/tutorials/build-a-team-based-app.mdx b/docs-mintlify/guides/other/tutorials/build-a-team-based-app.mdx
index db57f452c..9cb21e21d 100644
--- a/docs-mintlify/guides/other/tutorials/build-a-team-based-app.mdx
+++ b/docs-mintlify/guides/other/tutorials/build-a-team-based-app.mdx
@@ -17,7 +17,7 @@ If you have not installed Stack yet (handler routes, `HexclaveProvider`, environ
## Prerequisites
-- Hexclave installed as in [Build a SaaS with Hexclave](/guides/other/tutorials/build-a-saas-with-hexclave) (Next.js App Router examples below assume `hexclaveServerApp` in `stack/server.ts` and `@hexclave/next` in the app).
+- Hexclave installed as in [Build a SaaS with Hexclave](/guides/other/tutorials/build-a-saas-with-hexclave) (Next.js App Router examples below assume `hexclaveServerApp` in `hexclave/server.ts` and `@hexclave/next` in the app).
- A project in the [dashboard](https://app.hexclave.com/projects) where you can edit **Teams** and **Team permissions**.
@@ -42,7 +42,7 @@ Use the **current user** as the scope: `listTeams` / `useTeams`, and `getTeam` /
```tsx title="app/team/[teamId]/page.tsx"
import Link from "next/link";
- import { hexclaveServerApp } from "@/stack/server";
+ import { hexclaveServerApp } from "@/hexclave/server";
type PageProps = { params: { teamId: string } };
@@ -101,7 +101,7 @@ If your framework types route `params` as a `Promise` (newer Next.js), `await` t
```tsx title="app/team/page.tsx"
import Link from "next/link";
- import { hexclaveServerApp } from "@/stack/server";
+ import { hexclaveServerApp } from "@/hexclave/server";
export default async function TeamsIndexPage() {
const user = await hexclaveServerApp.getUser({ or: "redirect" });
@@ -179,7 +179,7 @@ export function CreateWorkspaceButton() {
For imports, support tools, or other **server** jobs, `hexclaveServerApp.createTeam` creates a team at project scope (see [Teams](/guides/apps/teams/overview)); wire membership separately if your flow requires it.
```tsx title="scripts/provision-team.example.ts"
-import { hexclaveServerApp } from "@/stack/server";
+import { hexclaveServerApp } from "@/hexclave/server";
export async function provisionEmptyTeam(displayName: string) {
return await hexclaveServerApp.createTeam({ displayName });
@@ -226,7 +226,7 @@ Replace `export_reports` with a permission you created.
```tsx title="app/team/[teamId]/reports/page.tsx"
- import { hexclaveServerApp } from "@/stack/server";
+ import { hexclaveServerApp } from "@/hexclave/server";
type PageProps = { params: { teamId: string } };
@@ -289,7 +289,7 @@ For listing effective permissions, use `listPermissions` / `usePermissions` ([RB
```tsx title="app/actions/reports.ts"
"use server";
-import { hexclaveServerApp } from "@/stack/server";
+import { hexclaveServerApp } from "@/hexclave/server";
export async function requestReportExportAction(teamId: string) {
const user = await hexclaveServerApp.getUser({ or: "throw" });
diff --git a/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx b/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
index 990e40ab9..012e1426e 100644
--- a/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
+++ b/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
@@ -27,7 +27,7 @@ You typically combine **one or more** of:
```tsx title="middleware.ts"
import { NextRequest, NextResponse } from "next/server";
- import { hexclaveServerApp } from "@/stack/server";
+ import { hexclaveServerApp } from "@/hexclave/server";
export async function middleware(request: NextRequest) {
const user = await hexclaveServerApp.getUser();
@@ -48,7 +48,7 @@ You typically combine **one or more** of:
```tsx title="app/app/dashboard/page.tsx"
- import { hexclaveServerApp } from "@/stack/server";
+ import { hexclaveServerApp } from "@/hexclave/server";
export default async function DashboardPage() {
await hexclaveServerApp.getUser({ or: "redirect" });
@@ -76,7 +76,7 @@ You typically combine **one or more** of:
- **`{ or: "throw" }`** — use in **server actions**, **route handlers**, and other places where redirecting would be wrong; map errors to `401`/`403` responses yourself.
```tsx title="app/api/me/route.ts"
-import { hexclaveServerApp } from "@/stack/server";
+import { hexclaveServerApp } from "@/hexclave/server";
import { NextResponse } from "next/server";
export async function GET() {
diff --git a/docs-mintlify/snippets/docs-apps-home-grid.jsx b/docs-mintlify/snippets/docs-apps-home-grid.jsx
index 2339423c9..050b0a42a 100644
--- a/docs-mintlify/snippets/docs-apps-home-grid.jsx
+++ b/docs-mintlify/snippets/docs-apps-home-grid.jsx
@@ -21,7 +21,6 @@ export const DocsAppsHomeGrid = () => {
{ name: "Payments", href: "/guides/apps/payments/overview", iconSrc: "/images/app-icons/payments.svg" },
{ name: "Analytics", href: "/guides/apps/analytics/overview", iconSrc: "/images/app-icons/analytics.svg" },
{ name: "Teams", href: "/guides/apps/teams/overview", iconSrc: "/images/app-icons/teams.svg" },
- { name: "Fraud Protection", href: "/guides/apps/fraud-protection/overview", iconSrc: "/images/app-icons/fraud-protection.svg" },
{ name: "RBAC", href: "/guides/apps/rbac/overview", iconSrc: "/images/app-icons/rbac.svg" },
{ name: "API Keys", href: "/guides/apps/api-keys/overview", iconSrc: "/images/app-icons/api-keys.svg" },
{ name: "Data Vault", href: "/guides/apps/data-vault/overview", iconSrc: "/images/app-icons/data-vault.svg" },
diff --git a/docs-mintlify/snippets/skeletons/auth-skeletons.jsx b/docs-mintlify/snippets/skeletons/auth-skeletons.jsx
new file mode 100644
index 000000000..72b10fa1d
--- /dev/null
+++ b/docs-mintlify/snippets/skeletons/auth-skeletons.jsx
@@ -0,0 +1,188 @@
+// Lightweight, decorative skeletons used to illustrate the Authentication app.
+// They are intentionally non-interactive and use neutral placeholders so they
+// read as "examples" rather than live UI.
+//
+// NOTE: Mintlify evaluates each exported component in isolation, so every
+// component must be fully self-contained — no shared module-level constants or
+// helper components. That's why ACCENT / Frame are redefined inside each one.
+
+export const SignInSkeleton = () => {
+ const ACCENT = "#6b5df7";
+
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const provider = (label) => (
+
+ );
+
+ const field = (label) => (
+
+ );
+
+ return (
+
+
+
+
+
+ {provider("Google")}
+ {provider("GitHub")}
+
+
+ {field("Email")}
+ {field("Password")}
+
+ Continue
+
+
+
+
+ );
+};
+
+export const AuthMethodsSkeleton = () => {
+ const ACCENT = "#6b5df7";
+
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const toggle = (on) => (
+
+ );
+
+ const row = (label, on) => (
+
+ );
+
+ return (
+
+
+
+ {row("Email & password", true)}
+ {row("Magic link / OTP", true)}
+ {row("Passkey", true)}
+ {row("Google", true)}
+ {row("GitHub", true)}
+ {row("Microsoft", false)}
+
+
+
+ );
+};
+
+export const UserDirectorySkeleton = () => {
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const cell = (w) => ;
+
+ const badge = (text, tone) => {
+ const tones = {
+ green: "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-400",
+ zinc: "bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300",
+ };
+ return (
+
+ {text}
+
+ );
+ };
+
+ const row = (initial, color, w1, w2, status) => (
+
+
+
+ {initial}
+
+ {cell(w1)}
+
+ {cell(w2)}
+
{badge(status[0], status[1])}
+
+ );
+
+ return (
+
+
+
+
+ User
+ Email
+ Status
+
+ {row("A", "#6b5df7", "68px", "120px", ["Active", "green"])}
+ {row("M", "#0ea5e9", "84px", "104px", ["Active", "green"])}
+ {row("S", "#f59e0b", "56px", "132px", ["Invited", "zinc"])}
+ {row("R", "#ef4444", "72px", "92px", ["Active", "green"])}
+
+
+
+ );
+};
diff --git a/docs-mintlify/snippets/skeletons/email-skeletons.jsx b/docs-mintlify/snippets/skeletons/email-skeletons.jsx
new file mode 100644
index 000000000..728dc97dd
--- /dev/null
+++ b/docs-mintlify/snippets/skeletons/email-skeletons.jsx
@@ -0,0 +1,153 @@
+// Lightweight, decorative skeletons used to illustrate the Emails app.
+// They are intentionally non-interactive and use neutral placeholders so they
+// read as "examples" rather than live UI.
+//
+// NOTE: Mintlify evaluates each exported component in isolation, so every
+// component must be fully self-contained — no shared module-level constants or
+// helper components. That's why ACCENT / Frame are redefined inside each one.
+
+export const EmailPreviewSkeleton = () => {
+ const ACCENT = "#6b5df7";
+
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const bodyLine = (w) => (
+
+ );
+
+ return (
+
+
+
+
+
+
+
+ {bodyLine("100%")}
+ {bodyLine("92%")}
+ {bodyLine("96%")}
+ {bodyLine("60%")}
+
+
+ Get started
+
+
+
+
+
+
+ );
+};
+
+export const EmailTemplatesSkeleton = () => {
+ const ACCENT = "#6b5df7";
+
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const row = (label, tag) => (
+
+ );
+
+ return (
+
+
+
+ {row("Email verification", "Transactional")}
+ {row("Password reset", "Transactional")}
+ {row("Magic link / OTP", "Transactional")}
+ {row("Team invitation", "Transactional")}
+ {row("Product update", "Marketing")}
+
+
+
+ );
+};
+
+export const DeliveryStatsSkeleton = () => {
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const tile = (label, value, dot) => (
+
+
+
{value}
+
+ {[7, 11, 6, 13, 9, 14, 10].map((h, i) => (
+
+ ))}
+
+
+ );
+
+ return (
+
+
+
+ {tile("Sent", "8,241", "#10b981")}
+ {tile("Bounced", "37", "#f59e0b")}
+ {tile("Spam", "4", "#ef4444")}
+
+
+
+ );
+};
diff --git a/docs-mintlify/snippets/skeletons/payments-skeletons.jsx b/docs-mintlify/snippets/skeletons/payments-skeletons.jsx
new file mode 100644
index 000000000..6d95c04a4
--- /dev/null
+++ b/docs-mintlify/snippets/skeletons/payments-skeletons.jsx
@@ -0,0 +1,165 @@
+// Lightweight, decorative skeletons used to illustrate the Payments app.
+// They are intentionally non-interactive and use neutral placeholders so they
+// read as "examples" rather than live UI.
+//
+// NOTE: Mintlify evaluates each exported component in isolation, so every
+// component must be fully self-contained — no shared module-level constants or
+// helper components. That's why ACCENT / Frame are redefined inside each one.
+
+export const PricingSkeleton = () => {
+ const ACCENT = "#10b981";
+
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const tier = (name, price, highlighted) => (
+
+
+ {name}
+ {highlighted && (
+
+ Popular
+
+ )}
+
+
+ {price}
+ /mo
+
+
+ {["80%", "65%", "72%"].map((w, i) => (
+
+ ))}
+
+
+ );
+
+ return (
+
+
+
+ {tier("Free", "$0", false)}
+ {tier("Pro", "$20", true)}
+ {tier("Enterprise", "$99", false)}
+
+
+
+ );
+};
+
+export const EntitlementsSkeleton = () => {
+ const ACCENT = "#10b981";
+
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const meter = (label, value, pct) => (
+
+
+ {label}
+ {value}
+
+
+
+ );
+
+ return (
+
+
+
+ {meter("Credits", "820", "82%")}
+ {meter("Seats", "4 / 5", "80%")}
+ {meter("API quota", "12k", "47%")}
+
+
+
+ );
+};
+
+export const TransactionsSkeleton = () => {
+ const Frame = ({ label, children }) => (
+
+ );
+
+ const row = (amount, status, color) => (
+
+
+
+ {amount}
+
+ {status}
+
+
+
+ );
+
+ return (
+
+
+
+ {row("$20.00", "Paid", "#10b981")}
+ {row("$99.00", "Renewed", "#10b981")}
+ {row("$20.00", "Refunded", "#f59e0b")}
+ {row("$20.00", "Failed", "#ef4444")}
+
+
+
+ );
+};
diff --git a/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts b/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts
index 0b3eb3dfd..5c0b51df0 100644
--- a/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts
+++ b/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts
@@ -79,6 +79,7 @@ const docsJson = {
"pages": [
"guides/going-further/cli",
"guides/going-further/local-vs-cloud-dashboard",
+ "guides/going-further/hosted-vs-handler",
"guides/going-further/hexclave-config"
]
},
@@ -95,6 +96,7 @@ const docsJson = {
"guides/apps/authentication/connected-accounts",
"guides/apps/authentication/jwts",
"guides/apps/authentication/sign-up-rules",
+ "guides/apps/authentication/fraud-protection",
"guides/apps/authentication/cli-authentication",
{
"group": "All Auth Providers",
@@ -109,18 +111,50 @@ const docsJson = {
"guides/apps/authentication/auth-providers/google",
"guides/apps/authentication/auth-providers/linkedin",
"guides/apps/authentication/auth-providers/microsoft",
- "guides/apps/authentication/auth-providers/passkey",
"guides/apps/authentication/auth-providers/spotify",
"guides/apps/authentication/auth-providers/twitch",
+ "guides/apps/authentication/auth-providers/x-twitter",
+ "guides/apps/authentication/auth-providers/passkey",
"guides/apps/authentication/auth-providers/two-factor-auth",
- "guides/apps/authentication/auth-providers/x-twitter"
+ "guides/apps/authentication/auth-providers/custom-oidc"
]
}
]
},
- "guides/apps/emails/overview",
- "guides/apps/payments/overview",
- "guides/apps/analytics/overview",
+ {
+ "group": "Emails",
+ "icon": "/images/app-icons/emails.svg",
+ "pages": [
+ "guides/apps/emails/overview",
+ "guides/apps/emails/guide",
+ "guides/apps/emails/templates-and-themes",
+ "guides/apps/emails/drafts"
+ ]
+ },
+ {
+ "group": "Payments",
+ "icon": "/images/app-icons/payments.svg",
+ "pages": [
+ "guides/apps/payments/overview",
+ "guides/apps/payments/setup",
+ "guides/apps/payments/products-and-pricing",
+ "guides/apps/payments/items-and-entitlements",
+ "guides/apps/payments/checkout",
+ "guides/apps/payments/subscriptions",
+ "guides/apps/payments/billing-and-invoices",
+ "guides/apps/payments/granting-products",
+ "guides/apps/payments/customers"
+ ]
+ },
+ {
+ "group": "Analytics",
+ "icon": "/images/app-icons/analytics.svg",
+ "pages": [
+ "guides/apps/analytics/overview",
+ "guides/apps/analytics/queries-and-tables",
+ "guides/apps/analytics/replays-and-clickmaps"
+ ]
+ },
{
"group": "Teams",
"icon": "/images/app-icons/teams.svg",
@@ -129,7 +163,6 @@ const docsJson = {
"guides/apps/teams/team-selection"
]
},
- "guides/apps/fraud-protection/overview",
"guides/apps/rbac/overview",
"guides/apps/api-keys/overview",
"guides/apps/data-vault/overview",
@@ -254,6 +287,74 @@ const docsJson = {
]
},
"redirects": [
+ {
+ "source": "/sdk/objects/stack-app",
+ "destination": "/sdk/objects/hexclave-app"
+ },
+ {
+ "source": "/sdk/hooks/use-stack-app",
+ "destination": "/sdk/hooks/use-hexclave-app"
+ },
+ {
+ "source": "/rest-api/overview",
+ "destination": "/api/overview"
+ },
+ {
+ "source": "/getting-started/setup",
+ "destination": "/guides/getting-started/setup"
+ },
+ {
+ "source": "/docs/getting-started/setup",
+ "destination": "/guides/getting-started/setup"
+ },
+ {
+ "source": "/docs/next/getting-started/setup",
+ "destination": "/guides/getting-started/setup"
+ },
+ {
+ "source": "/docs/sdk",
+ "destination": "/sdk/overview"
+ },
+ {
+ "source": "/docs/apps/analytics",
+ "destination": "/guides/apps/analytics/overview"
+ },
+ {
+ "source": "/docs/apps/api-keys",
+ "destination": "/guides/apps/api-keys/overview"
+ },
+ {
+ "source": "/docs/others/convex",
+ "destination": "/guides/integrations/convex/overview"
+ },
+ {
+ "source": "/docs/concepts/teams",
+ "destination": "/guides/apps/teams/overview"
+ },
+ {
+ "source": "/docs/concepts/custom-user-data",
+ "destination": "/guides/getting-started/user-fundamentals#custom-metadata"
+ },
+ {
+ "source": "/guides/going-further/user-metadata",
+ "destination": "/guides/getting-started/user-fundamentals#custom-metadata"
+ },
+ {
+ "source": "/others/js-client",
+ "destination": "/sdk/objects/hexclave-app"
+ },
+ {
+ "source": "/guides/going-further/stack-app",
+ "destination": "/sdk/objects/hexclave-app"
+ },
+ {
+ "source": "/guides/apps/fraud-protection/overview",
+ "destination": "/guides/apps/authentication/fraud-protection"
+ },
+ {
+ "source": "/guides/apps/payments/guide",
+ "destination": "/guides/apps/payments/setup"
+ },
{
"source": "/guides/going-further/backend-integration",
"destination": "/guides/going-further/local-vs-cloud-dashboard"