How to Alert Slack When a MySQL KPI Crosses a Threshold

Build a Spojit workflow that runs on a schedule, reads a live KPI straight from your MySQL database, tests it against a target with the math connector, and only posts to Slack when the value actually breaches the threshold.

What This Integration Does

Most teams want to know the moment a number goes wrong: refunds spike, paid signups stall, error counts climb, gross margin slips under target. Polling a dashboard by hand is unreliable, and a job that posts the number every single run trains everyone to ignore it. This tutorial wires up a quiet, dependable alert. A Schedule trigger fires on a cron you choose, a Connector node reads one live KPI from MySQL, a Condition node (backed by the math connector for the actual comparison) decides whether the value is out of bounds, and a Connector node on Slack posts a message only on the runs that breach. On a normal day the channel stays silent.

The workflow is stateless and idempotent: each run reads the KPI fresh, compares it, and either alerts or exits. Nothing is written back to your database and no state is carried between runs, so re-running it never causes double counting or side effects. The trigger output is just { scheduledAt }; everything else is derived live from the query. Because the alert is gated behind a Condition, a clean run simply ends after the comparison with no Slack message sent. If you want a heartbeat on quiet days too, you can add a second Schedule (for example a Monday morning "all clear") that runs the same query and always posts a summary, but the core pattern here is alert-only.

Prerequisites

  • A MySQL connection added in Spojit under Connections, with read access to the table(s) that hold your KPI. See the MySQL connector reference for setup.
  • A Slack connection added under Connections, with permission to post to the channel you want to alert. See the Slack connector reference.
  • The exact channel ID or name you want to alert (for example #ops-alerts or a channel ID like C0123ABCD).
  • A KPI you can express as a single SQL query that returns one number, plus the numeric threshold and direction (alert when above, or alert when below).
  • The math connector is built in and needs no connection.

Step 1: Add a Schedule trigger

Create a new workflow and add a Trigger node. Set Trigger Type to Schedule. Schedules use a standard 5-field cron expression plus an IANA timezone, so business hours line up with where your team actually is. For a check every weekday at 9am Sydney time, use:

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

For a tighter watch, run hourly during business hours with 0 9-17 * * 1-5. A single Schedule trigger can hold multiple cron entries, so you can mix, say, an hourly weekday check with one weekend sweep. The trigger output is { scheduledAt }, which you can reference downstream as {{ scheduledAt }} if you want the timestamp in your alert text.

Step 2: Read the KPI with a MySQL Connector node

Add a Connector node, choose the MySQL connector, and set it to Direct mode so it deterministically runs one tool with no AI cost. Select the execute-query tool. In the query field, write SQL that returns exactly one numeric value in a predictably named column. Aliasing the column keeps the downstream reference stable. For example, refunds issued in the last 24 hours:

SELECT COUNT(*) AS kpi_value
FROM refunds
WHERE created_at >= NOW() - INTERVAL 1 DAY

Use ? placeholders and the params field for any dynamic values rather than string-building them into the query. The execute-query tool returns { rows, fields }, where rows is an array of result rows. Name this node's output variable something clear like kpi, so the value is reachable at {{ kpi.rows.0.kpi_value }} (the first row, your aliased column).

Step 3: Compute the breach test with the math connector

Add a second Connector node on the math connector in Direct mode, and pick the calculate tool. This evaluates a math expression and returns { result }, which gives you a single, deterministic boolean to branch on, instead of relying on string comparison. Pass your live KPI in through the variables field and reference it in the expression. To alert when refunds exceed a target of 25:

expression: kpi > target
variables:  { "kpi": {{ kpi.rows.0.kpi_value }}, "target": 25 }

For a "value fell below target" KPI (for example paid signups under 10), flip the operator to kpi < target. Name the output variable breach, so the comparison result is available as {{ breach.result }} (it resolves to true or false). Keeping the comparison in math means the threshold logic lives in one obvious place and is easy to tune later.

Step 4: Gate the alert with a Condition node

Add a Condition node so the workflow only continues to Slack when the KPI is actually out of bounds. Configure the condition to test that {{ breach.result }} equals true. Wire the Slack step (next) to the true branch, and leave the false branch empty so a healthy run simply ends with nothing sent. This is what keeps the channel quiet on normal days: the comparison runs every time, but only a real breach reaches the notification.

Step 5: Post the alert with a Slack Connector node

On the true branch, add a Connector node on the Slack connector in Direct mode and select the send-message tool. Set the channel field to your alert channel (for example #ops-alerts or its channel ID), and template the message text with the live values so the alert is self-explanatory:

channel: #ops-alerts
text:    :rotating_light: KPI breach: refunds in last 24h = {{ kpi.rows.0.kpi_value }} (target 25). Checked {{ scheduledAt }}.

Because every value in the message is pulled from upstream variables, whoever reads the alert sees the number, the target, and the time without opening anything else. If you would rather have the intelligent layer phrase the alert in plain language or decide which of several channels to post to, you can instead run this node in Agent mode with a prompt, but Direct mode is the cheaper, predictable choice for a fixed alert.

Step 6: Save and enable

Save the workflow, then enable it so the Schedule trigger starts firing. While you are still tuning the threshold, you can ask Miraxa, the intelligent layer across your automation, to refine the canvas for you with a prompt such as "Add a Condition node that checks if {{ breach.result }} is true and connect the true branch to the Slack send-message node." Miraxa knows the workflow you are editing and will wire the nodes; if anything is ambiguous it asks first.

Tips

  • Always alias your KPI column (AS kpi_value) and aggregate to a single row. If the query can return many rows, wrap it so it returns one number, otherwise {{ kpi.rows.0.kpi_value }} may not point at what you expect.
  • Keep the threshold as a target variable in the math expression rather than hard-coding it in two places, so you only change one number when the target moves.
  • To reduce noise further, widen the cron interval (hourly instead of every few minutes) and let your query aggregate over a rolling window, so a brief blip does not page anyone.
  • Add the timestamp ({{ scheduledAt }}) and the actual value to the Slack text so on-call can triage at a glance without logging in.

Common Pitfalls

  • Cron timezone drift. Cron is evaluated in the IANA timezone you set on the trigger, not UTC and not the viewer's local time. A "9am" alert in the wrong timezone fires at the wrong hour, so set the timezone explicitly.
  • Comparing a string to a number. SQL aggregates can come back as strings. Passing the value into the math calculate variables coerces it to a number for the comparison, which is why the math step is more reliable than comparing raw text in the Condition.
  • Empty result set. If the query matches no rows, {{ kpi.rows.0.kpi_value }} is empty and the math expression can fail. Use COALESCE(...) or COUNT(*) so the query always returns a real number, even zero.
  • Schema drift. If a column or table is renamed in MySQL, the query silently starts returning the wrong thing or errors out. Keep the query simple and revisit it when the underlying schema changes.

Testing

Before enabling the schedule, validate the logic in isolation. Temporarily set the math target to a value you know the current KPI will breach (for example target: -1 for a count, which any non-negative value exceeds), so the Condition's true branch fires and you confirm the Slack message lands in the right channel with the right text. Then set target to a value the KPI will not breach and confirm no message is sent and the run ends cleanly on the false branch. Check the execution history to see the resolved {{ kpi.rows.0.kpi_value }} and {{ breach.result }} at each step. Once both the alert and the silent cases behave, restore your real threshold and enable the workflow.

Learn More

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