Skip to content
Guides

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.

RequirementValue
Schemehttps
Maximum length2048 characters
UniquenessThe same URL cannot repeat in the current store and seller scope
Successful responseAny 2xx
Timeout15 seconds

Delivery headers

HeaderContent
X-Ecomiq-EventEvent type, such as catalog.product.created
X-Ecomiq-DeliveryDelivery UUID
X-Ecomiq-TimestampAttempt time in Unix seconds
X-Ecomiq-Signaturesha256=<hmac>, only when a secret is configured

Verify the signature

Ecomiqevent senderYour endpointthe receiverPOST with the eventX-Ecomiq-Signature, X-Ecomiq-TimestampRecompute the HMACcompare in constant time200answer before processingIf the signature does not match, drop it: do not process the event

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.

TypeRequired configuration
BasicUsername and Password
BearerToken
OAuth2Public HTTPS TokenUrl, ClientId, and ClientSecret

Delivery and retries

Something changesan order, a productEcomiq sendssigned POSTYour endpointanswers within 15 s2xxdelivery closedAny other status or timeoutit is retriedAnswer 2xx first and process after: the clock is running

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

EventModel
catalog.product.createdProductChangedWebhookModel
catalog.product.updatedProductChangedWebhookModel
catalog.product.deletedProductDeletedWebhookModel
catalog.product.status_changedProductStatusChangedWebhookModel
catalog.stock.changedStockChangedWebhookModel
catalog.inventory.batch_processedInventoryBatchProcessedWebhookModel
catalog.price.changedPriceChangedWebhookModel
catalog.brand.createdBrandWebhookModel
catalog.brand.updatedBrandWebhookModel
catalog.brand.deletedBrandDeletedWebhookModel
catalog.category.createdCategoryWebhookModel
catalog.category.updatedCategoryWebhookModel
catalog.category.deletedCategoryDeletedWebhookModel
catalog.collection.createdCollectionWebhookModel
catalog.collection.updatedCollectionWebhookModel
catalog.collection.deletedCollectionDeletedWebhookModel

Channels

EventModel
channel.listing.publishedListingPublishedWebhookModel
channel.listing.price_changedListingPriceChangedWebhookModel
channel.createdChannelCreatedWebhookModel
channel.sales.enabledChannelStateWebhookModel
channel.deletedChannelDeletedWebhookModel

Customers

EventModel
customer.createdCustomerWebhookModel

Carts

EventModel
cart.createdCartCreatedWebhookModel
cart.updatedCartUpdatedWebhookModel
cart.completedCartCompletedWebhookModel
cart.item_addedCartItemWebhookModel
cart.item_removedCartItemWebhookModel

Orders

EventModel
order.confirmedOrderWebhookModel
order.invoicedOrderWebhookModel
order.in_progressOrderWebhookModel
order.ready_to_pick_upOrderWebhookModel
order.in_transitOrderWebhookModel
order.deliveredOrderWebhookModel
order.cancelledOrderWebhookModel
order.returnedOrderWebhookModel
order.closedOrderWebhookModel
order.invalidatedOrderWebhookModel

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

FieldJSON typeNullableDescription
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)YesSeller identifier when the product belongs to a seller
ProductIdinteger (int64)NoProduct identifier
ProductobjectNoProduct payload
OccurredOnstring (date-time)NoEvent occurrence date
TargetChannelIdsinteger[] (int64)NoChannels targeted by the product event
ChangeTypestringNoWhether the change was created or updated

ProductDeletedWebhookModel

Events: catalog.product.deleted

FieldJSON typeNullableDescription
ProductIdinteger (int64)NoProduct identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)NoSeller identifier
OccurredOnstring (date-time)NoEvent occurrence date

ProductStatusChangedWebhookModel

Events: catalog.product.status_changed

FieldJSON typeNullableDescription
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
ProductIdinteger (int64)NoProduct identifier
SellerIdinteger (int64)NoSeller identifier
IsActivebooleanNoWhether the product is active
OccurredOnstring (date-time)NoEvent occurrence date

StockChangedWebhookModel

Events: catalog.stock.changed

FieldJSON typeNullableDescription
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
ProductIdinteger (int64)NoProduct identifier
VariantIdinteger (int64)NoVariant identifier
LocationIdinteger (int64)NoLocation identifier
AvailableQuantityintegerNoAvailable stock quantity
OccurredOnstring (date-time)NoEvent occurrence date

