Schedule icon
List icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon

Shopify Product Stockout Sentinel

Check Shopify product variant inventory on a schedule, change nothing in the store, and alert Slack before a variant runs out of stock.

Categories
BusinessData

A product variant can quietly sell down to zero between the merchandising team's last glance at the dashboard and the next one, and nothing in Shopify pages anyone until a customer tries to buy something that is no longer there. This blueprint turns a scheduled, read-only product list into a Day 2 inventory governance gate: io.kestra.plugin.shopify.products.List returns every matching product's variants and their inventoryQuantity, a single jq query folds that down to the single worst stock level in the batch, and the result routes to a quiet log, an informational Slack note, or a Slack page depending on severity.

How it works

  1. list_products (io.kestra.plugin.shopify.products.List) retrieves up to product_limit products in product_status from the Shopify Admin API, including each product's variants and their inventoryQuantity. This is a read: nothing is created, updated, or deleted. allowFailure: true keeps an API failure from aborting the flow outright.
  2. classify_stock_level (io.kestra.plugin.core.debug.Return) first guards for a missing products output, then hands the product list to one jq query that collects every variant's inventoryQuantity into a flat list, takes the minimum, and classifies it: no variants at all (missing output or zero products matched) becomes UNREACHABLE, zero or negative stock becomes CRITICAL, at or below warning_stock_threshold becomes WARNING, otherwise HEALTHY.
  3. route_by_stock_level (io.kestra.plugin.core.flow.Switch) branches on that classification: HEALTHY logs a quiet confirmation; WARNING posts an informational Slack note; CRITICAL and UNREACHABLE each page Slack; a defaults fallback logs the raw value for any classification outside the four known cases.
  4. log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run, independent of which Switch branch fired, re-deriving the worst inventory quantity for readability so the check's own history stays queryable even if Slack delivery fails.
  5. The flow-level errors block pages Slack separately if the flow fails outright, so a broken sentinel is never mistaken for a fully stocked catalog.
  6. The stockout_schedule trigger checks every 6 hours and ships disabled: true, with explicit inputs: overrides for product_status and warning_stock_threshold so the scheduled run's scope and tolerance are pinned regardless of this flow's own defaults.

What you get

  • A scheduled, read-only stock check across a batch of Shopify products with zero custom Admin API scripting.
  • A single status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on.
  • Three distinct Slack messages, one per severity, each stating how many products were checked.
  • An audit log line on every run, separate from Slack, carrying the worst observed inventory quantity, so the check's own history survives a webhook outage.

Who it's for

  • E-commerce and merchandising teams running Shopify who have no orchestrator-side alert for a variant quietly running out of stock.
  • Operations teams who want a restock signal before a bestseller goes to zero, not after a customer complains.
  • Teams standardizing Day 2 inventory governance across sales channels inside Kestra instead of checking the Shopify dashboard by hand.

Why orchestrate this with Kestra

Shopify's own low-stock notifications are tied to specific apps or manual dashboard review, not a declarative, auditable check you control. Kestra's scheduled, read-only products.List call folds every checked variant's stock into one typed classification, with the schedule, the threshold logic, and the Slack call all captured as one execution history, with the Switch branch ready to grow into real remediation (creating a purchase order draft, opening a ticket) without rewriting the check itself.

Prerequisites

  • A Shopify store with the Admin API enabled and an access token with read_products scope.
  • A Slack incoming webhook for the WARNING, CRITICAL, UNREACHABLE, and flow-failure alerts.

Secrets

  • SHOPIFY_STORE_DOMAIN: the store's *.myshopify.com domain.
  • SHOPIFY_ACCESS_TOKEN: Shopify Admin API access token with read_products scope.
  • SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.

In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_, base64-encoded, and read back in flows with {{ secret('NAME') }}, for example SECRET_SLACK_WEBHOOK=$(echo -n 'https://hooks.slack.com/services/<placeholder>' | base64). This keeps credentials out of the flow YAML but, per Kestra's own documentation, offers no encryption at rest or access control beyond the host environment; use the Enterprise secrets backend for stronger guarantees.

Quick start

  1. Shopify is a managed SaaS with no official local Docker image; use a free Shopify Partner development store to get a real storeDomain and a custom app access token without a paid plan.
  2. If Kestra runs in Docker and you proxy Shopify API calls through a local gateway for testing, check docker network ls for Kestra's network and attach that gateway container to it: docker network connect YOUR_KESTRA_NETWORK <container_name> Avoid host port 8080 for any such local proxy since Kestra's own UI already uses it.
  3. In the development store's admin, create a custom app under Settings > Apps and sales channels > Develop apps, grant it read_products, and install it to get an access token.
  4. Add SHOPIFY_STORE_DOMAIN, SHOPIFY_ACCESS_TOKEN, and SLACK_WEBHOOK as secrets, create a test product with a low variant quantity, and run the flow once manually to confirm status reads WARNING or CRITICAL depending on the quantity you set.
  5. Enable stockout_schedule once pointed at a real store.

How to extend

  • Add vendor or productType filtering to list_products to scope the check to one supplier or category instead of the whole catalog.
  • Page through results with sinceId if the catalog has more products than product_limit can return in one call.
  • Change the jq query to report every low or zero-stock variant by SKU in the Slack message instead of just the single worst value.
  • Push status into a KV store and alert only on state transitions instead of every run.

Inputs

  • product_status: type SELECT, values ["active", "archived", "draft"], default "active".
  • product_limit: type INT, default 100, maximum 250 per Shopify's own API limit.
  • warning_stock_threshold: type INT, default 5.

Outputs

  • status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.
  • Example path: {{ outputs.classify_stock_level.value }}, also exposed as the flow output {{ outputs.status }}.

Links

Pitfalls

  • product_limit caps at 250 per Shopify's own API pagination limit; a catalog larger than that needs sinceId-based paging to check every product, not just the first batch.
  • A product with zero variants returned (for example, filtered out entirely by product_status) contributes nothing to the jq fold; if every matched product has no variants, the check reports UNREACHABLE rather than silently reporting healthy.
  • This flow only ever calls products.List; it never calls products.Update, so there is nothing destructive to default to dry-run for, and no variant's stock is ever changed by this check.
  • inventoryQuantity reflects Shopify's last-known count; a variant tracked with inventoryManagement set to a third-party fulfillment service may not reflect real-time warehouse stock.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.