# CLI reference (https://docs.open-e2ee.dev/reference/cli.md)



`oe` is the OpenE2EE command-line interface. It creates and links Relay projects, and keeps `open-e2ee.config.ts` and the console in step. It also stages iOS notification profiles. `oe help` prints the commands, global flags, exit codes, and environment variables on this page. `oe help --json` returns them as data.

## Install [#install]

`oe` needs Node.js 22.18 or later. Install it globally:

```bash
npm install --global @open-e2ee/oe
```

To run one command without an installation, use `npx @open-e2ee/oe@latest` in place of `oe`:

```bash
npx @open-e2ee/oe@latest auth login
```

Do not run `npx oe`. The npm package `oe` is not an OpenE2EE package.

`@open-e2ee/oe` is a small Node.js launcher. npm installs the native package for macOS, Linux, or Windows on arm64 or x64 as an optional dependency. Installation does not run a postinstall download.

## Commands [#commands]

Global flags can come before or after the command. `oe COMMAND --help` shows the usage of one command.

| Command                                                                                             | What it does                                                                                                                                                                                                                                                                                                                           |
| --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `oe new [signal-relay] [--project SLUG] [--name NAME] [--env-var NAME] [--dry-run]`                 | Creates a project and its Sandbox environment. Writes `open-e2ee.config.ts`, the Relay connection in `.env.local`, and the `@open-e2ee/oe` devDependency.                                                                                                                                                                              |
| `oe auth login [--accept-terms] [--organization ORG] [--timeout DURATION]`                          | Logs in with a browser and stores the session in the operating-system keychain. `--accept-terms` accepts the OpenE2EE terms for the organization. `--organization` puts the session in that organization. Without it, a person at a terminal who belongs to more than one organization chooses one. `--timeout` defaults to 5 minutes. |
| `oe auth status`                                                                                    | Shows the person, the organization, the terms state, and where the session is. When the control API lists the organizations, the last line names each one with its role and marks the current one. Exits with code 4 without a session.                                                                                                |
| `oe auth logout`                                                                                    | Removes the stored session.                                                                                                                                                                                                                                                                                                            |
| `oe org list`                                                                                       | Lists the organizations of the account with the role of each, and marks the current one with `*`.                                                                                                                                                                                                                                      |
| `oe org switch [ORG]`                                                                               | Moves the stored session to another organization. `ORG` is an ID, or a name that only one organization has. Without `ORG`, a person at a terminal chooses one.                                                                                                                                                                         |
| `oe doctor [--env sandbox\|production] [--wait] [--timeout DURATION]`                               | Checks the config, session, project, and Relay connection of the selected environment. `--wait` then waits for the first acknowledged Sandbox message.                                                                                                                                                                                 |
| `oe link [PROJECT] [--env-var NAME] [--yes] [--dry-run]`                                            | Links the directory to a Relay project that exists, and writes the Relay connection of each active environment to its env file. Without `PROJECT`, it rewrites only the env files.                                                                                                                                                     |
| `oe project list`                                                                                   | Lists the Relay projects of the organization of the session. When the list is empty and the person belongs to another organization, `next` is `oe org switch`.                                                                                                                                                                         |
| `oe project show [PROJECT] [--env sandbox\|production]`                                             | Reads one project and the state of each environment.                                                                                                                                                                                                                                                                                   |
| `oe project connection [PROJECT] [--env sandbox\|production]`                                       | Prints the Relay connection URL of one environment. Text mode prints only the URL.                                                                                                                                                                                                                                                     |
| `oe config push [--env sandbox\|production] [--yes] [--dry-run]`                                    | Applies the Sandbox section of `open-e2ee.config.ts`, then the Production section when the file has one.                                                                                                                                                                                                                               |
| `oe config pull [--env sandbox\|production] [--yes] [--dry-run]`                                    | Writes the Relay policy of each active environment into `open-e2ee.config.ts`.                                                                                                                                                                                                                                                         |
| `oe notifications status [--env sandbox\|production]`                                               | Shows the allowed notification profiles and the providers of the environment.                                                                                                                                                                                                                                                          |
| `oe notifications setup ios [--profile background-only\|visible-alert] [--env sandbox\|production]` | Stages an iOS notification profile. The default profile is `background-only`.                                                                                                                                                                                                                                                          |
| `oe notifications add-nse [--env sandbox\|production]`                                              | Stages a generic Notification Service Extension.                                                                                                                                                                                                                                                                                       |
| `oe notifications verify ios [--app-bundle PATH] [--env sandbox\|production]`                       | Verifies the iOS notification setup. `--app-bundle` inspects a signed `.app` bundle.                                                                                                                                                                                                                                                   |
| `oe notifications apple-filtering-request`                                                          | Gives the URL of the Apple request for the notification-filtering entitlement in `data.requestUrl` of the JSON output.                                                                                                                                                                                                                 |
| `oe agent setup [--scope project\|user] [--check]`                                                  | Installs the OpenE2EE skills for coding agents. `--check` reports each skill as current, missing, or changed, and writes nothing.                                                                                                                                                                                                      |
| `oe version`                                                                                        | Prints the CLI version.                                                                                                                                                                                                                                                                                                                |
| `oe help [COMMAND]`                                                                                 | Shows all commands, or the usage of one command.                                                                                                                                                                                                                                                                                       |