InventoryBatchProcessedWebhookModel

Events: catalog.inventory.batch_processed

FieldJSON typeNullableDescription
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
ModestringNoBatch adjustment mode
AdjustmentsCountintegerNoNumber of adjusted inventory lines
ProductsCountintegerNoNumber of affected products
VariantsCountintegerNoNumber of affected variants
LocationsCountintegerNoNumber of affected locations
LinesarrayNoAdjusted inventory lines
OccurredOnstring (date-time)NoEvent occurrence date

PriceChangedWebhookModel

Events: catalog.price.changed

FieldJSON typeNullableDescription
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
ProductIdinteger (int64)NoProduct identifier
SellerIdinteger (int64)NoSeller identifier
Pricenumber (decimal)NoCurrent product price
CompareAtPricenumber (decimal)YesOriginal comparison price
CurrencystringNoCurrency code
OccurredOnstring (date-time)NoEvent occurrence date

BrandWebhookModel

Events: catalog.brand.created · catalog.brand.updated

FieldJSON typeNullableDescription
BrandIdinteger (int64)NoBrand identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
NamestringNoBrand name
SlugstringNoBrand slug
IsActivebooleanYesWhether the brand is active
OccurredOnstring (date-time)NoEvent occurrence date

BrandDeletedWebhookModel

Events: catalog.brand.deleted

FieldJSON typeNullableDescription
BrandIdinteger (int64)NoBrand identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
OccurredOnstring (date-time)NoEvent occurrence date

CategoryWebhookModel

Events: catalog.category.created · catalog.category.updated

FieldJSON typeNullableDescription
CategoryIdinteger (int64)NoCategory identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
NamestringNoCategory name
SlugstringNoCategory slug
ParentIdinteger (int64)YesParent category identifier
IsActivebooleanYesWhether the category is active
OccurredOnstring (date-time)NoEvent occurrence date

CategoryDeletedWebhookModel

Events: catalog.category.deleted

FieldJSON typeNullableDescription
CategoryIdinteger (int64)NoCategory identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
OccurredOnstring (date-time)NoEvent occurrence date

CollectionWebhookModel

Events: catalog.collection.created · catalog.collection.updated

FieldJSON typeNullableDescription
CollectionIdinteger (int64)NoCollection identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
NamestringNoCollection name
SlugstringNoCollection slug
DescriptionstringYesCollection description
StatusstringNoCollection status
TypestringNoCollection type
DynamicOperatorstringNoDynamic rule operator
ValidFromstring (date-time)YesCollection start date
ValidTostring (date-time)YesCollection end date
ImageUrlstringYesCollection image URL
OccurredOnstring (date-time)NoEvent occurrence date

CollectionDeletedWebhookModel

Events: catalog.collection.deleted

FieldJSON typeNullableDescription
CollectionIdinteger (int64)NoCollection identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
OccurredOnstring (date-time)NoEvent occurrence date

ListingPublishedWebhookModel

Events: channel.listing.published

FieldJSON typeNullableDescription
ListingIdinteger (int64)NoListing identifier
ChannelIdinteger (int64)NoChannel identifier
ProductIdinteger (int64)NoProduct identifier
ExternalIdstringNoExternal listing identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)NoSeller identifier
OccurredOnstring (date-time)NoEvent occurrence date

ListingPriceChangedWebhookModel

Events: channel.listing.price_changed

FieldJSON typeNullableDescription
ListingIdinteger (int64)NoListing identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)NoSeller identifier
ChannelIdinteger (int64)NoChannel identifier
ProductIdinteger (int64)NoProduct identifier
ExternalIdstringYesExternal listing identifier
OldPricenumber (decimal)NoPrevious listing price
NewPricenumber (decimal)NoNew listing price
OccurredOnstring (date-time)NoEvent occurrence date

ChannelCreatedWebhookModel

Events: channel.created

FieldJSON typeNullableDescription
ChannelIdinteger (int64)NoChannel identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)YesSeller identifier
NamestringNoChannel name
IntegrationIdstringNoProvider identifier
ChannelTypestringNoChannel type
IsPrincipalbooleanNoWhether the channel is the principal channel
OccurredOnstring (date-time)NoEvent occurrence date

