New to Kestra?
Use blueprints to kickstart your first workflows.
Check Shopify product variant inventory on a schedule, change nothing in the store, and alert Slack before a variant runs out of stock.
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.
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.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.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.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.errors block pages Slack separately if the flow fails outright, so a broken sentinel is never mistaken for a fully stocked catalog.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.status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on.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.
read_products scope.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.
storeDomain and a custom app access token without a paid plan.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.read_products, and install it to get an access token.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.stockout_schedule once pointed at a real store.vendor or productType filtering to list_products to scope the check to one supplier or category instead of the whole catalog.sinceId if the catalog has more products than product_limit can return in one call.status into a KV store and alert only on state transitions instead of every run.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.status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.{{ outputs.classify_stock_level.value }}, also exposed as the flow output {{ outputs.status }}.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.product_status) contributes nothing to the jq fold; if every matched product has no variants, the check reports UNREACHABLE rather than silently reporting healthy.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.