How to Detect Anomalies in Daily Inventory Counts with AI Reasoning

Build a Spojit workflow that pulls your BigCommerce inventory each morning, lets an agent flag suspicious swings and their likely causes as clean structured data, and posts a prioritized Slack alert for each anomaly through a reusable subworkflow.

What This Integration Does

Raw inventory counts rarely tell you when something has quietly gone wrong. A product that dropped from 400 units to 12 overnight, a SKU that jumped back up without a known restock, or a count stuck at exactly the same number for a week can all signal a feed glitch, a miskeyed adjustment, or theft. Eyeballing a spreadsheet every morning does not scale past a handful of products. This workflow watches your BigCommerce catalog for you: it reads the current inventory levels on a schedule, asks an agent to reason over them and surface only the genuinely suspicious items, and then routes a clear, ranked alert into Slack so the right person can investigate.

A Schedule trigger fires once each weekday morning. A Connector node in Direct mode lists your BigCommerce products and their stock levels. A second Connector node in Agent mode hands those counts to the agent, which returns a strict JSON array of anomalies via a Response Schema. The workflow then loops over that array and calls a reusable Subworkflow once per anomaly to send a prioritized Slack message. The workflow is read-only against your store: it never changes inventory, so re-runs are safe and idempotent, and each run leaves behind only the Slack alerts it posted plus an entry in your execution history.

Prerequisites

  • A BigCommerce connection added under Connections, with API access scoped to read products and inventory. See Adding a New Connection if you have not connected BigCommerce yet.
  • A Slack connection with permission to post to your alerts channel (for example #inventory-alerts), plus the channel name or ID you want to post to.
  • A way to know your store's "normal" stock baseline. The simplest version compares against the previous run; a richer version stores yesterday's counts in a database or knowledge collection. This tutorial uses the within-run reasoning approach so it works with no extra storage.
  • Familiarity with the visual designer. If you are new, read Creating Your First Workflow first.

Step 1: Start with a Schedule trigger

Add a Trigger node and set its type to Schedule. Schedule triggers use a 5-field Unix cron expression plus an IANA timezone. To run at 7:00 AM on weekdays, enter:

Cron: 0 7 * * 1-5
Timezone: Australia/Sydney

Pick the timezone your operations team works in so "daily" lands in their morning, not in UTC. A single Schedule trigger can hold more than one schedule if you want a second sweep later in the day. The trigger output is { scheduledAt }, which you can reference downstream as {{ scheduledAt }} if you want to stamp the alert with the run time. For a deeper walkthrough see Setting Up a Schedule Trigger.

Step 2: Pull current inventory from BigCommerce (Direct mode)

Add a Connector node, choose the BigCommerce connection, and keep it in Direct mode so the call is deterministic and costs no AI credits. Select the list-products tool. Each returned product includes its name, sku, and inventory_level, which are exactly the fields the reasoning step needs.

Set the inputs to page through the catalog. limit accepts up to 250 products per page (default 50):

page: 1
limit: 250

Name the output variable, for example products, so later steps can read {{ products }}. If your catalog is larger than 250 products, see the Tips section on paging. To learn the difference between the two connector modes, read Using Connector Nodes in Direct Mode.

Step 3: Let the agent flag anomalies as structured output (Agent mode)

Add a second Connector node and switch it to Agent mode. In Agent mode you give the agent a prompt and a Response Schema that forces the output into strict JSON, so downstream nodes get predictable fields instead of free text. Paste your inventory list into the prompt using a variable:

You are reviewing a daily BigCommerce inventory snapshot.
Here is the current product list: {{ products }}

Identify only products whose stock looks suspicious: a large
sudden drop or spike, a negative or impossible value, a count
that looks frozen, or stock that is unexpectedly at or near zero
for a normally active product. For each one, give the most
likely cause and a severity. Ignore products that look normal.

Set a Response Schema that returns an array of anomalies. For example:

{
  "type": "object",
  "properties": {
    "anomalies": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "sku": { "type": "string" },
          "name": { "type": "string" },
          "currentLevel": { "type": "number" },
          "severity": { "type": "string", "enum": ["high", "medium", "low"] },
          "likelyCause": { "type": "string" },
          "recommendedAction": { "type": "string" }
        },
        "required": ["sku", "name", "severity", "likelyCause"]
      }
    }
  },
  "required": ["anomalies"]
}

