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; with the framework-agnostic browser client it looks like:

```ts
import { createAuthdogClient } from "@authdog/javascript";

const authdog = createAuthdogClient({ publicKey: PK_AUTHDOG });

// 1. Kick off sign-in (redirects to the identity flow)
authdog.signIn();

// 2. On return, consume the callback and persist the session
authdog.handleRedirectCallback();

// ...later
authdog.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.