In `oe config`, a Production change or a replaced value needs `--yes` or the answer of a person at a terminal. `--dry-run` changes nothing.

### Notifications [#notifications]

Push is a best-effort wake. The durable Relay mailbox, authenticated pull, and acknowledgment are delivery authority. [Delivery and push](https://docs.open-e2ee.dev/relay/delivery-and-push.md) describes the iOS workflow.

`oe notifications status` reads the notification settings of the environment. Its JSON `data` has `allowedProfiles`, `environment`, and `providers`.

`oe notifications apple-filtering-request` puts `https://developer.apple.com/contact/request/notification-service/` in `data.requestUrl`. Run it with `--json` to see the URL, because text mode prints only a note. Apple's notification-filtering entitlement lets an approved, signed extension suppress an alert. It does not improve APNs delivery or execution. `oe` keeps filtering unavailable until Apple approval, signed extension inspection, and physical-device suppression evidence all pass.

### Organizations [#organizations]

A person can belong to more than one organization. The session is in one organization at a time. Each project command reads only the projects of that organization.

```bash
oe org list
oe org switch "Beta LLC"
oe auth login --organization "Beta LLC"
```

In JSON, `oe org list` puts the ID of the current organization in `data.current`. Each entry of `data.organizations` has `id`, `name`, `role`, `current`, and `switchable`.

`oe org switch` matches a name in any letter case. Without `ORG`, a person at a terminal chooses from a list, and Enter keeps the current organization. After a switch, `next` is `oe project list`. An unknown or ambiguous `ORG` fails with `USAGE_ERROR`, and `next` is `oe org list`. For an unknown `ORG`, `data.candidates` lists every organization of the account. For an ambiguous `ORG`, it lists the organizations with that name.

`oe auth login --organization ORG` logs in, then switches to `ORG` when the session is in another organization. Without the flag, a person at a terminal who belongs to more than one organization chooses one after a new login.

## Global flags [#global-flags]

| Flag                              | Effect                                                                                                                              |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `--json`                          | Writes one JSON document to stdout for the result, for success and for failure. `oe auth login` writes one pending event before it. |
| `--json-stream`                   | Writes newline-delimited JSON progress events, then one final event, to stdout.                                                     |
| `--agent yes\|no\|auto`           | Says whether a coding agent runs `oe`. The default, `auto`, reads the environment variables that coding agents set.                 |
| `--env sandbox\|production`, `-e` | Selects the environment.                                                                                                            |
| `--control-url URL`               | Uses another control API. The URL must use HTTPS, except on loopback.                                                               |
| `-h`, `--help`                    | Shows help.                                                                                                                         |

Without `--env`, `oe doctor`, `oe project`, and `oe notifications` read `OE_ENV`, then use `sandbox`. `oe new` always uses `sandbox`. `oe config pull` reads each active environment, and `oe config push` applies each environment section of the config. Only `--env` narrows a pull or a push to one environment. `OE_ENV` does not.

## Environment variables [#environment-variables]

| Variable                 | Effect                                                                                                                                                         |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OE_ACCESS_TOKEN`        | A scoped access token for CI or an agent. `oe` keeps it in memory and never stores it. When it is set, `oe` uses it in place of the stored login.              |
| `OE_ACCESS_TOKEN_SCOPES` | The scopes of `OE_ACCESS_TOKEN`, separated by commas or spaces. When it is set, `oe` checks the scope of each command before the request.                      |
| `OE_ENV`                 | The environment of `oe doctor`, `oe project`, and `oe notifications` when `--env` is not given: `sandbox` or `production`. `oe new` and `oe config` ignore it. |
| `OE_OPERATION_ID`        | The idempotency key for each remote mutation of one run. Set it only to retry one mutation.                                                                    |

An access token is short-lived. When `OE_ACCESS_TOKEN` has a value and `oe` exits with code 4, get a new token and set it in `OE_ACCESS_TOKEN` again. An agent that has no login gets its token through the [auth.md](https://console.open-e2ee.dev/auth.md) registration and token exchange. `oe auth login` and `oe auth logout` do not change `OE_ACCESS_TOKEN`. To use the stored login, unset `OE_ACCESS_TOKEN`.

## Exit codes [#exit-codes]

| Code | Meaning                                                                                                                                                                                                                                                                 |
| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`  | The command succeeded.                                                                                                                                                                                                                                                  |
| `1`  | The command failed. `code` tells why.                                                                                                                                                                                                                                   |
| `2`  | The command line is invalid, or a required input is missing, for example `--yes`.                                                                                                                                                                                       |
| `4`  | Authentication is required. Run `next`, which is `oe auth login`. After a failed switch, `next` adds `--organization ORG`. `ACCESS_TOKEN_INVALID` has no `next`. For `ACCESS_TOKEN_INVALID`, set `OE_ACCESS_TOKEN` to a new token, or unset it and run `oe auth login`. |
| `5`  | A person must act before the command can continue. `error` tells what to do. When `action.url` is present, it is the page that the person opens.                                                                                                                        |
| `6`  | The failure is temporary. `next` is the same command. Run it again later.                                                                                                                                                                                               |

## JSON output [#json-output]

In JSON mode, each run writes one JSON document to stdout. The document has these fields:

| Field     | Value                                             |
| --------- | ------------------------------------------------- |
| `status`  | `ok`, `error`, `pending`, or `progress`.          |
| `command` | The command, for example `config push`.           |
| `message` | A summary for a person.                           |
| `data`    | The result of the command.                        |
| `error`   | The error text.                                   |
| `code`    | The error code.                                   |
| `next`    | The command that moves the task forward.          |
| `action`  | A step for a person: `kind`, `url`, and `reason`. |

```json
{"status":"ok","command":"project","message":"...","data":{}}
{"status":"error","command":"config push","error":"...","code":"CONFIG_NOT_FOUND","next":"oe new"}
```

Switch on `code`, not on the text of `error`. When `action` is present, give `action.url` to a person. In text mode, a failure goes to stderr as `error:`, `action:`, and `next:` lines, and stdout stays clean.

These are the error codes that a script usually handles:

| Code                                                                 | Meaning                                                                                                                                                                                                                                                                                                  |
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AUTHENTICATION_REQUIRED`                                            | No session and no `OE_ACCESS_TOKEN`. Exit code 4.                                                                                                                                                                                                                                                        |
| `SESSION_EXPIRED`                                                    | The WorkOS session expired. Exit code 4. `next` is `oe auth login`. When the session expires during an organization switch, `next` is `oe auth login --organization ORG`.                                                                                                                                |
| `ORGANIZATION_REQUIRED`                                              | The command needs an organization, and the session is in none. Exit code 1. `next` is `oe org switch`. With `OE_ACCESS_TOKEN`, there is no `next`.                                                                                                                                                       |
| `ORGANIZATION_SIGN_IN_REQUIRED`                                      | The organization needs its own sign-in, for example SSO or MFA enrollment. Exit code 5. The stored session does not change. `next` is `oe auth login --organization ORG`. The person chooses that organization on the sign-in page.                                                                      |
| `ORGANIZATION_SWITCH_FAILED`                                         | The control API confirmed another organization after a switch. Exit code 1. `next` is `oe org list`.                                                                                                                                                                                                     |
| `CONTROL_UPDATE_REQUIRED`                                            | The control API does not list organizations yet, so `oe org` and `oe auth login --organization` cannot run. Exit code 1.                                                                                                                                                                                 |
| `TERMS_REQUIRED`                                                     | The organization has not accepted the OpenE2EE terms. Exit code 5. `data.retry` is the command to run again after the acceptance.                                                                                                                                                                        |
| `TERMS_PERMISSION_REQUIRED`                                          | Only an administrator of the organization can accept the terms. Exit code 5.                                                                                                                                                                                                                             |
| `ALREADY_SET_UP`                                                     | `oe new` ran in a directory that has `open-e2ee.config.ts`.                                                                                                                                                                                                                                              |
| `PROJECT_EXISTS`                                                     | The organization already uses the project slug.                                                                                                                                                                                                                                                          |
| `PROJECT_NOT_FOUND`                                                  | No project that this account can read has the slug in the organization of the session. Exit code 1. `next` is `oe project list`. When the account belongs to more than one organization, `error` says that the project can be in another one. In `oe new`, `next` is `oe new` with another project slug. |
| `CONFIG_NOT_FOUND`                                                   | The directory has no `open-e2ee.config.ts`.                                                                                                                                                                                                                                                              |
| `ENVIRONMENT_NOT_ACTIVE`                                             | The selected environment of the project is not active. For Sandbox, `next` is `oe new`. For Production, `next` is `oe config push`.                                                                                                                                                                      |
| `CONFIRMATION_REQUIRED`                                              | A change needs `--yes`. Review the change, then run the command again with `--yes`.                                                                                                                                                                                                                      |
| `BILLING_PERMISSION_REQUIRED`, `FREE_PROJECT_LIMIT`, `CARD_REQUIRED` | A Production activation needs the billing permission, an open Free plan slot, or a card on the organization. Exit code 5. For `CARD_REQUIRED`, `action.url` is the card page.                                                                                                                            |
| `NODE_REQUIRED`                                                      | `oe` needs Node.js 22.18 or later. Exit code 5.                                                                                                                                                                                                                                                          |
| `CONTROL_UNAVAILABLE`                                                | The control API is temporarily unavailable. Exit code 6.                                                                                                                                                                                                                                                 |

## Agent mode [#agent-mode]

`oe` detects a coding agent from the environment variables that the agent sets:

| Variable                                                    | Agent                     |
| ----------------------------------------------------------- | ------------------------- |
| `OPENCODE=1`                                                | OpenCode                  |
| `QWEN_CODE_SESSION_ID`                                      | Qwen Code                 |
| `PI_CODING_AGENT=true` or `AI_AGENT=pi`                     | Pi                        |
| `CURSOR_AGENT=1`                                            | Cursor Agent              |
| `CLAUDECODE=1`                                              | Claude Code               |
| `CODEX_THREAD_ID`                                           | OpenAI Codex              |
| `AMP_CURRENT_THREAD_ID`                                     | Amp                       |
| `GEMINI_CLI=1`                                              | Gemini CLI                |
| `AUGMENT_AGENT=1`                                           | Auggie                    |
| `CRUSH=1` or `AI_AGENT=crush`                               | Crush                     |
| `COPILOT_AGENT=1` or `AI_AGENT=github_copilot_vscode_agent` | GitHub Copilot in VS Code |
| `OZ_RUN_ID`                                                 | Warp                      |

A variable without a value in this table matches any value except an empty value, `0`, `false`, `no`, or `off`, in any letter case. Under an agent, the default output is JSON, and `oe` never prompts and never opens a browser. Pass `--agent yes` for an agent that `oe` does not detect. `--agent no` restores text output.

Under an agent, `oe auth login` writes a pending event, then the result after the person approves. Start the command in the background. Give the person `action.url` and `data.userCode`. The person opens the URL and confirms that the page shows the code. On another device, the person goes to `data.bareVerificationUrl` and enters the code. `data.expiresInSeconds` is the time that the code stays valid.

Under an agent or `--json`, `oe auth login` does not ask for an organization. The session keeps its organization. When the control API lists the organizations, `data.organizations` has each one. To change the organization, run `oe auth login --organization ORG` or `oe org switch ORG`. Without `ORG`, `oe org switch` fails with `USAGE_ERROR`, and `data.organizations` lists the choices. An agent session belongs to one organization and cannot switch. `oe org switch` and `--organization` refuse a session from `OE_ACCESS_TOKEN`. These refusals exit with code 2.

`oe agent setup` installs the OpenE2EE skills for coding agents. It writes `.agents/skills/<name>` and links `.claude/skills/<name>` to it.

## Relay connection variable [#relay-connection-variable]

`oe new` and `oe link` write the Sandbox connection URL to `.env.local`. The variable name comes from the `dependencies` and `devDependencies` of `package.json`. The first row of this table that matches sets the name:

| Dependency                          | Variable                          |
| ----------------------------------- | --------------------------------- |
| `next`                              | `NEXT_PUBLIC_OPEN_E2EE_RELAY_URL` |
| `expo`                              | `EXPO_PUBLIC_OPEN_E2EE_RELAY_URL` |
| `vite`                              | `VITE_OPEN_E2EE_RELAY_URL`        |
| None of these, or no `package.json` | `OPEN_E2EE_RELAY_URL`             |

`--env-var NAME` sets a different name. The URL is public configuration. It is not a credential. `oe config push` writes `.env.production.local`.
