Generate content for products
When an admin creates or updates a product, a third-party system is used to generate product description and metadata. For example, the AI system can generate product description based on the product name and other attributes.
Webhook name
observer.catalog_product_save_after
Payloads
The following observer.catalog_product_save_after
payload was obtained from execution of the application code. Some data has been adjusted or deleted for brevity.
Default payload
Configured payload
Copied to your clipboard{"eventName": "catalog_product_save_after","data": {"product": {"store_id": 0,"entity_id": "1","attribute_set_id": "16","type_id": "simple","sku": "Pr-1","name": "Product 1","tax_class_id": "0","description": "<p>Product 1 description</p>","price": "10.00","extension_attributes": {...},"quantity_and_stock_status": {...},"category_ids": {...},"stock_data": {...},"media_gallery": {...},...},"data_object": {...}}}
Copied to your clipboard{"product": {"name": "Product 1","sku": "Pr-1"}}
Configuration
The following configuration contains rules to call the third-party endpoint only if the product full and short description are empty. It helps to avoid unnecessary calls to the third-party system if the required data is already present.
Configuration
webhook.xml (PaaS)
Copied to your clipboard<method name="observer.catalog_product_save_before" type="before"><hooks><batch name="product_generate_content"><hook name="product_generate_content" url="{env:APP_BUILDER_URL}/product-description" timeout="5000" softTimeout="1000" priority="300" required="true" fallbackErrorMessage="The product could not be updated"><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="product.name" source="data.product.name" /><field name="product.sku" source="data.product.sku" /></fields><rules><rule field="product.short_description" operator="isEmpty" /><rule field="product.description" operator="isEmpty" /></rules></hook></batch></hooks></method>
Admin (SaaS)
Copied to your clipboardWebhook method: observer.catalog_product_save_beforeWebhook type: beforeBatch name: product_generate_contentHook name: product_generate_contentURL: <Host>/product-descriptionTimeout: 1000Soft timeout: 5000Fallback Error Message: The product could not be updatedRequired: RequiredActive: YesMethod: POSTDeveloper Console OAuthClient 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 FieldsName: product.nameSource: data.product.nameActive: YesName: product.skuSource: data.product.skuActive: YesHook RulesName: product.short_descriptionOperator: isEmptyActive: YesName: product.descriptionOperator: isEmptyActive: Yes
Copied to your clipboard<method name="observer.catalog_product_save_before" type="before"><hooks><batch name="product_generate_content"><hook name="product_generate_content" url="{env:APP_BUILDER_URL}/product-description" timeout="5000" softTimeout="1000" priority="300" required="true" fallbackErrorMessage="The product could not be updated"><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="product.name" source="data.product.name" /><field name="product.sku" source="data.product.sku" /></fields><rules><rule field="product.short_description" operator="isEmpty" /><rule field="product.description" operator="isEmpty" /></rules></hook></batch></hooks></method>
Copied to your clipboardWebhook method: observer.catalog_product_save_beforeWebhook type: beforeBatch name: product_generate_contentHook name: product_generate_contentURL: <Host>/product-descriptionTimeout: 1000Soft timeout: 5000Fallback Error Message: The product could not be updatedRequired: RequiredActive: YesMethod: POSTDeveloper Console OAuthClient 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 FieldsName: product.nameSource: data.product.nameActive: YesName: product.skuSource: data.product.skuActive: YesHook RulesName: product.short_descriptionOperator: isEmptyActive: YesName: product.descriptionOperator: isEmptyActive: Yes
Endpoint code example
The following code example shows how to implement the webhook on your custom endpoint.
Copied to your clipboardconst axios = require('axios');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 Runtimeasync function main (params) {// create a Loggerconst logger = Core.Logger('main', { level: params.LOG_LEVEL || 'info' })try {// 'info' is the default level if not setlogger.info('Calling the main action')// log parameters, only if params.LOG_LEVEL === 'debug'logger.debug(stringParameters(params))//check for missing request input parameters and headersconst requiredParams = [/* add required params */]const requiredHeaders = ['Authorization']const errorMessage = checkMissingRequestInputs(params, requiredParams, requiredHeaders)if (errorMessage) {// return and log client errorsreturn errorResponse(400, errorMessage, logger)}const { product: product} = params;// Here we are making a request to the AI system to generate description for the provided productconst apiUrl = `${params.AI_API_URL}`;const headers = {'Authorization': `Bearer ${params.AI_API_KEY}`,'Content-Type': 'application/json',};const data = {prompt: 'generate a description for product ' + product.name,temperature: 0.7,max_tokens: 1000,};const result = await axios.post(apiUrl, data, { headers: headers });// Handle the response data hereconsole.log('Data:', result.data);// Fetch the description from the responseconst description = result.data.choices[0].text.trim();// Creates operations to update the product description and short descriptionlet operations = [];operations.push({op: "replace",path: "data/product/short_description",value: description.slice(0, 100)});operations.push({op: "replace",path: "data/product/description",value: `<div data-content-type="row">${description}/div>`});// If no updates is needed the success operation must be returned// operations.push({// op: 'success'// })return {statusCode: 200,body: JSON.stringify(operations)}} catch (error) {// log any server errorslogger.error(error)// return with 500return errorResponse(500, 'server error', logger)}}exports.main = main