Scenario 2: Low Stock Threshold
Publisher: User Data Function | Consumer: Eventhouse
Business context
A retail company receives inventory updates from an external warehouse management system (WMS). When a product's stock drops below the reorder threshold, the WMS sends a webhook to a User Data Function in Microsoft Fabric. The function normalizes the payload and publishes a Retail.Inventory.LowStockThreshold Business Event.
Eventhouse automatically stores every event as a queryable record. The inventory team uses KQL to identify which products breach thresholds most frequently, track patterns over time, and build reorder predictions.
The problem without Business Events: The User Data Function would need to call the inventory system, the analytics store, and any notification service directly. Each integration is a hard dependency that breaks if the downstream system changes.
The solution with Business Events: The User Data Function publishes one event. Eventhouse stores it automatically. Any additional consumer can be added later without touching the function.
Architecture
flowchart LR
subgraph External
WMS[Warehouse\nManagement System]
end
subgraph Fabric
UDF[User Data Function\nWebhook receiver]
BE([Retail.Inventory.LowStockThreshold\nBusiness Event])
EH[(Eventhouse\nKQL Database)]
end
WMS -->|HTTP webhook| UDF
UDF -->|Publish event| BE
BE -->|Auto-ingest| EH
Step 1: Create the Business Event
Before publishing any event, define it in Real-Time Hub. Eventhouse integration is enabled by default during this step.
- Go to Real-Time Hub → Business Events → Create.
- Create or select an Event Schema Set. Use
RetailInventoryas the schema set name. You will need this name later when connecting the Event Schema Set to the User Data Function through the connection manager. - Name the event
Retail.Inventory.LowStockThreshold. -
In the schema editor, paste the following JSON:
{ "type": "record", "name": "Retail.Inventory.LowStockThreshold", "fields": [ { "name": "product_id", "type": "string", "doc": "Unique identifier of the product" }, { "name": "product_name", "type": "string", "doc": "Display name of the product" }, { "name": "store_id", "type": "string", "doc": "Identifier of the store or warehouse reporting the condition" }, { "name": "current_stock", "type": "int", "doc": "Current units available at the time of detection" }, { "name": "threshold", "type": "int", "doc": "Minimum stock level that triggered this alert" }, { "name": "supplier_id", "type": "string", "doc": "Identifier of the preferred supplier for reordering" }, { "name": "detected_at", "type": "string", "doc": "Timestamp when the condition was detected, ISO 8601 format" } ] } -
Confirm that Analyze in Eventhouse is enabled. Create a new Eventhouse or select an existing one in your workspace. This creates a dedicated KQL table for this Business Event automatically. No additional ingestion configuration is needed.
- Select Create.
Step 2: Publisher - User Data Function
The User Data Function receives a webhook from the external WMS and publishes the Business Event.
Create the User Data Function
- In your Fabric workspace, select + New item and create a User Data Function named
PublishLowStockEvent. - Inside the UDF item, select New function.
Connect to the schema set
Before writing the code, you need to connect the function to the Event Schema Set.
- In the Home ribbon, select Manage connections.
- In the Connections pane, select + Add connection.
- Search for
RetailInventory, select the schema set, and select Connect. - An alias is automatically generated using the schema set name (
RetailInventoryby default). Add this alias to the@udf.connectiondecorator in your function code. - Close the Manage connections pane.
Function code
Replace the default function code with the following. For full details on publishing Business Events from User Data Functions, see the User Data Function publisher documentation and the end-to-end tutorial.
import fabric.functions as fn
import logging
udf = fn.UserDataFunctions()
# The alias must match the connection configured in Manage connections
@udf.connection(argName="businessEventsClient", alias="RetailInventory")
@udf.function()
def publish_low_stock_event(
businessEventsClient: fn.FabricBusinessEventsClient,
product_id: str,
product_name: str,
store_id: str,
current_stock: int,
threshold: int,
supplier_id: str,
detected_at: str
) -> str:
logging.info("publish_low_stock_event invoked.")
event_data = {
"product_id": product_id,
"product_name": product_name,
"store_id": store_id,
"current_stock": current_stock,
"threshold": threshold,
"supplier_id": supplier_id,
"detected_at": detected_at
}
businessEventsClient.PublishEvent(
type="Retail.Inventory.LowStockThreshold",
event_data=event_data,
data_version="v1"
)
return "Event 'Retail.Inventory.LowStockThreshold' published successfully"
Step 3: Consumer - Eventhouse
Because Eventhouse integration was enabled during Business Event creation, a dedicated KQL table was created automatically in your Eventhouse database. Every published event is ingested into that table with no additional configuration.
Open your Eventhouse KQL database and run the following queries to verify events are flowing and explore the data.
View the most recent events:
Count alerts per store over the last 7 days:
['Retail.Inventory.LowStockThreshold']
| where ingestion_time() > ago(7d)
| summarize AlertCount = count() by store_id
| order by AlertCount desc
Identify products with recurring low stock:
['Retail.Inventory.LowStockThreshold']
| where ingestion_time() > ago(30d)
| summarize Occurrences = count(), AvgStock = avg(current_stock) by product_id, product_name
| where Occurrences > 3
| order by Occurrences desc
For more information, see Eventhouse and Business Events integration.
Step 4: End-to-end test
Once you have the Business Event defined, the User Data Function connected, and the Eventhouse integration active, invoke the function with hardcoded values to verify the full flow.
Call publish_low_stock_event with the following test values:
| Parameter | Value |
|---|---|
product_id |
prod-mx-7821 |
product_name |
Wireless Keyboard |
store_id |
store-mx-042 |
current_stock |
4 |
threshold |
10 |
supplier_id |
sup-451 |
detected_at |
2024-06-22T09:00:00Z |
Then run the following KQL query in your Eventhouse database and confirm the row appears:
['Retail.Inventory.LowStockThreshold']
| where product_id == "prod-mx-7821"
| order by ingestion_time() desc
| take 1
If the row is present, your end-to-end setup is working. You can then configure the external WMS to call the User Data Function endpoint as a webhook.
What happens next
With events persisted in Eventhouse, the inventory team can extend the solution without modifying the publisher.
flowchart LR
BE([Retail.Inventory.LowStockThreshold]) --> EH[(Eventhouse)]
EH --> KQL[KQL queries\nand analysis]
EH --> RTD[Real-Time Dashboard\nlive operational view]
EH --> ML[ML model input\npredictive reorder]
| Extension | What it enables |
|---|---|
| Real-Time Dashboard | Live tile showing current low-stock alerts across all stores |
| KQL scheduled query | Daily summary of products below threshold sent to a report |
| ML model input | Historical event data as training features for reorder prediction |
| Activator rule | Add an alert action without changing the User Data Function |
For full details on querying and visualizing Business Events in Eventhouse, see the Eventhouse integration documentation.