Add a chat assistant to your Drupal Commerce store with automatic product sync.
You're in the developer setup guide. Looking for an overview of what Emporiqa does for your Drupal Commerce store? See the integration page.
For store owners. Install, configure, sync — with step-by-step screenshots.
StartFor developers. Architecture, Drush commands, webhook payload, and alter hooks.
ReadInstall the module, paste two credentials, run one sync. Your chat widget goes live on your store.
First, install and enable the module. Pull it in with Composer from drupal.org, then enable it via Drush:
composer require drupal/emporiqa
drush en emporiqa
Or use Extend → Install new module in the admin UI. On install, the module auto-detects your product fields and creates the Emporiqa display mode.
Go to Configuration → Web services → Emporiqa (/admin/config/services/emporiqa). Paste your Store ID and Connection Secret — both come from your Emporiqa dashboard under Store Settings → Integration. Save the form.
Still on the Settings tab, expand the Sync Settings section. Confirm Sync Products and Sync Pages are enabled, pick which Enabled Languages to include in webhook payloads, and optionally restrict the Product Types to sync (leave all unchecked to sync every product type).
Expand the Product Field Mapping section. The module auto-detects your category, brand, image, stock, and variation attribute fields on install — confirm the right fields were picked. If you use the Commerce Stock module, stock levels are read automatically. Save the form after any change.
Pages are synced using Drupal's native display modes. For each content type you want synced as a page, go to Structure → Content types → [Your type] → Manage display, open the Emporiqa tab, and drag the fields you want included (e.g. Body) into the active area. Save. The rendered output of that display becomes the page content sent to Emporiqa.
Open the Sync tab on the settings page (/admin/config/services/emporiqa/sync). Click Test Connection first, then Sync All to send all products and pages. A batch progress bar shows the items going over. Larger catalogs take a few minutes.
Prefer the command line? Run drush emporiqa:sync-all.
Open your storefront. The chat bubble appears in the bottom-right corner. Click it, ask a product question, and watch the salesperson answer from your catalog.
Need to customize sync behavior, add tier prices, or integrate with a custom ERP?
See Developer DocsArchitecture, configuration reference, Drush commands, webhook payload schema, and alter hooks.
The module listens to entity CRUD hooks (hook_entity_insert, hook_entity_update, hook_entity_delete) for Commerce products, variations, and configured node types. Events are pushed onto a dedicated Drupal queue worker and delivered to Emporiqa asynchronously, so saving a product never blocks on the network.
All translations and channels for a single product or page consolidate into one webhook event. Variations cascade up: editing a variation triggers a parent product re-sync so search always sees the full catalog state. Conversion tracking is handled by an event subscriber on Commerce order state transitions (OrderCompleteSubscriber).
Webhook delivery retries up to 3 times on transient errors (HTTP 429, 502, 503, 504) and network failures, with linear backoff between attempts. The queue is processed by Drupal's cron — if the queue grows past 10,000 items, a warning is logged (typically meaning the webhook endpoint is unreachable or cron isn't running).
| Drupal | 10.3+ or 11.x |
| Drupal Commerce | 2.40+ or 3.x (commerce_product required) |
| PHP | 8.1 or higher |
| Emporiqa account | Free sandbox or paid subscription |
All settings live at Configuration → Web services → Emporiqa (/admin/config/services/emporiqa). The Sync tab lives at /admin/config/services/emporiqa/sync. The administer emporiqa permission is required.
| Setting | Description |
|---|---|
| Store ID | Your unique store identifier from the Emporiqa dashboard. |
| Connection Secret | Shared secret used for HMAC-SHA256 signing of webhook and order tracking requests. |
| Webhook URL | Emporiqa webhook endpoint. Default works for most stores. |
Most stores don't need to change these.
| Setting | Description |
|---|---|
| Sync Products | Enable/disable Commerce product synchronization. |
| Sync Pages | Enable/disable node synchronization as pages. |
| Languages | Which Drupal languages to sync. The module consolidates translations into one payload per entity. |
| Product Types | Which Commerce product types to sync. |
| Content Types | Which node content types to sync as pages (via the Emporiqa display mode). |
| Order Transitions | Commerce order transitions that trigger conversion tracking. Default: place. |
| Batch Size | Items per webhook request during full sync (1-100). |
On install, the module auto-detects your product fields (category, brand, images, stock, attributes) from the Commerce product and variation entity schemas. The mappings are stored in config and can be adjusted on the settings form.
Pages are synced using Drupal's native display modes — no custom code needed. The module creates an Emporiqa display on install and enables it for common content types (page, article).
The rendered output of the Emporiqa display becomes the page content sent to the webhook. Different content types can expose different fields, all configured through Drupal's admin — no module code touched. The same approach works for product descriptions when the Emporiqa display is enabled on a product type.
/emporiqa/api/cart/*. Checkout happens on your Commerce checkout page; the widget redirects when the customer is ready.hook_emporiqa_channels_alter.Products and pages sync via webhooks on create, update, and delete using Drupal entity hooks, delivered through a dedicated queue worker for reliability and retry.
The widget is attached automatically via an asset library. Language and currency are detected from the Drupal request context.
Products and pages sync with translated names, descriptions, categories, and attributes from all configured Drupal languages, consolidated into one payload per entity.
Full parent/child sync for Commerce variations. Variation changes trigger a parent product re-sync. Deleting a variation sends a delete event and re-syncs the parent.
Customers can add, update, remove, and checkout from the chat. Routes: /emporiqa/api/cart/*, working directly with commerce_cart.
On install, the module discovers your category, brand, image, stock, and attribute fields on product and variation entities. Zero-config for standard Commerce setups.
Trigger a full sync from the Sync tab with Drupal's batch API. Test Connection sends a dry-run payload for validation without storing data.
An event subscriber on Commerce order state transitions sends an order.completed event, linking chat sessions to revenue on your dashboard.
Each product or page is sent as a single webhook event containing all languages and channels. Translatable fields are nested by channel and language code.
{
"type": "product.updated",
"data": {
"identification_number": "product-42",
"sku": "product-42",
"channels": [""],
"names": {"": {"en": "Hiking Boots", "de": "Wanderschuhe"}},
"descriptions": {"": {"en": "Waterproof leather boots...", "de": "Wasserdichte Lederstiefel..."}},
"links": {"": {"en": "https://store.com/hiking-boots", "de": "https://store.com/de/wanderschuhe"}},
"categories": {"": {"en": ["Footwear > Hiking"], "de": ["Schuhe > Wandern"]}},
"attributes": {"": {"en": {"Material": "Leather"}, "de": {"Material": "Leder"}}},
"brands": {"": "TrailPeak"},
"prices": {"": [{"currency": "EUR", "current_price": 129.99, "regular_price": 149.99}]},
"images": {"": ["https://store.com/files/boots-front.jpg"]},
"availability_statuses": {"": "available"},
"stock_quantities": {"": 42},
"is_parent": false,
"parent_sku": null,
"variation_attributes": {}
}
}
{
"type": "page.created",
"data": {
"identification_number": "page-45",
"channels": [""],
"titles": {"": {"en": "Shipping Policy", "de": "Versandrichtlinien"}},
"contents": {"": {"en": "<p>Rendered Emporiqa display...</p>", "de": "<p>Gerenderte Emporiqa-Anzeige...</p>"}},
"links": {"": {"en": "https://store.com/shipping-policy", "de": "https://store.com/de/versandrichtlinien"}}
}
}
| Type | Pattern | Fields |
|---|---|---|
| Translatable | {channel: {lang: value}} |
names, descriptions, links, categories, attributes, variation_attributes, titles, contents |
| Shared | {channel: value} |
brands, prices, images, availability_statuses, stock_quantities |
| Flat | Direct value | identification_number, sku, channels, is_parent, parent_sku |
The module provides Drupal alter hooks you can implement in a custom module (mymodule.module) to control sync behavior, modify payloads, and extend order tracking and cart operations. Full documentation lives in emporiqa.api.php.
| Hook | Description |
|---|---|
hook_emporiqa_entity_sync_alter |
Set $sync = FALSE to skip a specific entity (product, variation, or page). |
hook_emporiqa_data_alter |
Modify the formatted payload before it is queued for delivery. |
hook_emporiqa_channels_alter |
Assign products to sales channels (e.g. b2b, retail, amazon). |
hook_emporiqa_price_entry_alter |
Modify an individual price entry (tax-inclusive/exclusive, extras). |
hook_emporiqa_tier_prices_alter |
Provide tier pricing (volume discounts) from custom fields or external pricing engines. |
hook_emporiqa_checkout_route_alter |
Override the route used for the chat cart's checkout URL. |
hook_emporiqa_cart_alter |
Cancel or modify cart operations (add, remove, update, clear, checkout). |
hook_emporiqa_order_tracking_alter |
Provide order tracking data from a custom ERP or order system. |
/**
* Implements hook_emporiqa_entity_sync_alter().
*/
function mymodule_emporiqa_entity_sync_alter(
bool &$sync,
\Drupal\Core\Entity\EntityInterface $entity,
string $entity_type,
string $operation,
): void {
if (
$entity_type === 'page'
&& $entity instanceof \Drupal\node\NodeInterface
&& str_contains($entity->getTitle(), '[DRAFT]')
) {
$sync = FALSE;
}
}
/**
* Implements hook_emporiqa_price_entry_alter().
*/
function mymodule_emporiqa_price_entry_alter(
array &$entry,
array $context,
): void {
$entry['price_incl_tax'] = round($entry['current_price'] * 1.20, 2);
$entry['price_excl_tax'] = $entry['current_price'];
}
/**
* Implements hook_emporiqa_tier_prices_alter().
*/
function mymodule_emporiqa_tier_prices_alter(
array &$tier_prices,
array $context,
): void {
$variation = $context['variation'];
if ($variation->hasField('field_tier_prices')) {
foreach ($variation->get('field_tier_prices') as $item) {
$tier_prices[] = [
'min_quantity' => (int) $item->min_qty,
'price' => (float) $item->price,
'currency' => $context['currency'],
];
}
}
}
/**
* Implements hook_emporiqa_cart_alter().
*/
function mymodule_emporiqa_cart_alter(array &$context): void {
if ($context['operation'] === 'add') {
$total_qty = 0;
foreach ($context['items'] as $item) {
$total_qty += $item['quantity'] ?? 1;
}
if ($total_qty > 10) {
$context['cancel'] = TRUE;
$context['cancel_message'] = 'Maximum 10 items per operation.';
}
}
}
Clear the cache with drush cr after adding a new hook implementation so Drupal picks it up.
The module provides Drush commands for deployment pipelines, cron jobs, and debugging.
# Full sync (products + pages)
drush emporiqa:sync-all # alias: em:sa
# Sync products only
drush emporiqa:sync-products # alias: em:sp
# Sync pages only
drush emporiqa:sync-pages # alias: em:spg
# Test the webhook connection (dry run with a real product)
drush emporiqa:test-connection # alias: em:tc
# Adjust batch size for large catalogs
drush emporiqa:sync-products --batch-size=25
Drush runs on the command line and doesn't know your site's public URL. For correct product and page URLs in webhook payloads, create drush/drush.yml in your Drupal root:
options:
uri: 'https://your-store-domain.com'
Emporiqa can look up order status on behalf of customers during chat conversations. The module registers a signed endpoint that Emporiqa calls when a customer asks about their order.
POST /emporiqa/api/order/tracking
The endpoint is rate-limited to 50 requests per hour per IP using Drupal's Flood API.
If Commerce Order is installed, the module looks up orders by order number and returns status, items, and totals. No code needed. Email verification is always required — the customer must provide the email on the order before any data is returned.
The endpoint is always active once the module is installed. To enable order lookup from chat, set the Order Tracking API URL in your Emporiqa dashboard (Store Settings → Integration) to:
https://your-store.com/emporiqa/api/order/tracking
For non-Commerce order systems, implement hook_emporiqa_order_tracking_alter() in a custom module:
/**
* Implements hook_emporiqa_order_tracking_alter().
*/
function mymodule_emporiqa_order_tracking_alter(
?array &$response,
string $order_identifier,
array $body,
): void {
$order = my_erp_get_order($order_identifier);
if ($order) {
$response = [
'order_id' => $order->id,
'status' => $order->status,
'placed_at' => $order->created_at,
'items' => $order->items,
];
}
}
Requests are authenticated using the same HMAC-SHA256 connection secret. Requests older than 5 minutes are rejected. See the Webhook Setup Guide for the full request/response schema.
Order tracking is optional. Without the dashboard URL configured, order questions route to the Customer Support agent instead.
drush emporiqa:sync-products to force a full sync.drush ws --filter=emporiqa.drush cr.drush emporiqa:test-connection to validate the connection with a dry-run payload.drush queue:list. Process stuck items with drush queue:run emporiqa_webhook.drush ws --filter=emporiqa.drush emporiqa:sync-products.Webhook architecture, queue workers, variations, and customization with alter hooks.
Cross-language search, 65+ languages, how translations sync.
Evaluation checklist: data sync, handoff, pricing, languages, attribution.
Prove chat ROI: session to cart to purchase attribution.