How to Forward Shopify Orders to a MySQL Reporting Database and Slack

Catch every new Shopify order with a signed webhook, write its line items into a MySQL reporting table, and post a Slack alert whenever the order value clears a threshold.

What This Integration Does

If your finance and operations teams want order data in a database they control rather than waiting on exports, this workflow keeps a MySQL reporting table current the moment an order is placed. Each Shopify order lands within seconds, gets reshaped into clean rows, and is inserted into your warehouse table so dashboards and SQL reports always reflect the latest sales. On top of that, high-value orders trigger an instant Slack message so the right people can act fast on big tickets.

The run model is event-driven. A Shopify orders/create webhook calls your Spojit workflow's Webhook trigger, which verifies the payload using a Shopify signing connection before the workflow proceeds. A Transform node flattens the order into row objects, a Connector node writes them to MySQL, and a Condition node decides whether to fire the Slack alert. The workflow leaves one or more rows in your reporting table per order and, conditionally, one Slack message. Shopify can resend the same webhook, so the workflow is built to insert idempotently and tolerate replays without creating duplicate rows.

Prerequisites

  • A Shopify connection in Spojit (API key connection) with read access to orders, used here to enrich data if needed.
  • A Shopify webhook signing connection so the Webhook trigger can verify the HMAC. See Setting Up a Webhook Connection.
  • A MySQL connection pointed at your reporting database, with insert and select rights on the target table.
  • A Slack connection authorized to post in the alert channel, plus the channel ID or name.
  • A reporting table created ahead of time. For example:
    CREATE TABLE shopify_order_lines (
      order_id      BIGINT,
      order_name    VARCHAR(32),
      line_item_id  BIGINT,
      sku           VARCHAR(64),
      title         VARCHAR(255),
      quantity      INT,
      line_total    DECIMAL(12,2),
      order_total   DECIMAL(12,2),
      currency      VARCHAR(8),
      created_at    DATETIME,
      PRIMARY KEY (line_item_id)
    );
    The line_item_id primary key is what lets replays insert safely.

Step 1: Add a Webhook trigger and verify Shopify signatures

In the Workflow Designer, add a Trigger node and set its type to Webhook. Choose Shopify as the signing scheme and select your Shopify signing connection so Spojit checks the X-Shopify-Hmac-Sha256 header on every call. Copy the generated workflow URL. In your Shopify admin, create a webhook subscription for the orders/create topic and paste that URL. The trigger returns 202 with an executionId and exposes the parsed order body as {{ trigger }}. Turn on event-id dedup using Shopify's X-Shopify-Webhook-Id header so retried deliveries are skipped at the door.

Step 2: Reshape the order into row objects with a Transform node

Add a Transform node after the trigger. The Shopify payload nests one order with a line_items array, so map it into one flat object per line item that matches your table columns. Produce an array like this from {{ trigger }}:

{
  "rows": [
    {
      "order_id": {{ trigger.id }},
      "order_name": "{{ trigger.name }}",
      "line_item_id": {{ item.id }},
      "sku": "{{ item.sku }}",
      "title": "{{ item.title }}",
      "quantity": {{ item.quantity }},
      "line_total": {{ item.price }} * {{ item.quantity }},
      "order_total": {{ trigger.total_price }},
      "currency": "{{ trigger.currency }}",
      "created_at": "{{ trigger.created_at }}"
    }
  ]
}

Expose the result as {{ orderRows }}. Building one object per line item up front keeps the database step a single clean insert.

Step 3: Insert the rows into MySQL

Add a Connector node in Direct mode, choose the MySQL connector, and pick the insert-rows tool. Set table to shopify_order_lines and map rows to {{ orderRows.rows }}. Because rows accepts an array of objects whose keys are column names, the flattened structure from Step 2 drops straight in. Save the node output as {{ insertResult }} so later steps and execution logs can confirm how many rows were written.

Step 4: Branch on order value with a Condition node

Add a Condition node to decide whether this order deserves a Slack alert. Compare the order total against your threshold, for example {{ trigger.total_price }} greater than 500. Route the true branch toward the Slack step and leave the false branch empty so ordinary orders finish silently after the database write. Keeping the threshold here, rather than inside the message, means Miraxa and your teammates can see the alerting rule at a glance on the canvas.

Step 5: Post a high-value order alert to Slack

On the true branch, add a Connector node in Direct mode, choose the Slack connector, and pick the send-message tool. Set the channel to your alerts channel and compose a templated text field:

:moneybag: High-value order {{ trigger.name }}
Total: {{ trigger.currency }} {{ trigger.total_price }}
Customer: {{ trigger.customer.first_name }} {{ trigger.customer.last_name }}
Items written to reporting DB: {{ insertResult.affectedRows }}

If you would rather have an AI agent decide which channel to use or enrich the message with order context, switch the node to Agent mode, though Direct mode is cheaper and fully predictable for a fixed alert.

Step 6: Save, enable, and confirm idempotency

Save the workflow and enable it. Confirm the path is trigger to Transform to MySQL insert-rows to Condition, with Slack send-message on the true branch only. Because line_item_id is the primary key, a replayed webhook attempting to reinsert the same line items will conflict instead of duplicating. If you prefer an upsert, replace Step 3 with a Connector node using the MySQL execute-query tool and an INSERT ... ON DUPLICATE KEY UPDATE statement with ? placeholders mapped through params.

Tips

  • Shopify sends money values as strings. Cast or compare carefully in the Condition node so "500.00" is treated as a number, not text.
  • Set the MySQL column types to match Shopify's precision: use DECIMAL for money and DATETIME for the ISO timestamp in created_at.
  • For very large orders, batching all line items into one insert-rows call is faster than looping. Reach for a Loop node only if you must transform each item individually.
  • Ask Miraxa, the intelligent layer across your automation, to "explain why my last run inserted zero rows" when a payload shape changes; it can read the execution and point at the Transform output.

Common Pitfalls

  • Wrong signing scheme. If the trigger rejects calls, confirm the Webhook trigger uses the Shopify scheme and the connection matches the store's webhook secret, not an app API key.
  • Duplicate rows from replays. Without a primary key on line_item_id and without event-id dedup, Shopify retries will multiply rows. Keep both safeguards on.
  • Timezone drift. Shopify created_at is ISO 8601 with an offset. Store it as-is or normalize to UTC before insert so reports across days line up.
  • Threshold on the wrong field. Compare against {{ trigger.total_price }}, the order grand total, not a single line item price, or you will alert on the wrong orders.

Testing

Before pointing production traffic at the workflow, place a low-value test order in a development store so the Slack branch stays quiet, then confirm the row appears in shopify_order_lines and the execution log shows insertResult.affectedRows matching the line item count. Next, place a test order above your threshold and verify the Slack message arrives with the right total. Finally, resend the same webhook from Shopify's admin and confirm no duplicate rows are created and no second Slack alert fires, proving the dedup and primary key safeguards work.

Learn More

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