<!-- Markdown mirror. Canonical: https://empryo.com/docs/providers/subscriptions -->

# Subscriptions

> Sign in to Claude, ChatGPT, Copilot, Cursor, Devin, Gemini, Grok, Kimi and more from Empryo with no add-on. One /login picker, credentials in your keychain.

You already pay for Claude or ChatGPT. `/login` spends that plan instead of an API key, with no relay in the middle and no second tool to install. Empryo runs the OAuth flow, keeps the credential in your system keychain, and refreshes it when it expires.

Seventeen sign-in lanes exist today. Most run an OAuth flow, browser or device code. A few take a pasted key instead. The lanes that serve model ids under the shared **Subscriptions** provider:

| Account | How you sign in | Models |
|---------|-----------------|--------|
| Claude Pro/Max | browser OAuth | `subscriptions/claude-*` |
| ChatGPT Codex | browser OAuth | `subscriptions/gpt-*` |
| ChatGPT Codex (device code) | device code | `subscriptions/gpt-*` |
| Grok (SuperGrok / X Premium) | browser OAuth | `subscriptions/grok-*` |
| Grok (device code) | device code | `subscriptions/grok-*` |
| Cursor | browser OAuth | `subscriptions/cursor/*` |
| Devin (Cognition) | browser OAuth | `subscriptions/devin/*` |
| Gemini CLI (Google Cloud Code Assist) | browser OAuth | `subscriptions/google-gemini-cli/*` |
| Antigravity (Google) | browser OAuth | `subscriptions/google-antigravity/*` |
| Kimi Code (Moonshot) | device code | `subscriptions/kimi-code/*` |
| Fire Pass (Fireworks) | pasted API key | `subscriptions/*` |

Four more sign in through the same picker but keep their own provider row, because each has its own catalog and its own billing:

| Account | How you sign in | Models |
|---------|-----------------|--------|
| GitHub Copilot | device code | `copilot/*` |
| GitHub Copilot (Enterprise) | device code against your GHE host | `copilot/*` |
| OpenRouter | browser, mints a key back | `openrouter/*` |
| Ollama Cloud | pasted API key | `ollama-cloud/*` |
| LLM Gateway, LLM Gateway DevPass | browser, mints a key back | `llmgateway/*` |

Gemini CLI and Antigravity both serve `gemini-*` base models, so each lane namespaces its ids under its own account (`google-gemini-cli/…`, `google-antigravity/…`) to keep billing and routing unambiguous. Cursor and Devin namespace the same way so a shared base id (a `claude-*` or `gpt-*`) never steals another lane's turn.

> **Warning:**
> **Use at your own risk.** A subscription token is issued for the vendor's own client, and Empryo has to look like that client for the request to be served. I am not affiliated with any of these vendors, and I have not cleared this use with any of them. These lanes have been exercised through the desktop UI against mock IPC, not verified against live paid accounts, so treat vendor acceptance as unconfirmed.

> Whether your plan permits it is between you and your provider. Read your subscription terms before you log in. Rate limiting, revoked credentials and account suspension are all possible, and I cannot restore a suspended account.

## Signing in

Run `/login` on the terminal or the desktop app. The picker lists every account Empryo can sign into, marks the ones you are already on, and starts the flow you choose.

```
Sign in
  Claude Pro/Max                 you@example.com
  ChatGPT Codex
  ChatGPT Codex (device code)
  Cursor
  Devin (Cognition)
  GitHub Copilot                 device code
  GitHub Copilot (Enterprise)    ghe.example.com
  Grok (SuperGrok / X Premium)
  Gemini CLI
  Antigravity
  Kimi Code                      device code
  Fire Pass                      paste key
  OpenRouter
  Ollama Cloud                   paste key
  LLM Gateway                    signed in
```

Each row carries the provider's own mark, the flow it runs, and the account you are on. An enterprise Copilot row shows its host, because the same checkmark means a different account on github.com than it does on a tenant.

Skip the picker with the account name:

```bash
/login claude          # Claude Pro/Max
/login codex           # ChatGPT, browser
/login codex-device    # ChatGPT, device code
/login grok            # Grok (xai-oauth resolves here too)
/login cursor          # Cursor, browser
/login devin           # Devin, browser
/login google-gemini-cli   # Gemini CLI (Google Cloud Code Assist)
/login google-antigravity  # Antigravity (Google)
/login kimi-code       # Kimi Code, device code
/login copilot         # GitHub Copilot
/login copilot-enterprise  # GitHub Copilot on a GHE host
/login openrouter      # OpenRouter, browser PKCE
/login firepass        # Fire Pass, masked key entry, no browser
/login ollama-cloud    # Ollama Cloud, masked key entry
/login kilo            # Kilo Gateway, device code
/login zai-coding-plan # Z.AI GLM coding plan, browser then paste the redirect
/login alibaba-coding-plan # Qwen coding plan, masked key entry
/login alibaba-token-plan  # Qwen token plan, masked key entry
/login xiaomi          # Xiaomi MiMo, masked key entry
```

