> ## Documentation Index
> Fetch the complete documentation index at: https://twenty-claude-cool-pascal-5ay683.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Application Config

> Declare your app's identity, default role, variables, and marketplace metadata with defineApplication.

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](/developers/extend/apps/logic/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](/developers/extend/apps/logic/credits).

```ts src/application-config.ts theme={null}
import { defineApplication } from 'twenty-sdk/define';

export default defineApplication({
  universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d',
  displayName: 'My Twenty App',
  description: 'My first Twenty app',
  applicationVariables: {
    DEFAULT_RECIPIENT_NAME: {
      universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
      label: 'Default recipient name',
      description: 'Default recipient name for postcards',
      value: 'Jane Doe',
      isSecret: false,
    },
  },
});
```

Notes:

* `universalIdentifier` fields are deterministic IDs you own. Generate them once and keep them stable across syncs. The application's own `universalIdentifier` 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](/developers/extend/apps/operations/publishing#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.
* The default role is detected automatically from the role file marked with [`defineApplicationRole()`](/developers/extend/apps/config/roles) — 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 inside the app's **Settings** tab (in place of the default variable configuration section), declare a front component with [`defineSettingsFrontComponent()`](/developers/extend/apps/layout/front-components#custom-settings-component) in its own file. Only one is allowed per app. System-managed sections (auto-upgrade, App URL, connections) always remain visible.

## 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`.

```ts src/application-config.ts theme={null}
import { defineApplication, FieldType } from 'twenty-sdk/define';

export default defineApplication({
  // ...identity, role...
  applicationVariables: {
    MAX_POSTCARDS: {
      universalIdentifier: '5f4497e4-9030-4085-85eb-2c48b8d53713',
      description: 'Maximum postcards per batch',
      type: FieldType.NUMBER,
      value: 10,
    },
    DEFAULT_REGION: {
      universalIdentifier: '76c5c321-b6b6-46eb-b4fc-f9f04bb04227',
      description: 'Default shipping region',
      type: FieldType.SELECT,
      options: [
        { label: 'Europe', value: 'eu' },
        { label: 'United States', value: 'us' },
      ],
      value: 'eu',
    },
  },
});
```

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):

| Type                                  | `process.env` string                   |
| ------------------------------------- | -------------------------------------- |
| `TEXT`, `SELECT`, `DATE`, `DATE_TIME` | the raw value (`"eu"`, `"2026-01-01"`) |
| `BOOLEAN`                             | `"true"` / `"false"`                   |
| `NUMBER`, `NUMERIC`                   | decimal string (`"10"`, `"2.5"`)       |
| `MULTI_SELECT`, `ARRAY`               | JSON array (`'["email","postcard"]'`)  |
| `RAW_JSON`, `RICH_TEXT`               | JSON object (`'{"retries":3}'`)        |

Parse the string back into the type you expect:

```ts theme={null}
const maxCards = Number(process.env.MAX_POSTCARDS); // "10" -> 10
const enabled = process.env.ENABLE_TRACKING === 'true'; // "true" -> true
const channels = JSON.parse(process.env.ENABLED_CHANNELS ?? '[]'); // '["email"]' -> ["email"]
const config = JSON.parse(process.env.PROVIDER_CONFIG ?? '{}'); // '{"retries":3}' -> { retries: 3 }
```

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()`](/developers/extend/apps/config/roles) 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](/developers/extend/apps/logic/logic-functions#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](/developers/extend/apps/config/roles) for the full reference.

## Marketplace metadata

If you plan to [publish your app](/developers/extend/apps/operations/publishing), these optional fields control how it appears in the marketplace:

| Field                 | Description                                                                                                                                                                                     |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `author`              | Author or company name                                                                                                                                                                          |
| `category`            | App category for marketplace filtering                                                                                                                                                          |
| `logo`                | Path to your app logo bundled in `public/` (e.g., `public/logo.png`)                                                                                                                            |
| `galleryImages`       | Array of gallery image paths bundled in `public/` (e.g., `public/screenshot-1.png`)                                                                                                             |
| `aboutDescription`    | Longer markdown description for the "About" tab. If omitted, the marketplace uses the package's `README.md` from npm                                                                            |
| `billing.description` | What the app costs, in one short friendly line shown next to the install button (e.g. `$0.02 per minute recorded`). Set it if your app [charges credits](/developers/extend/apps/logic/credits) |
| `websiteUrl`          | Link to your website                                                                                                                                                                            |
| `termsUrl`            | Link to terms of service                                                                                                                                                                        |
| `emailSupport`        | Support email address                                                                                                                                                                           |
| `issueReportUrl`      | Link to issue tracker                                                                                                                                                                           |

<Note>
  `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.
</Note>
