Webhooks — how BulkFlow stays synced
BulkFlow doesn't poll Shopify on a schedule. Instead, Shopify pushes events to BulkFlow in real time via webhooks. Here's exactly what we listen for, why, and how to debug if something looks off.
The full webhook list
| Topic | Why we listen | What we do with it |
|---|---|---|
| orders/paid | Customer paid for an order — time to deduct stock | Subtract units from each line item's bulk pool |
| refunds/create | A refund was issued | Credit units back to the bulk pool (unless already shipped + you disabled refund-credit in Settings) |
| orders/cancelled | Order cancelled before shipping | Credit units back to the bulk pool |
| fulfillments/create | Order is being fulfilled (Plus only — used for multi-location routing) | Note WHICH location is fulfilling so we deduct from the right pool |
| inventory_levels/update | Inventory was changed by ANY app or admin | Audit-log the change so you can spot another app fighting BulkFlow |
| locations/create, locations/update | New Shopify location appeared OR an existing one was renamed | Refresh the location list, re-prompt multi-location setup if appropriate |
| app/uninstalled | You uninstalled BulkFlow | Mark the install row as uninstalled, stop background jobs for this shop |
| app_subscriptions/update | Your plan changed (Shopify's authoritative source) | Update your plan in BulkFlow, fire any tier migration flows |
| customers/data_request | A customer requested their data (GDPR mandatory) | Respond per BulkFlow's privacy policy |
| customers/redact | A customer requested deletion (GDPR mandatory) | We don't store customer PII so this is a no-op acknowledgement |
| shop/redact | Shopify is requesting we delete all data for this shop, ~48h after uninstall | Cascade-delete all data for this shop |
How to check webhook health
Settings → Webhook Health shows the current registered status of each topic.
- ✅ OK — Webhook is registered with Shopify and BulkFlow has received traffic on it within the expected window
- ⚠️ STALE — Registered, but no traffic in 14+ days (could be normal for low-volume webhooks like
locations/update) - ❌ MISSING — Not registered. Click Re-register webhooks to fix.
Common webhook problems
"Webhook is MISSING for a topic"
Click Re-register webhooks. BulkFlow re-issues the registration with Shopify. The fix takes ~5 seconds.
If it keeps coming back as MISSING:
- Check Settings → Connection — is BulkFlow still connected to Shopify? If "Not Connected," click Reconnect.
- Check that your Shopify scopes still include the necessary permissions (read_orders, read_inventory, etc.) — sometimes Shopify removes scopes if a recent app update added new ones you didn't approve.
- If still broken: support@bulkflow.app with the topic name and we'll trace it.
"Webhook returned 401"
This means BulkFlow rejected a webhook from Shopify because the HMAC signature didn't validate. Causes:
- Your Shopify app's Client Secret in the Partner Dashboard doesn't match what's in BulkFlow's config
- You rotated the secret in Partner Dashboard without updating BulkFlow
- A bug in our HMAC verification (rare — we have tests)
The fix is usually: in Partner Dashboard → BulkFlow → click "Reveal Secret" → copy → paste into BulkFlow's env config → redeploy → click Re-register.
"My orders aren't deducting stock"
Walk this checklist:
- Webhook Health — is
orders/paidshowing OK? - Movements — go to the relevant time range; is there a row for the order at all?
- If yes, check the deduction reason. "Skipped — no managed bulk pool" means the variant isn't bound to any pool. Go to Variant Mapping (Shopify Health) and bind it.
- If no, the webhook didn't fire. Check Shopify Admin → Notifications → Webhooks → is the order actually paid (not just placed)?
- If still nothing: support@bulkflow.app with the order number.
HMAC verification (technical)
Every webhook BulkFlow receives is HMAC-verified against your app's Client Secret. If a webhook can't be cryptographically tied to Shopify, we reject it with 401.
This is the security boundary that prevents:
- Random internet traffic claiming to be Shopify
- A leaked webhook URL being used to inject fake inventory deductions
If you ever rotate your Client Secret in Partner Dashboard, BulkFlow will reject all subsequent webhooks as 401 until you update the secret in BulkFlow's config.
Performance notes
- Webhooks process in <100ms in the common case
- Deductions are idempotent — Shopify retries on non-200 responses, and BulkFlow safely no-ops on duplicate webhook IDs
- Background jobs (digest emails, drift detection) run separately on a 5-minute cadence and don't depend on webhook timing