Brand and vendor aliases resolve to the right lane: `/login anthropic` → Claude, `/login chatgpt` → Codex, `/login xai-oauth` → Grok, `/login zai` and `/login glm` → the GLM coding plan, `/login qwen` → the Alibaba coding plan.

A key is never typed on the command line. The rows marked "masked key entry" open a masked field instead: the TUI's keys panel, a password field in the desktop dialog. For Alibaba and Xiaomi the key is also checked against each of the vendor's regional hosts before it is stored, and the host that accepted it is remembered, so nothing has to ask you which region you bought.

The same names work without a UI:

```bash
empryo --headless --login claude
empryo --headless --login codex --no-browser
```

`--no-browser` prints the URL instead of opening one, for a machine you reached over SSH.

Picking an account you are already signed into asks first. Signing in again replaces the stored grant; signing out deletes it.

### When the browser cannot reach you

Claude's callback listens on port 54545 and falls back to a random port if that one is taken. OpenAI validates the redirect URI against a registered address, so ChatGPT's callback must be port 1455 and cannot fall back. If something else holds 1455, the sign-in stops before it opens a browser and says so.

Use the device code when that happens, or when the browser lives on another machine:

```bash
/login codex-device
```

It prints a code, you type the code at `auth.openai.com/codex/device`, and no local port is involved.

### Remote and headless machines

A loopback callback needs a browser that can reach the port on the same host. Over SSH, inside a container, or on a Windows Server box with no desktop session, that browser is somewhere else. Three ways through, in order of least trouble:

1. **Use a device-code lane.** ChatGPT (`codex-device`), Grok (`grok-device`), Copilot, Copilot Enterprise and Kimi Code all authorize from any browser on any machine. Nothing binds a port.
2. **Forward the port.** `ssh -L 54545:localhost:54545 host` for Claude, 1455 for ChatGPT, 54549 for OpenRouter. Run `/login` on the remote and open the printed URL locally.
3. **Paste a key.** Fire Pass and Ollama Cloud take a key directly, as does every coding-plan provider.

`--no-browser` prints the URL instead of trying to launch one, which is what you want on a box with no `xdg-open` and no default handler.

Empryo binds both `127.0.0.1` and `::1` so a dev server holding one family cannot swallow the code, and it skips the IPv6 bind on a kernel with IPv6 disabled. Many container images ship that way.

Credentials land wherever the platform keeps secrets: the login keychain on macOS, DPAPI on Windows, libsecret on a Linux desktop. A headless Linux box usually runs no secret service, so Empryo falls back to a `0600` file under the config directory. `/keys` says which backend holds a given key.

## Where the models show up

Every signed-in account appears under one **Subscriptions** provider, with a group per account. Model ids read `subscriptions/claude-opus-5`, `subscriptions/gpt-5.6-sol`, `subscriptions/cursor/composer` and so on, so moving between plans is a model choice rather than a provider migration.

Each lane discovers its catalog live: it asks the vendor's own API for the models the account can use, falls back to [models.dev](https://models.dev) metadata for context windows and pricing, and finally to a pinned cold-start list when neither answers. A lane you are signed out of contributes nothing to the picker.

Sign out of a lane and its models leave the picker. Offering a model that fails on the first turn is worse than a shorter list.

> **Note:**
> Signed out, the Subscriptions row stays visible and reads `not signed in`. Press Enter on it and the account picker opens. The row is the discovery path, so hiding it would leave the feature findable only by someone who already knew to type `/login`.

## What Empryo sends

A subscription token is not an API key. Anthropic and OpenAI serve it to their own client and identify that client by the shape of the whole request, so this lane reproduces it: the SDK telemetry headers, the beta list, the client's identity block ahead of your system prompt, and an attestation hash over the serialized body. Getting one of them wrong does not degrade the request, it gets it refused.

Two consequences worth knowing:

- Claude subscriptions carry no long-context credit, so Empryo never advertises the 1M beta on this lane. Models with a native 1M window still serve it.
- The ChatGPT backend only streams. A non-streaming call fails there with a readable error rather than a corrupted response.

## Claude expires every 30 days

Anthropic ends the whole refresh-token family about 30 days after you sign in, however healthy the rotations have been. Only signing in again recovers it.

Empryo tracks the deadline from your login and warns on the account row inside the last five days:

```
A  Claude Pro/Max     sign in again within 4d
```

Run `/login claude` before it lapses and nothing breaks mid-turn.

## Signing out

`/login`, pick the account, choose **Sign out**. That deletes the stored credential. It does not touch the account itself, and signing in again is the same flow as the first time.

## Subscriptions, proxy, or an API key

