---
title: "Connect an existing app"
description: "Link an app to a Linguana project, write environment credentials, and refresh them per environment."
canonical_url: "https://docs.linguana.dev/docs/cli/link"
markdown_url: "https://docs.linguana.dev/docs/cli/link.md"
x_farming_labs_generated_preamble: true
agent:
  task: "Connect an existing app directory to a Linguana project with the CLI."
  outcome: "linguana.json records the project and an ignored environment file holds matching build and browser values."
  prerequisites:
    - "Node.js 22.12 or later."
    - "Owner or Admin access to an active Linguana organization."
    - "A package.json in the app directory."
  files:
    - "linguana.json"
    - ".env.local or .env.<environment>.local"
    - ".gitignore"
  commands:
    - "npx @linguanahq/cli login"
    - "npx @linguanahq/cli link"
    - "npx @linguanahq/cli doctor"
  verification:
    - "npx @linguanahq/cli doctor reports every check as passing."
  rollback:
    - "Remove the Linguana variables from the environment file and revoke the CLI token in the dashboard."
  failureModes:
    - symptom: "ENV_CONFLICT"
      resolution: "Review the named variables, then retry with --overwrite-env to replace them."
---

# Connect an existing app
URL: /docs/cli/link
LLM index: /llms.txt
Description: Link an app to a Linguana project, write environment credentials, and refresh them per environment.
Related: /docs/cli/authentication, /docs/use-linguana/tokens-and-environments

<!-- farming-labs:agent-contract:start -->
## Agent Contract

Task: Connect an existing app directory to a Linguana project with the CLI.
Outcome: linguana.json records the project and an ignored environment file holds matching build and browser values.

### Prerequisites

- Node.js 22.12 or later.
- Owner or Admin access to an active Linguana organization.
- A package.json in the app directory.

### Files

- `linguana.json`
- `.env.local or .env.<environment>.local`
- `.gitignore`

### Commands

- `npx @linguanahq/cli login`
- `npx @linguanahq/cli link`
- `npx @linguanahq/cli doctor`

### Verification

- npx @linguanahq/cli doctor reports every check as passing.

### Rollback

- Remove the Linguana variables from the environment file and revoke the CLI token in the dashboard.

### Failure Modes

- ENV_CONFLICT — Recovery: Review the named variables, then retry with --overwrite-env to replace them.
<!-- farming-labs:agent-contract:end -->

# Connect an existing app

`link` connects the app in the current directory to a Linguana project. It records the project in `linguana.json` and writes build and browser credentials into an ignored environment file. It does not change your components or Vite configuration.

```bash title="terminal"
cd my-app
npx @linguanahq/cli link
```

Run it in the app directory, next to `package.json`. If you are not signed in, the CLI starts [browser sign-in](/docs/cli/authentication) first.

## What `link` does

1. **Detects the framework** from `package.json`: TanStack Start when `@tanstack/react-start` is installed, otherwise exactly one of React, Vue, Svelte, or SolidJS. If it finds none or several, pass `--framework`.
2. **Selects an organization.** It uses `--organization`, the organization of an already linked project, or asks you. You can also create one; the suggested name is `<your name>'s workspace`. Noninteractive runs select your only organization automatically, and need `--new-organization <name>` when you have none.
3. **Selects a project.** It uses `--project`, the project already recorded in `linguana.json`, or asks you to choose an active project or create one. New projects are named after the directory unless you pass `--new-project <name>`.
4. **Enables languages** for a newly created project, or when you pass `--locales`. Existing projects keep their language settings otherwise.
5. **Writes credentials** for the selected environment, reusing a valid existing build token or creating a new one.
6. **Writes `linguana.json`** with `"mode": "connected"`, the organization and project IDs, the API origin, and the source language.
7. **Prints the next steps**: your dashboard project link and the integration guide for your framework.

Connecting credentials requires Owner or Admin access to an active organization. Projects pending deletion cannot be linked. Apps created from templates must link to a project with English source content.

### Choose resources explicitly

```bash title="terminal"
# Look up IDs
npx @linguanahq/cli orgs list
npx @linguanahq/cli projects list --organization ORGANIZATION_ID

# Link an existing project
npx @linguanahq/cli link --project PROJECT_ID

# Create a project in an existing organization, with three languages
npx @linguanahq/cli link --organization ORGANIZATION_ID --new-project "My app" --locales fr,es,ar

# Create a new organization and project
npx @linguanahq/cli link --new-organization "Acme" --new-project "Storefront"
```

`orgs list` and `projects list` print one `ID  Name` pair per line, or the full API objects with `--json`. When you omit `--organization`, `projects list` asks you to choose one; noninteractive runs select your only organization automatically and otherwise require `--organization`.

`--organization` and `--new-organization` cannot be combined, and neither can `--project` and `--new-project`. A new organization cannot hold an existing project. If `--project` belongs to a different organization than `--organization`, `link` stops with `PROJECT_SCOPE_MISMATCH`.

## The environment file

`link` targets the `defaultEnvironment` recorded in `linguana.json`, or `development` for a new link, and writes `.env.local` for development. Pass `--environment` to target another environment:

| Environment | File written |
| --- | --- |
| `development` (default) | `.env.local` |
| `staging` | `.env.staging.local` |
| `production` | `.env.production.local` |

The file contains matching build and browser values:

