diff --git a/packages/ui/app/src/auth/OAuth2Client.ts b/packages/ui/app/src/auth/OAuth2Client.ts
index 2d00078391..ffa99f5402 100644
--- a/packages/ui/app/src/auth/OAuth2Client.ts
+++ b/packages/ui/app/src/auth/OAuth2Client.ts
@@ -16,16 +16,15 @@ export class OAuth2Client {
     private readonly environment: string;
     private readonly scope: string | undefined;
     private readonly jwks: string | undefined;
+    private readonly redirectUri?: string;
 
-    constructor(
-        config: AuthEdgeConfigOAuth2Ory,
-        private readonly redirect_uri?: string,
-    ) {
+    constructor(config: AuthEdgeConfigOAuth2Ory) {
         this.clientId = config.clientId;
         this.clientSecret = config.clientSecret;
         this.environment = config.environment;
         this.scope = config.scope;
         this.jwks = config.jwks;
+        this.redirectUri = config.redirectUri;
     }
 
     public async getToken(code: string): Promise<OAuthTokenResponse> {
@@ -34,8 +33,8 @@ export class OAuth2Client {
         form.append("client_secret", this.clientSecret);
         form.append("grant_type", "authorization_code");
         form.append("client_id", this.clientId);
-        if (this.redirect_uri != null) {
-            form.append("redirect_uri", this.redirect_uri);
+        if (this.redirectUri != null) {
+            form.append("redirect_uri", this.redirectUri);
         }
 
         const response = await fetch(urlJoin(this.environment, "/token"), {
@@ -55,8 +54,8 @@ export class OAuth2Client {
         form.append("client_secret", this.clientSecret);
         form.append("grant_type", "refresh_token");
         form.append("client_id", this.clientId);
-        if (this.redirect_uri != null) {
-            form.append("redirect_uri", this.redirect_uri);
+        if (this.redirectUri != null) {
+            form.append("redirect_uri", this.redirectUri);
         }
 
         const response = await fetch(urlJoin(this.environment, "/token"), {
@@ -74,8 +73,8 @@ export class OAuth2Client {
         const url = new URL(urlJoin(this.environment, "/auth"));
         url.searchParams.set("response_type", "code");
         url.searchParams.set("client_id", this.clientId);
-        if (this.redirect_uri != null) {
-            url.searchParams.set("redirect_uri", this.redirect_uri);
+        if (this.redirectUri != null) {
+            url.searchParams.set("redirect_uri", this.redirectUri);
         }
         if (state != null) {
             url.searchParams.set("state", state);
diff --git a/packages/ui/app/src/auth/getApiKeyInjectionConfig.ts b/packages/ui/app/src/auth/getApiKeyInjectionConfig.ts
index 23331b9c6e..7291f31628 100644
--- a/packages/ui/app/src/auth/getApiKeyInjectionConfig.ts
+++ b/packages/ui/app/src/auth/getApiKeyInjectionConfig.ts
@@ -34,7 +34,7 @@ export async function getAPIKeyInjectionConfig(
 ): Promise<APIKeyInjectionConfig> {
     const config = await getAuthEdgeConfig(domain);
     if (config?.type === "oauth2" && config.partner === "ory" && config["api-key-injection-enabled"]) {
-        const client = new OAuth2Client(config, `https://${domain}/api/auth/callback`);
+        const client = new OAuth2Client(config);
         const tokens = cookies != null ? await client.getOrRefreshAccessTokenEdge(cookies) : undefined;
 
         if (tokens != null) {
@@ -71,7 +71,7 @@ export async function getAPIKeyInjectionConfigNode(
 ): Promise<APIKeyInjectionConfig> {
     const config = await getAuthEdgeConfig(domain);
     if (config?.type === "oauth2" && config.partner === "ory" && config["api-key-injection-enabled"]) {
-        const client = new OAuth2Client(config, `https://${domain}/api/auth/callback`);
+        const client = new OAuth2Client(config);
         const tokens = cookies != null ? await client.getOrRefreshAccessTokenNode(cookies) : undefined;
 
         if (tokens != null) {
diff --git a/packages/ui/app/src/auth/getAuthEdgeConfig.ts b/packages/ui/app/src/auth/getAuthEdgeConfig.ts
index 6f5df55d67..a75f2e1521 100644
--- a/packages/ui/app/src/auth/getAuthEdgeConfig.ts
+++ b/packages/ui/app/src/auth/getAuthEdgeConfig.ts
@@ -1,3 +1,4 @@
+import { captureMessage } from "@sentry/nextjs";
 import { get } from "@vercel/edge-config";
 import { AuthEdgeConfig, AuthEdgeConfigSchema } from "./types";
 
@@ -8,8 +9,17 @@ export async function getAuthEdgeConfig(currentDomain: string): Promise<AuthEdge
     const toRet = domainToTokenConfigMap?.[currentDomain];
 
     if (toRet != null) {
-        // if the config is present, it should be valid
-        return AuthEdgeConfigSchema.parse(toRet);
+        const config = AuthEdgeConfigSchema.safeParse(toRet);
+
+        // if the config is present, it should be valid.
+        // if it's malformed, custom auth for this domain will not work and may leak docs to the public.
+        if (!config.success) {
+            // eslint-disable-next-line no-console
+            console.error(config.error);
+            captureMessage(`Could not parse AuthEdgeConfigSchema for ${currentDomain}`, "fatal");
+        }
+
+        return config.data;
     }
 
     return;
diff --git a/packages/ui/app/src/auth/types.ts b/packages/ui/app/src/auth/types.ts
index c8b429598b..a4af571830 100644
--- a/packages/ui/app/src/auth/types.ts
+++ b/packages/ui/app/src/auth/types.ts
@@ -17,6 +17,7 @@ export const AuthEdgeConfigOAuth2OrySchema = z.object({
     scope: z.optional(z.string()),
     clientId: z.string(),
     clientSecret: z.string(),
+    redirectUri: z.string().optional(),
     "api-key-injection-enabled": z.optional(z.boolean()),
 });
 export const AuthEdgeConfigOAuth2WebflowSchema = z.object({
@@ -25,6 +26,7 @@ export const AuthEdgeConfigOAuth2WebflowSchema = z.object({
     scope: z.optional(z.union([z.string(), z.array(z.string())])),
     clientId: z.string(),
     clientSecret: z.string(),
+    redirectUri: z.string().optional(),
 });
 
 export const AuthEdgeConfigBasicTokenVerificationSchema = z.object({
diff --git a/packages/ui/app/src/playground/PlaygroundAuthorizationForm.tsx b/packages/ui/app/src/playground/PlaygroundAuthorizationForm.tsx
index 79fc57225c..0a25f70d76 100644
--- a/packages/ui/app/src/playground/PlaygroundAuthorizationForm.tsx
+++ b/packages/ui/app/src/playground/PlaygroundAuthorizationForm.tsx
@@ -396,7 +396,7 @@ export function PlaygroundAuthorizationFormCard({
             }
             url.searchParams.set("state", state.toString());
 
-            if (apiKeyInjection.partner === "ory") {
+            if (apiKeyInjection.partner === "ory" || apiKeyInjection.partner === "webflow") {
                 const redirect_uri = urlJoin(window.location.origin, callbackApiRoute);
                 url.searchParams.set("redirect_uri", redirect_uri);
             }
diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/api-key-injection.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/api-key-injection.ts
index 1ffbd38f54..ef4dbd3212 100644
--- a/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/api-key-injection.ts
+++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/api-key-injection.ts
@@ -8,7 +8,6 @@ import {
     withSecureCookie,
 } from "@fern-ui/ui/auth";
 import { NextRequest, NextResponse } from "next/server";
-import urlJoin from "url-join";
 import { WebflowClient } from "webflow-api";
 import type { OauthScope } from "webflow-api/api/types/OAuthScope";
 
@@ -27,12 +26,10 @@ export default async function handler(req: NextRequest): Promise<NextResponse<AP
                 authenticated: false,
                 url: WebflowClient.authorizeURL({
                     clientId: edgeConfig.clientId,
-
-                    // TODO: subpaths will not work
-                    // redirectUri: `https://${domain}/api/fern-docs/oauth/webflow/callback`,
+                    redirectUri: edgeConfig.redirectUri,
 
                     // note: this is not validated
-                    scope: (edgeConfig.scope as OauthScope | OauthScope[]) ?? "authorized_user:read",
+                    scope: (edgeConfig.scope as OauthScope | OauthScope[]) ?? [],
                 }),
             });
         }
@@ -59,7 +56,7 @@ export default async function handler(req: NextRequest): Promise<NextResponse<AP
         let exp = expires;
 
         if (edgeConfig != null && edgeConfig.type === "oauth2" && edgeConfig.partner === "ory") {
-            const oauthClient = new OAuth2Client(edgeConfig, urlJoin(`https://${domain}/api/auth/callback`));
+            const oauthClient = new OAuth2Client(edgeConfig);
 
             const token = OryAccessTokenSchema.parse(await oauthClient.decode(access_token));
             exp = token.exp == null ? undefined : new Date(token.exp * 1000);
diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/callback.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/callback.ts
index 990c50037d..f2a6fe86ef 100644
--- a/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/callback.ts
+++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/callback.ts
@@ -15,13 +15,14 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
     if (req.method !== "GET") {
         return new NextResponse(null, { status: 405 });
     }
+    const domain = getXFernHostEdge(req);
 
     // The authorization code returned by AuthKit
     const code = req.nextUrl.searchParams.get("code");
     const state = req.nextUrl.searchParams.get("state");
     const error = req.nextUrl.searchParams.get("error");
     const error_description = req.nextUrl.searchParams.get("error_description");
-    const redirectLocation = state ?? req.nextUrl.origin;
+    const redirectLocation = state ?? `https://${domain}/`;
 
     if (error != null) {
         return redirectWithLoginError(redirectLocation, error_description ?? error);
@@ -31,7 +32,6 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
         return redirectWithLoginError(redirectLocation, "Couldn't login, please try again");
     }
 
-    const domain = getXFernHostEdge(req);
     const config = await getAuthEdgeConfig(domain);
 
     if (config != null && config.type === "oauth2" && config.partner === "ory") {
diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/logout.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/logout.ts
index c06e2dae8d..76e181e775 100644
--- a/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/logout.ts
+++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/auth/logout.ts
@@ -1,10 +1,13 @@
+import { getXFernHostEdge } from "@/server/xfernhost/edge";
 import { NextRequest, NextResponse } from "next/server";
 
 export const runtime = "edge";
 
 export default async function GET(req: NextRequest): Promise<NextResponse> {
+    const domain = getXFernHostEdge(req);
+
     const state = req.nextUrl.searchParams.get("state");
-    const redirectLocation = state ?? req.nextUrl.origin;
+    const redirectLocation = state ?? `https://${domain}/`;
 
     const res = NextResponse.redirect(redirectLocation);
     res.cookies.delete("fern_token");
diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/ory/callback.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/ory/callback.ts
index 9f9724fd71..50753196d1 100644
--- a/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/ory/callback.ts
+++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/ory/callback.ts
@@ -8,7 +8,6 @@ import {
     withSecureCookie,
 } from "@fern-ui/ui/auth";
 import { NextRequest, NextResponse } from "next/server";
-import urlJoin from "url-join";
 
 export const runtime = "edge";
 
@@ -23,11 +22,13 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
         return new NextResponse(null, { status: 405 });
     }
 
+    const domain = getXFernHostEdge(req);
+
     const code = req.nextUrl.searchParams.get("code");
     const state = req.nextUrl.searchParams.get("state");
     const error = req.nextUrl.searchParams.get("error");
     const error_description = req.nextUrl.searchParams.get("error_description");
-    const redirectLocation = state ?? req.nextUrl.origin;
+    const redirectLocation = state ?? `https://${domain}/`;
 
     if (error != null) {
         return redirectWithLoginError(redirectLocation, error_description ?? error);
@@ -37,14 +38,13 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
         return redirectWithLoginError(redirectLocation, "Couldn't login, please try again");
     }
 
-    const domain = getXFernHostEdge(req);
     const config = await getAuthEdgeConfig(domain);
 
     if (config == null || config.type !== "oauth2" || config.partner !== "ory") {
         return redirectWithLoginError(redirectLocation, "Couldn't login, please try again");
     }
 
-    const oauthClient = new OAuth2Client(config, urlJoin(`https://${domain}`, req.nextUrl.pathname));
+    const oauthClient = new OAuth2Client(config);
     try {
         const { access_token, refresh_token } = await oauthClient.getToken(code);
         const token = OryAccessTokenSchema.parse(await oauthClient.decode(access_token));
diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/webflow/callback.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/webflow/callback.ts
index 9a34aefd80..bd99c5736d 100644
--- a/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/webflow/callback.ts
+++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/oauth/webflow/callback.ts
@@ -16,11 +16,13 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
         return new NextResponse(null, { status: 405 });
     }
 
+    const domain = getXFernHostEdge(req);
+
     const code = req.nextUrl.searchParams.get("code");
     const state = req.nextUrl.searchParams.get("state");
     const error = req.nextUrl.searchParams.get("error");
     const error_description = req.nextUrl.searchParams.get("error_description");
-    const redirectLocation = state ?? req.nextUrl.origin;
+    const redirectLocation = state ?? `https://${domain}/`;
 
     if (error != null) {
         return redirectWithLoginError(redirectLocation, error_description ?? error);
@@ -30,7 +32,6 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
         return redirectWithLoginError(redirectLocation, "Couldn't login, please try again");
     }
 
-    const domain = getXFernHostEdge(req);
     const config = await getAuthEdgeConfig(domain);
 
     if (config == null || config.type !== "oauth2" || config.partner === "ory") {
@@ -41,6 +42,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
         const accessToken = await WebflowClient.getAccessToken({
             clientId: config.clientId,
             clientSecret: config.clientSecret,
+            redirectUri: config.redirectUri,
             code,
         });