> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gavai.io/llms.txt
> Use this file to discover all available pages before exploring further.

# gavai login

> Authenticate the CLI with your gavAI account via OAuth 2.1 + PKCE.

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="CLI" />

This page covers `gavai login`, which opens a browser, runs the OAuth 2.1 + PKCE flow against `console.gavai.app`, and writes the resulting tokens to `~/.config/gavai/credentials.json`. After you run it once on a machine, every other CLI command reads the credentials file and refreshes the access token silently when it expires.

## Synopsis

```bash theme={null}
gavai login
```

## Arguments

None.

## Flags

<Note>
  The CLI is the source of truth for the current flag set. Run `gavai login --help` for the authoritative list.
</Note>

## What happens, in order

1. The CLI starts a local HTTP listener on a random loopback port and generates a PKCE verifier/challenge pair.
2. Your default browser opens to `console.gavai.app` with the challenge attached.
3. You sign in and approve the requested scopes.
4. The browser redirects to the local listener with an authorization code.
5. The CLI exchanges the code and verifier for an access token (`gst_*`) and a refresh token, then writes both to `~/.config/gavai/credentials.json` along with `expires_at`, `active_workspace`, and `endpoint`.

The full protocol details live in [API authentication](/api-reference/authentication).

## Examples

Authenticate the current shell:

```bash theme={null}
gavai login
```

Confirm the session was stored:

```bash theme={null}
gavai whoami
```

<ExpectedOutput>
  ```text theme={null}
  gavin@acme.io (workspace: acme)
  ```
</ExpectedOutput>

Force a fresh session by deleting the existing credentials first:

```bash theme={null}
gavai logout && gavai login
```

## Exit codes

<ErrorTable
  errors={[
{ code: "0", cause: "Authenticated. Tokens written to disk.", fix: "—" },
{ code: "2", cause: "Invalid arguments.", fix: "Run `gavai login --help` to see the supported flags." },
{ code: "4", cause: "Authorization denied in the browser.", fix: "Re-run and approve the requested scopes." },
{ code: "7", cause: "Network error reaching `console.gavai.app` or the token endpoint.", fix: "Check your network. Retry once the connection is back." },
]}
/>

See [CLI overview — exit codes](/cli/overview#exit-codes) for the full table.

<Card title="gavai whoami" href="/cli/commands/whoami">
  Verify which user and workspace the new session is bound to.
</Card>