Name the output variable review. The anomaly list is then available as {{ review.anomalies }}. For more on forcing reliable JSON from Agent mode, see How to Use Structured Output for Reliable AI Data Extraction and Using Connector Nodes in Agent Mode.

Step 4: Build a reusable "Post Inventory Alert" subworkflow

Create a separate workflow that does one job: take a single anomaly and post a Slack alert. Give it a Manual trigger so it can be called as a step, and design its Input to receive one anomaly object (its sku, name, severity, likelyCause, and recommendedAction).

Inside that subworkflow, add a Connector node on the Slack connection in Direct mode using the send-message tool. Map its fields to the incoming anomaly:

channel: #inventory-alerts
text: [{{ input.severity }}] {{ input.name }} ({{ input.sku }})
      now at {{ input.currentLevel }} units.
      Likely cause: {{ input.likelyCause }}.
      Suggested action: {{ input.recommendedAction }}.

Building the alert as its own workflow means any other inventory or order workflow can reuse the exact same Slack formatting later. Save and note the workflow name so the parent can select it. For the node mechanics see Using Subworkflow Nodes, and for a broader pattern see How to Build a Reusable Subworkflow Library.

Step 5: Loop over anomalies and call the subworkflow per item

Back in the main workflow, add a Loop node set to ForEach and point it at {{ review.anomalies }}. The Loop exposes the current item (for example as {{ anomaly }}) on each pass.

Inside the loop body, add a Subworkflow node. Set Workflow to the "Post Inventory Alert" workflow you built in Step 4, and set its Input to the current anomaly:

{{ anomaly }}

The parent pauses while each child run posts its Slack message, then continues to the next anomaly. Each child appears as its own entry in execution history, which makes a failed alert easy to find later. For loop options see Using Loop Nodes.

Step 6: Send a roll-up summary (optional)

If you would rather get one digest than many pings, add a Condition node before the loop to check whether {{ review.anomalies }} is empty, and route the non-empty branch to a single Send Email node that lists every anomaly. Send Email uses Spojit's built-in mail service, so no connection is needed; set Recipients to your ops list and template the Body with the anomaly fields. Keep external recipients on the org allowlist under Settings → General → Email recipients. You can keep both the per-item Slack alerts and the email digest, or use whichever fits your team. See Using Send Email Nodes.

Step 7: Enable the workflow

Save the main workflow and toggle it on so the Schedule trigger becomes active. From this point Spojit runs the sweep automatically at the cron time you set. You can confirm it is live and watch runs accumulate from the executions view described in Monitoring Workflow Executions.

Tips

  • For catalogs over 250 products, wrap the list-products call in a Loop that increments page until a page returns fewer than limit items, then collect every page before sending the combined list to the agent.
  • Tighten the Agent-mode prompt with concrete thresholds (for example "treat a drop greater than 70 percent as high severity") so the severity field stays consistent across runs.
  • Use severity in the subworkflow to control noise: post high items to #inventory-alerts and quietly log low items elsewhere, or skip them entirely with a Condition node.
  • Ask Miraxa to scaffold the canvas for you: try "Add a ForEach Loop over {{ review.anomalies }} and put a Subworkflow node inside it that calls Post Inventory Alert."

Common Pitfalls

  • Timezone mismatch: a cron of 0 7 * * 1-5 means 7 AM in the IANA timezone you set, not UTC. Double-check the timezone or alerts will land at the wrong hour.
  • Missing inventory scope: if the BigCommerce connection cannot read inventory, inventory_level comes back empty and the agent has nothing to judge. Confirm the connection's API scope before going live.
  • Free-text instead of JSON: without a Response Schema on the Agent-mode node, the output is prose and {{ review.anomalies }} will not resolve. Always define the schema.
  • Slack channel format: the send-message tool needs a valid channel name or ID the connection can post to; a typo or a private channel the connection has not joined will fail the alert step.

Testing

Before enabling the schedule, validate on a small scope. Temporarily set the list-products node's limit to a handful of items, or use the keyword_filter input to target a few known SKUs, then run the main workflow once from the Run button. Inspect the Agent-mode output to confirm {{ review.anomalies }} is well-formed JSON and that the flagged items make sense. Run the "Post Inventory Alert" subworkflow on its own with a single hand-crafted anomaly to verify the Slack message format. Once both behave, restore the full limit, turn the schedule on, and watch the first scheduled run in your execution history.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.