Skip to main content
Every app must have exactly one defineApplication call. It declares:
  • Identity — universal identifier, display name, description.
  • Permissions — which role its logic functions and front components run under.
  • Variables (optional) — key–value pairs exposed to your code as environment variables.
  • Pre-install / post-install / uninstall hooks (optional) — see Logic Functions.
  • Billing (optional)billing.recurring for subscription fees the platform raises automatically, billing.operations for what your app charges credits for, and billing.description for the marketplace; see Charging Credits.
src/application-config.ts
Notes:
  • universalIdentifier fields are UUIDs you generate once and own. Keep them stable across syncs. The application’s own universalIdentifier must be a valid UUID: the server rejects a manifest whose identifier is not one. It is unique across the whole instance and is bound to the workspace that owns its registration, so only that workspace can sync the app — see Registration ownership.
  • applicationVariables accept an optional label — a user-friendly name displayed in the app’s settings page instead of the raw variable key.
  • applicationVariables become environment variables for your functions and front components. In logic functions (server-side), they are available as process.env.VARIABLE_NAME. In front components, use getApplicationVariable('VARIABLE_NAME') from twenty-sdk/front-component. Variables marked with isSecret: true are only injected into logic functions. Front components receive only non-secret variables.
  • Your app can also read and update its own variable values at runtime, which is how a custom settings component saves what the user typed: see Reading and updating variables at runtime. An application only ever reaches its own variables, in its own workspace.
  • The default role is detected automatically from the role file marked with defineApplicationRole() — you do not need to reference it from defineApplication().
  • Pre-install, post-install, and uninstall functions are detected automatically during the manifest build — you do not need to reference them in defineApplication().
  • Passing defaultRoleUniversalIdentifier explicitly is still supported for backward compatibility, but is deprecated in favor of defineApplicationRole().
  • serverVariables are instance-scoped configuration and secrets (e.g. API keys). Unlike applicationVariables, they declare no value in the manifest — the workspace operator fills them in from the app’s settings, and they are injected into logic functions only once set.
  • Both kinds of variable accept isDeprecated: true. Use it to retire a variable instead of deleting it: keeping the key declared preserves the stored value (deleting it destroys the value the operator entered), and the variable is still injected, so your code can fall back to it — process.env.NEW_API_KEY ?? process.env.API_KEY. A deprecated variable disappears from the app’s settings once it has no value, and never counts toward the app’s configuration check, so isDeprecated beats isRequired.
  • To render a custom configuration UI as the app’s Settings tab (in place of the default Variables tab), declare a front component with defineSettingsFrontComponent() in its own file. Only one is allowed per app. System-managed sections (auto-upgrade, App URL, connections) stay in the app’s General tab.

Variable types

Both applicationVariables and serverVariables accept an optional type (and, for SELECT / MULTI_SELECT, an options list). Supported types: TEXT (default), BOOLEAN, NUMBER, NUMERIC, DATE, DATE_TIME, SELECT, MULTI_SELECT, ARRAY, RAW_JSON, RICH_TEXT.
src/application-config.ts
The type only affects presentation and validation — it selects the matching input in the workspace settings UI (a toggle, number field, dropdown, date picker, JSON editor, …) and lets the build validate your config (for example, SELECT / MULTI_SELECT must declare non-empty options). It does not change how the value reaches your code. Values are always injected as strings — this is inherent to environment variables (process.env.* is string-only). When your logic function runs, the executor serializes each value by its declared type while building process.env, so the string format is consistent no matter how the value was set (manifest default, settings UI, or a previous version): Parse the string back into the type you expect:
The same applies to front components reading values via getApplicationVariable('VARIABLE_NAME') — the returned value is a string; parse it as needed.

Default function role

The role declared with defineApplicationRole() controls what the app’s logic functions and front components can access:
  • The runtime tokens injected into your logic functions are derived from this role. A call acting as a person is further narrowed to what that person can do, so it can never exceed either one. See Whose access a call uses.
  • The typed API client is restricted to the permissions granted to that role.
  • Follow least-privilege: declare only the permissions your functions need.
When you scaffold a new app, the CLI creates a starter role file at src/roles/default-role.ts. See Roles & Permissions for the full reference.

Marketplace metadata

If you plan to publish your app, these optional fields control how it appears in the marketplace:
logoUrl and screenshots are deprecated aliases of logo and galleryImages. External absolute URLs (http:// or https://) are not supported for these fields: they are dropped with a warning at build time. Bundle the images in your app’s public/ folder instead.