Events
How to register an endpoint, verify deliveries, and read each event.
There are 39 events across 6 categories. Each delivery's JSON body is the event's model, with no shared envelope.
Configure the endpoint
Register an endpoint with POST /. On creation, a webhook.endpoint.test event is sent to the URL, and the endpoint is saved only if it returns 2xx.
| Requirement | Value |
|---|---|
| URL | HTTPS, public, up to 2048 characters. It can't resolve to a private or local network. |
| Uniqueness | One URL per store (and per seller, when the token belongs to a seller) |
| Successful response | Any 2xx |
| Timeout | 15 seconds |
Delivery headers
| Header | Content |
|---|---|
X-Ecomiq-Event | Event type, such as catalog.product.created |
X-Ecomiq-Delivery | Delivery UUID |
X-Ecomiq-Timestamp | Attempt time in Unix seconds |
X-Ecomiq-Signature | sha256=<hmac>, when the endpoint has a secret |
Verify the signature
With a secret configured, each delivery carries the HMAC-SHA256 of the raw body as lowercase hex. Compute it over the body exactly as received, before parsing JSON, and discard the event if it doesn't match.
import { createHmac, timingSafeEqual } from "node:crypto"
export function isValidSignature(rawBody: string, header: string, secret: string) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex")
const received = header.replace(/^sha256=/, "")
const a = Buffer.from(expected, "hex")
const b = Buffer.from(received, "hex")
return a.length === b.length && timingSafeEqual(a, b)
}Additional authentication
With auth, each delivery includes credentials in Authorization. With headers, you add fixed headers.
auth.type | auth.config |
|---|---|
basic | Username and Password |
bearer | Token |
oAuth2 | Public HTTPS TokenUrl, ClientId, and ClientSecret; optional Scope and TokenProperty (defaults to access_token) |
Delivery and retries
Return 2xx within 15 seconds and process afterwards. Any other status or a timeout is retried with the same X-Ecomiq-Delivery, so an event can arrive more than once: process it idempotently.
Each successful response resets the failure counter. After 10 consecutive failures the endpoint is paused; fix the receiver and call POST /.
To resend a failed delivery, call POST /. The resend keeps the body and carries a new X-Ecomiq-Delivery.
Available events
Catalog
| Event | Model |
|---|---|
catalog.product.created | ProductChangedWebhookModel |
catalog.product.updated | ProductChangedWebhookModel |
catalog.product.deleted | ProductDeletedWebhookModel |
catalog.product.status_changed | ProductStatusChangedWebhookModel |
catalog.stock.changed | StockChangedWebhookModel |
catalog.inventory.batch_processed | InventoryBatchProcessedWebhookModel |
catalog.price.changed | PriceChangedWebhookModel |
catalog.brand.created | BrandWebhookModel |
catalog.brand.updated | BrandWebhookModel |
catalog.brand.deleted | BrandDeletedWebhookModel |
catalog.category.created | CategoryWebhookModel |
catalog.category.updated | CategoryWebhookModel |
catalog.category.deleted | CategoryDeletedWebhookModel |
catalog.collection.created | CollectionWebhookModel |
catalog.collection.updated | CollectionWebhookModel |
catalog.collection.deleted | CollectionDeletedWebhookModel |
Channels
| Event | Model |
|---|---|
channel.listing.published | ListingPublishedWebhookModel |
channel.listing.price_changed | ListingPriceChangedWebhookModel |
channel.created | ChannelCreatedWebhookModel |
channel.sales.enabled | ChannelStateWebhookModel |
channel.deleted | ChannelDeletedWebhookModel |
Operations
| Event | Model |
|---|---|
operation.finished | OperationWebhookModel |
Customers
| Event | Model |
|---|---|
customer.created | CustomerWebhookModel |
Carts
| Event | Model |
|---|---|
cart.created | CartCreatedWebhookModel |
cart.updated | CartUpdatedWebhookModel |
cart.completed | CartCompletedWebhookModel |
cart.item_added | CartItemWebhookModel |
cart.item_removed | CartItemWebhookModel |
Orders
| Event | Model |
|---|---|
order.created | OrderWebhookModel |
order.confirmed | OrderWebhookModel |
order.invoiced | OrderWebhookModel |
order.in_progress | OrderWebhookModel |
order.ready_to_pick_up | OrderWebhookModel |
order.in_transit | OrderWebhookModel |
order.delivered | OrderWebhookModel |
order.cancelled | OrderWebhookModel |
order.returned | OrderWebhookModel |
order.closed | OrderWebhookModel |
order.invalidated | OrderWebhookModel |
Payload fields
Fields use camelCase. IDs are strings: store them as they are, without converting them to numbers.
ProductChangedWebhookModel
Events: catalog.product.created · catalog.product.updated
| Field | JSON type | Nullable | Description |
|---|---|---|---|
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | Yes | Identifier of the seller that owns the product |
productId | string | No | Product identifier |
product | object | No | Product payload |
occurredOn | string (date-time) | No | Event occurrence date |
targetChannelIds | string[] | No | Channels targeted by the product event |
changeType | string | No | Whether the change was created or updated |
ProductDeletedWebhookModel
Events: catalog.product.deleted
| Field | JSON type | Nullable | Description |
|---|---|---|---|
productId | string | No | Product identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | No | Seller identifier |
occurredOn | string (date-time) | No | Event occurrence date |
ProductStatusChangedWebhookModel
Events: catalog.product.status_changed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
productId | string | No | Product identifier |
sellerId | string | No | Seller identifier |
isActive | boolean | No | Whether the product is active |
occurredOn | string (date-time) | No | Event occurrence date |
StockChangedWebhookModel
Events: catalog.stock.changed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
productId | string | No | Product identifier |
variantId | string | No | Variant identifier |
locationId | string | No | Location identifier |
availableQuantity | integer | No | Available stock quantity |
occurredOn | string (date-time) | No | Event occurrence date |
InventoryBatchProcessedWebhookModel
Events: catalog.inventory.batch_processed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
mode | string | No | Batch adjustment mode |
adjustmentsCount | integer | No | Number of adjusted inventory lines |
productsCount | integer | No | Number of affected products |
variantsCount | integer | No | Number of affected variants |
locationsCount | integer | No | Number of affected locations |
lines | array | No | Adjusted inventory lines |
occurredOn | string (date-time) | No | Event occurrence date |
PriceChangedWebhookModel
Events: catalog.price.changed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
productId | string | No | Product identifier |
sellerId | string | No | Seller identifier |
price | number (decimal) | No | Current product price |
compareAtPrice | number (decimal) | Yes | Original comparison price |
currency | string | No | Currency code |
occurredOn | string (date-time) | No | Event occurrence date |
BrandWebhookModel
Events: catalog.brand.created · catalog.brand.updated
| Field | JSON type | Nullable | Description |
|---|---|---|---|
brandId | string | No | Brand identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
name | string | No | Brand name |
slug | string | No | Brand slug |
isActive | boolean | Yes | Whether the brand is active |
occurredOn | string (date-time) | No | Event occurrence date |
BrandDeletedWebhookModel
Events: catalog.brand.deleted
| Field | JSON type | Nullable | Description |
|---|---|---|---|
brandId | string | No | Brand identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
occurredOn | string (date-time) | No | Event occurrence date |
CategoryWebhookModel
Events: catalog.category.created · catalog.category.updated
| Field | JSON type | Nullable | Description |
|---|---|---|---|
categoryId | string | No | Category identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
name | string | No | Category name |
slug | string | No | Category slug |
parentId | string | Yes | Parent category identifier |
isActive | boolean | Yes | Whether the category is active |
occurredOn | string (date-time) | No | Event occurrence date |
CategoryDeletedWebhookModel
Events: catalog.category.deleted
| Field | JSON type | Nullable | Description |
|---|---|---|---|
categoryId | string | No | Category identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
occurredOn | string (date-time) | No | Event occurrence date |
CollectionWebhookModel
Events: catalog.collection.created · catalog.collection.updated
| Field | JSON type | Nullable | Description |
|---|---|---|---|
collectionId | string | No | Collection identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
name | string | No | Collection name |
slug | string | No | Collection slug |
description | string | Yes | Collection description |
status | string | No | Collection status |
type | string | No | Collection type |
dynamicOperator | string | No | Dynamic rule operator |
validFrom | string (date-time) | Yes | Collection start date |
validTo | string (date-time) | Yes | Collection end date |
imageUrl | string | Yes | Collection image URL |
occurredOn | string (date-time) | No | Event occurrence date |
CollectionDeletedWebhookModel
Events: catalog.collection.deleted
| Field | JSON type | Nullable | Description |
|---|---|---|---|
collectionId | string | No | Collection identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
occurredOn | string (date-time) | No | Event occurrence date |
ListingPublishedWebhookModel
Events: channel.listing.published
| Field | JSON type | Nullable | Description |
|---|---|---|---|
listingId | string | No | Listing identifier |
channelId | string | No | Channel identifier |
productId | string | No | Product identifier |
externalId | string | No | External listing identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | No | Seller identifier |
occurredOn | string (date-time) | No | Event occurrence date |
ListingPriceChangedWebhookModel
Events: channel.listing.price_changed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
listingId | string | No | Listing identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | No | Seller identifier |
channelId | string | No | Channel identifier |
productId | string | No | Product identifier |
externalId | string | Yes | External listing identifier |
oldPrice | number (decimal) | No | Previous listing price |
newPrice | number (decimal) | No | New listing price |
occurredOn | string (date-time) | No | Event occurrence date |
ChannelCreatedWebhookModel
Events: channel.created
| Field | JSON type | Nullable | Description |
|---|---|---|---|
channelId | string | No | Channel identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | Yes | Seller identifier |
name | string | No | Channel name |
integrationId | string | No | Provider identifier |
channelType | string | No | Channel type |
isPrincipal | boolean | No | Whether the channel is the principal channel |
occurredOn | string (date-time) | No | Event occurrence date |
ChannelStateWebhookModel
Events: channel.sales.enabled
| Field | JSON type | Nullable | Description |
|---|---|---|---|
channelId | string | No | Channel identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | Yes | Seller identifier |
occurredOn | string (date-time) | No | Event occurrence date |
ChannelDeletedWebhookModel
Events: channel.deleted
| Field | JSON type | Nullable | Description |
|---|---|---|---|
channelId | string | No | Channel identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | Yes | Seller identifier |
connectionId | string | No | Connection identifier |
providerId | string | No | Provider identifier |
occurredOn | string (date-time) | No | Event occurrence date |
OperationWebhookModel
Events: operation.finished
| Field | JSON type | Nullable | Description |
|---|---|---|---|
id | string | No | Operation identifier |
type | string | No | Operation type |
resource | string | No | Resource |
status | string | No | Terminal status |
totalRows | integer | No | Total rows |
succeededRows | integer | No | Successful rows |
failedRows | integer | No | Failed rows |
completedOn | string (date-time) | No | Completion date |
CustomerWebhookModel
Events: customer.created
| Field | JSON type | Nullable | Description |
|---|---|---|---|
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
customerId | string | No | Customer identifier |
email | string | Yes | Customer email |
firstName | string | Yes | Customer first name |
lastName | string | Yes | Customer last name |
customerType | string | No | Customer type |
customerStatus | string | No | Customer status |
occurredOn | string (date-time) | No | Event occurrence date |
CartCreatedWebhookModel
Events: cart.created
| Field | JSON type | Nullable | Description |
|---|---|---|---|
cartId | string | No | Cart identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
currencyCode | string | No | Currency code |
createdBy | string (uuid) | No | Actor identifier |
occurredOn | string (date-time) | No | Event occurrence date |
CartUpdatedWebhookModel
Events: cart.updated
| Field | JSON type | Nullable | Description |
|---|---|---|---|
cartId | string | No | Cart identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
updatedBy | string (uuid) | No | Actor identifier |
occurredOn | string (date-time) | No | Event occurrence date |
CartCompletedWebhookModel
Events: cart.completed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
cartId | string | No | Cart identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
totalAmount | number (decimal) | No | Cart total amount |
currencyCode | string | No | Currency code |
completedBy | string (uuid) | No | Actor identifier |
occurredOn | string (date-time) | No | Event occurrence date |
CartItemWebhookModel — cart.item_added
Events: cart.item_added
| Field | JSON type | Nullable | Description |
|---|---|---|---|
cartId | string | No | Cart identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
cartItemId | string | No | Cart item identifier |
productId | string | No | Product identifier |
variantId | string | No | Variant identifier |
quantity | integer | No | Quantity added |
unitPrice | number (decimal) | No | Unit price |
actorId | string (uuid) | Yes | Actor identifier |
occurredOn | string (date-time) | No | Event occurrence date |
CartItemWebhookModel — cart.item_removed
Events: cart.item_removed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
cartId | string | No | Cart identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
cartItemId | string | No | Cart item identifier |
productId | string | No | Product identifier |
variantId | string | No | Variant identifier |
quantity | integer | Yes | Always null when an item is removed |
unitPrice | number (decimal) | Yes | Always null when an item is removed |
actorId | string (uuid) | Yes | Actor identifier |
occurredOn | string (date-time) | No | Event occurrence date |
OrderWebhookModel
Events: order.created · order.confirmed · order.invoiced · order.in_progress · order.ready_to_pick_up · order.in_transit · order.delivered · order.cancelled · order.returned · order.closed · order.invalidated
| Field | JSON type | Nullable | Description |
|---|---|---|---|
orderId | string | No | Order identifier |
tenantId | string | No | Tenant identifier |
storeId | string | No | Store identifier |
sellerId | string | Yes | Identifier of the seller that owns the order |
customerId | string | Yes | Customer identifier |
orderNumber | string | No | Order number |
orderGroup | string | Yes | Order group identifier |
state | string | No | Current order lifecycle state |
fulfillmentStatus | string | No | Current fulfillment status |
returnStatus | string | No | Current return status |
currencyCode | string | No | Currency code |
subTotal | number (decimal) | No | Order subtotal |
shipping | number (decimal) | No | Shipping amount |
tax | number (decimal) | No | Tax amount |
total | number (decimal) | No | Order total |
email | string | Yes | Customer email |
firstName | string | Yes | Customer first name |
lastName | string | Yes | Customer last name |
cancelReason | string | Yes | Cancellation reason |
trackingCode | string | Yes | Tracking number |
trackingUrl | string | Yes | Tracking URL |
carrierName | string | Yes | Carrier name |
orderDate | string (date-time) | No | Order date |
occurredOn | string (date-time) | No | Event occurrence date |
Last updated: