plugins/commerce/app-management/skills/commerce-app-business-config/SKILL.md
Manage custom business configuration in an Adobe Commerce app. Use when the user wants to add, modify, or remove merchant-configurable settings (config fields, admin config, store configuration) exposed through Commerce Admin. Creates typed config fields (text, password, email, url, tel, boolean, list) in businessConfig.schema. Requires a base app initialized with commerce-app-init.
npx skillsauth add adobe/skills commerce-app-business-configInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Adds or modifies the businessConfig.schema array in an existing app.commerce.config.ts.
Each entry in the schema defines one merchant-configurable setting that Commerce Admin will render as a UI field.
Other extensibility domains (webhooks, events) are added separately via their own skills.
app.commerce.config.ts present in the project root, andsrc/commerce-extensibility-1/ directory and installed node_modules (the @adobe/aio-commerce-lib-app dependency).app.commerce.config.ts is missing, stop and invoke commerce-app-init first (it writes the config, then runs init).src/commerce-extensibility-1/ or node_modules), run npx @adobe/aio-commerce-lib-app init before continuing. Init is idempotent — it finds the existing config, skips the interactive prompts, installs dependencies, and generates the project files.For each setting the user wants to expose, gather:
list, text, password, email, url, tel, booleanlist fields additionally: selectionMode ("single" or "multiple") and options (each with a label and value string)Apply the following per-type validation rules before writing. Surface any issues to the user before proceeding.
| Field | Constraint |
| ----------------------- | ----------------------------------------------------------------------------------------------- |
| name | Required, non-empty string |
| type | Required; one of list, text, password, email, url, tel, boolean |
| label | Optional string |
| description | Optional string |
| list.selectionMode | Required for list fields: "single" or "multiple" |
| list.options | Required for list fields; each option needs both label and value strings |
| list/single default | Required; must match one of the option value strings (non-empty) |
| list/multiple default | Optional array of strings (defaults to []); each element must match an option value |
| text default | Optional string (defaults to "") |
| password default | Must be "" — any non-empty default is rejected to prevent secrets in config |
| email default | Optional; "" or a fully valid email address |
| url default | Optional; "" or a fully valid absolute URL |
| tel default | Optional; "" or matches /^\+?[0-9\s\-()]+$/ (digits, spaces, hyphens, parens, optional +) |
| boolean default | Optional boolean (defaults to false) |
businessConfig.schema must contain at least one field — an empty array is rejected at build time.
app.commerce.config.tsAdd (or merge into) the top-level businessConfig.schema array, preserving all other domains. If the config already has a businessConfig key, append to businessConfig.schema rather than replacing it.
Minimal examples:
businessConfig: {
schema: [
// Password (masked input — API keys, secrets)
{ name: "api_key", type: "password", label: "API Key", default: "" },
// Single-select list
{
name: "region", type: "list", selectionMode: "single",
label: "Region",
options: [{ label: "EU", value: "eu" }, { label: "US", value: "us" }],
default: "eu", // required; must match an option value
},
// Boolean toggle
{ name: "debug_mode", type: "boolean", label: "Enable Debug Mode", default: false },
// Dynamic list — options resolved at runtime via a factory that receives the action's params.
// Required `default` factory for single-select; optional for multiple (falls back to []).
{
name: "paymentMethod", type: "dynamicList", selectionMode: "single",
label: "Default Payment Method",
options: async (params) => {
const methods = await fetchPaymentMethods(params.SOME_API_KEY);
return methods.map((m) => ({ label: m.title, value: m.code }));
},
default: (resolvedOptions) => resolvedOptions[0].value,
},
],
}
See assets/business-config.ts for the full reference showing all field types.
Run init so that commerce/configuration/1 is added to app.config.yaml and install.yaml, and the required @adobe/aio-commerce-lib-config dependency is installed. This is idempotent — safe to run even if the extension is already registered.
npx @adobe/aio-commerce-lib-app init
Build the project to confirm the updated config is valid:
aio app build
A build failure with a validation error points directly to the offending field.
Use @adobe/aio-commerce-lib-config to read the values merchants set in Commerce Admin.
The library must be initialized with the generated schema on every action invocation before any config call.
import {
initialize,
getConfigurationByKey,
getConfiguration,
byCodeAndLevel,
} from "@adobe/aio-commerce-lib-config";
// Schema is generated by `aio app build` into .generated/configuration-schema.json
// under the commerce-configuration-1 extension; adjust the relative path for your action.
import schema from "../../.generated/configuration-schema.json" with { type: "json" };
export async function main(params) {
await initialize({ schema });
// Read a single field — config is null if the key has never been set
const { config } = await getConfigurationByKey(
"api_key", // the `name` from your schema
byCodeAndLevel("global", "global"), // scope
);
const apiKey = config?.value ?? "";
// Read all fields for a scope
const { config: allConfig } = await getConfiguration(
byCodeAndLevel("global", "global"),
);
// allConfig is an array of { name, value, origin } entries
return { statusCode: 200, body: { success: true } };
}
| Selector | When to use |
| ----------------------------------------- | -------------------------------------------------- |
| byCodeAndLevel("global", "global") | App-wide settings — applies to all stores |
| byCodeAndLevel(storeCode, "store_view") | Per store view (most specific) |
| byCode(storeCode) | Resolves using the default level for the scope |
| byScopeId(scopeId) | When you have the scope's numeric ID from Commerce |
Values inherit from parent scopes — a field not set on store_view falls back to store, website, then global.
aio app build generates AIO_COMMERCE_CONFIG_ENCRYPTION_KEY automatically into .env the first time it encounters a password field (and validates it on subsequent builds). No manual setup needed.
To decrypt values at runtime, the key must be available to the action. Wire it as an input in the action's ext.config.yaml:
inputs:
AIO_COMMERCE_CONFIG_ENCRYPTION_KEY: $AIO_COMMERCE_CONFIG_ENCRYPTION_KEY
With the key in place, getConfigurationByKey returns the plaintext value — no extra decryption code needed.
list/single default missing: Single-select list fields require a default — it can't be omitted. It must exactly match one of the option value strings.defineConfig not found: Ensure @adobe/aio-commerce-lib-app is installed and defineConfig is imported from @adobe/aio-commerce-lib-app/config.aio app build completes without errorsAfter aio app build passes:
commerce-app-webhooks to intercept Commerce operations before or after they executecommerce-app-eventing to subscribe to Commerce or external eventscommerce-app-admin-ui to add custom columns, mass actions, order view buttons, or menu entries in Commerce Admintools
Use the run-workflow MCP to discover, compose, execute, publish, and save Adobe Firefly workflows. TRIGGER when: user asks what actions are available, what the MCP can do, how to process images/video/3D via workflow, wants to build/run/save/publish a workflow, OR pastes any workflow/batch/execution ID. BARE ID (UUID/workflowId/batchId) = INSPECT ONLY — call inspect_run, NEVER run_workflow_submit. ALWAYS call list_actions first for capability/discovery questions. DO NOT TRIGGER for direct Firefly API calls without MCP (use firefly-api-specs).
tools
Run predefined featured workflows via run-workflow MCP. TRIGGER when user names a featured workflow (retargeting, banners at scale, localization, packaging, banner advertising, etc.) or asks to run a known marketing/production workflow. Requires run-workflow MCP. ALWAYS call get_featured_workflow before compose_workflow. DO NOT TRIGGER for custom one-off workflows with no named template — use run-workflow skill.
tools
Migrate an Adobe Commerce App Builder project from the Integration Starter Kit or Checkout Starter Kit to the new App Management approach. Run from the root of the App Builder project to be migrated. Pass --auto to skip confirmation prompts (suitable for CI or batch use) — auto mode prints a summary of all Q&A questions answered with their defaults. Pass --doc-scan-only to scan README.md and env.dist for outdated content without modifying any files. Use when the user wants to migrate an App Builder project from the Integration Starter Kit or Checkout Starter Kit to the App Management approach, or mentions upgrading their Adobe Commerce extension architecture.
development
Add or modify webhook interceptors in an Adobe Commerce app. Use when the user wants to intercept Commerce operations to validate input, append data, or modify behavior — before or after execution. Requires a base app initialized with commerce-app-init.