diff --git a/auth/connection-lifecycle.mdx b/auth/connection-lifecycle.mdx
new file mode 100644
index 0000000..f8ae6f3
--- /dev/null
+++ b/auth/connection-lifecycle.mdx
@@ -0,0 +1,150 @@
+---
+title: "Connection Lifecycle"
+description: "How connections stay authenticated, and what to do when one breaks"
+---
+
+Once a Managed Auth connection is `AUTHENTICATED`, Kernel runs periodic health checks and automatic re-authentication to keep the session valid. This page covers the runtime lifecycle of a connection: how the check-and-reauth loop works, how to tune it, what blocks auto-reauth, and how to debug a connection that won't stay logged in.
+
+## The lifecycle
+
+After the initial login, every connection moves through this loop:
+
+
+
+ On a configurable cadence, Kernel spins up a browser with the profile and verifies the session is still logged in. If it is, nothing else happens until the next check.
+
+
+ If the check finds the session expired and the connection's `can_reauth` is `true`, Kernel runs the saved login flow with the stored credentials in the background. A successful login resets the loop.
+
+
+ If auto-reauth isn't possible — credentials aren't linked, the saved flow requires human input, or the login keeps failing — the connection's `status` flips to `NEEDS_AUTH` and a new login session is required.
+
+
+
+## Cadence
+
+Health checks run on a configurable interval. Your plan sets the minimum:
+
+| Plan | Minimum interval |
+|------|------------------|
+| Hobbyist | 1 hour |
+| Start-Up | 20 minutes |
+| Enterprise | Fully configurable (as low as 5 minutes) |
+
+You can raise the interval above your plan's minimum, but not below it. Update it with `health_check_interval` (in seconds) — changes take effect immediately, so the next check uses the new value:
+
+
+```typescript TypeScript
+await kernel.auth.connections.update(auth.id, {
+ health_check_interval: 1800, // 30 minutes
+});
+```
+
+```python Python
+await kernel.auth.connections.update(
+ auth.id,
+ health_check_interval=1800, # 30 minutes
+)
+```
+
+
+### Sessions that expire faster than the interval
+
+Kernel keeps re-authenticating even when every health check finds the session expired. A successful login resets the auto-reauth state, so a site whose session TTL is shorter than your health check interval is re-authenticated on every cycle rather than being given up on.
+
+If you're seeing the connection flip to `NEEDS_AUTH` frequently and want shorter detection windows, lower `health_check_interval` toward your plan's minimum.
+
+## Can this connection auto-reauth?
+
+Check the `can_reauth` boolean on a connection. It's `true` only when **both** of these hold:
+
+1. **A credential is linked** — stored in Kernel or sourced via [1Password](/integrations/1password).
+2. **No external action is required** — the saved login flow doesn't need a human (no SMS/email OTP, no push notification, no manual MFA selection).
+
+If either fails, the connection will move to `NEEDS_AUTH` on the next expired session and wait for a fresh login.
+
+### External actions that block auto-reauth
+
+After a successful login, Kernel saves the login flow. If that flow includes steps that require human action, the connection can't auto-reauth because those steps can't be replayed without user input.
+
+If your flow requires one of these, you can still automate around it:
+
+- **Switch to TOTP** — If the site supports authenticator apps, add a `totp_secret` to your credential. Codes are generated on demand, so the flow no longer needs external action. If a code expires before the site accepts it, Kernel retries with a fresh one.
+- **Trigger manual re-auth** — Start a new login session and route the user through the [Hosted UI](/auth/hosted-ui) or [Programmatic](/auth/programmatic) flow.
+
+## Triggering re-auth manually
+
+Call `.login()` on any connection to trigger authentication immediately, without waiting for the next scheduled health check. If the profile is already logged in, it returns quickly without starting a new flow. If the connection needs auth, it starts a new login session.
+
+This is useful when your workflow needs to ensure a connection is authenticated *right now*:
+
+
+```typescript TypeScript
+const state = await kernel.auth.connections.retrieve(auth.id);
+
+if (state.status === 'NEEDS_AUTH') {
+ const login = await kernel.auth.connections.login(auth.id);
+ // Handle login flow as usual
+}
+```
+
+```python Python
+state = await kernel.auth.connections.retrieve(auth.id)
+
+if state.status == "NEEDS_AUTH":
+ login = await kernel.auth.connections.login(auth.id)
+ # Handle login flow as usual
+```
+
+
+## When a login fails
+
+If a login attempt fails — whether triggered by a health check, an auto-reauth, or a manual `.login()` — Kernel retries with exponential backoff. After repeated failures the flow is marked failed and the connection surfaces an error code on `flow_status`.
+
+Common codes:
+
+| Code | Meaning |
+|------|---------|
+| `credentials_invalid` | The stored or submitted credentials were rejected by the site. |
+| `bot_detected` | The login page blocked the session as automated. |
+| `captcha_blocked` | A CAPTCHA was presented and couldn't be solved. |
+| `unsupported_auth_method` | The site required a method Kernel doesn't currently support (e.g. passkeys). |
+
+See the [API reference](https://kernel.sh/docs/api-reference/managed-auth/start-login-flow) for the full list.
+
+### Recovering
+
+- **`credentials_invalid`** — Update the linked [credential](/auth/credentials) and call `.login()` to re-run the flow.
+- **`bot_detected` / `captcha_blocked`** — Pin the connection to a cleaner [proxy](/auth/configuration#custom-proxy) (ISP or custom). For aggressive sites, also enable stealth and review the [bot detection guide](/browsers/bot-detection/overview).
+- **`unsupported_auth_method`** — Switch the account to a supported sign-in method (e.g. password + TOTP instead of a passkey) and re-link the credential.
+
+## Debugging a flaky connection
+
+Two tools handle most investigations:
+
+1. **Dashboard live view** — The **Browser Sessions** tab in the Kernel dashboard shows every auth browser session (logins, health checks, reauths) with a live view. Watch a session in real time to see exactly where it's getting stuck.
+
+2. **Session recordings** — To record only the next single login attempt without recording subsequent health checks and reauths, pass `record_session: true` on `.login()`:
+
+
+```typescript TypeScript
+const login = await kernel.auth.connections.login(auth.id, {
+ record_session: true,
+});
+```
+
+```python Python
+login = await kernel.auth.connections.login(
+ auth.id,
+ record_session=True,
+)
+```
+
+
+To record every auth session on the connection (logins, health checks, and reauths), set `record_session: true` connection-wide — see [Record Sessions for Debugging](/auth/configuration#record-sessions-for-debugging).
+
+## See also
+
+- [Connection Configuration](/auth/configuration) — `health_check_interval`, `proxy`, `record_session`, and other shared options
+- [Credentials](/auth/credentials) — what gets stored and how it powers auto-reauth
+- [FAQ](/auth/faq) — quick answers to common questions
diff --git a/auth/faq.mdx b/auth/faq.mdx
index fde51b6..4efb13d 100644
--- a/auth/faq.mdx
+++ b/auth/faq.mdx
@@ -4,44 +4,7 @@ title: FAQ
## How does automatic re-authentication work?
-When you link credentials to a connection, Kernel monitors the login session and re-authenticates automatically when it expires. Periodic health checks detect logged-out sessions and trigger re-auth in the background, so the profile stays logged in without additional action on your part.
-
-
-Automatic re-authentication only works when the stored credentials are complete and don't require human input. If login needs SMS/email OTP, push notifications, or manual MFA selection, you'll need to trigger a new login session manually.
-
-
-
-## How often are health checks performed?
-
-Health checks run on configurable cadences. Your plan sets the minimum interval:
-- **Hobbyist** — minimum every 1 hour
-- **Start-Up** — minimum every 15 minutes
-- **Enterprise** — fully configurable
-
-You can increase the interval above your plan's minimum, but not below it.
-
-## What if my site's session expires faster than the health check interval?
-
-Kernel keeps re-authenticating even when every health check finds the session expired. A successful login resets the auto-reauth state, so a site whose session TTL is shorter than your health check interval is re-authenticated on every cycle rather than being given up on.
-
-If you're seeing the connection flip to `NEEDS_AUTH` frequently and want shorter detection windows, lower the connection's `health_check_interval` down to your plan's minimum. Enterprise plans can go as low as 5 minutes.
-
-## How do I know if a Kernel can automatically re-authenticate a connection?
-
-Check the `can_reauth` field on a connection. This boolean checks the following conditions:
-
-1. **Credential linked** — A credential must be attached to the connection (stored in Kernel or via an external provider like [1Password](/integrations/1password))
-2. **No external action required** — The learned login flow doesn't require human intervention
-
-Only if all of the above conditions are met will `can_reauth` be `true`. When true, Kernel will attempt to automatically re-authenticate the connection.
-
-### External actions that prevent auto-reauth
-
-After a successful login, Kernel saves the login flow. If the flow includes steps that require human action—like SMS/email OTP, push notifications, or manual MFA selection—Kernel marks the connection as unable to auto-reauth because those steps can't be automated without user input.
-
-If your login flow requires one of these, you can still automate around it:
-- **Switch to TOTP** — If the site supports authenticator apps, add a `totp_secret` to your credential. TOTP codes are generated automatically, so the login flow won't require external action. If a TOTP code expires or times out before the site accepts it, Kernel automatically retries with a fresh code.
-- **Trigger manual re-auth** — Start a new login session and route the user through the [Hosted UI](/auth/hosted-ui) or [Programmatic](/auth/programmatic) flow.
+When you link credentials to a connection, Kernel runs periodic health checks, detects logged-out sessions, and re-authenticates in the background so the profile stays logged in. See [Connection Lifecycle](/auth/connection-lifecycle) for the full lifecycle, cadence options, and `can_reauth` rules.
## What are sign-in options?
@@ -57,13 +20,7 @@ Passkey-based authentication (e.g., Google accounts with passkeys enabled) is no
## What happens if login fails?
-If a login attempt fails, Kernel will retry with exponential backoff. After multiple failures, the [login flow](/auth/hosted-ui) will be marked as failed and you'll receive an error with a specific error code. Common codes include `credentials_invalid`, `bot_detected`, and `captcha_blocked`. See the [API reference](https://kernel.sh/docs/api-reference/managed-auth/start-login-flow) for the full list of error codes.
-
-Common failure reasons include:
-
-- Invalid credentials
-- Bot detection blocking the login page
-- CAPTCHAs that couldn't be solved
+Kernel retries with exponential backoff, then surfaces an error code (`credentials_invalid`, `bot_detected`, `captcha_blocked`, etc.). See [Connection Lifecycle](/auth/connection-lifecycle#when-a-login-fails) for the full list and recovery steps.
## Can I use Managed Auth with any website?
@@ -75,28 +32,7 @@ Yes. Managed Auth and browser profiles are available during your trial period wi
## How do I re-authenticate a connection before the next health check?
-Call `.login()` on any connection at any time to trigger authentication immediately. If the profile is already logged in, it returns quickly without starting a new flow. If the connection needs auth, it starts a new login session.
-
-This is useful when your workflow needs to ensure a connection is authenticated right now, without waiting for the next scheduled health check.
-
-
-```typescript TypeScript
-const state = await kernel.auth.connections.retrieve(auth.id);
-
-if (state.status === 'NEEDS_AUTH') {
- const login = await kernel.auth.connections.login(auth.id);
- // Handle login flow as usual
-}
-```
-
-```python Python
-state = await kernel.auth.connections.retrieve(auth.id)
-
-if state.status == "NEEDS_AUTH":
- login = await kernel.auth.connections.login(auth.id)
- # Handle login flow as usual
-```
-
+Call `.login()` on the connection to trigger auth immediately. See [Triggering re-auth manually](/auth/connection-lifecycle#triggering-re-auth-manually) for the pattern.
## What types of flows does Managed Auth support?
@@ -104,9 +40,7 @@ Managed Auth handles login and authentication flows end-to-end: entering credent
## How do I debug a managed auth session?
-Go to the **Browser Sessions** tab in the Kernel dashboard to watch what the managed auth session is doing in real time. Each auth login runs in a browser session with a live view, so you can see exactly where the flow is getting stuck. This is useful for diagnosing login flow problems or understanding why a session isn't staying authenticated.
-
-For flakes that only show up intermittently or are hard to reproduce live, set `record_session: true` on the connection to capture a [replay](/browsers/replays) of every auth browser session — logins, periodic health checks, and automatic reauths. To record only a single login attempt without recording subsequent health checks and reauths, pass `record_session: true` on `.login()` instead. The entire browser session is recorded, the `replay_id` is persisted on each session, and recordings count toward your normal replay storage. See [Connection Configuration](/auth/configuration#record-sessions-for-debugging) for examples.
+Use the **Browser Sessions** tab in the dashboard for live view, or set `record_session: true` to capture replays of every auth browser session. See [Debugging a flaky connection](/auth/connection-lifecycle#debugging-a-flaky-connection) for details.
## Can I attach multiple auth connections to one profile?
diff --git a/auth/overview.mdx b/auth/overview.mdx
index b5ebfca..07c95d3 100644
--- a/auth/overview.mdx
+++ b/auth/overview.mdx
@@ -97,6 +97,8 @@ await page.goto("https://netflix.com")
+The steps above are the integration loop — what you wire up once per connection. After the initial login, the connection enters its runtime loop of periodic health checks and automatic re-authentication; see [Connection Lifecycle](/auth/connection-lifecycle) for how that works and how to tune it.
+
## Choose Your Integration
@@ -128,7 +130,7 @@ The most valuable workflows live behind logins. Managed Auth provides:
- **SSO/OAuth support** - "Sign in with Google/GitHub/Microsoft" buttons work out-of-the-box, with common SSO provider domains automatically allowed
- **2FA/OTP handling** - TOTP codes automated with automatic retry on expiry, SMS/email/push OTP are supported
- **Post-login URL** - Get the URL where login landed (`post_login_url`) so you can start automations from the right page
-- **Session monitoring** - Automatic re-authentication when sessions expire with stored credentials
+- **Session monitoring** - [Periodic health checks](/auth/connection-lifecycle) and automatic re-authentication when sessions expire with stored credentials
- **Secure by default** - Credentials encrypted at rest, never exposed in API responses, or passed to LLMs
## Security
diff --git a/docs.json b/docs.json
index 640fc3d..d56d541 100644
--- a/docs.json
+++ b/docs.json
@@ -104,6 +104,7 @@
]
},
"auth/configuration",
+ "auth/connection-lifecycle",
"auth/credentials",
"auth/profiles",
"auth/faq"