ChannelStateWebhookModel

Events: channel.sales.enabled

FieldJSON typeNullableDescription
ChannelIdinteger (int64)NoChannel identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)YesSeller identifier
OccurredOnstring (date-time)NoEvent occurrence date

ChannelDeletedWebhookModel

Events: channel.deleted

FieldJSON typeNullableDescription
ChannelIdinteger (int64)NoChannel identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)YesSeller identifier
ConnectionIdinteger (int64)NoConnection identifier
ProviderIdstringNoProvider identifier
OccurredOnstring (date-time)NoEvent occurrence date

CustomerWebhookModel

Events: customer.created

FieldJSON typeNullableDescription
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
CustomerIdinteger (int64)NoCustomer identifier
EmailstringYesCustomer email
FirstNamestringYesCustomer first name
LastNamestringYesCustomer last name
CustomerTypestringNoCustomer type
CustomerStatusstringNoCustomer status
OccurredOnstring (date-time)NoEvent occurrence date

CartCreatedWebhookModel

Events: cart.created

FieldJSON typeNullableDescription
CartIdinteger (int64)NoCart identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
CurrencyCodestringNoCurrency code
CreatedBystring (uuid)NoActor identifier
OccurredOnstring (date-time)NoEvent occurrence date

CartUpdatedWebhookModel

Events: cart.updated

FieldJSON typeNullableDescription
CartIdinteger (int64)NoCart identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
UpdatedBystring (uuid)NoActor identifier
OccurredOnstring (date-time)NoEvent occurrence date

CartCompletedWebhookModel

Events: cart.completed

FieldJSON typeNullableDescription
CartIdinteger (int64)NoCart identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
TotalAmountnumber (decimal)NoCart total amount
CurrencyCodestringNoCurrency code
CompletedBystring (uuid)NoActor identifier
OccurredOnstring (date-time)NoEvent occurrence date

CartItemWebhookModel — cart.item_added

Events: cart.item_added

FieldJSON typeNullableDescription
CartIdinteger (int64)NoCart identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
CartItemIdinteger (int64)NoCart item identifier
ProductIdinteger (int64)NoProduct identifier
VariantIdinteger (int64)NoVariant identifier
QuantityintegerNoQuantity added
UnitPricenumber (decimal)NoUnit price
ActorIdstring (uuid)YesActor identifier
OccurredOnstring (date-time)NoEvent occurrence date

CartItemWebhookModel — cart.item_removed

Events: cart.item_removed

FieldJSON typeNullableDescription
CartIdinteger (int64)NoCart identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
CartItemIdinteger (int64)NoCart item identifier
ProductIdinteger (int64)NoProduct identifier
VariantIdinteger (int64)NoVariant identifier
QuantityintegerYesAlways null when an item is removed
UnitPricenumber (decimal)YesAlways null when an item is removed
ActorIdstring (uuid)YesActor identifier
OccurredOnstring (date-time)NoEvent 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

FieldJSON typeNullableDescription
OrderIdinteger (int64)NoOrder identifier
TenantIdinteger (int64)NoTenant identifier
StoreIdinteger (int64)NoStore identifier
SellerIdinteger (int64)YesSeller identifier when the order belongs to a seller
CustomerIdinteger (int64)YesCustomer identifier
OrderNumberstringNoOrder number
OrderGroupstringYesOrder group identifier
StatusstringNoLegacy order status for compatibility
StatestringNoCurrent order lifecycle state
FulfillmentStatusstringNoCurrent fulfillment status
ReturnStatusstringNoCurrent return status
CurrencyCodestringNoCurrency code
SubTotalnumber (decimal)NoOrder subtotal
Shippingnumber (decimal)NoShipping amount
Taxnumber (decimal)NoTax amount
Totalnumber (decimal)NoOrder total
EmailstringYesCustomer email
FirstNamestringYesCustomer first name
LastNamestringYesCustomer last name
CancelReasonstringYesCancellation reason
TrackingCodestringYesTracking number
TrackingUrlstringYesTracking URL
CarrierNamestringYesCarrier name
OrderDatestring (date-time)NoOrder date
OccurredOnstring (date-time)NoEvent occurrence date

On this page