
Commerce App Webhooks
- 65 installs
- 13 repo stars
- Updated August 4, 2026
- adobe/aio-commerce-sdk
commerce-app-webhooks is a Claude Code skill that adds or modifies webhook interceptors in an Adobe Commerce app to intercept Commerce operations before or after execution.
About
commerce-app-webhooks adds or modifies webhook interceptors in an Adobe Commerce app built with the aio-commerce-sdk. A developer uses it to intercept Commerce operations, before or after execution, to validate input, append data, or modify behavior. Each interceptor routes to either a runtime action in the app or an explicit external URL, with per-field validation applied before writing the config. It requires a base app initialized with commerce-app-init.
- Adds or modifies webhook interceptors in an Adobe Commerce app
- Intercepts Commerce operations before or after execution to validate, append, or modify
- Routes handlers to a runtime action or an explicit external URL with validation rules
Commerce App Webhooks by the numbers
- 65 all-time installs (skills.sh)
- Ranked #3,110 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
commerce-app-webhooks capabilities & compatibility
- Capabilities
- commerce webhooks · operation interception · webhook validation
- Use cases
- api development
- Pricing
- Free
What commerce-app-webhooks says it does
Add or modify webhook interceptors in an Adobe Commerce app.
Webhooks intercept Commerce operations — you can validate input, append data, or modify behavior before or after an operation executes.
npx skills add https://github.com/adobe/aio-commerce-sdk --skill commerce-app-webhooksAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 65 |
|---|---|
| repo stars | ★ 13 |
| Last updated | August 4, 2026 |
| Repository | adobe/aio-commerce-sdk ↗ |
What it does
Add webhook interceptors to an Adobe Commerce app to validate, append, or modify Commerce operations before or after execution.
Who is it for?
Intercepting Commerce operations to validate input, append data, or modify behavior via webhook interceptors.
Skip if: Other extensibility domains like events or business config, which have their own skills.
When should I use this skill?
A user wants to intercept a Commerce operation before or after it executes to validate, append, or modify data.
What you get
Validated webhook interceptor entries wired into the app.commerce.config.ts webhooks array.
- webhook interceptor entries in app.commerce.config.ts
By the numbers
- 2 webhook types (before, after)
- 3 categories (validation, append, modification)
- requires Node.js 22+
Files
Configure Commerce App Webhooks
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.
Prerequisites
- Verify the app is scaffolded and initialized, not merely that the config exists. Require both:
app.commerce.config.tspresent in the project root, and- the project initialized — signalled by the generated
src/commerce-extensibility-1/directory and installednode_modules(the@adobe/aio-commerce-lib-appdependency). - If
app.commerce.config.tsis missing, stop and invokecommerce-app-initfirst (it writes the config, then runs init). - If the config is present but the project is not initialized (no
src/commerce-extensibility-1/ornode_modules), runnpx @adobe/aio-commerce-lib-app initbefore continuing. Init is idempotent — it finds the existing config, skips the interactive prompts, installs dependencies, and generates the project files.
Step 1 — Understand intent
Ask the user what they want to intercept and how:
- What operation: the
webhook_method(the Commerce operation, e.g.,plugin.magento.catalog_product.save) andwebhook_type(beforeorafter) - How the handler is reached: either a runtime action in this app (
runtimeAction: "<package>/<action>") or an explicit external URL (webhook.url) — these are mutually exclusive - Category (optional):
validation(block if invalid),append(add data), ormodification(alter data) — used for conflict detection - Batch and hook identifiers:
batch_namegroups related hooks;hook_nameuniquely identifies this hook within the batch
Step 2 — Derive config values
Apply 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 |
Step 3 — Update app.commerce.config.ts
Add 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.
Creating the handler action
For webhook entries that use runtimeAction, create the action file under src/actions/ and register it in app.config.yaml.
Register the action
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: trueThe <package>/<action> format in runtimeAction maps directly: my-app/validate-product → package my-app, action validate-product.
Handler skeleton
// 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 |
Step 4 — Validate
Build the project to confirm the updated config is valid:
aio app buildA build failure with a validation error points directly to the offending config field.
Common Issues
- `batch_name` or `hook_name` rejected: Use underscores as separators (
my_app,validate_product_save) — hyphens, dots, and spaces are not accepted. - Both `runtimeAction` and `webhook.url` set: These are mutually exclusive — use
runtimeActionwhen the handler lives in this app;webhook.urlfor an external endpoint. - `url` at wrong level: For URL-based entries,
urlmust be inside the nestedwebhookobject, not at the top level alongsidelabel. - `app-management` package name conflict: The framework generates this package in
ext.config.yamlon every build. Use any other name for your own actions. - Function path is relative to `src/commerce-extensibility-1/`: Do not use
src/...or project-root-relative paths.actions/validate-product/index.jsresolves correctly;src/commerce-extensibility-1/actions/validate-product/index.jsdoes not. - `defineConfig` not found: Ensure
@adobe/aio-commerce-lib-appis installed anddefineConfigis imported from@adobe/aio-commerce-lib-app/config.
Quality Bar
aio app buildcompletes without errors
Chaining
After aio app build passes:
- Add merchant settings — invoke
commerce-app-business-configto expose configurable settings in Commerce Admin - Add event subscriptions — invoke
commerce-app-eventingto subscribe to Commerce or external events
References
- assets/webhooks-config.ts — Reference config showing both runtime action and URL-based webhook entry shapes
import { defineConfig } from "@adobe/aio-commerce-lib-app/config";
export default defineConfig({
metadata: {
id: "my-commerce-app", // alphanumeric + hyphens only, max 100 chars
displayName: "My Commerce App", // shown in App Management UI, max 50 chars
description: "A Commerce app built with aio-commerce-sdk.", // max 255 chars
version: "1.0.0", // Major.Minor.Patch only, no pre-release identifiers
},
webhooks: [
// Entry type 1: handler is a runtime action in this app
{
label: "Validate Product Save", // required, non-empty
description: "Validates product data before saving.", // required, non-empty
category: "validation", // optional: "validation" | "append" | "modification"
// env: ["paas"], // optional: scope to Commerce environments ("paas" | "saas"); omitted = all
runtimeAction: "my-package/validate-product", // <package>/<action>; mutually exclusive with webhook.url
webhook: {
webhook_method: "plugin.magento.catalog_product.save", // Commerce operation to intercept
webhook_type: "before", // "before" or "after"
batch_name: "my_app", // [a-zA-Z0-9_]+ only — no hyphens or dots
hook_name: "validate_product_save", // [a-zA-Z0-9_]+ only — no hyphens or dots
method: "POST", // HTTP method
// timeout: 5000, // optional: max ms to wait (positive integer)
// soft_timeout: 3000, // optional: soft limit before warning
// required: true, // optional: if true, Commerce aborts if hook fails
// priority: 100, // optional: execution order within batch
// fields: [{ name: "sku" }, { name: "price" }], // optional: restrict payload fields
// rules: [{ field: "type_id", operator: "equal", value: "simple" }], // optional: conditional trigger
// headers: [{ name: "X-Api-Key", value: "secret" }], // optional: custom headers
},
},
// Entry type 2: handler is an external URL
{
label: "Append Tax Data", // required, non-empty
description: "Appends tax calculation from external service.", // required, non-empty
category: "append", // optional: "validation" | "append" | "modification"
webhook: {
webhook_method: "plugin.magento.sales_order.place",
webhook_type: "before",
batch_name: "my_app",
hook_name: "append_tax",
method: "POST",
url: "https://tax-service.example.com/webhook", // must be absolute URL; mutually exclusive with runtimeAction
},
},
],
});
{
"skill_name": "commerce-app-webhooks",
"evals": [
{
"id": 1,
"prompt": "I need to validate product data before it gets saved to Commerce. Add a webhook that intercepts the product save operation and routes it to a runtime action in my app.",
"expected_output": "The agent adds a webhooks entry to app.commerce.config.ts with a runtimeAction-based webhook. The entry has label, description, a runtimeAction in <package>/<action> format, and a webhook object with webhook_method, webhook_type, batch_name ([a-zA-Z0-9_]+), hook_name ([a-zA-Z0-9_]+), and method. No webhook.url is present. aio app build completes without errors.",
"assertions": [
"app.commerce.config.ts contains a webhooks array",
"At least one webhook entry has a runtimeAction field in <package>/<action> format",
"No url field appears inside the webhook object for runtimeAction entries",
"batch_name matches [a-zA-Z0-9_]+ (no hyphens, dots, or spaces)",
"hook_name matches [a-zA-Z0-9_]+ (no hyphens, dots, or spaces)",
"aio app build completes without errors"
]
},
{
"id": 2,
"prompt": "We use an external fraud detection service. When an order is placed, I want Commerce to call that service's endpoint before completing the order. The service URL is https://fraud.example.com/check.",
"expected_output": "The agent adds a URL-based webhook entry to app.commerce.config.ts. The entry has label, description, and a webhook object containing url set to https://fraud.example.com/check, webhook_method for order placement, webhook_type before, batch_name, hook_name, and method. No runtimeAction field is present at the top level.",
"assertions": [
"app.commerce.config.ts contains a webhooks array",
"At least one webhook entry has webhook.url set to an absolute URL",
"No runtimeAction field is present on the url-based webhook entry",
"batch_name matches [a-zA-Z0-9_]+",
"hook_name matches [a-zA-Z0-9_]+",
"aio app build completes without errors"
]
},
{
"id": 3,
"prompt": "Add two webhooks to my Commerce app: one that validates customer data on registration (using a runtime action), and one that appends loyalty points data to order placement (calling an external loyalty service at https://loyalty.example.com/points).",
"expected_output": "The agent adds two webhook entries to app.commerce.config.ts. The first uses runtimeAction and has category validation. The second uses webhook.url and has category append. Both have valid batch_name and hook_name identifiers. After build passes, the agent suggests commerce-app-business-config or commerce-app-eventing as next steps.",
"assertions": [
"app.commerce.config.ts contains a webhooks array with at least two entries",
"One entry uses runtimeAction (no webhook.url)",
"One entry uses webhook.url (no runtimeAction)",
"All batch_name and hook_name values match [a-zA-Z0-9_]+",
"aio app build completes without errors",
"Agent mentions commerce-app-business-config or commerce-app-eventing as next steps"
]
}
]
}
Related skills
FAQ
Where can a webhook handler live?
Either a runtime action in this app (runtimeAction: "<package>/<action>") or an explicit external URL (webhook.url); the two are mutually exclusive.
What categories exist?
validation (block if invalid), append (add data), or modification (alter data), used for conflict detection.