"use client"; import * as React from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { ArrowLeft, ArrowRight, Check, Code2, FolderKanban, KeyRound, Play, Route, ShieldCheck, Zap } from "lucide-react"; import type { CreatedKey } from "@/actions/api-keys"; import { completeOnboarding } from "@/actions/account"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Alert } from "@/components/ui/alert"; import { CodeBlock } from "@/components/ui/code-block"; import { cn } from "@/lib/utils"; import { curlSnippet } from "@/lib/account-snippets"; import { CreateKeyForm, type ProjectOption } from "@/components/dashboard/api-keys/create-key-dialog"; import { KeyReveal } from "@/components/dashboard/api-keys/key-reveal"; import { CreateProjectDialog } from "@/components/dashboard/projects/create-project-dialog"; const STEPS = [ { id: "welcome", label: "Welcome", icon: Zap }, { id: "project", label: "Project", icon: FolderKanban }, { id: "key", label: "API key", icon: KeyRound }, { id: "request", label: "First request", icon: Play }, { id: "code", label: "Generated code", icon: Code2 }, ] as const; export function OnboardingFlow({ projects, currentProject, hasKey, hasRequest, alreadyCompleted, userName, }: { projects: ProjectOption[]; currentProject: ProjectOption; hasKey: boolean; hasRequest: boolean; alreadyCompleted: boolean; userName: string; }) { const router = useRouter(); const [step, setStep] = React.useState(0); const [created, setCreated] = React.useState(null); const [finishing, startFinish] = React.useTransition(); const [error, setError] = React.useState(null); const keyReady = hasKey || Boolean(created); const apiKeyForSnippet = created?.plaintext ?? "fch_live_YOUR_API_KEY"; const last = step === STEPS.length - 1; function finish() { setError(null); startFinish(async () => { const res = await completeOnboarding(); if (!res.ok) { setError(res.error); return; } router.push("/dashboard"); router.refresh(); }); } return (
{/* Progress */}
    {STEPS.map((s, i) => { const done = i < step || (i === 1) || (i === 2 && keyReady) || (i === 3 && hasRequest); const active = i === step; const Icon = s.icon; return (
  1. ); })}
{alreadyCompleted && step === 0 ? You already finished onboarding โ€” feel free to walk through it again. : null} {step === 0 ? (
Step 1 of 5

Welcome to Fetcha{userName ? `, ${userName.split(" ")[0]}` : ""}

One API that fetches any public web page for you, choosing the right network and location automatically, retrying when a target blocks, and returning clean HTML, text or JSON.

    {[ { icon: Route, title: "Intelligent routing", desc: "Every request is scored on success history, cost, latency, network health and geography, then escalated only when needed." }, { icon: ShieldCheck, title: "Built-in resilience", desc: "Retries with a fresh IP or alternate network, circuit breakers per domain and sticky sessions when you need the same identity." }, { icon: Code2, title: "Developer first", desc: "A single POST /v1/fetch, request IDs on every call, a Playground that writes your code, and SDKs for JS and Python." }, ].map((b) => (
  • {b.title}
    {b.desc}
  • ))}
) : null} {step === 1 ? (
Step 2 of 5

Create your first project

Projects separate keys, logs, defaults and spending limits. We created one for you โ€” keep it or add another for a different environment.

{currentProject.name} {currentProject.environment} Current
{currentProject.id}
New project } />
{projects.length > 1 ?

You have {projects.length} projects. The current one is used for the key in the next step.

: null}
) : null} {step === 2 ? (
Step 3 of 5

Generate an API key

Keys are shown once and stored hashed. This one gets the default scopes for fetching, sessions and usage.

{created ? ( ) : hasKey ? ( Manage keys } > Continue with it, or create another one below for this walkthrough. ) : null} {!created ? (
) : null}
) : null} {step === 3 ? (
Step 4 of 5

Run your first request

Paste this in a terminal. {created ? "It already contains the key you just created." : "Replace the placeholder with your key."} The response includes request_id, the page content and routing metadata.

{hasRequest ? Nice โ€” this organization has already made requests. The Requests page shows every one of them. : null}
) : null} {step === 4 ? (
Step 5 of 5

Get generated code

The Playground writes the request in cURL, JavaScript, Python, Go, PHP, Ruby, Java and C# as you configure it. Copy it straight into your project.

Playground ยท Code tab
Snippets stay in sync with the request you build.
Documentation
API reference, error codes, sessions, SDK install from source.
{error ? {error} : null}
) : null}
{!last ? ( ) : null} {last ? ( ) : ( )}
); }