skills/vtex/skills/vtex-io-app-settings

vtex-io-app-settings

Installation
SKILL.md

App Settings & Configuration Boundaries

When this skill applies

Use this skill when deciding or implementing how a VTEX IO app should expose configurable settings.

  • Defining settingsSchema
  • Adding merchant-configurable app behavior
  • Reviewing whether configuration belongs in app settings or custom data
  • Reading and validating settings in app code

Do not use this skill for:

  • runtime infrastructure settings in service.json
  • Master Data entity design
  • policy declaration details
  • route auth modeling

Decision rules

  • Use app settings for stable configuration that merchants or operators should be able to manage explicitly.
  • Use settingsSchema for app-level configuration managed through VTEX Admin, and use store/contentSchemas.json for Store Framework block configuration that varies by page or block instance.
  • If the value is global for the app or account, it usually belongs in settingsSchema. If it varies per page, block, or theme composition, it usually belongs in contentSchemas.json.
  • Do not use app settings as a substitute for high-volume operational data storage.
  • Use JSON Schema explicitly with properties, required, default, enum, format, and related constraints instead of a generic root type: object only.
  • Use settingsSchema.access: "public" only for non-sensitive values that are intentionally safe to expose to frontend code through publicSettingsForApp.
  • If access is omitted, do not assume frontend GraphQL consumers can read the settings. Public frontend access must be an explicit choice.
  • Use app settings for API keys, tokens, and secrets instead of hardcoding them in the codebase.
  • Never expose secrets from app settings directly in HTTP responses, GraphQL responses, HTML, or browser-side props.
  • Never expose secrets from app settings in logs either.
  • For sensitive fields such as API keys or passwords, keep them as type: "string" and consider marking them with format: "password". Some platform consumers, such as Apps GraphQL when using hidePasswords, may use this metadata to mask values in responses. Do not rely on this as the only security layer: secrets must still be treated as backend-only and never exposed in responses or logs.
  • UI-specific hints such as ui:widget: "password" may be supported by some renderers, but they are not part of the core JSON Schema guarantees. Do not assume the standard VTEX Admin App Settings UI will enforce them.
  • Read backend settings through ctx.clients.apps.getAppSettings(ctx.vtex.appId ?? process.env.VTEX_APP_ID) and centralize normalization or validation in a helper instead of spreading ad hoc access patterns through handlers.
  • When reading or saving this app's own settings at runtime, use the correct app identifier such as process.env.VTEX_APP_ID or ctx.vtex.appId and rely on the app token plus standard app-settings permissions. Do not declare extra License Manager policies in manifest.json or add workspace-wide policies such as read-workspace-apps or undocumented policies such as write-workspace-apps just to "fix" a 403.
  • Pixel apps that need configuration should also consume settings through ctx.clients.apps.getAppSettings(...) on the backend side of the pixel app. If a value must be available to injected JavaScript, expose only non-sensitive fields through access: "public" and publicSettingsForApp, keeping secrets strictly on the server side.
  • Make code resilient to missing or incomplete settings by validating or applying defaults at the consumption boundary.
  • Never assume settings are identical across accounts or workspaces. Each workspace may have different app configuration during development, rollout, or debugging.

Settings vs configuration builder:

  • Use settingsSchema when the configuration is specific to this app and the merchant is expected to edit it in Apps > App Settings.
  • Consider a separate app using the configuration builder when the configuration contract needs to be shared across multiple apps, managed separately from the runtime app lifecycle, or injected directly into service context through ctx.vtex.settings.
  • Prefer a configuration app when the main goal is structured service configuration delivered through VTEX IO runtime context, instead of settings fetched ad hoc by the app itself.

Hard constraints

Constraint: Configurable app behavior must have a schema

Merchant-configurable settings MUST be modeled through an explicit schema instead of ad hoc unvalidated objects.

Why this matters

Without a schema, configuration becomes ambiguous, harder to validate, and easier to break across environments.

Detection

If code depends on app-level configuration but no schema or validation contract exists, STOP and define it first.

Correct

{
  "settingsSchema": {
    "title": "My App Settings",
    "type": "object",
    "properties": {
      "enableModeration": {
        "title": "Enable moderation",
        "type": "boolean",
        "default": false,
        "description": "If true, new content will require approval before going live."
      },
      "apiKey": {
        "title": "External API key",
        "type": "string",
        "minLength": 1,
        "description": "API key for the external moderation service.",
        "format": "password"
      },
      "mode": {
        "title": "Mode",
        "type": "string",
        "enum": ["sandbox", "production"],
        "default": "sandbox"
      }
    },
    "required": ["apiKey"]
  }
}

