Added a new IS_GRAPHQL_QUERY_TIMING_ENABLED feature flag that, when activated per workspace, logs the execution time and operation name of every GraphQL query across both the standard Yoga pipeline and the direct execution fast path. The timing context propagates via AsyncLocalStorage to also instrument buildColumnsToSelect and formatResult — giving a breakdown of column selection and result transformation costs without any caller changes.
A CLI and SDK to develop, build, and publish applications that extend Twenty CRM.
- Typed GraphQL clients:
CoreApiClient(auto-generated per app for workspace data) andMetadataApiClient(pre-built with the SDK for workspace configuration & file uploads) - Built‑in CLI for auth, dev mode (watch & sync), uninstall, and function management
- Works great with the scaffolder: create-twenty-app
Documentation
See Twenty application documentation https://docs.twenty.com/developers/extend/capabilities/apps
Prerequisites
- Node.js 24+ (recommended) and Yarn 4
- Docker (for the local Twenty dev server) or a remote Twenty workspace
Installation
npm install twenty-sdk
# or
yarn add twenty-sdk
Usage
Usage: twenty [options] [command]
CLI for Twenty application development
Options:
-V, --version output the version number
-r, --remote <name> Use a specific remote (overrides the default set by remote switch)
-h, --help display help for command
Commands:
dev [appPath] Watch and sync local application changes
build [appPath] Build, sync, and generate API client into .twenty/output/
deploy [appPath] Build and deploy to a Twenty server
publish [appPath] Build and publish to npm
typecheck [appPath] Run TypeScript type checking on the application
uninstall [appPath] Uninstall application from Twenty
remote Manage remote Twenty servers
server Manage a local Twenty server instance
add [entityType] Add a new entity to your application
exec [appPath] Execute a logic function with a JSON payload
logs [appPath] Watch application function logs
help [command] display help for command
In a scaffolded project (via create-twenty-app), use yarn twenty <command> instead of calling twenty directly. For example: yarn twenty help, yarn twenty dev, etc.
Global Options
--remote <name>(or-r <name>): Use a specific remote configuration. Defaults tolocal. See Configuration for details.
Commands
Server
Manage a local Twenty dev server (all-in-one Docker image).
twenty server start— Start the local server (pulls image if needed). Automatically configures thelocalremote.- Options:
-p, --port <port>: HTTP port (default:2020).
- Options:
twenty server stop— Stop the local server.twenty server logs— Stream server logs.- Options:
-n, --lines <lines>: Number of initial lines to show (default:50).
- Options:
twenty server status— Show server status (running/stopped/healthy).twenty server reset— Delete all data and start fresh.
The server comes pre-seeded with a workspace and user (tim@apple.dev / tim@apple.dev).
Examples:
# Start the local server
twenty server start
# Check if it's ready
twenty server status
# Follow logs during first startup
twenty server logs
# Stop the server (data is preserved)
twenty server stop
# Wipe everything and start over
twenty server reset
Remote
Manage remote server connections and authentication.
-
twenty remote add [nameOrUrl]— Add a new remote or re-authenticate an existing one.- Options:
--token <token>: API key for non-interactive auth.--url <url>: Server URL (alternative to positional arg).--as <name>: Name for this remote (otherwise derived from URL hostname).--local: Connect to local development server (http://localhost:2020) via OAuth.--port <port>: Port for local server (use with--local).
- Behavior: If
nameOrUrlmatches an existing remote name, re-authenticates it. Otherwise, creates a new remote and authenticates via OAuth (with API key fallback).
- Options:
-
twenty remote remove <name>— Remove a remote and its credentials. -
twenty remote list— List all configured remotes with their auth status and URLs. -
twenty remote switch [name]— Set the default remote.- If omitted, shows an interactive selection.
-
twenty remote status— Print the current remote name, server URL, and auth status.
Examples:
# Add a remote interactively (recommended)
twenty remote add
# Provide values in flags (non-interactive, for CI)
twenty remote add https://api.twenty.com --token $TWENTY_API_KEY
# Add a local development remote
twenty remote add --local
# Name a remote explicitly
twenty remote add https://api.twenty.com --as production
# Re-authenticate an existing remote by name
twenty remote add production
# Check status
twenty remote status
# List all configured remotes
twenty remote list
# Switch default remote
twenty remote switch production
# Remove a remote
twenty remote remove production
App
Application development commands.
-
twenty dev [appPath]— Start development mode: watch and sync local application changes.- Behavior: Builds your application (functions and front components), computes the manifest, syncs everything to your remote, then watches the directory for changes and re-syncs automatically. Displays an interactive UI showing build and sync status in real time. Press Ctrl+C to stop.
-
twenty build [appPath]— Build the application, sync to the server, generate the typed API client, then rebuild with the real client.- Options:
--tarball: Also pack the output into a.tgztarball.
- Options:
-
twenty publish [appPath]— Build and publish the application to npm.- Behavior: Builds the application and runs
npm publishon the output directory. - Options:
--tag <tag>: npm dist-tag (e.g.beta,next).
- Behavior: Builds the application and runs
-
twenty deploy [appPath]— Build and deploy the application to a Twenty server.- Behavior: Builds the tarball, uploads it to the server, and installs the application.
- Options:
--server <url>: Target Twenty server URL.--token <token>: Auth token for the server.
-
twenty typecheck [appPath]— Run TypeScript type checking on the application (runstsc --noEmit). Exits with code 1 if type errors are found. -
twenty uninstall [appPath]— Uninstall the application from the current remote.
Entity
twenty add [entityType]— Add a new entity to your application.- Arguments:
entityType: one ofobject,field,function,front-component,role,view,navigation-menu-item, orskill. If omitted, an interactive prompt is shown.
- Options:
--path <path>: The path where the entity file should be created (relative to the current directory).
- Behavior:
object: prompts for singular/plural names and labels, then creates a*.object.tsdefinition file.field: prompts for name, label, type, and target object, then creates a*.field.tsdefinition file.function: prompts for a name and scaffolds a*.function.tslogic function file.front-component: prompts for a name and scaffolds a*.front-component.tsxfile.role: prompts for a name and scaffolds a*.role.tsrole definition file.view: prompts for a name and target object, then creates a*.view.tsdefinition file.navigation-menu-item: prompts for a name and scaffolds a*.navigation-menu-item.tsfile.skill: prompts for a name and scaffolds a*.skill.tsskill definition file.
- Arguments:
Function
-
twenty logs [appPath]— Stream application function logs.- Options:
-u, --functionUniversalIdentifier <id>: Only show logs for a specific function universal ID.-n, --functionName <name>: Only show logs for a specific function name.
- Options:
-
twenty exec [appPath]— Execute a logic function with a JSON payload.- Options:
--preInstall: Execute the pre-install logic function defined in the application manifest (required if--postInstall,-n, and-unot provided).--postInstall: Execute the post-install logic function defined in the application manifest (required if--preInstall,-n, and-unot provided).-n, --functionName <name>: Name of the function to execute (required if--postInstalland-unot provided).-u, --functionUniversalIdentifier <id>: Universal ID of the function to execute (required if--postInstalland-nnot provided).-p, --payload <payload>: JSON payload to send to the function (default:{}).
- Options:
Examples:
# Start dev mode (watch, build, and sync)
twenty dev
# Start dev mode with a custom remote
twenty dev --remote my-custom-remote
# Type check the application
twenty typecheck
# Add a new entity interactively
twenty add
# Add a new function
twenty add function
# Add a new front component
twenty add front-component
# Add a new view
twenty add view
# Add a new navigation menu item
twenty add navigation-menu-item
# Add a new skill
twenty add skill
# Build the app (output in .twenty/output/)
twenty build
# Build and create a tarball
twenty build --tarball
# Publish to npm
twenty publish
# Publish with a dist-tag
twenty publish --tag beta
# Deploy directly to a Twenty server (builds, uploads, and installs)
twenty deploy --server https://app.twenty.com
# Uninstall the app from the remote
twenty uninstall
# Watch all function logs
twenty logs
# Watch logs for a specific function by name
twenty logs -n my-function
# Execute a function by name (with empty payload)
twenty exec -n my-function
# Execute a function with a JSON payload
twenty exec -n my-function -p '{"name": "test"}'
# Execute a function by universal identifier
twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -p '{"key": "value"}'
# Execute the pre-install function
twenty exec --preInstall
# Execute the post-install function
twenty exec --postInstall
Configuration
The CLI stores configuration per user in a JSON file:
- Location:
~/.twenty/config.json - Structure: Remotes keyed by name. The active remote is selected with
--remote <name>or by thedefaultRemotesetting.
Example configuration file:
{
"defaultRemote": "production",
"remotes": {
"local": {
"apiUrl": "http://localhost:2020",
"apiKey": "<your-api-key>"
},
"production": {
"apiUrl": "https://api.twenty.com",
"accessToken": "<oauth-token>",
"refreshToken": "<refresh-token>",
"oauthClientId": "<client-id>"
}
}
}
Notes:
- If a remote is missing,
apiUrldefaults tohttp://localhost:2020. twenty remote addwrites credentials for the active remote (OAuth tokens or API key).twenty remote add --as my-remotesaves under a custom name.twenty remote switchsets thedefaultRemotefield, used when-ris not specified.twenty remote listshows all configured remotes and their authentication status.
How to use a local Twenty instance
If you're already running a local Twenty instance, you can connect to it instead of using Docker. Pass the port your local server is listening on (default: 3000):
# During scaffolding
npx create-twenty-app@latest my-app --port 3000
# Or after scaffolding
twenty remote add --local --port 3000
Troubleshooting
- Auth errors: run
twenty remote addagain (or add a new remote) and ensure the API key has the required permissions. - Typings out of date: restart
twenty devto refresh the client and types. - Not seeing changes in dev: make sure dev mode is running (
twenty dev).
Contributing
Development Setup
To contribute to the twenty-sdk package, clone the repository and install dependencies:
git clone https://github.com/twentyhq/twenty.git
cd twenty
yarn install
Development Mode
Run the SDK build in watch mode to automatically rebuild on file changes:
npx nx run twenty-sdk:dev
This will watch for changes and rebuild the dist folder automatically.
Production Build
Build the SDK for production:
npx nx run twenty-sdk:build
Running the CLI Locally
After building, you can run the CLI directly:
npx nx run twenty-sdk:start -- <command>
# Example: npx nx run twenty-sdk:start -- remote status
Or run the built CLI directly:
node packages/twenty-sdk/dist/cli.cjs <command>