App Options
App Options are the settings an organization fills in when installing your app โ API URLs, feature toggles, credentials. You declare them once, at the top level of the manifest, and every part of your app reads from the same set of values: components, functions, the API Proxy and Portal Extension hooks.
Options used to live on components
Earlier app versions declared options per component. That model is retired: all existing component options were migrated to app level, and the platform transparently keeps serving them to consumers that still read options from components. Declare new options only at the top level โ deploys that declare an option on a component with the same key as an app-level option are rejected.
Declaring optionsโ
Add a top-level options array to your manifest.json:
{
"name": "My Integration",
"options": [
{ "key": "api_base_url", "label": "API Base URL", "type": "text", "required": true },
{ "key": "sync_enabled", "label": "Enable sync", "type": "boolean" },
{ "key": "api_key", "label": "API Key", "type": "secret", "required": true },
{ "key": "internal_hint", "label": "Routing hint", "type": "text", "sensitive": true }
],
"components": [ ... ]
}
| Field | Required | Description |
|---|---|---|
key | โ | Unique identifier across the whole app. This is what you reference everywhere ({{Options.api_key}}, input.app_options.api_key, proxy auth) โ treat it as a public contract |
type | โ | text, number, boolean, secret or object |
label | โ | Human-readable label shown to the installing organization |
description | โ | Help text shown below the input |
required | โ | The app installs, but is marked partially successful and stays unusable until all required options are filled |
sensitive | โ | Write-only, server-side only โ see below. Always true for type: secret |
repeatable | โ | The value becomes a list of entries (each gets a stable server-assigned id) |
fields | object type | Declares the sub-fields of an object option โ primitives only, no nesting |
The installing organization sees all app options in a single App settings card on the app's configuration page.
Sensitive vs. non-sensitiveโ
Every option is one of two kinds โ the same model most hosting platforms use for environment variables:
- Non-sensitive (default) โ readable wherever the app runs, including the end-customer's browser: journey blocks, portal blocks, capabilities. Use for URLs, labels, toggles, IDs.
- Sensitive โ write-only and server-side only. The value is never returned by any read API. The installer sees Configured โ and when the value was last changed, and can replace it โ but never read it back. Sensitive values only ever surface in server-side channels: API Proxy credential injection, Portal Extension / External Product Catalog hook templates, and function runs.
Options of type: secret are stored encrypted and are always sensitive. Set sensitive: true on a non-secret type for values that aren't credentials but still must not reach a browser (internal endpoints, routing hints).
Rule of thumb: if you would put it in a secret environment variable โ API keys, tokens, client secrets โ make it type: secret. If in doubt, mark it sensitive; you can't loosen a leaked value after the fact.
Required optionsโ
Marking an option required: true doesn't block the installation itself โ the app installs, but is flagged as partially successful and cannot be used until the organization fills in all required values.

Consuming optionsโ
Where the configured values show up, by surface:
| Surface | How you access options | Sensitive values? |
|---|---|---|
| API Proxy | Reference option keys in the auth configuration โ credentials are injected server-side | โ |
| Portal Extension / External Product Catalog hooks | {{Options.api_key}} template variables โ resolved server-side when the hook fires | โ |
| Functions | input.app_options contains all option values โ functions always run server-side | โ |
| Custom Journey Block, Custom Capability, Custom Page, Custom Portal Block (browser code) | Options are passed into your component per its surface contract | Non-sensitive only |
Anything that executes in a browser only ever receives non-sensitive options. There is no way to read a sensitive value from frontend code โ route those calls through the API Proxy instead, which attaches the credentials server-side.