---
title: Publishing
description: Distribute your Twenty app to the marketplace or deploy it internally.
---

<Warning>
  Apps are currently in alpha. The feature works but is still evolving.
</Warning>

## Overview

Once your app is [built and tested locally](/developers/extend/apps/building), you have two paths for distributing it:

- **Deploy a tarball** — upload your app directly to a specific Twenty server for internal or private use.
- **Publish to npm** — list your app in the Twenty marketplace for any workspace to discover and install.

Both paths start from the same **build** step.

## Building your app

Run the build command to compile your app and generate a distribution-ready `manifest.json`:

```bash filename="Terminal"
yarn twenty build
```

This compiles TypeScript sources, transpiles logic functions and front components, and writes everything to `.twenty/output/`. Add `--tarball` to also produce a `.tgz` package for manual distribution or the deploy command.

## Deploying to a server (tarball)

For apps you don't want publicly available — proprietary tools, enterprise-only integrations, or experimental builds — you can deploy a tarball directly to a Twenty server.

### Prerequisites

Before deploying, you need a configured remote pointing to the target server. Remotes store the server URL and authentication credentials locally in `~/.twenty/config.json`.

Add a remote:

```bash filename="Terminal"
yarn twenty remote add --api-url https://your-twenty-server.com --as production
```

### Deploying

Build and upload your app to the server in one step:

```bash filename="Terminal"
yarn twenty deploy
# To deploy to a specific remote:
# yarn twenty deploy --remote production
```

### Sharing a deployed app

<Warning>
Sharing private (tarball) apps across workspaces is an **Enterprise** feature. The **Distribution** tab will show an upgrade prompt instead of the share controls until your workspace has a valid Enterprise key. See [Settings > Admin Panel > Enterprise](/settings/admin-panel#enterprise) to activate it.
</Warning>

Tarball apps are not listed in the public marketplace, so other workspaces on the same server won't discover them by browsing. Once your workspace is on the Enterprise plan, you can share a deployed app like this:

1. Go to **Settings > Applications > Registrations** and open your app
2. In the **Distribution** tab, click **Copy share link**
3. Share this link with users on other workspaces — it takes them directly to the app's install page

The share link uses the server's base URL (without any workspace subdomain) so it works for any workspace on the server.

### Version management

When updating an already deployed tarball app, the server requires the `version` in `package.json` to be **strictly higher** (per [semver](https://semver.org) ordering) than the currently deployed version. Re-deploying the same version, or pushing a lower one, is rejected before the tarball is stored — you'll see a `VERSION_ALREADY_EXISTS` error from the CLI.

To release an update:

1. Bump the `version` field in your `package.json` (e.g. `1.2.3` → `1.2.4`, `1.3.0`, or `2.0.0`)
2. Run `yarn twenty deploy` (or `yarn twenty deploy --remote production`)
3. Workspaces that have the app installed will see the upgrade available in their settings

<Note>
Pre-release tags work as expected: bumping `1.0.0-rc.1` → `1.0.0-rc.2` is allowed, and a final release like `1.0.0` is correctly recognized as higher than `1.0.0-rc.5`. The version in `package.json` must itself be a valid semver string.
</Note>

{/* TODO: add screenshot of the Upgrade button */}

## Publishing to npm

Publishing to npm makes your app discoverable in the Twenty marketplace. Any Twenty workspace can browse, install, and upgrade marketplace apps directly from the UI.

### Requirements

- An [npm](https://www.npmjs.com) account
- The `twenty-app` keyword in your `package.json` `keywords` array (already included when you scaffold with `create-twenty-app`)

```json filename="package.json"
{
  "name": "twenty-app-postcard-sender",
  "version": "1.0.0",
  "keywords": ["twenty-app"]
}
```

### Marketplace metadata

The `defineApplication()` config supports optional fields that control how your app appears in the marketplace. Use `logoUrl` and `screenshots` to reference images from the `public/` folder:

```ts src/application-config.ts
export default defineApplication({
  universalIdentifier: '...',
  displayName: 'My App',
  description: 'A great app',
  defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
  logoUrl: 'public/logo.png',
  screenshots: [
    'public/screenshot-1.png',
    'public/screenshot-2.png',
  ],
});
```

See the [defineApplication accordion](/developers/extend/apps/building#defineentity-functions) in the Building Apps page for the full list of marketplace fields (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.).

### Publish

```bash filename="Terminal"
yarn twenty publish
```

To publish under a specific dist-tag (e.g., `beta` or `next`):

```bash filename="Terminal"
yarn twenty publish --tag beta
```

### How marketplace discovery works

The Twenty server syncs its marketplace catalog from the npm registry **every hour**.

You can trigger the sync immediately instead of waiting:

```bash filename="Terminal"
yarn twenty catalog-sync
# To target a specific remote:
# yarn twenty catalog-sync --remote production
```

The metadata shown in the marketplace comes from your `defineApplication()` config — fields like `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl`, and `termsUrl`.

<Note>
If your app does not define an `aboutDescription` in `defineApplication()`, the marketplace will automatically use your package's `README.md` from npm as the about page content. This means you can maintain a single README for both npm and the Twenty marketplace. If you want a different description in the marketplace, explicitly set `aboutDescription`.
</Note>

### CI publishing

Use this GitHub Actions workflow to publish automatically on every release (uses [OIDC](https://docs.npmjs.com/trusted-publishers)):

```yaml filename=".github/workflows/publish.yml"
name: Publish
on:
  release:
    types: [published]

permissions:
  contents: read
  id-token: write

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "24"
          registry-url: https://registry.npmjs.org
      - run: yarn install --immutable
      - run: npx twenty build
      - run: npm publish --provenance --access public
        working-directory: .twenty/output
```

For other CI systems (GitLab CI, CircleCI, etc.), the same three commands apply: `yarn install`, `yarn twenty build`, then `npm publish` from `.twenty/output`.

<Note>
**npm provenance** is optional but recommended. Publishing with `--provenance` adds a trust badge to your npm listing, letting users verify the package was built from a specific commit in a public CI pipeline. See the [npm provenance docs](https://docs.npmjs.com/generating-provenance-statements) for setup instructions.
</Note>

## Installing apps

Once an app is published (npm) or deployed (tarball), workspaces can install it through the UI.

Go to the **Settings > Applications** page in Twenty, where both marketplace and tarball-deployed apps can be browsed and installed.

{/* TODO: add screenshot of the UI when the app is registered */}

You can also install apps from the command line:

```bash filename="Terminal"
yarn twenty install
```

<Note>
The server enforces semver versioning on install, mirroring the rules on deploy:

- Installing the same version that is already installed in your workspace is rejected with an `APP_ALREADY_INSTALLED` error.
- Installing a lower version than the one currently installed is rejected with a `CANNOT_DOWNGRADE_APPLICATION` error.

To install a newer version, deploy or publish it first, then re-run `yarn twenty install`.
</Note>
