V3 App Settings Convention
App settings in V3 use app.settings to store configuration for features like authentication, push notifications, and analytics. This page describes the naming convention that controls which settings are visible to the app runtime (preview and published apps), to Studio editors, or to server-side code only.
The Underscore Convention
The prefix of a top-level settings key sets its tier:
| Key pattern | Tier | Who can read it | Example |
|---|---|---|---|
settingName |
Public | App runtime + Studio + backend | app.settings.saml2 (IdP URL, attribute mappings) |
_settingName |
Private | Studio editors + backend + server app actions. Never sent to the app runtime, preview or bundles. | app.settings._saml2 (IdP certificate) |
__settingName |
Protected | Backend + server app actions only. Write-only over HTTP: nobody can read it back, not Studio editors and not API tokens. | app.settings.__mailProvider (third-party API key) |
Settings keys that start with a single _ are editor-private. They are visible to Studio editors managing the app but are NOT included in the app runtime (preview iframe, published apps, bundled apps).
Settings keys that start with __ are protected: they are not included in the app runtime either, and they are not visible to Studio editors. See Protected settings.
The convention uses top-level key namespacing. All private settings for a feature go under a single _feature (or __feature) key, not mixed into the public feature key.
How It Works
app.settings = {
// Public settings — visible to the running app, Studio, and backend
saml2: {
idpUrl: 'https://idp.company.com/sso',
attributeMappings: { email: 'Email', name: 'Name' }
},
// Editor-private settings — visible to Studio editors, NOT to the running app
_saml2: {
idpCertificate: '-----BEGIN CERTIFICATE-----\nMIID...'
},
// Protected settings — NOT readable over HTTP by anyone, NOT in the running app
__mailProvider: {
apiKey: 'sk_live_...'
}
}
When the app loads in the preview iframe or as a published app, every top-level key starting with _ (_saml2 and __mailProvider here) is stripped from window.ENV.appSettings. The running app only sees:
window.ENV.appSettings = {
saml2: {
idpUrl: 'https://idp.company.com/sso',
attributeMappings: { email: 'Email', name: 'Name' }
}
// _saml2 and __mailProvider are NOT here
}
Backend code that runs inside the Fliplet API (server-side passports, hooks) reads the full app.settings from the model — no filtering is applied there.
App action code does not run inside the API and never reads the model. A V3 app action with environment: 'server' receives the underscore keys as context.settings — see Reading settings in a server action. client and any actions get no private settings; on the device they only see what the app runtime sees.
Protected settings
Use a __ key for a secret that must never be read back, such as a third-party API key used by a server action.
- Every HTTP read of app settings omits top-level keys starting with
__. This applies to Studio editors, admins, API tokens and app action (task) tokens. - Studio editors can write and delete
__keys throughPOSTandDELETE /v1/apps/:id/settings, but can never read them back. To change a value, overwrite it. PUT /v1/admin/apps/:appignores__keys in thesettingsbody and keeps the app’s current values. Rotating a protected key there returns200and changes nothing — usePOST /v1/apps/:id/settings.- App action (task) tokens can not write or delete
__keys — the request fails with403. - V3 app version snapshots taken from this release onwards do not store
__keys, and older snapshots that still hold them never return them. Restoring an app version keeps the app’s current__values. - Like
_keys,__keys are never sent to the app runtime, preview or bundles. - Server app actions receive
__keys incontext.settings, together with_keys.
Reading settings in a server action
A V3 app action with environment: 'server' receives every top-level app setting whose key starts with _ (both _private and __protected) as context.settings:
async function execute(context) {
const settings = context.settings || {};
// Saved by a Fliplet AI Builder secure panel field (settingKey: 'mailApiKey')
const apiKey = settings._aiartifact_mailApiKey;
// Or, for a protected key saved through the REST API:
// const apiKey = (settings.__mailProvider || {}).apiKey;
if (!apiKey) {
return { sent: false, error: 'MAIL_PROVIDER_NOT_CONFIGURED' };
}
// Send apiKey in a request header or body.
// Never return it, log it, throw it or put it in a URL.
return { configured: true };
}
- A Fliplet AI Builder panel field of type
securewith asecuredestination is saved by Studio as_aiartifact_<settingKey>and read ascontext.settings._aiartifact_<settingKey>. These are_(private) keys: Studio editors of the app can read them over the API. - Keep
settingKeysimple and alphanumeric. Studio replaces every character outsidea-zA-Z0-9_.-with_, so a key containing a space,@or/is stored under a different name than you wrote.-and.survive but break dot access — a key containing either needs bracket access, for examplesettings['_aiartifact_mail-api-key']. - Values always come from the master app and are read fresh on every run. Published apps read the master’s values; a rotated value applies to the next run without republishing.
clientandanyactions always getcontext.settings = {}.- If the underscore settings exceed 100 KB in total, every server action of the app fails until they are reduced.
See context.settings in App Actions V3 for the complete example, the limits and the rules for keeping values out of logs and responses.
Usage Patterns
Reading Public Settings (App Runtime)
// In a Vue component or app code — reads from the runtime environment
var appSettings = window.ENV.appSettings || {};
var saml2Config = appSettings.saml2;
if (saml2Config && saml2Config.idpUrl) {
// SAML2 is configured — show SSO login button
}
Reading All Settings (Studio)
// In Studio code (AppSettings.vue, tools, etc.) — reads full settings including _prefixed
var response = await Fliplet.API.request({ url: 'v1/apps/' + appId + '/settings/' });
var settings = response;
// settings._saml2 is available here (editor context)
var certificate = settings._saml2 && settings._saml2.idpCertificate;
// __ keys are never returned, even to editors:
// settings.__mailProvider is undefined here
Saving Settings
Settings are saved with POST /v1/apps/:id/settings, on the master app id. The setting keys go flat in the request body — they are not nested under a settings property.
The endpoint performs a shallow merge: top-level keys in your request overwrite existing keys with the same name, keys you don’t include are preserved, and nested objects are replaced entirely rather than deep-merged.
// Save both public and private settings together.
// The keys are top-level in `data` — NOT wrapped in { settings: {...} }.
// This MERGES at the top level: saml2 and _saml2 are set/replaced,
// but other top-level keys (customCSS, etc.) are preserved.
await Fliplet.API.request({
url: 'v1/apps/' + appId + '/settings',
method: 'POST',
data: {
saml2: {
idpUrl: 'https://idp.company.com/sso',
attributeMappings: { email: 'Email', name: 'Name' }
},
_saml2: {
idpCertificate: certificateText
}
}
});
// WARNING: This replaces the ENTIRE _saml2 object.
// If _saml2 previously had { idpCertificate, otherField },
// after this call it only has { idpCertificate }.
// Always send the complete object for each top-level key.
PUT /v1/apps/:id does not write app settings. It only updates name, startingPageId, hooks, isTemplate, dependencies and icon, plus isSystemTemplate and isHidden for admins. Sending settings to it returns 200 and stores nothing, so a server action reading the value afterwards sees it as not configured, with no error anywhere.
Removing Settings
Delete settings keys with DELETE /v1/apps/:id/settings, on the master app id, passing an array of top-level key names as keys. There is no way to delete a nested property — write the complete parent object instead.
DELETE /v1/apps/:id/settings
{ "keys": ["_saml2", "__mailProvider"] }
await Fliplet.API.request({
url: 'v1/apps/' + appId + '/settings',
method: 'DELETE',
data: {
keys: ['_saml2', '__mailProvider']
}
});
DO and DON’T
// DO: Use top-level namespacing for private settings
app.settings._saml2 = { idpCertificate: '...' };
// DON'T: Mix private keys inside a public namespace
app.settings.saml2 = { idpUrl: '...', _certificate: '...' };
// The _ filter only operates on top-level keys. Nested _ keys are NOT filtered.
// DO: Put related public and private config in sibling keys
app.settings.push = { enabled: true }; // public
app.settings._push = { apnsCertificate: '...' }; // editor-private
// DON'T: Store secrets that should never leave the server in _ keys
// _ keys are visible to Studio editors.
// DO: Store server-only secrets in top-level __ keys
app.settings.__mailProvider = { apiKey: '...' };
// __ keys are write-only over HTTP: nobody can read them back.
// Only server app actions (context.settings) and backend code read them.
// DON'T: Read a __ key back to check or merge it — it is never returned.
// Always write the complete object for the key.
// DO: Check for existence before reading settings
var saml2 = app.settings.saml2 || {};
// DON'T: Assume settings exist — they may not be configured yet
var url = app.settings.saml2.idpUrl; // Throws if saml2 is undefined
When to Use Private Settings
Use _ prefix for settings that:
- Contain credentials, certificates, or keys that app users should not see
- Are only needed by Studio UI, backend processing or server app actions, not by the running app
- Would be a security risk if exposed in client-side JavaScript
Credentials collected by a Fliplet AI Builder secure panel field are always saved as _aiartifact_<settingKey> keys, with every character outside a-zA-Z0-9_.- replaced by _. They are private, not protected: Studio editors of the app can read them over the API, and server app actions read them via context.settings.
When saving settings through the REST API, use __ prefix instead when Studio editors do not need to read the value back — for example an API key that only a server app action uses.
Examples:
_saml2.idpCertificate— X.509 certificate for SAML signature verification_push.apnsCertificate— Apple Push Notification certificate (future)_analytics.apiKey— Analytics service API key (future)
Related
- App Actions V3 —
context.settingsreference and full example - Session JS APIs — session management
- V3 Authentication Patterns — auth flows for V3 apps
- App Security — app-level access control