From c8a5da999b407a905dad36f4c0ad49d84b3e31ca Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Sat, 29 Aug 2026 08:21:46 -0700 Subject: [PATCH] feat: drop the webhook step from onboarding (nobody wires a webhook on first sign-in) and make the self-hosted cloud step minimal: a two-line pitch that says it is about warmup, no button of its own, Get started requests the code and turns into Waiting for approval until the link lands, Skip for now stays --- web/src/app/onboarding/page.tsx | 59 ++++--------------- .../components/app/cloud/CloudLinkCard.tsx | 30 ++++++++++ 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/web/src/app/onboarding/page.tsx b/web/src/app/onboarding/page.tsx index 4c41ff6ba..9da21ccd4 100644 --- a/web/src/app/onboarding/page.tsx +++ b/web/src/app/onboarding/page.tsx @@ -14,7 +14,6 @@ import AuthButton from "@/components/auth/button"; import useCompleteOnboarding from "@/lib/api/hooks/auth/useCompleteOnboarding"; import useUpdateOrganization from "@/lib/api/hooks/app/organizations/useUpdateOrganization"; import useCurrentOrganization from "@/lib/api/hooks/app/organizations/useCurrentOrganization"; -import createWebhook from "@/lib/api/client/app/webhooks/createWebhook"; import type { AppError } from "@/lib/api/client/normalizeError"; import buildError from "@/lib/helper/buildError"; @@ -33,15 +32,6 @@ const schema = z.object({ referral_source: z.enum(["reddit", "x", "facebook", "google", "other"], { error: "Let us know how you found us", }), - // Optional final step: connect a webhook. Skippable, so an empty value is OK; - // when present it must look like a URL. - webhook_url: z - .string() - .trim() - .url("Enter a valid URL (https://…)") - .refine((u) => u.startsWith("https://"), "Webhook URLs must use https") - .optional() - .or(z.literal("")), }); type OnboardingForm = z.infer; @@ -64,19 +54,14 @@ const BASE_STEPS = [ title: "A few quick questions", subtitle: "This helps us tailor Warmbly to how you send.", }, - { - fields: ["webhook_url"] as const, - title: "Connect a webhook", - subtitle: "Optional. Get a realtime HTTP callback when things happen in your workspace. You can skip this and add it later.", - }, ]; // Self-hosted instances get one more step: linking to Warmbly Cloud so the // pool warms their mailboxes. Skippable; Settings > Warmbly Cloud has it too. const CLOUD_STEP = { fields: [] as const, - title: "Warm your mailboxes in the Warmbly pool", - subtitle: "Optional. Link this instance to Warmbly Cloud and we run warmup for up to 10 mailboxes for free. Everything else stays on this server.", + title: "Warm up your mailboxes", + subtitle: "Get started links this instance to Warmbly Cloud so it warms your mailboxes. You can skip it.", }; const ROLES = [ @@ -177,6 +162,7 @@ export default function OnboardingPage() { const cloudStep = selfHosted ? STEPS.length - 1 : -1; const [cloudLinked, setCloudLinked] = useState(false); const [cloudOrg, setCloudOrg] = useState(""); + const [cloudStart, setCloudStart] = useState(0); const [step, setStep] = useState(0); const isLast = step === STEPS.length - 1; @@ -214,16 +200,6 @@ export default function OnboardingPage() { role: data.role, team_size: data.team_size, }); - // Optional webhook. Best effort: an empty URL skips it, and a hiccup - // here must never block completing onboarding. - const webhookUrl = (data.webhook_url ?? "").trim(); - if (webhookUrl) { - try { - await createWebhook({ url: webhookUrl, event_types: [], enabled: true }); - } catch { - /* keep going — onboarding completion matters more */ - } - } queryClient.removeQueries({ queryKey: ["auth", "me"] }); navigate("/app/emails"); } catch (e) { @@ -236,6 +212,11 @@ export default function OnboardingPage() { if (pending) return; const ok = await trigger(STEPS[step].fields as unknown as (keyof OnboardingForm)[]); if (!ok) return; + if (step === cloudStep && !cloudLinked) { + // First press asks the cloud for a code; the card then waits for approval. + setCloudStart((n) => n + 1); + return; + } if (isLast) await finish(); else setStep((s) => s + 1); }; @@ -353,6 +334,8 @@ export default function OnboardingPage() {
{ @@ -362,34 +345,16 @@ export default function OnboardingPage() { />
)} - {step === 3 && ( -
- Webhook URL - - -

- We'll POST a signed callback here for every workspace event. Subscribe to specific events and - manage signing secrets later in Settings. You can skip this for now. -

-
- )} - {isLast ? "Get started" : "Continue"} + {step === cloudStep && cloudStart > 0 && !cloudLinked ? "Waiting for approval" : isLast ? "Get started" : "Continue"} - {isLast && ( + {isLast && step === cloudStep && (