Webhooks
Status: Implemented. Delivery is handled by
NotificationService(inventory/services/notifications.py) and persisted via theNotificationChannel/NotificationDeliverymodels — payloads are HMAC-SHA256 signed and retried with exponential backoff.
Webhooks let external systems receive a notification when a monitoring rule
raises an alert for a product, instead of polling. They are not a generic
change feed: a webhook fires only when a MonitoringRule opens a new EventLog
(e.g. stock crossed a threshold, or a batch is approaching expiry). Delivery
timing is described below.
Event Types
The event type is the rule's trigger type (inventory/constants.py):
| Type | Raised when |
|---|---|
THRESHOLD |
A product's stock crosses a configured min/max threshold |
DATE_OFFSET |
A batch is within the configured window of its expiry date |
TEST |
A manual test delivery from the dashboard |
Payload Format
The delivered body (NotificationService._send_webhook) is:
{
"event_id": "550e8400-...",
"type": "THRESHOLD",
"company_id": "550e8400-...",
"product": {
"id": "550e8400-...",
"sku": "WIDGET-A",
"name": "Widget A"
},
"payload": {
"message": "Stock quantity 5 is below minimum threshold of 10.",
"severity": "WARNING",
"rule_id": "550e8400-...",
"batch_id": null,
"status": "OPEN"
},
"created_at": "2026-04-22T14:30:00Z"
}
Delivery timing
There is no real-time scheduler in the default stack. Alerts are evaluated —
and their webhooks/emails delivered, plus any due retries flushed — when
python manage.py check_rules runs. Schedule it (cron, Celery beat, a k8s
CronJob, …) at whatever cadence your expiry/threshold SLAs require.
Configuration
Webhooks are configurable per company via the Authority Dashboard:
- URL: an
httporhttpsendpoint to receive POST requests - Events: the rule trigger types the channel subscribes to
- Secret: HMAC-SHA256 signing key for payload verification
- Retry policy: up to 5 attempts with exponential backoff (
2^n × 60s), retried on the nextcheck_rulesrun
Security
- Payloads are signed with the
X-PI-Signature: sha256=<hex>header (HMAC-SHA256 of the raw JSON body) - Both
httpandhttpsURLs are accepted; there is no IP allowlist, but URLs resolving to private / loopback / link-local addresses are refused at save time and again at send time (SSRF guard) - Failed deliveries are recorded in
NotificationDeliveryfor debugging
Integration Pattern
Webhooks are the recommended way to react to inventory events. Polling the
existing API remains possible as a fallback (send the key as an X-Api-Key
header rather than in the URL):