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

# Session tools

> Manage login state and switch between sandbox and live modes.

Five tools manage your Veil session inside a chat. Start every workflow with `veil_get_session_status` to check whether you are already logged in before prompting for credentials.

***

### veil\_get\_session\_status

Check whether there is an active Veil session for this installation. Use this at the start of any workflow.

**Inputs**

None.

**Outputs**

<ResponseField name="status" type="string" required>
  `authenticated` when logged in, `unauthenticated` when not.
</ResponseField>

<ResponseField name="user" type="object">
  Present when `status` is `authenticated`. Contains `id`, `email`, `onboardingStatus`, `accountType`, `username` (null if not claimed), `displayUsername`, and `activeMode` (`sandbox` or `live`).
</ResponseField>

<ResponseField name="expiresAt" type="string">
  ISO 8601 timestamp when the current session expires.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_get_session_status]
→ status: "authenticated", user.email: "you@example.com",
  user.activeMode: "sandbox", expiresAt: "2026-05-14T15:00:00Z"
```

***

### veil\_request\_otp

Send a one-time password to the user's email to begin login.

**Inputs**

<ParamField body="email" type="string" required>
  Email address to send the login code to.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `sent` on success. `rate_limited` if too many requests were made recently. `email_send_failed` or `error` on failure.
</ResponseField>

<ResponseField name="requestId" type="string">
  ID to pass to `veil_verify_otp` along with the code.
</ResponseField>

<ResponseField name="expiresInSeconds" type="number">
  How long the code is valid.
</ResponseField>

<ResponseField name="retryAfterSeconds" type="number">
  Seconds to wait before retrying when `status` is `rate_limited`.
</ResponseField>

<ResponseField name="reason" type="string">
  Error description on failure.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_request_otp with email="you@example.com"]
→ status: "sent", requestId: "req_abc123", expiresInSeconds: 300
```

***

### veil\_verify\_otp

Complete login by submitting the six-digit code from the user's email.

**Inputs**

<ParamField body="requestId" type="string" required>
  From a previous `veil_request_otp` call.
</ParamField>

<ParamField body="otp" type="string" required>
  The six-digit numeric code from the email.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `logged_in` on success. `invalid`, `expired`, `too_many_attempts`, `already_used`, or `error` on failure.
</ResponseField>

<ResponseField name="user" type="object">
  Present when `status` is `logged_in`. Contains `id`, `email`, `isNewUser`, `onboardingStatus`, and `accountType`.
</ResponseField>

<ResponseField name="attemptsRemaining" type="number">
  Remaining attempts when `status` is `invalid`.
</ResponseField>

<ResponseField name="reason" type="string">
  Error description on failure.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_verify_otp with requestId="req_abc123", otp="482910"]
→ status: "logged_in", user.email: "you@example.com", user.isNewUser: false
```

***

### veil\_logout

Revoke the current Veil session. Safe to call when already logged out.

**Inputs**

None.

**Outputs**

<ResponseField name="status" type="string" required>
  `logged_out` if there was an active session, `not_logged_in` if already logged out, `error` on failure.
</ResponseField>

<ResponseField name="reason" type="string">
  Error description when `status` is `error`.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_logout]
→ status: "logged_out"
```

***

### veil\_set\_mode

Switch this installation between sandbox (test) and live (real money) modes.

<Note>
  Mode switching affects only this installation. The dashboard and other paired devices are not changed. Use the dashboard's mode toggle to change all surfaces at once.
</Note>

`veil_set_mode` is a write operation, so it's subject to a tighter rate limit than the read-only session tools.

**Inputs**

<ParamField body="mode" type="string" required>
  `sandbox` or `live`.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `switched` on success, `error` on failure.
</ResponseField>

<ResponseField name="activeMode" type="string">
  The mode now active: `sandbox` or `live`.
</ResponseField>

<ResponseField name="reason" type="string">
  Error description when `status` is `error`.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_set_mode with mode="live"]
→ status: "switched", activeMode: "live"
```

## See also

* [In-chat flow](/mcp/in-chat-flow) — how the login sequence fits into an offramp conversation