| | Subscriptions | [Proxy](/docs/providers/proxy) | API key |
|---|---|---|---|
| Install | nothing | `empryo addon install proxy` | nothing |
| Covers | 17 lanes, including Claude, ChatGPT, Copilot, Grok, Cursor, Devin, Gemini | Claude, ChatGPT, Gemini, Grok | every provider |
| Credential | your keychain | the relay's auth files | your keychain |
| Accounts per provider | several, with auto-switch | several | one key |
| Pays with | your plan | your plan | credit, per token |
| Quota meters | per-model windows | [per-model windows](/docs/providers/proxy) | [per-token spend](/docs/concepts/cost-tracking) |

Run both if you like. They are separate providers with separate credentials, and a model id says which one serves the turn.

## More than one account

Sign in again from `/login` and pick **Add another account**. Both plans stay
signed in, and the picker marks the one paying for the next turn.

One email can hold more than one subscription (a Team seat and a personal Max
plan). Empryo keys accounts by organization as well as by account, so those
are two rows, not one overwriting the other.

**When a plan runs out, Empryo moves to the next one.** The exhausted account
sits out until its window resets, and the terminal says which account took
over, beside the plan meter. Two things keep that honest:

- Only an exhausted plan triggers a switch. Ordinary throttling ("too many
  requests") is waited out on the same account, because benching a healthy
  subscription for an hour over a momentary blip is the worse mistake.
- Limits are per model family. A spent weekly cap on one family does not
  sideline the account for the others.

Empryo also switches *before* a request when the meter already says the active
plan is spent, so a long turn does not die halfway to discover it.

Pick an account by hand any time from `/login`. The same list switches, adds
and signs out, one account or all of them.

## What is left of the plan

Empryo asks each vendor's own usage route with the credential it holds, so the numbers match what the vendor's app shows: the 5-hour window, the weekly one, and the per-model weekly caps where the plan has them, each with its reset time.

Four lanes report a meter of their own shape, and all four render as the same card:

| Lane | What the meter reads | Unit |
|------|----------------------|------|
| Claude Pro/Max | 5-hour and weekly windows, per-model weekly caps | percent |
| ChatGPT Codex | 5-hour and weekly windows | percent |
| GitHub Copilot | premium, chat and completions against the seat's entitlement | requests |
| OpenRouter | spend against the key's credit limit | USD |

Every lane fills the same `QuotaWindow` record: id, label, percent used, reset time, and optionally the used/limit amounts in a declared unit. A renderer reads the unit rather than the provider, so the terminal status bar, `/usage`, the desktop Usage Center and Hearth all show a new lane the day it lands.

Two lanes need reading with care:

- Copilot meters premium requests, not tokens. Past the entitlement an overage seat keeps working and bills for it, so the card shows the overage count and a dollar figure derived from GitHub's published per-request rate. GitHub does not report that dollar figure, so every surface marks it an estimate. Override the rate with `EMPRYO_COPILOT_PREMIUM_USD` if it changes before Empryo ships an update.
- OpenRouter reports lifetime spend and, when the key has one, its credit limit. A key with no limit has a balance rather than an allowance, so the card shows spend with no bar.

On a subscription model the status bar wears the plan meter, on the terminal and the desktop app alike. `/usage` lists every window per account, the desktop Usage Center gives each signed-in plan its own card, and a Hearth chat answers `/subscriptions` with native and relay plans in one report.

Per-token cost and cached-token figures Empryo shows on these lanes are **estimates** derived from token counts and models.dev pricing, not billed invoices. A subscription bills against your plan's windows, so read the plan meter for what you have spent. The dollar estimate is a guide, not the vendor's ledger.

## Troubleshooting

#### Port 1455 is in use

Only ChatGPT needs that exact port, because OpenAI checks the redirect URI. Free it, or run `/login codex-device` instead.

#### The browser opened but nothing came back

The callback lands on `localhost`, which resolves to both IPv4 and IPv6. Empryo binds both, so a dev server holding one of them cannot swallow the code. If the page still hangs, the sign-in is waiting for a redirect that never arrived: close it and use the device code.

#### Signed in, but no models

Check the account in `/login`. A stored credential that has expired past its 30-day window still reads as signed in until the next refresh fails. Sign in again.

#### Usage credits are required for long context requests

A 1M-beta model on a subscription. Empryo does not send that beta, so this points at a model reaching the ChatGPT or Claude backend some other way. Pick a model with a native 1M window instead.

#### Workspace is not authorized in this region

An enterprise ChatGPT workspace pinned to a region. Empryo reads the residency from your token and declares it, so a fresh sign-in fixes a credential stored before that was handled.

## Related

- [Proxy](/docs/providers/proxy) - the same kinds of plan through the relay add-on, for several accounts per provider
- [GitHub Copilot](/docs/providers/copilot) - the other flat-rate plan, billed monthly
- [Providers overview](/docs/providers/overview) - every provider Empryo speaks
- [Cost tracking](/docs/concepts/cost-tracking) - what per-token spend looks like next to a plan
