AutomationFlowsE-commerce › Handle Order Status Webhooks with Microsoft Sql Server and Email

Handle Order Status Webhooks with Microsoft Sql Server and Email

ByPrakash Software @prakashsoftware on n8n.io

This workflow receives order status updates via a webhook, validates and normalizes the payload, routes by status, and either returns an immediate JSON response or (for rejections) logs activity to Microsoft SQL Server and sends a rejection email via SMTP. Receives a POST…

Webhook trigger★★★★☆ complexity19 nodesEmail SendMicrosoft Sql
E-commerce Trigger: Webhook Nodes: 19 Complexity: ★★★★☆ Added:
Handle Order Status Webhooks with Microsoft Sql Server and Email — n8n workflow card showing Email Send, Microsoft Sql integration

This workflow corresponds to n8n.io template #16294 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "Upm5BuaThcR3UJNg",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Status Update Automation Workflow",
  "tags": [],
  "nodes": [
    {
      "id": "e5422f56-d65c-41ca-9812-1e51cc8989f4",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -544,
        -224
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Status Update Automation Workflow\n\n### How it works\n\n1. The workflow is triggered by an incoming order status webhook and validates the payload.\n2. Valid payloads are normalized (orderId, name, email, oldStatus) and routed based on the status value.\n3. For 'approved', 'pending', and 'completed' statuses it returns the appropriate webhook response; for 'rejected' it prepares and sends an email, logs the rejection, then responds.\n4. Invalid payloads are replied to immediately with an error response.\n\n### Setup steps\n\n- [ ] Configure the webhook node (Order Status Webhook) and expose its URL where the source system will POST status updates.\n- [ ] Add credentials for sending email (SMTP / email provider) used by the Send Rejection Email node.\n- [ ] Configure the Microsoft SQL credentials and connection string for the Log Rejection node.\n- [ ] Verify any respondToWebhook nodes are set to return the expected HTTP status/body for each branch.\n\n### Customization\n\nAdjust the Route By Status conditions to match your status values; edit the Prepare Rejection Email node to change subject/body templates; extend branches to add more status-specific processing or notifications."
      },
      "typeVersion": 1
    },
    {
      "id": "08cd7ab9-aae7-46e0-8e12-e2bff2765b77",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        336
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 528,
        "content": "## Receive and validate webhook\n\nTrigger and initial validation cluster: receives the incoming webhook payload and validates it; invalid payloads go to the error response."
      },
      "typeVersion": 1
    },
    {
      "id": "5d15cb85-492a-4eb6-8b4b-503990e6ffdc",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 368,
        "content": "## Format and route data\n\nNormalize incoming data into standard fields and route the flow based on the order status value."
      },
      "typeVersion": 1
    },
    {
      "id": "2c91f907-2039-4197-b0d0-8a11739d9954",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 336,
        "content": "## Handle rejection and log\n\nPrepare and send a rejection email, persist the rejection to the SQL database, then return the final rejection response to the caller."
      },
      "typeVersion": 1
    },
    {
      "id": "dbbdc01c-bf67-4bda-a986-677fbae03b7f",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1088,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 352,
        "content": "## Approved webhook response\n\nRespond immediately for approved orders (simple webhook response node)."
      },
      "typeVersion": 1
    },
    {
      "id": "124f1c0d-3422-46f1-bfab-36a7430fed7b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 352,
        "content": "## Pending webhook response\n\nRespond immediately for pending orders (simple webhook response node)."
      },
      "typeVersion": 1
    },
    {
      "id": "1cab0e1e-1e0c-4d0a-b1c9-c25499573bcc",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        592
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 352,
        "content": "## Completed webhook response\n\nRespond immediately for completed orders (simple webhook response node)."
      },
      "typeVersion": 1
    },
    {
      "id": "aa6e67ce-7c87-45b2-bbba-bf147fde70bc",
      "name": "Validate Payload",
      "type": "n8n-nodes-base.if",
      "notes": "Validates required fields: email, orderId, and newStatus. Returns 400 error if validation fails.",
      "position": [
        368,
        464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "email-check",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.body.email }}",
              "rightValue": ""
            },
            {
              "id": "orderid-check",
              "operator": {
                "type": "number",
                "operation": "exists"
              },
              "leftValue": "={{ $json.body.orderId }}",
              "rightValue": ""
            },
            {
              "id": "status-check",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.body.newStatus }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "b3e9ebde-8ebe-42e8-958b-1158a86f8740",
      "name": "Error Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "Returns 400 error when payload validation fails. Customize error message as needed.",
      "position": [
        656,
        688
      ],
      "parameters": {
        "options": {
          "responseCode": 400
        },
        "respondWith": "json",
        "responseBody": "={{ { \"success\":  false, \"error\":  \"Missing required fields\", \"required\":  [\"email\", \"orderId\", \"newStatus\"] } }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "e37f2f83-8550-4c86-a843-c25dfcc5aac5",
      "name": "Format Data",
      "type": "n8n-nodes-base.set",
      "notes": "Extracts and formats all required fields from webhook payload. Generates timestamp and formatted reason for downstream nodes.",
      "position": [
        880,
        352
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "orderId",
              "type": "number",
              "value": "={{ $json.body.orderId }}"
            },
            {
              "id": "2",
              "name": "name",
              "type": "string",
              "value": "={{ $json.body.name ?? \"Customer\" }}"
            },
            {
              "id": "3",
              "name": "email",
              "type": "string",
              "value": "={{ $json.body.email }}"
            },
            {
              "id": "4",
              "name": "oldStatus",
              "type": "string",
              "value": "={{ $json.body.oldStatus ?? \"Unknown\" }}"
            },
            {
              "id": "5",
              "name": "newStatus",
              "type": "string",
              "value": "={{ $json.body.newStatus }}"
            },
            {
              "id": "6",
              "name": "updatedBy",
              "type": "string",
              "value": "={{ $json.body.updatedBy ?? \"System\" }}"
            },
            {
              "id": "7",
              "name": "reason",
              "type": "string",
              "value": "={{ $json.body.reason ?? \"No reason provided\" }}"
            },
            {
              "id": "8",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "000fe3fa-805b-4b00-ac5b-9f60c50b357f",
      "name": "Route By Status",
      "type": "n8n-nodes-base.switch",
      "notes": "Routes workflow based on newStatus field. Each status type triggers different processing logic.",
      "position": [
        1120,
        320
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Approved",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "approved",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.newStatus }}",
                    "rightValue": "Approved"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Rejected",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "rejected",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.newStatus }}",
                    "rightValue": "Rejected"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Pending",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "pending",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.newStatus }}",
                    "rightValue": "Pending"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Completed",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "completed",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.newStatus }}",
                    "rightValue": "Completed"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "654fe5e8-50c7-4a21-bf42-2f0a3cf3b584",
      "name": "Approved Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1136,
        -32
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ { \"success\":  true, \"orderId\":  $json.orderId, \"status\":  $json.newStatus, \"timestamp\":  $json.timestamp } }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "c2db4c67-ca48-4264-93e7-17b0f0ab7262",
      "name": "Prepare Rejection Email",
      "type": "n8n-nodes-base.set",
      "notes": "Generates professional HTML email with rejection details. Customize email template to match your brand.",
      "position": [
        1344,
        336
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "emailSubject",
              "type": "string",
              "value": "={{ \"Order #\" + $json.orderId + \" - Rejected\" }}"
            },
            {
              "id": "2",
              "name": "emailBody",
              "type": "string",
              "value": "={{ \"Hello \" + $json.name + \",\\n\\nYour order #\" + $json.orderId + \" has been rejected.\\n\\nReason: \" + $json.reason + \"\\n\\nRejected by: \" + $json.updatedBy + \"\\nDate: \" + $json.timestamp + \"\\n\\nPlease contact support if you have questions.\" }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "8ad2f590-e887-478c-b547-43566e084313",
      "name": "Send Rejection Email",
      "type": "n8n-nodes-base.emailSend",
      "notes": "Sends rejection email to customer. Configure SMTP credentials and update fromEmail address.",
      "onError": "continueErrorOutput",
      "position": [
        1536,
        336
      ],
      "parameters": {
        "text": "={{ $json.emailBody }}",
        "options": {},
        "subject": "={{ $json.emailSubject }}",
        "toEmail": "={{ $json.email }}",
        "fromEmail": "user@example.com",
        "emailFormat": "text"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "ed484931-f406-4cac-a09e-1a2ded709ade",
      "name": "Pending Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "Returns success response for pending status. Extend this path with additional logic as needed.",
      "position": [
        1344,
        736
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ { \"success\":  true, \"orderId\":  $json.orderId, \"status\":  $json.newStatus, \"timestamp\":  $json.timestamp } }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "1fe4abff-9623-4dfb-9a23-5b9a02b8cf07",
      "name": "Completed Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "Returns success response for completed status. Add completion logic (e.g., invoice generation) here.",
      "position": [
        1072,
        784
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ { \"success\":  true, \"orderId\":  $json.orderId, \"status\":  $json.newStatus, \"timestamp\":  $json.timestamp } }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "114b3c12-ab1c-4ec9-acac-228028036dd5",
      "name": "Order Status Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        64,
        464
      ],
      "parameters": {
        "path": "<__PLACEHOLDER_VALUE__order-status-update__>",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "d4a0c59e-8e94-4bde-b5cd-7c0061174207",
      "name": "Log Rejection",
      "type": "n8n-nodes-base.microsoftSql",
      "onError": "continueErrorOutput",
      "position": [
        1760,
        320
      ],
      "parameters": {
        "query": "{{ \"INSERT INTO OrderStatusLog (OrderId, OldStatus, NewStatus, Reason, UpdatedBy, UpdatedAt) VALUES (\" + $json.orderId + \", '\" + $json.oldStatus + \"', '\" + $json.newStatus + \"', '\" + $json.reason.replace(/'/g, \"''\") + \"', '\" + $json.updatedBy + \"', '\" + $json.timestamp + \"')\" }}",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "microsoftSql": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "64ea0ab3-74b4-4849-975d-a2af12421996",
      "name": "Rejection Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1984,
        304
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ { \"success\":  true, \"orderId\":  $json.orderId, \"status\":  $json.newStatus, \"emailSent\":  true, \"timestamp\":  $json.timestamp } }}"
      },
      "typeVersion": 1.5
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "8b7d2d63-9e20-4fd7-8491-c218802834f6",
  "nodeGroups": [],
  "connections": {
    "Format Data": {
      "main": [
        [
          {
            "node": "Route By Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Rejection": {
      "main": [
        [
          {
            "node": "Rejection Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route By Status": {
      "main": [
        [
          {
            "node": "Approved Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Rejection Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Pending Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Completed Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Payload": {
      "main": [
        [
          {
            "node": "Format Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Order Status Webhook": {
      "main": [
        [
          {
            "node": "Validate Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Rejection Email": {
      "main": [
        [
          {
            "node": "Log Rejection",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Prepare Rejection Email": {
      "main": [
        [
          {
            "node": "Send Rejection Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow receives order status updates via a webhook, validates and normalizes the payload, routes by status, and either returns an immediate JSON response or (for rejections) logs activity to Microsoft SQL Server and sends a rejection email via SMTP. Receives a POST…

Source: https://n8n.io/workflows/16294/ — original creator credit. Request a take-down →

More E-commerce workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

E-commerce

Small online store owners, new entrepreneurs, and anyone getting started with e-commerce automation. Perfect for beginners who want to automate their order process without complexity or additional cos

Email Send
E-commerce

Stop paying for expensive plugins to recover your valuable revenue from abandoned carts on your WooCommerce store When a product is added to a user's cart on your store, it fetches the cart contents v

WooCommerce, Email Send, HTTP Request
E-commerce

Order Confirmation. Uses emailSend. Webhook trigger; 2 nodes.

Email Send
E-commerce

WF01 — Payout Reconciler (Stripe + Shopify → Xero). Uses httpRequest, postgres. Webhook trigger; 54 nodes.

HTTP Request, Postgres