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

# Publish command for shareable build URLs

> Turn a project on extension.dev into a shareable URL with the Extension.js publish command. Requires an access token and prints the share link.

Ask [extension.dev](https://docs.extension.dev?utm_source=extension-js-org\&utm_medium=sponsor\&utm_campaign=docs-seam) for a shareable URL to a project you already have there.

`publish` is a thin client. It does not compile, package, or upload anything. It sends one authenticated request to the platform and prints the URL the platform answers with.

## When to use `publish`

* Sending a reviewer a link to a build instead of a zip file.
* Wiring a share link into CI after `build` produced the artifacts.
* Pinning a share link to one specific build rather than the project's latest.

`publish` resolves a project that already exists on extension.dev, so it needs a build the platform recorded. To send someone the build sitting in your own `dist/` right now, upload that build instead: [Share an unpublished build for review](https://docs.extension.dev/share/unpublished-build-for-review?utm_source=extension-js-org\&utm_medium=sponsor\&utm_campaign=docs-seam).

<Note>
  `publish` talks to the extension.dev platform, which is a separate product
  from Extension.js. The Extension.js commands that run on your machine
  (`create`, `dev`, `build`, `preview`, `start`) never need an account.
  `publish` does.
</Note>

## Usage

<CodeGroup>
  ```bash npm theme={null}
  extension publish [project-path] [options]
  ```

  ```bash pnpm theme={null}
  extension publish [project-path] [options]
  ```

  ```bash yarn theme={null}
  extension publish [project-path] [options]
  ```

  ```bash bun theme={null}
  extension publish [project-path] [options]
  ```

  ```bash deno theme={null}
  extension publish [project-path] [options]
  ```
</CodeGroup>

The project that gets published is whatever your token is scoped to. The path argument does not upload anything. It names the local directory whose project name the scope check below compares against.

## Token requirement

`publish` refuses to run without an access token. It looks in three places, in this order:

1. `--token <token>` on the command line.
2. `EXTENSION_DEV_TOKEN` in the environment (preferred for CI).
3. The stored device login that `npx @extension.dev/mcp login` writes.

Without any of them, the command exits with code `1` before any network call happens, and prints this to stderr:

```plaintext theme={null}
No token. Publishing needs an extension.dev access token.
Get one: https://docs.extension.dev/tools/publish
Pass --token, set EXTENSION_DEV_TOKEN, or run npx @extension.dev/mcp login.
```

Create a token from the extension.dev dashboard or the project access-tokens API, documented in [Access tokens](https://docs.extension.dev/tools/access-tokens?utm_source=extension-js-org\&utm_medium=sponsor\&utm_campaign=docs-seam).

## Scope checks

A stored device login is scoped to one project. Publishing from an unrelated directory would mint a share link for that project without naming it anywhere obvious. `publish` treats that mismatch as a refusal, not a warning:

* When the directory's project name does not match the stored login's project, the command refuses and names both.
* Pass `--project <slug>` to publish the login's project on purpose from anywhere.
* Passing `--project` with a slug that does not match the stored login also refuses.
* A `--token` or `EXTENSION_DEV_TOKEN` token skips the stored-login comparison entirely.

The local project name comes from `package.json`, then `manifest.json`, then `src/manifest.json`, then the folder name.

## Arguments and flags

| Flag | What it does | Default |
| - | - | - |
| `[project-path]` | Directory whose project name the scope check reads. Not uploaded. | `process.cwd()` |
| `--token <token>` | extension.dev access token. | `EXTENSION_DEV_TOKEN`, then stored login |
| `--api <url>` | Platform base URL. Useful for self-hosted or staging endpoints. | `EXTENSION_DEV_API_URL`, then platform URL |
| `--ttl <hours>` | Share-link lifetime in hours, from 1 to 168. Private projects only. | `24` |
| `--build-sha <sha>` | Pin the share URL to a specific build instead of the latest. | latest build |
| `--project <slug>` | Name the project this publish is for, when it is not the directory you are in. | unset |
| `--output <pretty\|json>` | Output format. `json` prints the full platform response. | `pretty` |

## What it prints

Pretty output is a single line, the share URL, so it pipes cleanly:

```bash theme={null}
extension publish
# https://<workspace>.extension.dev/<project>
```

`--output json` prints one envelope. The platform response sits in `value`, and it carries more than the URL:

```json theme={null}
{
  "schema": 1,
  "ok": true,
  "command": "publish",
  "status": "published",
  "value": {
    "shareUrl": "https://<workspace>.extension.dev/<project>?share=<share-token>",
    "visibility": "private",
    "token": "<share-token>",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "ttlHours": 24,
    "project": "<project>",
    "tokenSource": "stored-login"
  },
  "error": null,
  "warnings": []
}
```

A public project answers with `value.shareUrl` and `value.visibility` only. There is no token to carry.

With `--build-sha`, the URL points at that build instead of the project overview: `https://<workspace>.extension.dev/<project>/builds/<sha>`.

## Public and private projects

A project is either public or private. `publish` only reads that setting and never changes it. The platform decides what kind of link you get:

| Project visibility | What comes back |
| - | - |
| Public | The project's own address, with no token. `--ttl` is ignored because nothing expires. |
| Private | That same address plus `?share=<token>`, which stops working after `--ttl` hours. |

Both answers point at the same page. Visibility decides whether a token is attached, not which address you get.

## Pinning to a build

`--build-sha` links to one build instead of the project's latest. The platform verifies the sha against the project's build index and answers with a `404` and an `UNKNOWN_BUILD` code when no completed build matches, so a typo fails loudly instead of producing a link to the wrong artifact.

<CodeGroup>
  ```bash npm theme={null}
  extension publish --build-sha=9fceb02
  ```

  ```bash pnpm theme={null}
  extension publish --build-sha=9fceb02
  ```

  ```bash yarn theme={null}
  extension publish --build-sha=9fceb02
  ```

  ```bash bun theme={null}
  extension publish --build-sha=9fceb02
  ```

  ```bash deno theme={null}
  extension publish --build-sha=9fceb02
  ```
</CodeGroup>

## Examples

### Publishing from CI

```bash theme={null}
EXTENSION_DEV_TOKEN=$EXTENSION_DEV_TOKEN extension build --browser=chrome --zip
SHARE_URL=$(EXTENSION_DEV_TOKEN=$EXTENSION_DEV_TOKEN extension publish)
echo "Review build: $SHARE_URL"
```

### A short-lived link for one reviewer

<CodeGroup>
  ```bash npm theme={null}
  extension publish --ttl=4
  ```

  ```bash pnpm theme={null}
  extension publish --ttl=4
  ```

  ```bash yarn theme={null}
  extension publish --ttl=4
  ```

  ```bash bun theme={null}
  extension publish --ttl=4
  ```

  ```bash deno theme={null}
  extension publish --ttl=4
  ```
</CodeGroup>

## Behavior notes

* `publish` never compiles. Run [`build`](/docs/commands/build) first when you want the link to point at fresh output.
* Every failure path exits with code `1`: a missing token, an unreachable platform, or any non-2xx response, which is printed as `publish failed (<status>): <message>`.
* `--api` accepts a base URL with or without a trailing slash. The command appends `/api/cli/publish` itself.
* `--ttl` is clamped by the platform to the 1 to 168 hour range.
* The `?share=` token this command returns is not the 30 day revocable preview link. That link comes from a different verb, which uploads the build; `publish` uploads nothing. See [the platform's publish page](https://docs.extension.dev/tools/publish?utm_source=extension-js-org\&utm_medium=sponsor\&utm_campaign=docs-seam).

## Next steps

* Produce the artifacts to share with [`build`](/docs/commands/build).
* Validate the artifacts locally first with [`preview`](/docs/commands/preview).
* Hand someone an unpublished build behind a link with no zip and no install, in [Share an unpublished build for review](https://docs.extension.dev/share/unpublished-build-for-review?utm_source=extension-js-org\&utm_medium=sponsor\&utm_campaign=docs-seam).
* Read how builds are recorded in [Builds](https://docs.extension.dev/builds/overview?utm_source=extension-js-org\&utm_medium=sponsor\&utm_campaign=docs-seam).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.