Custom flows give you full, headless control over authentication: you call the Authdog identity APIs and SDK primitives directly and build a bespoke sign-in experience yourself. Reach for this when the [Account portal](/docs/account-portal) and [components](/docs/components) can't express the UI or logic you need.

## When to go headless

Most apps are well served by the hosted portal or embeddable components. Choose custom flows when you have requirements those can't meet — a highly bespoke sign-in UI, a non-standard multi-step onboarding, tight integration with your own state management, or a native surface where you want to own every screen.

The trade-off is responsibility: you own the UI, the error and edge-case handling, MFA and step-up prompts, and the overall correctness of the flow. Authdog still issues and validates the session, but the experience around it is yours.

## The primitives

A headless flow is built from a few primitives that mirror what the portal and components do internally:

- **Start sign-in** — begin authentication for a chosen connection (credentials, social, or SSO) and drive any challenges (including MFA) yourself.
- **Handle the callback** — receive the result of the authentication step and exchange it for a session.
- **Establish the session** — set the `authdog-session` for the environment so the browser and your backend can use it.
- **Sign out** — clear the session.

## Illustrative shape

The precise API surface depends on your SDK; the shape looks like:

```ts
import { startSignIn, handleCallback, signOut } from "@authdog/sdk";

// 1. Kick off sign-in for a connection
const flow = await startSignIn({ connection: "email" });

// 2. On return, exchange the result for a session
await handleCallback(flow);   // establishes authdog-session

// ...later
await signOut();
```

## Validating the session

Once established, the `authdog-session` is validated the same way regardless of how it was created — see [Sessions & tokens](/docs/concepts/sessions-tokens). Your server enforces it on protected routes as described in [Backend requests](/docs/backend). If you find yourself rebuilding UI that the SDK already provides, consider dropping back to [components](/docs/components) for those surfaces.