```dotenv title=".env.local"
LINGUANA_PROJECT_ID="PROJECT_ID"
LINGUANA_ENVIRONMENT="development"
LINGUANA_API_URL="https://linguana-api.mohammedibrahim.dev"
LINGUANA_UPLOAD="true"
LINGUANA_TRANSLATE="false"
VITE_LINGUANA_PROJECT_ID="PROJECT_ID"
VITE_LINGUANA_ENVIRONMENT="development"
VITE_LINGUANA_API_URL="https://linguana-api.mohammedibrahim.dev"
VITE_LINGUANA_PUBLISHABLE_KEY="PUBLISHABLE_KEY"
LINGUANA_PROJECT_TOKEN="BUILD_TOKEN"
```

| Build/server variable | Browser variable |
| --- | --- |
| `LINGUANA_PROJECT_ID` | `VITE_LINGUANA_PROJECT_ID` |
| `LINGUANA_ENVIRONMENT` | `VITE_LINGUANA_ENVIRONMENT` |
| `LINGUANA_API_URL` | `VITE_LINGUANA_API_URL` |
| `LINGUANA_PROJECT_TOKEN` | `VITE_LINGUANA_PUBLISHABLE_KEY` |
| `LINGUANA_UPLOAD` | — |
| `LINGUANA_TRANSLATE` | — |

The build token is private. The publishable key is a separate, public catalog-read key. Never prefix build tokens with `VITE_`. `LINGUANA_UPLOAD` is `true` so builds upload their source text; `LINGUANA_TRANSLATE` is `false` so builds never start paid translation by themselves.

The CLI also:

- Adds `.env.local`, `.env.*.local`, and `.linguana/` to `.gitignore` when they are missing.
- Preserves unrelated variables, comments, and line endings, and updates Linguana values in place.
- Refuses to write an environment file that Git already tracks, or any path that passes through a symbolic link.
- Writes the file with owner-only permissions.

### Conflicting values

If the file already has different values for Linguana variables, the CLI lists the variable names (never their values) and asks before replacing them. Noninteractive runs stop with `ENV_CONFLICT` instead. Pass `--overwrite-env` to authorize replacement:

```bash title="terminal"
npx @linguanahq/cli link --project PROJECT_ID --overwrite-env
```

Duplicate definitions of the same Linguana variable stop with `DUPLICATE_ENV`; remove the duplicate first.

## Wire the values into your app

`link` configures linkage and credentials only. Your app must read the values:

- Templates already read them. Restart the dev server after linking.
- For an existing Vite app, load the environment with Vite's `loadEnv` in `vite.config.ts`, add the Linguana adapter before your framework plugin, and pass the public values to the runtime provider. Follow [Connect hosted translations](/docs/getting-started/connect-your-app), the [framework guide](/docs/getting-started/frameworks), or the [TanStack Start guide](/docs/getting-started/tanstack-start).

Then run [`doctor`](/docs/cli/doctor) to check the result.

## Refresh credentials with `env pull`

```bash title="terminal"
npx @linguanahq/cli env pull
npx @linguanahq/cli env pull --environment staging
npx @linguanahq/cli env pull --environment production
```

`env pull` reads the project from `linguana.json`, fetches the current public configuration, and validates the local build token. It reuses a valid token for the same project and environment and creates a new one only when needed. Run `link` first; otherwise `env pull` stops with `PROJECT_NOT_LINKED`.

Pulling another environment writes that environment's file and leaves `defaultEnvironment` in `linguana.json` unchanged. For Vite apps, build staging with `npm run build -- --mode staging`; Vite's normal production build reads `.env.production.local`. Private and public project and environment values must match.

The server cannot return a previously issued token secret again. If the local token is missing, revoked, or scoped to another project or environment, the CLI creates a replacement. When an invalid token is already in the file, it asks before replacing it, or requires `--overwrite-env` when noninteractive.

## Recovery and token reuse

Setup is safe to repeat. Interrupted operations save nonsecret idempotency keys in the ignored `.linguana/setup.json`, so running `link` again resumes with the same organization and project instead of creating duplicates.

If a token response was lost, replay returns metadata only; setup revokes that operation's unusable token before creating a replacement. It never rotates unrelated tokens or deletes projects. If writing the environment file fails after a new token was created, the CLI revokes that token.

Only one setup can run in an app at a time. A stale setup lock left by a crashed process on the same machine is recovered automatically. If setup was interrupted on another machine sharing the directory, remove `.linguana/cli.lock` and retry.

## Next.js apps

The CLI does not recognize Next.js yet. In a Next.js app, `link` detects React, writes the same variables, and prints Vite instructions that do not apply. `doctor` reports its SDK and Vite checks as failing.

You can still use `link` to create a project and a development build token:

```bash title="terminal"
npx @linguanahq/cli link --framework react --new-project "My Next.js app"
```

Then read the values in `next.config.ts` as shown in the [Next.js guide](/docs/getting-started/nextjs#use-credentials-from-the-cli). Next.js loads `.env.local` and, for production builds, `.env.production.local` before it evaluates `next.config.ts`. It does not load `.env.staging.local`; set staging values in your build host instead. The `VITE_` names are only names in Next.js and are not exposed to the browser automatically.

<ExpectedResult>
`linguana.json` records `"mode": "connected"` with your project ID, the environment file contains matching private and public values, and `npx @linguanahq/cli doctor` reports every check as passing for Vite apps.
</ExpectedResult>

<FailureGuide symptom="Connecting credentials requires Owner or Admin access" cause="Your role in the selected organization cannot create project credentials, or the organization is not active." check="Ask an owner to change your role or to run link, or choose an organization where you are an Owner or Admin." />

<NextStep>
[Check your setup with doctor](/docs/cli/doctor), then [translate and publish a language](/docs/use-linguana/publish-language).
</NextStep>

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
