# Auth

<!-- BEGIN GENERATED: guide-map -->
## Guide map

Read only the section needed for the next decision. Line ranges are exact
for this generated runtime target.

| Section | What it covers | Lines |
| --- | --- | --- |
| [Purpose](#purpose) | Scope, ownership, and the central operating question. | 29-39 |
| [Use This When](#use-this-when) | Tasks and conditions that belong in this guide. | 40-50 |
| [Do Not Use This For](#do-not-use-this-for) | Boundaries and the correct routes for excluded work. | 51-64 |
| [Mental Model](#mental-model) | Concepts and state boundaries needed for correct operation. | 65-115 |
| [Preflight](#preflight) | State, authority, inputs, and readiness to verify before execution. | 116-149 |
| [Risk And Side Effects](#risk-and-side-effects) | Effects, approvals, cost, privacy, and retry boundaries. | 150-163 |
| [Canonical Operating Pattern](#canonical-operating-pattern) | The normal end-to-end execution sequence. | 164-185 |
| [Core SDK Surfaces](#core-sdk-surfaces) | Exact source-backed callable contracts owned by this guide. | 186-216 |
| [Data Contracts](#data-contracts) | Required identities, fields, evidence, handles, and durable shapes. | 217-228 |
| [Examples](#examples) | Representative usage patterns and code. | 229-299 |
| [Verification](#verification) | Evidence required before reporting success. | 300-313 |
| [Token Lifecycle](#token-lifecycle) | it into the local profile DB. | 314-323 |
| [Sync And Async APIs](#sync-and-async-apis) | The sync facade `vf.auth.*`, `vf.workspaces.*`, and `vf.session.*` returns dictionaries and is the default for scripts and agents. | 324-330 |
| [Troubleshooting](#troubleshooting) | Failure diagnosis and corrective routes. | 331-340 |
| [Related References](#related-references) | Adjacent boundaries and when to cross them. | 341-351 |
<!-- END GENERATED: guide-map -->


## Purpose

Auth owns SDK login, token/profile storage, workspace focus, and workspace
identity verification. It answers one question before any workspace-scoped work:
who is this SDK client authenticated as, and which workspace will this call
affect?

Auth is a routing boundary. If it is wrong, reads, Workflow Play sync, provider
calls, sequence/source staging, and customer-visible mutations can hit the wrong
workspace.

## Use This When

- A clean SDK install needs login or a programmatic token profile.
- An agent needs to focus a project, workflow root, or local daemon on a
  specific workspace.
- A workspace slug/profile resolves oddly, or a slug fails and a workspace UUID
  may be needed.
- A generated workflow is about to run, repair, sync, stage, or publish outputs.
- Provider work depends on workspace identity and you need to prove the active
  org/profile before calling CRM, enrichment, source, sequence, or host APIs.

## Do Not Use This For

- Provider-specific readiness or schema questions beyond checking the selected
  workspace and calling the relevant status function. Use `crm.md`,
  `enrichment.md`, `linkedin.md`, `host_browser.md`, or the matching provider
  reference.
- Sequence source staging, publishing, sending, sender setup, or purchases after
  auth is verified. Use `sequences.md`, `sequence_authoring.md`, `senders.md`,
  or `mailboxes.md`.
- Generic app/browser testing unrelated to Verbiflow host sessions or provider
  evidence.
- Bypassing workspace focus with ad hoc org headers. Fix the profile/context
  root instead.

## Mental Model

| Concept | What it is | Where it lives | Normal agent use |
| --- | --- | --- | --- |
| SDK profile | Local named credentials plus optional workspace/base URL metadata. | `~/.verbiflow/state.sqlite` with `0600` permissions and `storage_kind="sqlite_0600"`. | Store SDK/programmatic tokens. Do not expose token values. |
| Default profile | Shared fallback profile for new sessions without project context or env overrides. | The same local SQLite profile DB. | Change only when the user intentionally changes the fallback workspace. |
| Project-local context | Token-free workspace focus for a workflow/project root. | `.verbiflow/context.json` under the project tree. | Preferred for agents and generated workflows. |
| Environment overrides | Process-local routing such as `VERBIFLOW_PROFILE`, `VERBIFLOW_ORG_ID`, `VERBIFLOW_ORG_SLUG`, and `VERBIFLOW_BASE_URL`. | Current shell/process environment. | Terminal-only or CI-only. Do not use to silently override a focused workflow root. |
| Session context | Live Bridge bootstrap, selected workspace, connected integrations, and browser sign-in hint. | Returned by `vf.session.init()`. | Verify readiness before provider/workflow work. |
| Anonymous claim session | Temporary Supabase anonymous session used only while a browser/OAuth claim is pending. | Local SDK profile DB until the claim resolves or expires. | Complete login. Never treat it as workspace-ready auth. |
| Claim token | Opaque token returned by `vf.auth.login(...)` that links the pending anonymous session to the browser-completed login. | Returned in the login payload and sent to Bridge by `vf.auth.claim_status(...)`. | Poll for the durable SDK token after the human signs in. |

Resolution order is intentionally strict. A client constructed from a root
resolves org identity in tiers: explicit constructor arguments first, then
`VERBIFLOW_ORG_ID`/`VERBIFLOW_ORG_SLUG`, then the play-root workspace pin
(`VERBIFLOW_WORKSPACE` env or a plain `workspace` file; a UUID pins `org_id`, a
slug pins `org_slug`), then `.verbiflow/context.json`, then the profile. The SDK
fails fast when an environment override conflicts with the pin, or when a pin
and env name the workspace by different keys (one by id, one by slug) so they
cannot be compared. Unset the conflicting env var or repin with
`vf.workflows.set_workspace(...)`; do not keep running with mixed context.

### Anonymous Claim Flow

The SDK uses anonymous auth only as a bootstrapping mechanism for a clean login.
It is not a workspace session and must not be used for workflows, provider calls,
Workflow Play sync, sequence/source staging, sending, purchases, or any
workspace-scoped operation.

| State | How it is created | What is safe | What must not happen |
| --- | --- | --- | --- |
| No SDK profile | Fresh install or deleted local profile. | Start `vf.auth.login(...)` or save a programmatic token. | Do not run workspace/provider calls. |
| Anonymous pending claim | `vf.auth.login(...)` starts an anonymous Supabase session and returns claim fields. OAuth providers include `claim_url`; magic-link login does not. | Show/open the claim URL or email link and call `vf.auth.claim_status(...)`. | Do not call workflows, CRM, enrichment, sequence, or Play sync as if logged in. |
| Claimed SDK profile | Human finishes Google/Microsoft/magic-link login and `vf.auth.claim_status(...)` receives an SDK token. | Run `vf.auth.ensure_logged_in()`, focus a workspace/root, and verify `vf.session.init()`. | Do not assume the selected workspace is correct until printed and verified. |
| Focused project root | `vf.auth.focus_workspace(...)` writes `.verbiflow/context.json`. | Run/replay/sync the workflow from that root. | Do not override it with conflicting env profile/org/base URL. |

Who needs to claim a token:

- Clean SDK installs with no usable saved profile.
- Agent or local workflow roots whose saved profile is missing, expired, or
  rejected and cannot be refreshed.
- Humans choosing OAuth or magic-link login instead of a pre-provisioned
  programmatic token.

Who does not need a claim:

- CI, daemon, or automation using a provisioned programmatic token.
- Existing focused workflow roots with a valid saved profile and matching
  `.verbiflow/context.json`.
- Workspace switching or project focusing when a valid SDK token already exists.

## Preflight

For provider work, workflow operations, Workflow Play sync, or sequence/source
changes, first verify the exact workspace:

```python
import verbiflow as vf

auth = vf.auth.ensure_logged_in()
session = vf.session.init()
print({
    "profile": auth.get("profile_name"),
    "base_url": auth.get("base_url"),
    "org_id": auth.get("org_id"),
    "org_slug": auth.get("org_slug"),
    "selected_workspace": session["auth"].get("selected_workspace"),
    "connected_integrations": session.get("connected_integrations"),
})
```

If the printed workspace is not the intended workspace, stop and focus the
project root before making provider calls or mutations.

For generated workflow roots, preflight is stricter:

1. Resolve the exact workflow root.
2. Read the play-root `workspace` pin and `.verbiflow/context.json` from that
   root if present. The pin outranks `.verbiflow/context.json` for org routing.
3. Print `profile`, `base_url`, `org_id`, `org_slug`, and selected workspace.
4. Verify provider readiness with the relevant status function, such as
   `vf.crm.status(provider="hubspot")`.
5. If local context conflicts with `VERBIFLOW_PROFILE`, `VERBIFLOW_ORG_ID`,
   `VERBIFLOW_ORG_SLUG`, or `VERBIFLOW_BASE_URL`, stop and fix the context.

## Risk And Side Effects

| Operation class | Examples | Risk rule |
| --- | --- | --- |
| Read-only auth inspection | `auth.current`, `auth.status`, `session.init`, `auth.validate`, `auth.list_workspaces` | Safe after checking that printed workspace context is expected. |
| Local profile/context write | `auth.save_profile`, `auth.use_profile`, `auth.workspace_profile`, `auth.focus_workspace`, `auth.set_default_profile`, `auth.delete_profile`, `auth.claim_status` when claimed | Mutates local SDK state. Print profile/workspace and avoid changing shared defaults unless intentional. |
| Platform auth write | `auth.login`, `auth.refresh`, `auth.logout`, `auth.switch_workspace`, `workspaces.ensure` when creation is enabled, `workspaces.create` | May touch Bridge auth/session/workspace state. Use only for explicit setup or repair. |
| Workspace-scoped provider/workflow work after auth | CRM, enrichment, source, Workflow Play sync, sequence staging | Auth only proves routing. Follow the domain reference before executing these calls. |
| Customer-visible or billing mutations | sequence publish/send, sender/channel actions, mailbox/domain purchases | Auth is necessary but not sufficient. Require explicit confirmation in the owning reference. |

Do not call `vf.workspaces.ensure()` casually in agents: its SDK default can
create a workspace when none exists. Pass `create_if_missing=False` unless
creation is the user's explicit intent.

## Canonical Operating Pattern

1. Inspect current auth with `vf.auth.current()` when debugging, or
   `vf.auth.ensure_logged_in()` when you expect a usable token/workspace.
2. If no usable profile exists, complete the anonymous claim flow or save a
   provisioned programmatic token.
3. Focus the project/workflow root with `vf.auth.focus_workspace(...)` when work
   should be durable for that root.
4. Run `vf.session.init()` and print the selected workspace plus connected
   integrations.
5. Call the relevant provider/status function before provider or workflow work.
6. Re-check auth after profile switches, root changes, daemon startup, or any
   unexpected workspace mismatch.

For diagnostics, prefer these reads:

```python
vf.auth.current()               # local profile, local context, base URL, org id/slug
vf.auth.status(validate=True)   # local status plus Bridge validation
vf.session.init()               # live auth, workspace, integration, browser context
```

## Core SDK Surfaces

<!-- BEGIN GENERATED: core-sdk-surfaces -->
| Function | Use When | Inputs | Output And Handles To Preserve | Side Effect | Source |
| --- | --- | --- | --- | --- | --- |
| `vf.auth.claim_status` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | <strong>Required</strong>: `claim_token` (str; required): Claim token parameter for `auth.claim_status`.<br><strong>Optional</strong>: `profile_name` (str; optional; default `'default'`): Local SDK auth profile name to write or update.; `use_now` (bool; optional; default `True`): Whether the current client should immediately switch to the saved/claimed profile.<br><strong>Note</strong>: `claim_token` must come from `auth.login(...)`. `profile_name` controls the saved local SDK profile, and `use_now=False` saves without switching the current client. | `dict` with claim lifecycle; when claimed, persists SDK token/profile and returns workspace data.<br><strong>Inspect</strong>: Pending results may have no `saved_profile`. Claimed results include `saved_profile` and `state_db` only after Bridge returns an SDK token; workspace fields depend on Bridge status payload.<br><strong>Common next calls</strong>: `vf.auth.ensure_logged_in` | local | installed SDK: `verbiflow/auth.py:549-608` |
| `vf.auth.current` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | No inputs. | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Inspect</strong>: Use `local_context`, `client`, and `profile_name` to debug profile/root/base URL mismatches without printing tokens.<br><strong>Common next calls</strong>: `vf.auth.validate`, `vf.session.init` | read | installed SDK: `verbiflow/auth.py:39-79` |
| `vf.auth.delete_profile` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | <strong>Required</strong>: `name` (str; required): Name for the SDK profile, host session, provider query, or created platform resource.<br><strong>Optional</strong>: none | `dict` confirming local profile deletion and the SDK state DB path. | local | installed SDK: `verbiflow/auth.py:359-379` |
| `vf.auth.ensure_logged_in` | Use at the top of agent scripts to fail fast unless a valid SDK profile and workspace are active. | <strong>Required</strong>: none<br><strong>Optional</strong>: `validate` (bool; optional; default `True`): When true, call Bridge to validate the local token/workspace instead of checking local state only. | `dict` with redacted auth/profile/workspace status; it never exposes stored SDK tokens.<br><strong>Common next calls</strong>: `vf.session.init`, `vf.integrations.list` | read | installed SDK: `verbiflow/auth.py:410-453` |
| `vf.auth.focus_workspace` | Use when an agent session or project directory should remember its workspace without mutating the shared default. | <strong>Required</strong>: `workspace_id` (str; required): Workspace UUID or slug this project/session should use.<br><strong>Optional</strong>: `profile_name` (str \| None; optional; default `None`): Optional profile name. Defaults to the resolved workspace slug or id.; `root` (str \| None; optional; default `None`): Directory where .verbiflow/context.json should be written. Defaults to cwd.; `set_default` (bool; optional; default `False`): When true, also make the focused profile the shared default. Usually false for agents.<br><strong>Note</strong>: `root` should be the workflow or project root that owns `.verbiflow/context.json`; omit only when cwd is already that root. | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Inspect</strong>: Includes `workspace_profile(...)` output plus token-free `local_context` written under the selected root.<br><strong>Common next calls</strong>: `vf.auth.current`, `vf.session.init` | local | installed SDK: `verbiflow/auth.py:293-357` |
| `vf.auth.list_profiles` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | No inputs. | `dict` with redacted saved profiles, active profile, and SDK state DB path.<br><strong>Common next calls</strong>: `vf.auth.use_profile` | read | installed SDK: `verbiflow/auth.py:81-98` |
| `vf.auth.list_workspaces` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | No inputs. | `dict` with available workspaces and the selected workspace.<br><strong>Common next calls</strong>: `vf.auth.switch_workspace` | read | installed SDK: `verbiflow/auth.py:669-683` |
| `vf.auth.login` | Use on a fresh machine to start SDK browser login and workspace claim. | <strong>Required</strong>: none<br><strong>Optional</strong>: `next_path` (string; optional): Path to return to after web claim/login completes.; `provider` (google \| microsoft \| magic_link; optional): Browser identity provider for the SDK claim flow. Defaults to google so agents can start login without knowing the user's email.; `target_email` (string; optional): Email address for provider='magic_link' only. Do not pass for google or microsoft OAuth.; `web_base` (string; optional): Verbiflow web base URL used for browser claim/login handoff.; `workspace_domain` (string; optional): Domain used when creating or selecting a workspace during login.; `workspace_name` (string; optional): Workspace display name requested during SDK login/claim.<br><strong>Note</strong>: `provider='magic_link'` requires `target_email`; Google/Microsoft OAuth do not. `workspace_name` and `workspace_domain` are setup hints for the claim flow, not proof of final workspace focus. | `dict` with login status, provider, claim token, expiry, next poll instructions, and `claim_url` for OAuth providers.<br><strong>Inspect</strong>: OAuth providers include `claim_url`; `provider='magic_link'` returns pending claim fields without `claim_url`.<br><strong>Common next calls</strong>: `vf.auth.claim_status` | platform_write | installed SDK: `verbiflow/auth.py:455-547` |
| `vf.auth.logout` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | <strong>Required</strong>: none<br><strong>Optional</strong>: `delete_profile` (bool; optional; default `False`): When true, also remove the local SDK profile after remote logout. | `dict` with remote revoke status, local profile deletion status, and redacted profile metadata. | platform_write | installed SDK: `verbiflow/auth.py:642-667` |
| `vf.auth.refresh` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | No inputs. | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Common next calls</strong>: `vf.auth.ensure_logged_in` | platform_write | installed SDK: `verbiflow/auth.py:610-640` |
| `vf.auth.save_profile` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | <strong>Required</strong>: `api_key` (str; required): Verbiflow SDK or programmatic token to store in the local profile database.<br><strong>Optional</strong>: `base_url` (str \| None; optional; default `None`): URL value(s) for base.; `email` (str \| None; optional; default `None`): Email parameter for `auth.save_profile`.; `name` (str; optional; default `'default'`): Name for the SDK profile, host session, provider query, or created platform resource.; `org_id` (str \| None; optional; default `None`): Verbiflow organization/workspace UUID.; `org_slug` (str \| None; optional; default `None`): Verbiflow organization/workspace slug.; `set_default` (bool; optional; default `True`): Whether this profile should become the default profile for future SDK runs.; `use_now` (bool; optional; default `True`): Whether the current client should immediately switch to the saved/claimed profile.; `user_id` (str \| None; optional; default `None`): Verbiflow/Supabase user id associated with the saved SDK profile. | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Common next calls</strong>: `vf.auth.validate`, `vf.session.init` | local | installed SDK: `verbiflow/auth.py:100-152` |
| `vf.auth.set_default_profile` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | <strong>Required</strong>: `name` (str; required): Name for the SDK profile, host session, provider query, or created platform resource.<br><strong>Optional</strong>: none | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Common next calls</strong>: `vf.auth.current` | local | installed SDK: `verbiflow/auth.py:172-192` |
| `vf.auth.status` | Use for a cheap diagnostic of active profile, workspace, token source, and optional Bridge validation. | <strong>Required</strong>: none<br><strong>Optional</strong>: `validate` (bool; optional; default `False`): When true, call Bridge to validate the local token/workspace instead of checking local state only. | `dict` with redacted auth/profile/workspace status; it never exposes stored SDK tokens.<br><strong>Inspect</strong>: `validate=True` adds a `bridge` validation payload; `validate=False` is local state only.<br><strong>Common next calls</strong>: `vf.auth.ensure_logged_in`, `vf.auth.validate` | read | installed SDK: `verbiflow/auth.py:381-408` |
| `vf.auth.switch_workspace` | Use only when the same saved profile should be mutated to a different accessible workspace. | <strong>Required</strong>: none<br><strong>Optional</strong>: `org_id` (str \| None; optional; default `None`): Workspace organization UUID to switch to. Pass either org_id or org_slug.; `org_slug` (str \| None; optional; default `None`): Workspace slug to switch to. Pass either org_slug or org_id.<br><strong>Note</strong>: Pass `org_id` or `org_slug`; source accepts both, but `org_id` is used for routing headers when both are present. | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Common next calls</strong>: `vf.auth.ensure_logged_in` | platform_write | installed SDK: `verbiflow/auth.py:685-741` |
| `vf.auth.use_profile` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | <strong>Required</strong>: none<br><strong>Optional</strong>: `name` (str \| None; optional; default `None`): Name for the SDK profile, host session, provider query, or created platform resource. | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Common next calls</strong>: `vf.auth.validate`, `vf.session.init` | local | installed SDK: `verbiflow/auth.py:154-170` |
| `vf.auth.validate` | Use for SDK login, local profile storage, token validation, refresh, logout, and workspace focus. | No inputs. | `dict` with redacted auth/profile/workspace status; it never exposes stored SDK tokens.<br><strong>Inspect</strong>: Returns Bridge bootstrap context; it does not by itself assert that a target workspace is the intended one.<br><strong>Common next calls</strong>: `vf.session.init`, `vf.integrations.list` | read | installed SDK: `verbiflow/auth.py:743-765` |
| `vf.auth.workspace_profile` | Use to bind an accessible workspace to its own local SDK profile for thread-local or terminal-local operation. | <strong>Required</strong>: `workspace_id` (str; required): Workspace UUID or slug to bind into a local SDK profile.<br><strong>Optional</strong>: `profile_name` (str \| None; optional; default `None`): Optional profile name. Defaults to the resolved workspace slug or id.; `set_default` (bool; optional; default `False`): When true, make the profile the shared SDK default. Prefer false for parallel Codex threads.; `use_now` (bool; optional; default `True`): When true, load the new workspace profile into the current SDK client.<br><strong>Note</strong>: `workspace_id` may be a workspace UUID or slug. UUID routes with `org_id`; non-UUID routes with `org_slug`. | `dict` with redacted profile metadata, active workspace, storage kind, and SDK state DB path.<br><strong>Inspect</strong>: Preserve `profile`, `source_profile`, `state_db`, `shell`, and `response`; `shell` contains process-only exports.<br><strong>Common next calls</strong>: `vf.auth.use_profile`, `vf.session.init` | local | installed SDK: `verbiflow/auth.py:194-291` |
| `vf.integrations.list` | List workspace integrations and their connected state. | No inputs. | `integration_list` payload returned by the SDK operation. Inspect the documented function reference and `raw`/dictionary fields when debugging provider-specific data.<br><strong>Common next calls</strong>: `vf.integrations.status` | read | installed SDK: `verbiflow/integrations.py:12-26` |
| `vf.integrations.status` | Check whether a specific workspace integration is connected. | <strong>Required</strong>: `provider` (str; required): External provider or host session provider, such as hubspot, linkedin, or crunchbase.<br><strong>Optional</strong>: none | `integration_status` payload returned by the SDK operation. Inspect the documented function reference and `raw`/dictionary fields when debugging provider-specific data. | read | installed SDK: `verbiflow/integrations.py:28-42` |
| `vf.session.auth_status` | Return current SDK auth/workspace status from Bridge bootstrap context. | No inputs. | `dict` with redacted auth/profile/workspace status; it never exposes stored SDK tokens.<br><strong>Common next calls</strong>: `vf.session.init` | read | installed SDK: `verbiflow/session.py:79-113` |
| `vf.session.init` | Read SDK auth, workspace, integration, and host-browser context. | No inputs. | `dict` with active SDK profile, workspace, connected capability summary, and recommended next actions.<br><strong>Inspect</strong>: Inspect `auth.selected_workspace`, `auth.workspaces`, `connected_integrations`, `connected_tools`, `browser`, and `sdk.base_url`.<br><strong>Common next calls</strong>: `vf.auth.status`, `vf.integrations.list` | read | installed SDK: `verbiflow/session.py:15-77` |
| `vf.workflows.set_workspace` | Pin a workspace to a play folder by writing a plain `workspace` file at the root. Every client constructed from this folder resolves the pin locally: a UUID pins org_id, anything else pins org_slug. Explicit org args and VERBIFLOW_ORG_ID/VERBIFLOW_ORG_SLUG take precedence; a conflicting environment fails fast. Local-only — never mutates global SDK state and never calls the platform. | <strong>Required</strong>: `workspace_id` (str; required): Workspace UUID (pins org_id) or workspace slug (pins org_slug).<br><strong>Optional</strong>: `root` (str \| Path \| None; optional; default `None`): Play root path; defaults to cwd. Writes a plain `workspace` file there. | `workspace_pin` payload returned by the SDK operation. Inspect the documented function reference and `raw`/dictionary fields when debugging provider-specific data. | local | installed SDK: `verbiflow/workflows.py:940-978` |
| `vf.workspaces.create` | Use when the user explicitly wants a new Verbiflow workspace created. | <strong>Required</strong>: `name` (str; required): Display name for the new Verbiflow workspace.<br><strong>Optional</strong>: `domain` (str \| None; optional; default `None`): Optional company domain used by onboarding personalization.; `switch_current` (bool; optional; default `False`): When true, apply the new workspace to the current SDK client only. | `dict` with created workspace id/slug, workspace URL, onboarding template ids, personalization workflow id, and selection status.<br><strong>Inspect</strong>: Returns created workspace ids/slugs, workspace URL when available, onboarding ids, and `selected` based on `switch_current`.<br><strong>Common next calls</strong>: `vf.auth.focus_workspace`, `vf.session.init` | platform_write | installed SDK: `verbiflow/workspaces.py:91-166` |
| `vf.workspaces.ensure` | Use to validate/apply a workspace for the current process or create one when explicitly requested. Use auth.focus_workspace for durable agent focus. | <strong>Required</strong>: none<br><strong>Optional</strong>: `create_if_missing` (bool; optional; default `True`): When true, create a default workspace if the active user has none.; `default_name` (str; optional; default `'My Workspace'`): Workspace name used only when creating a missing default workspace.; `workspace_id` (str \| None; optional; default `None`): Optional workspace UUID or slug to validate/apply to the current client.<br><strong>Note</strong>: Default `create_if_missing=True` can create a workspace when none exist. Agents should pass `create_if_missing=False` unless creation is explicit. | `dict` with selected workspace id/slug, selection reason, workspace row when available, and current-client selection scope.<br><strong>Inspect</strong>: Returns `reason` such as `already_selected`, `explicit_workspace_id`, `single_available_workspace`, or creation result fields from `workspaces.create(...)`.<br><strong>Common next calls</strong>: `vf.session.init`, `vf.integrations.list` | platform_write | installed SDK: `verbiflow/workspaces.py:16-89` |
<!-- END GENERATED: core-sdk-surfaces -->

## Data Contracts

| Data | Required fields and meaning | Notes |
| --- | --- | --- |
| SDK profile | Profile name, SDK/programmatic token, optional `org_id`, `org_slug`, `base_url`, user/email metadata, token lifecycle fields. | Stored in `~/.verbiflow/state.sqlite`; never print raw token fields. |
| Local context | `schema_version`, `profile`, `base_url`, `org_id`, `org_slug`, `source_profile`, `focused_at`. | Written to `.verbiflow/context.json`; token-free but workspace-identifying. |
| `auth.current()` | `profile`, `profile_name`, `state_db`, `local_context`, `client.base_url`, `client.org_id`, `client.org_slug`, `client.has_api_key`. | Best first diagnostic for profile/context mismatch. |
| `auth.status(validate=True)` | `signed_in`, `profile`, `profile_name`, `base_url`, `org_id`, `org_slug`, `storage_kind`, optional `bridge`. | Use before work when you expect auth to be valid. |
| `session.init()` | `auth.user_id`, `auth.email`, `auth.org_id`, `auth.org_slug`, `auth.selected_workspace`, `auth.workspaces`, `connected_integrations`, `connected_tools`, `browser`, `sdk.base_url`. | Use to verify live workspace and provider readiness summary. |
| `auth.login()` pending claim | `status`, `provider`, `claim_token`, `expires_at`, optional `claim_url`, redacted `profile`, `next`. | Pending anonymous state only. |
| `auth.claim_status()` claimed result | Claim lifecycle status plus `saved_profile` and `state_db` when Bridge returns an SDK token. | After claim, run `ensure_logged_in()` and verify workspace. |

## Examples

### Clean install

```python
login = vf.auth.login()
print(login["claim_url"])
# Open the claim URL in a browser and finish sign-in. Then poll until claimed:
claim = vf.auth.claim_status(claim_token=login["claim_token"])
print(claim.get("status"), claim.get("saved_profile"))
vf.auth.ensure_logged_in()
```

`auth.login()` defaults to Google OAuth and does not require the agent to know
the user's email. It stores a temporary anonymous Supabase session locally while
the claim is pending. Google and Microsoft OAuth return a `claim_url` plus
`claim_token`; magic-link login returns pending claim fields and sends the link
to `target_email`.
`auth.claim_status(...)` polls the claim, receives an SDK token from Bridge,
persists returned workspace fields into the local profile DB when Bridge returns
them, and by default switches the active profile and shared default to that saved
SDK profile.

Use magic link only when the user explicitly wants email login:

```python
login = vf.auth.login(provider="magic_link", target_email="you@company.com")
# Click the email link, then:
vf.auth.claim_status(claim_token=login["claim_token"])
```

### Focus a project or workflow root

```python
workspaces = vf.auth.list_workspaces()
print(workspaces)
vf.auth.focus_workspace("workspace-slug", root="/path/to/workflow-or-project")
```

`auth.focus_workspace(...)` creates or updates a workspace-scoped SDK profile and
writes token-free `.verbiflow/context.json` for the root. Run SDK scripts from
inside that tree so they use the project context before the shared default.

If slug focus fails, list workspaces and retry with the workspace UUID:

```python
for row in vf.auth.list_workspaces().get("workspaces", []):
    print(row.get("id") or row.get("org_id"), row.get("slug") or row.get("org_slug"))

vf.auth.focus_workspace("workspace-uuid", root="/path/to/workflow-or-project")
vf.auth.ensure_logged_in()
vf.session.init()
```

### Shell-only terminal focus

```bash
verbiflow auth focus example-workspace --base-url http://127.0.0.1:8787
```

Use this when a project-local `.verbiflow/context.json` should be written. For a
pure terminal session where no project-local context should be written, use
`verbiflow auth workspace ... --shell` and eval the printed exports in that
shell only.

### CI or programmatic token

Use `vf.auth.save_profile(...)` or env vars supplied by the CI secret manager.
Always pass or verify the workspace explicitly. CI should not rely on a human
desktop default profile.

## Verification

Before leaving auth setup or proceeding to provider/workflow work, prove:

- `vf.auth.ensure_logged_in()` returns the expected `profile_name`, `base_url`,
  `org_id`, and `org_slug`.
- `vf.session.init()["auth"]["selected_workspace"]` is the intended workspace.
- `vf.session.init()["connected_integrations"]` includes the provider needed for
  the next step, or the provider-specific status call explains what is missing.
- Generated workflow roots have the intended `workspace` pin, plus
  `.verbiflow/context.json` for profile/base URL metadata.
- No conflicting `VERBIFLOW_PROFILE`, `VERBIFLOW_ORG_ID`,
  `VERBIFLOW_ORG_SLUG`, or `VERBIFLOW_BASE_URL` is active for a focused root.

## Token Lifecycle

- `vf.auth.refresh()` refreshes the active SDK token through Bridge and persists
  it into the local profile DB.
- `vf.auth.logout(delete_profile=False)` revokes the active SDK token when
  Bridge supports it. Pass `delete_profile=True` only when local profile removal
  is intended.
- `vf.auth.delete_profile(name)` removes a saved local SDK profile.
- Never print raw tokens, refresh tokens, cookies, or auth headers.

## Sync And Async APIs

The sync facade `vf.auth.*`, `vf.workspaces.*`, and `vf.session.*` returns
dictionaries and is the default for scripts and agents. Inside an existing async
runtime, use `client = vf.AsyncClient()` and `await client.auth.*(...)` instead
of nesting a sync call inside the event loop.

## Troubleshooting

| Symptom | First checks | Fix |
| --- | --- | --- |
| Slug focus fails | `vf.auth.list_workspaces()` and `vf.session.init()["auth"]["workspaces"]`. | Retry `vf.auth.focus_workspace(...)` with the workspace UUID and verify final `org_id`/`org_slug`. |
| Wrong workspace appears | `vf.auth.current()`, `VERBIFLOW_WORKSPACE`, the play-root `workspace` pin, and `.verbiflow/context.json` from the workflow root. | Unset conflicting env vars or repin with `vf.workflows.set_workspace(...)`. Do not patch around it with ad hoc org headers. |
| Token rejected | `vf.auth.status(validate=True)`. | Run `vf.auth.refresh()` if refreshable, otherwise start a new `vf.auth.login(...)` claim. |
| Multiple workspaces available | `vf.auth.list_workspaces()`. | Select explicitly with `vf.auth.focus_workspace(...)` for project roots or `vf.workspaces.ensure(workspace_id=..., create_if_missing=False)` for current-process setup. |
| Provider unavailable | `vf.session.init()` and the provider-specific status function. | Connect or repair the provider before workflow/provider calls. |

## Related References

| If you are doing this | Read next |
| --- | --- |
| Running, repairing, or reporting an existing workflow root | `workflow_operations.md` |
| Syncing Workflow Play code/output freshness or checking schedule readiness | `workflow_schedules.md` |
| Building or operating a multi-step Workflow Play | `workflow_composition.md` |
| Checking CRM properties, associations, suppression, or connected HubSpot/Salesforce data | `crm.md` |
| Running enrichment, source, LinkedIn, or provider-backed jobs after auth | `enrichment.md`, `audience_discovery.md`, or `linkedin.md` |
| Staging rows to a sequence, publishing, sending, or repairing sequence audience state | `sequences.md` and `sequence_authoring.md` |
| Using local browser/provider sessions as workflow evidence | `host_browser.md` |
