SnowBee can send real-time notifications to your Azure Service Bus queue or topic when important events occur, such as inventory changes or product updates. This allows your e-commerce platform to stay synchronized with SnowBee without polling the API.
Event notifications are configured per eCom Store in the SnowBee application. To set up Azure Service Bus notifications, you'll need the following information:
your-namespace.servicebus.windows.net)send)All event notifications are sent as JSON messages to your Azure Service Bus queue or topic. Each
message contains a subject property that indicates the event type, and a message body
with event details.
{
"entityId": "019c1234-5678-7abc-8def-123456789012",
"eventId": "019c5678-1234-7abc-8def-987654321098",
"datetime": "2025-01-20T10:30:00.000Z"
}
| Field | Type | Description |
|---|---|---|
entityId | string or null | The ID of the affected entity (product, SKU, order, etc.). May be null for schema-level events. |
eventId | string | A unique UUIDv7 identifier for this event. Use for idempotency and deduplication. |
datetime | string | ISO 8601 timestamp when the event occurred |
The message subject property (accessible via ServiceBusReceivedMessage.Subject in C# or similar in other languages) indicates the event type.
ProductUpdatedTriggered when a product is created or updated. This includes changes to product attributes, SKUs, images, or prices.
Recommended action: Call
GET /products/:productIdto fetch the updated product details.
Subject: ProductUpdated
{
"entityId": "019c1234-5678-7abc-8def-123456789012", // Product ID
"eventId": "019c5678-1234-7abc-8def-987654321098",
"datetime": "2025-01-20T10:30:00.000Z"
}
InventoryUpdatedTriggered when inventory availability changes for a SKU. This can happen due to:
The event is only sent if the SKU is in your eCom store's assortment.
Recommended action: Call
GET /skus/:skuId/inventoryto fetch the updated inventory levels.
Subject: InventoryUpdated
{
"entityId": "019c1234-5678-7abc-8def-123456789012", // SKU ID
"eventId": "019c5678-1234-7abc-8def-987654321098",
"datetime": "2025-01-20T10:30:00.000Z"
}
CampaignUpdatedTriggered when a campaign is created or updated. This includes changes to campaign dates, pricing, or SKU assignments.
Recommended action: Call
GET /campaignsto fetch the updated campaign list.
Subject: CampaignUpdated
{
"entityId": "019c1234-5678-7abc-8def-123456789012", // Campaign ID
"eventId": "019c5678-1234-7abc-8def-987654321098",
"datetime": "2025-01-20T10:30:00.000Z"
}
OrderReadyForPaymentCaptureTriggered when a picking list is closed for an order on an eCom store that has payment capture enabled. This indicates that the order (or part of it) has been picked and is awaiting payment capture before shipment can proceed. An order may have multiple picking lists (e.g., partial fulfillment), each with its own independent capture lifecycle.
Shipment of each picking list is blocked until its payment capture is confirmed. Call GET /orders/:orderId to fetch the order with line-level pickedQuantity and the pickingLists array (which includes each picking list's id and paymentCaptureStatus), then call POST /orders/:orderId/picking_lists/:pickingListId/payment_capture with status CONFIRMED or FAILED.
Required action: Call
GET /orders/:orderIdto get picked quantities, pricing, and picking list IDs, capture payment externally, then callPOST /orders/:orderId/picking_lists/:pickingListId/payment_capturewith the capture result.
Subject: OrderReadyForPaymentCapture
{
"entityId": "019c1234-5678-7abc-8def-123456789012", // Order ID
"eventId": "019c5678-1234-7abc-8def-987654321098",
"datetime": "2025-01-20T10:30:00.000Z"
}
OrderFulfilledTriggered when an invoice is created for an order, indicating the order (or part of it) has been fulfilled and is ready for shipping.
Recommended action: Call
GET /orders/:orderIdto fetch the order with invoice details.
Subject: OrderFulfilled
{
"entityId": "019c1234-5678-7abc-8def-123456789012", // Order ID
"eventId": "019c5678-1234-7abc-8def-987654321098",
"datetime": "2025-01-20T10:30:00.000Z"
}
RetailStoreUpdatedTriggered when a retail store's name, store number, or visit address changes. This event is only sent for retail stores that belong to a company linked to your eCom store via click & collect.
Recommended action: Call
GET /retail_storesto fetch the updated store details.
Subject: RetailStoreUpdated
{
"entityId": "019c1234-5678-7abc-8def-123456789012", // Retail Store ID
"eventId": "019c5678-1234-7abc-8def-987654321098",
"datetime": "2025-01-20T10:30:00.000Z"
}
These events are triggered when product schema data (lookup tables) are updated. The entityId will be null for these events since they affect the entire schema.
| Subject | Description | API Endpoint |
|---|---|---|
BrandsUpdated | A brand was added, modified, or removed | /product_schema/brands |
ProductCategoriesUpdated | A product category was added, modified, or removed | /product_schema/product_categories |
MainProductGroupsUpdated | A main product group was added, modified, or removed | /product_schema/main_product_groups |
SubProductGroupsUpdated | A sub product group was added, modified, or removed | /product_schema/sub_product_groups |
ProductConceptsUpdated | A product concept was added, modified, or removed | /product_schema/product_concepts |
ExclusivityLevelsUpdated | An exclusivity level was added, modified, or removed | /product_schema/exclusivity_levels |
MainActivitiesUpdated | A main activity was added, modified, or removed | /product_schema/main_activities |
UnitOfMeasuresUpdated | A unit of measure was added, modified, or removed | /product_schema/unit_of_measures |
ProductColorsUpdated | A product color was added, modified, or removed | /product_schema/product_colors |
SizesUpdated | A size was added, modified, or removed | /product_schema/sizes |
DynamicAttributesUpdated | A dynamic attribute was added, modified, or removed | /product_schema/dynamic_attributes |
DynamicAttributeOptionsUpdated | A dynamic attribute option was added, modified, or removed | /product_schema/dynamic_attributes_options |
Each event includes a unique eventId (UUIDv7). Store processed event IDs to avoid
reprocessing the same event if it's delivered multiple times (at-least-once delivery).
Events may arrive out of order. Use the datetime field to determine the chronological
order of events. For inventory updates, always fetch the latest state from the API rather than
applying incremental changes.
During bulk imports or updates, you may receive many events in a short period. Consider:
If your message handler fails, the message will be returned to the queue for retry (depending on your Azure Service Bus configuration). Implement proper dead-letter queue handling for messages that consistently fail.
using Azure.Messaging.ServiceBus;
using System.Text.Json;
public class SnowBeeEventHandler
{
private readonly SnowBeeApiClient _apiClient;
public async Task ProcessMessageAsync(ServiceBusReceivedMessage message)
{
var subject = message.Subject;
var body = JsonSerializer.Deserialize<SnowBeeEvent>(message.Body);
switch (subject)
{
case "InventoryUpdated":
await HandleInventoryUpdated(body.EntityId);
break;
case "ProductUpdated":
await HandleProductUpdated(body.EntityId);
break;
case "CampaignUpdated":
await HandleCampaignUpdated();
break;
case "OrderReadyForPaymentCapture":
await HandleOrderReadyForPaymentCapture(body.EntityId);
break;
case "OrderFulfilled":
await HandleOrderFulfilled(body.EntityId);
break;
case "RetailStoreUpdated":
await HandleRetailStoreUpdated(body.EntityId);
break;
case "BrandsUpdated":
case "ProductCategoriesUpdated":
case "SizesUpdated":
// ... other schema events
await HandleSchemaUpdated(subject);
break;
}
}
private async Task HandleInventoryUpdated(string skuId)
{
var inventory = await _apiClient.GetSkuInventory(skuId);
// Update your local inventory cache/database
}
private async Task HandleProductUpdated(string productId)
{
var product = await _apiClient.GetProduct(productId);
// Update your local product catalog
}
private async Task HandleCampaignUpdated()
{
var campaigns = await _apiClient.GetCampaigns();
// Update your local campaign/pricing data
}
private async Task HandleOrderReadyForPaymentCapture(string orderId)
{
var order = await _apiClient.GetOrder(orderId);
// Find picking lists with PENDING payment capture
foreach (var pickingList in order.PickingLists.Where(pl => pl.PaymentCaptureStatus == "PENDING"))
{
// Calculate capture amount from line items using pickedQuantity and pricing
// Capture payment via your payment provider
// Then report capture result to SnowBee:
// POST /orders/{orderId}/picking_lists/{pickingListId}/payment_capture
// { "status": "CONFIRMED", "capturedAmount": "1499.00" }
// or { "status": "FAILED" } if capture failed
await _apiClient.UpdatePaymentCapture(orderId, pickingList.Id, "CONFIRMED", capturedAmount);
}
}
private async Task HandleOrderFulfilled(string orderId)
{
var order = await _apiClient.GetOrder(orderId);
// Process fulfillment, update order status, trigger shipping
}
private async Task HandleRetailStoreUpdated(string retailStoreId)
{
var stores = await _apiClient.GetRetailStores();
// Update your local store data (name, number, address)
}
private async Task HandleSchemaUpdated(string schemaType)
{
// Refresh the relevant schema data
// e.g., reload brands, categories, sizes, etc.
}
}
public record SnowBeeEvent(
string? EntityId,
string EventId,
string Datetime
);
When setting up a new integration, you can trigger events for all entities in your eCom store assortment to populate your local data without polling.
POST /v1/tenants/TENANT_ID/ecom_stores/ECOM_STORE_ID/products/trigger_events
Authorization: Bearer YOUR_ACCESS_TOKEN
Response:
{
"eventsTriggered": 1250,
"message": "Successfully triggered 1250 product events"
}
This triggers ProductUpdated events for all products in your assortment.
POST /v1/tenants/TENANT_ID/ecom_stores/ECOM_STORE_ID/inventory/trigger_events
Authorization: Bearer YOUR_ACCESS_TOKEN
Response:
{
"eventsTriggered": 3500,
"message": "Successfully triggered 3500 inventory events"
}
This triggers InventoryUpdated events for all SKUs in your assortment, allowing
you to sync current inventory levels.