packages/twenty-docs/developers/extend/apps/publishing.mdx
Once your app is built and tested locally, you have two paths for distributing it:
Both paths start from the same build step.
Run the build command to compile your app and generate a distribution-ready manifest.json:
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.
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.
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:
yarn twenty remote add --api-url https://your-twenty-server.com --as production
Build and upload your app to the server in one step:
yarn twenty deploy
# To deploy to a specific remote:
# yarn twenty deploy --remote production
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:
The share link uses the server's base URL (without any workspace subdomain) so it works for any workspace on the server.
When updating an already deployed tarball app, the server requires the version in package.json to be strictly higher (per semver 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:
version field in your package.json (e.g. 1.2.3 → 1.2.4, 1.3.0, or 2.0.0)yarn twenty deploy (or yarn twenty deploy --remote production)Apps generated with create-twenty-app ship with two GitHub Actions workflows out of the box, under .github/workflows/. They are ready to run as soon as you push the repo to GitHub — no extra setup is needed for CI, and CD only requires a single secret.
ci.ymlRuns integration tests on every push to main and every pull request.
What it does:
twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main composite action (the CI equivalent of yarn twenty server start --test)..nvmrc, and installs dependencies with yarn install --immutable.yarn test, passing TWENTY_API_URL and TWENTY_API_KEY from the spawned instance so your tests can talk to a real server.Config knobs:
TWENTY_VERSION (env, defaults to latest) — pin the Twenty server version used in CI by editing this in ci.yml.github.ref and cancels in-progress runs on new pushes.No secrets are required — the test instance is ephemeral and lives only for the duration of the job.
cd.ymlDeploys your app to a configured Twenty server on every push to main, and optionally from a pull request when the deploy label is applied.
What it does:
twentyhq/twenty/.github/actions/deploy-twenty-app@main — the CI equivalent of yarn twenty deploy.twentyhq/twenty/.github/actions/install-twenty-app@main so the newly deployed version is installed into the target workspace.Required configuration:
| Setting | Where | Purpose |
|---|---|---|
TWENTY_DEPLOY_URL | env in cd.yml (defaults to http://localhost:3000) | The Twenty server to deploy to. Change this to your real server URL before first use. |
TWENTY_DEPLOY_API_KEY | GitHub repo Settings → Secrets and variables → Actions | API key with deploy permission on the target server. |
Triggering a preview deploy from a PR:
Add the deploy label to a pull request. The if: guard in cd.yml will run the job for that PR using the PR's head commit, letting you validate a change on the target server before merging.
Both workflows reference reusable actions at @main, so action updates in the twentyhq/twenty repo are picked up automatically. If you want deterministic builds, replace @main with a commit SHA or release tag on each uses: line.
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.
twenty-app keyword in your package.json keywords array (add it manually — it is not included by default in the create-twenty-app template){
"name": "twenty-app-postcard-sender",
"version": "1.0.0",
"keywords": ["twenty-app"]
}
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:
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 in the Building Apps page for the full list of marketplace fields (author, category, aboutDescription, websiteUrl, termsUrl, etc.).
yarn twenty publish
To publish under a specific dist-tag (e.g., beta or next):
yarn twenty publish --tag beta
The Twenty server syncs its marketplace catalog from the npm registry every hour.
You can trigger the sync immediately instead of waiting:
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.
Use this GitHub Actions workflow to publish automatically on every release (uses OIDC):
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.
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.
You can also install apps from the command line:
yarn twenty install
APP_ALREADY_INSTALLED error.CANNOT_DOWNGRADE_APPLICATION error.To install a newer version, deploy or publish it first, then re-run yarn twenty install.
</Note>