Webhooks
Webhook endpoint setup, delivery behavior, and event payload fields.
You can subscribe to 37 events across 5 categories. Each JSON body matches the model shown for that event; there is no shared envelope.
Configure the endpoint
Register an endpoint for the store with POST /v1/webhooks/endpoints. The URL must use HTTPS, be publicly reachable, and not resolve to a private or local network.
On creation, Ecomiq sends webhook.endpoint.test. The endpoint is saved only when that request returns 2xx within the 15-second timeout.
| Requirement | Value |
|---|---|
| Scheme | https |
| Maximum length | 2048 characters |
| Uniqueness | The same URL cannot repeat in the current store and seller scope |
| 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>, only when a secret is configured |
Verify the signature
When you configure a secret, Ecomiq signs the raw body with HMAC-SHA256 and encodes the result as lowercase hexadecimal. Verify the signature before parsing JSON.
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
You can configure credentials for Ecomiq to include on every request. You can also add fixed headers with headers.
| Type | Required configuration |
|---|---|
Basic | Username and Password |
Bearer | Token |
OAuth2 | Public HTTPS TokenUrl, ClientId, and ClientSecret |
Delivery and retries
Delivery is at least once. A failure can repeat the same event with the same X-Ecomiq-Delivery; process it idempotently and store that identifier only after your work completes.
A successful response resets the failure counter. After 10 consecutive failed attempts, Ecomiq pauses the endpoint. Fix the receiver, then call POST /v1/webhooks/endpoints/{id}/enable.
Re-enqueue a failed delivery with POST /v1/webhooks/deliveries/{id}/retry. A manual retry keeps the original body and creates 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 |
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.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
The current runtime uses PascalCase names and serializes snowflake IDs as JSON int64 numbers. In JavaScript, use a parser that preserves 64-bit integers: Number can lose precision.
ProductChangedWebhookModel
Events: catalog.product.created · catalog.product.updated
| Field | JSON type | Nullable | Description |
|---|---|---|---|
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | Yes | Seller identifier when the product belongs to a seller |
ProductId | integer (int64) | No | Product identifier |
Product | object | No | Product payload |
OccurredOn | string (date-time) | No | Event occurrence date |
TargetChannelIds | integer[] (int64) | 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 | integer (int64) | No | Product identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | No | Seller identifier |
OccurredOn | string (date-time) | No | Event occurrence date |
ProductStatusChangedWebhookModel
Events: catalog.product.status_changed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
ProductId | integer (int64) | No | Product identifier |
SellerId | integer (int64) | 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 | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
ProductId | integer (int64) | No | Product identifier |
VariantId | integer (int64) | No | Variant identifier |
LocationId | integer (int64) | 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 | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
ProductId | integer (int64) | No | Product identifier |
SellerId | integer (int64) | 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 | integer (int64) | No | Brand identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Brand identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Category identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
Name | string | No | Category name |
Slug | string | No | Category slug |
ParentId | integer (int64) | 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 | integer (int64) | No | Category identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Collection identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Collection identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
OccurredOn | string (date-time) | No | Event occurrence date |
ListingPublishedWebhookModel
Events: channel.listing.published
| Field | JSON type | Nullable | Description |
|---|---|---|---|
ListingId | integer (int64) | No | Listing identifier |
ChannelId | integer (int64) | No | Channel identifier |
ProductId | integer (int64) | No | Product identifier |
ExternalId | string | No | External listing identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | No | Seller identifier |
OccurredOn | string (date-time) | No | Event occurrence date |
ListingPriceChangedWebhookModel
Events: channel.listing.price_changed
| Field | JSON type | Nullable | Description |
|---|---|---|---|
ListingId | integer (int64) | No | Listing identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | No | Seller identifier |
ChannelId | integer (int64) | No | Channel identifier |
ProductId | integer (int64) | 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 | integer (int64) | No | Channel identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | 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 | integer (int64) | No | Channel identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | Yes | Seller identifier |
OccurredOn | string (date-time) | No | Event occurrence date |
ChannelDeletedWebhookModel
Events: channel.deleted
| Field | JSON type | Nullable | Description |
|---|---|---|---|
ChannelId | integer (int64) | No | Channel identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | Yes | Seller identifier |
ConnectionId | integer (int64) | No | Connection identifier |
ProviderId | string | No | Provider identifier |
OccurredOn | string (date-time) | No | Event occurrence date |
CustomerWebhookModel
Events: customer.created
| Field | JSON type | Nullable | Description |
|---|---|---|---|
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
CustomerId | integer (int64) | 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 | integer (int64) | No | Cart identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Cart identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Cart identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | 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 | integer (int64) | No | Cart identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
CartItemId | integer (int64) | No | Cart item identifier |
ProductId | integer (int64) | No | Product identifier |
VariantId | integer (int64) | 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 | integer (int64) | No | Cart identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
CartItemId | integer (int64) | No | Cart item identifier |
ProductId | integer (int64) | No | Product identifier |
VariantId | integer (int64) | 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.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 | integer (int64) | No | Order identifier |
TenantId | integer (int64) | No | Tenant identifier |
StoreId | integer (int64) | No | Store identifier |
SellerId | integer (int64) | Yes | Seller identifier when the order belongs to a seller |
CustomerId | integer (int64) | Yes | Customer identifier |
OrderNumber | string | No | Order number |
OrderGroup | string | Yes | Order group identifier |
Status | string | No | Legacy order status for compatibility |
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 |