plugins/commerce/app-management/skills/commerce-app-webhooks/SKILL.md
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.
npx skillsauth add adobe/skills commerce-app-webhooksInstall 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 webhook interceptors in an existing app.commerce.config.ts.
Webhooks intercept Commerce operations — you can validate input, append data, or modify behavior before or after an operation executes.
Other extensibility domains (events, business config) 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.Ask the user what they want to intercept and how:
webhook_method (the Commerce operation, e.g., plugin.magento.catalog_product.save) and webhook_type (before or after)runtimeAction: "<package>/<action>") or an explicit external URL (webhook.url) — these are mutually exclusivevalidation (block if invalid), append (add data), or modification (alter data) — used for conflict detectionbatch_name groups related hooks; hook_name uniquely identifies this hook within the batchApply the following validation rules before writing. Surface any issues to the user before proceeding.
| Field | Constraint |
| -------------------------- | ------------------------------------------------------------------------------------- |
| batch_name | [a-zA-Z0-9_]+ only — no hyphens, dots, or spaces |
| hook_name | [a-zA-Z0-9_]+ only — no hyphens, dots, or spaces |
| category | Optional; must be validation, append, or modification |
| runtimeAction | <package>/<action> format; mutually exclusive with webhook.url |
| webhook.url | Must be a valid absolute URL (https://...); mutually exclusive with runtimeAction |
| label | Required, non-empty |
| description | Required, non-empty |
| method | Required HTTP method (e.g., POST) |
| timeout / soft_timeout | Optional; positive integer (milliseconds) |
| priority / batch_order | Optional; positive integer |
app.commerce.config.tsAdd entries to the top-level webhooks array (or create it), preserving all other domains. If the config already has a webhooks key, append to it rather than replacing it.
Minimal examples:
// Runtime action handler (handler lives in this app)
webhooks: [
{
label: "Validate Product Save",
description: "Validates product data before saving.",
category: "validation", // optional
runtimeAction: "my-package/validate-product", // <package>/<action>
webhook: {
webhook_method: "plugin.magento.catalog_product.save",
webhook_type: "before",
batch_name: "my_app", // [a-zA-Z0-9_]+ only
hook_name: "validate_product", // [a-zA-Z0-9_]+ only
method: "POST",
},
},
];
// URL handler (external endpoint)
webhooks: [
{
label: "Fraud Check",
description: "Calls external fraud service before order placement.",
webhook: {
webhook_method: "plugin.magento.sales_order.place",
webhook_type: "before",
batch_name: "my_app",
hook_name: "fraud_check",
method: "POST",
url: "https://fraud.example.com/check", // inside webhook object, not top level
},
},
];
Each entry also accepts an optional env array ("paas" / "saas") to scope it to specific Commerce environments. When omitted, the webhook applies to all environments; when set, it is only subscribed at install time on the listed environments.
See assets/webhooks-config.ts for the full annotated reference.
For webhook entries that use runtimeAction, create the action file under src/actions/ and register it in app.config.yaml.
Add a user-defined package to src/commerce-extensibility-1/ext.config.yaml alongside the existing app-management package. Use any name except app-management (reserved by the framework):
# src/commerce-extensibility-1/ext.config.yaml
# (add below the auto-generated app-management package)
runtimeManifest:
packages:
app-management:
# ... auto-generated — do not edit
my-app: # your package name — any name except "app-management"
actions:
validate-product:
function: actions/validate-product/index.js # relative to src/commerce-extensibility-1/
web: "yes"
runtime: nodejs:24
annotations:
require-adobe-auth: true
The <package>/<action> format in runtimeAction maps directly: my-app/validate-product → package my-app, action validate-product.
// src/commerce-extensibility-1/actions/validate-product/index.ts
import {
ok,
successOperation,
exceptionOperation,
addOperation,
replaceOperation,
removeOperation,
} from "@adobe/aio-commerce-lib-webhooks/responses";
export async function main(params: Record<string, unknown>) {
// params contains the Commerce operation payload
// Allow the operation to proceed
return ok(successOperation());
// Block the operation (validation failure)
// return ok(exceptionOperation("Product SKU is required"));
// Append data to the operation result
// return ok(addOperation("result/custom_field", { value: "appended" }));
// Modify a field in the result
// return ok(replaceOperation("result/price", 99.99));
// Remove a field from the result
// return ok(removeOperation("result/unwanted_field"));
}
Operation types:
| Response | Effect |
| ------------------------------- | ----------------------------------------------- |
| successOperation() | Allow — operation proceeds unchanged |
| exceptionOperation(message) | Block — operation is rejected with this message |
| addOperation(path, value) | Append data at path in the result |
| replaceOperation(path, value) | Replace the value at path in the result |
| removeOperation(path) | Remove the field at path from the result |
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 config field.
batch_name or hook_name rejected: Use underscores as separators (my_app, validate_product_save) — hyphens, dots, and spaces are not accepted.runtimeAction and webhook.url set: These are mutually exclusive — use runtimeAction when the handler lives in this app; webhook.url for an external endpoint.url at wrong level: For URL-based entries, url must be inside the nested webhook object, not at the top level alongside label.app-management package name conflict: The framework generates this package in ext.config.yaml on every build. Use any other name for your own actions.src/commerce-extensibility-1/: Do not use src/... or project-root-relative paths. actions/validate-product/index.js resolves correctly; src/commerce-extensibility-1/actions/validate-product/index.js does not.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-business-config to expose configurable settings in Commerce Admincommerce-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 Admincommerce-app-storage to back webhook handlers with queryable DB storagetools
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
Integrate App Builder Database Storage (@adobe/aio-lib-db) into an Adobe Commerce app and scaffold a runtime action that reads and writes documents. Use when the user wants persistent, queryable storage backing a Commerce app — either from a web action (HTTP-invokable) or from an event/webhook handler. Requires a base app initialized with commerce-app-init.