Wrong

const settings = ctx.state.anything

Constraint: Sensitive settings must stay backend-only and must not be exposed to the frontend

Secrets stored in app settings such as API keys, tokens, or passwords MUST be treated as backend-only configuration.

Why this matters

App settings are a natural place for secrets, but exposing them in HTTP responses, GraphQL payloads, HTML, or frontend props turns configuration into a security leak.

Detection

If a route, resolver, or frontend-facing response returns raw settings or includes sensitive fields from settings, STOP and move the external call or secret usage fully to the backend boundary.

Correct

const { apiKey } = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
const result = await externalClient.fetchData({ apiKey })

ctx.body = result

Wrong

const settings = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
ctx.body = settings

Constraint: Public app settings access must never expose sensitive configuration

If settingsSchema.access is set to public, the exposed settings MUST contain only values that are safe to ship to frontend code through publicSettingsForApp.

Why this matters

access: "public" is a delivery choice, not a security control. Once settings are publicly exposed, storefront or frontend code can read them, so secrets and backend-only configuration must never be included there.

Detection

If a settings schema marks access as public and includes API keys, tokens, passwords, or any value intended only for backend integrations, STOP and keep those settings private.

Correct

{
  "settingsSchema": {
    "title": "Public Storefront Settings",
    "type": "object",
    "access": "public",
    "properties": {
      "bannerText": {
        "title": "Banner text",
        "type": "string"
      }
    }
  }
}

Wrong

{
  "settingsSchema": {
    "title": "My App Settings",
    "type": "object",
    "access": "public",
    "properties": {
      "apiKey": {
        "title": "External API key",
        "type": "string",
        "format": "password"
      }
    }
  }
}

Constraint: Settings must not be used as operational data storage

App settings MUST represent configuration, not high-volume mutable records.

Why this matters

Settings are for configuration boundaries, not for transactional or large-scale operational data.

Detection

If a proposed setting stores records that behave like orders, reviews, logs, or queue items, STOP and move that concern to a more appropriate data mechanism.

Correct

{
  "enableModeration": true
}

Wrong

{
  "allReviews": []
}

Constraint: Code must validate or default settings at the consumption boundary

Settings-dependent code MUST tolerate missing or incomplete values safely.

Why this matters

Configuration can drift across workspaces and accounts. Code that assumes every setting is present becomes fragile.

Detection

If code reads settings and assumes required fields always exist with no validation or defaults, STOP and make the dependency explicit.

Correct

const rawSettings = await ctx.clients.apps.getAppSettings(
  ctx.vtex.appId ?? process.env.VTEX_APP_ID
)
const settings = normalizeSettings(rawSettings)
const enabled = settings.enableModeration ?? false

Wrong

const enabled = settings.enableModeration.value

Preferred pattern

Use settings for stable, merchant-managed configuration, define them with explicit JSON Schema properties, and validate or normalize them where they are consumed. For secrets, keep the read and the external call on the backend and return only the business result to the frontend. Use frontend GraphQL access only for intentionally public settings, and keep backend-only settings behind getAppSettings(...).

Common failure modes

  • Using settings as operational storage.
  • Using only type: object without explicit properties and validation details.
  • Reading settings without defaults or validation.
  • Exposing raw settings or secrets to the frontend.
  • Marking settings as access: "public" when they contain backend-only or sensitive values.
  • Logging settings or secrets in plain text.
  • Hardcoding API keys or tokens instead of storing them in app settings.
  • Adding workspace-level policies such as read-workspace-apps or invalid policies such as write-workspace-apps as a generic workaround for app settings permission errors, instead of validating the correct appId and standard app-settings permissions.
  • Using settingsSchema when the requirement is really block-level Store Framework configuration.
  • Creating schemas that are too broad or vague.

Review checklist

  • Does this data really belong in app settings?
  • Does the settingsSchema declare explicit properties with clear types and required only where necessary?
  • Are sensitive fields represented safely, for example as string fields with format: "password", knowing that some consumers such as Apps GraphQL with hidePasswords may use that metadata to mask the output?
  • Does the consuming code validate or default missing values?
  • Are secrets kept backend-only and never exposed to the frontend?
  • If access: "public" is used, are all exposed settings intentionally safe for frontend consumption?
  • Is the settings surface small and intentional?

Reference

Weekly Installs
80
Repository
vtex/skills
GitHub Stars
16
First Seen
13 days ago
Installed on
kimi-cli80
gemini-cli80
deepagents80
antigravity80
amp80
cline80