Product stock validation

When a shopper adds or updates a product in the cart, a third-party inventory management system checks whether the requested quantity is available. If it is, allow the operation to proceed. Otherwise, display an error message.

Webhook name

observer.sales_quote_item_save_before

Payloads

The following observer.sales_quote_item_save_before default payload was obtained from execution of the application code. Some data has been removed for brevity.

data-slots=heading, code
data-repeat=2
data-languages=JSON, JSON

Default payload

{
    "eventName": "sales_quote_item_save_before",
    "subject": [],
    "data": {
        "item": {
            "applied_rule_ids": null,
            "applied_taxes": [],
            ...
            "base_price": 123,
            "base_price_incl_tax": 123,
            "base_row_total": 123,
            ...
            "custom_attributes_serializable": [],
            "discount_amount": 0,
            ...
            "discount_percent": 0,
            "extension_attributes": [],
            "free_shipping": false,
            "is_qty_decimal": false,
            "name": "Product 1",
            "previous_qty": null,
            "price": 123,
            "price_incl_tax": 123,
            "product": {
                "attribute_set_id": "4",
                "attributes": {
                    ...
                },
                "cart_qty": 1,
                "cost": null,
                "created_at": "2026-06-18 20:27:11",
                "customer_group_id": "1",
                "entity_id": "216",
                "extension_attributes": [],
                ...
                "name": "Product 1",
                "price": "123.000000",
                "qty": 1,
                "required_options": "0",
                "salable": true,
                "sku": "Product-1",
                ...
            },
            "product_id": "216",
            "product_type": "simple",
            "qty": 1,
            "qty_to_add": 1,
            "quote_id": "63",
            "row_total": 123,
            "row_total_incl_tax": 123,
            "row_weight": 12,
            "sku": "Product-1",
            ...
        },
        "data_object": {
            ...
        }
    }
}

Configured payload

{
    "item": {
        "name": "string",
        "sku": "string",
        "qty": "number"
    }
}

Configuration

data-slots=heading, code
data-repeat=2
data-languages=XML, YAML

webhook.xml (PaaS)

<method name="observer.sales_quote_item_save_before" type="before">
    <hooks>
        <batch name="quote_item_stock">
            <hook name="validate_stock" url="{env:APP_BUILDER_URL}/validate-stock" fallbackErrorMessage="The product stock cannot be validated" method="POST" timeout="5000" softTimeout="1000">
                <headers>
                    <header name="x-gw-ims-org-id">{env:APP_BUILDER_IMS_ORG_ID}</header>
                    <header name="Authorization">Bearer {env:APP_BUILDER_AUTH_TOKEN}</header>
                </headers>
                <fields>
                    <field name="item.sku" source="data.item.sku" />
                    <field name="item.qty" source="data.item.qty" />
                    <field name="item.name" source="data.item.name" />
                </fields>
            </hook>
        </batch>
    </hooks>
</method>

Admin (SaaS)

Hook Settings

Webhook method: observer.sales_quote_item_save_before
Webhook type: before
Batch name: quote_item_stock
Hook name: validate_stock
URL: <Host>/validate-stock
Timeout: 5000
Soft timeout: 1000
Fallback Error Message: The product stock cannot be validated
Required: Required
Active: Yes
Method: POST

Developer Console OAuth

Client ID: The client ID for the OAuth credential.
Client Secret: The client secret for the OAuth credential.
Organization ID: The organization ID for the OAuth credential.

Hook Fields

Name: item.sku
Source: data.item.sku
Active: Yes

Name: item.qty
Source: data.item.qty
Active: Yes

Name: item.name
Source: data.item.name
Active: Yes

Endpoint code example

The following code example shows how to implement the webhook on your custom endpoint.

const fetch = require('node-fetch')
const { Core } = require('@adobe/aio-sdk')
const { errorResponse, stringParameters, checkMissingRequestInputs } = require('../utils')
 
// main function that will be executed by Adobe I/O Runtime
async function main (params) {
  // create a Logger
  const logger = Core.Logger('main', { level: params.LOG_LEVEL || 'info' })
 
  try {
    // 'info' is the default level if not set
    logger.info('Calling the main action')
 
    // log parameters, only if params.LOG_LEVEL === 'debug'
    logger.debug(stringParameters(params))
 
    //check for missing request input parameters and headers
    const requiredParams = [/* add required params */]
    const requiredHeaders = ['Authorization']
    const errorMessage = checkMissingRequestInputs(params, requiredParams, requiredHeaders)
    if (errorMessage) {
      // return and log client errors
      return errorResponse(400, errorMessage, logger)
    }
 
    // Place the real call to a 3rd party endpoint here.
    // In this example, we check if the sku is equal to "Pr-1".
    // If it is, an exception with an out of stock message is thrown.
    
    const response = {statusCode: 200}
    const sku = params.item.sku
    if (sku === "Pr-1") {
      response.body = JSON.stringify({
        op: "exception",
        type: "Magento\\Framework\\GraphQl\\Exception\\GraphQlInputException",
        message: `App Builder Webhook Response: The product with sku "${sku}" is out of stock.`
      })
    } else {
      response.body = JSON.stringify({
        op: "success"
      })
    }
 
    return response
  } catch (error) {
    // log any server errors
    logger.error(error)
    // return with 500
    return errorResponse(500, 'server error', logger)
  }
}
 
exports.main = main

If the product is out of stock, the runtime AppBuilder action returns an exception message. The type is set to GraphQlInputException so that the error message is surfaced in GraphQL responses. The message is visible to the customer.

response.body = JSON.stringify({
  op: "exception",
  type: "Magento\\Framework\\GraphQl\\Exception\\GraphQlInputException",
  message: `App Builder Webhook Response: The product with sku "${sku}" is out of stock.`
})