{
  "id": "Ck3mAyvbSemr8I6J",
  "meta": {
    "aiBuilderAssisted": true
  },
  "name": "SAP B1 New Orders and Invoices WhatsApp Alerts",
  "tags": [],
  "nodes": [
    {
      "id": "df359bd8-1455-4cfb-a4e4-8eae3e1c59ae",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        416,
        -288
      ],
      "parameters": {
        "width": 480,
        "height": 880,
        "content": "## SAP B1 New Orders and Invoices WhatsApp Alerts\n\n### How it works\n\nThis workflow runs every 15 minutes to check SAP Business One for newly created sales orders and AR invoices since the last successful check. It logs in to SAP B1, fetches and normalizes both document types, merges the results, and sends a WhatsApp alert only when new documents exist. It then logs out of SAP B1 and saves the latest checkpoint timestamp for the next run.\n\n### Setup steps\n\n- Configure the SAP Business One Service Layer base URL, company database, username, and password in the SAP login, fetch, and logout HTTP Request nodes.\n- Set up the workflow Data Table used by the checkpoint nodes so the last checked timestamp can be read and updated reliably.\n- Configure the WhatsApp/Meta Graph API endpoint, access token, phone number ID, and recipient number in the alert formatting and sending nodes.\n- Verify the code nodes map the SAP B1 response fields that exist in your environment for sales orders and AR invoices.\n- Activate the schedule trigger after testing the SAP and WhatsApp credentials end to end.\n\n### Customization\n\nAdjust the schedule interval, SAP query filters, document fields included in the alert, recipient phone number, and message template to match business requirements."
      },
      "typeVersion": 1
    },
    {
      "id": "4bf9aa67-e586-474b-b47a-b39b35de4122",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 320,
        "content": "## Schedule and checkpoint\n\nStarts the workflow every 15 minutes, reads the previously saved last-check time, and resolves the timestamp to use when querying SAP B1 for new documents."
      },
      "typeVersion": 1
    },
    {
      "id": "b1078f9d-494e-4d4b-a11d-1f14ddf20d0b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1696,
        -208
      ],
      "parameters": {
        "color": 7,
        "height": 336,
        "content": "## Authenticate with SAP\n\nLogs in to the SAP Business One Service Layer before running the document queries."
      },
      "typeVersion": 1
    },
    {
      "id": "72b175d3-6d0d-4298-b0b6-d937956c7b3c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1968,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 560,
        "content": "## Fetch SAP documents\n\nRuns parallel SAP B1 requests for new sales orders and AR invoices, extracts the relevant records from each response, and appends both streams into a single document list."
      },
      "typeVersion": 1
    },
    {
      "id": "d062ce24-6b57-4d09-88f1-1365902afbec",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2688,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 496,
        "content": "## Decide and notify\n\nChecks whether any new documents were found, formats a WhatsApp message when needed, and sends the alert through the WhatsApp/Meta Graph API."
      },
      "typeVersion": 1
    },
    {
      "id": "e89c8ca5-997c-46f6-b33c-85d554207329",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3168,
        -176
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 320,
        "content": "## Logout and save state\n\nLogs out of SAP B1 after either path completes and updates the stored last-checked timestamp for the next scheduled run."
      },
      "typeVersion": 1
    },
    {
      "id": "e2e0bf71-7f58-42c7-8fc9-fc556863f3ed",
      "name": "When Every 15 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1024,
        -32
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "a6cb377d-4a8f-4ddd-b8f9-dc18d5df6529",
      "name": "Fetch Last Checked Timestamp",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1264,
        -32
      ],
      "parameters": {
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "keyName": "configKey",
              "keyValue": "sap_b1_alert_last_checked"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "TysbEO2uPKsfHc4F"
        }
      },
      "typeVersion": 1.1,
      "alwaysOutputData": true
    },
    {
      "id": "632d69d4-9c44-419f-bda2-8d12baa3c2fb",
      "name": "Calculate Since Timestamp",
      "type": "n8n-nodes-base.code",
      "position": [
        1504,
        -32
      ],
      "parameters": {
        "jsCode": "\nconst rows = $input.all();\nconst hasRow = rows.length > 0 && rows[0].json && rows[0].json.lastCheckedAt;\n\n// Default to 15 minutes ago if no stored timestamp exists\nconst since = hasRow\n  ? rows[0].json.lastCheckedAt\n  : new Date(Date.now() - 15 * 60 * 1000).toISOString();\n\n// Capture \"now\" so we save the same value we filter with\nconst now = new Date().toISOString();\n\nreturn [{ json: { since, now } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "ba0ef480-6241-4cb2-ae57-2ad3c4c6d885",
      "name": "Post SAP B1 Login",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1744,
        -32
      ],
      "parameters": {
        "url": "<__PLACEHOLDER_VALUE__https://your-sap-server:50000/b1s/v1/Login__>",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "CompanyDB",
              "value": "<__PLACEHOLDER_VALUE__SBODEMOGB__>"
            },
            {
              "name": "UserName",
              "value": "<__PLACEHOLDER_VALUE__manager__>"
            },
            {
              "name": "Password",
              "value": "<__PLACEHOLDER_VALUE__your-password__>"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "276ae2dd-ef5c-4d22-81df-acfec58308ef",
      "name": "Retrieve New Sales Orders",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2016,
        -128
      ],
      "parameters": {
        "url": "<__PLACEHOLDER_VALUE__https://your-sap-server:50000/b1s/v1/Orders__>",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "$select",
              "value": "DocNum,DocDate,CardCode,CardName,DocTotal,DocCurrency,Comments"
            },
            {
              "name": "$filter",
              "value": "=UpdateDate gt '{{ $('Calculate Since Timestamp').item.json.since.split('T')[0] }}'"
            },
            {
              "name": "$orderby",
              "value": "DocDate desc"
            },
            {
              "name": "$top",
              "value": "20"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Cookie",
              "value": "=B1SESSION={{ $('Post SAP B1 Login').item.json.SessionId }}; ROUTEID=.node1"
            }
          ]
        }
      },
      "executeOnce": true,
      "typeVersion": 4.3
    },
    {
      "id": "2f975ba4-e621-49d9-a664-73d949d3a898",
      "name": "Parse Sales Orders Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2256,
        -128
      ],
      "parameters": {
        "jsCode": "\nconst records = $input.first().json.value || [];\nreturn records.map(r => ({ json: { ...r, docType: 'Sales Order' } }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e44f2250-bc55-4986-8ead-54f95ed36a4a",
      "name": "Combine Orders and Invoices",
      "type": "n8n-nodes-base.merge",
      "position": [
        2496,
        -16
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "5de90c9a-66b8-4d3b-ad3a-f41d7b572ccb",
      "name": "Retrieve New AR Invoices",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2016,
        112
      ],
      "parameters": {
        "url": "<__PLACEHOLDER_VALUE__https://your-sap-server:50000/b1s/v1/Invoices__>",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "$select",
              "value": "DocNum,DocDate,CardCode,CardName,DocTotal,DocCurrency,Comments"
            },
            {
              "name": "$filter",
              "value": "=UpdateDate gt '{{ $('Calculate Since Timestamp').item.json.since.split('T')[0] }}'"
            },
            {
              "name": "$orderby",
              "value": "DocDate desc"
            },
            {
              "name": "$top",
              "value": "20"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Cookie",
              "value": "=B1SESSION={{ $('Post SAP B1 Login').item.json.SessionId }}; ROUTEID=.node1"
            }
          ]
        }
      },
      "executeOnce": true,
      "typeVersion": 4.3
    },
    {
      "id": "96bb3cbb-434b-4fa7-92d6-5b5fbd2486b7",
      "name": "Parse Invoice Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2256,
        112
      ],
      "parameters": {
        "jsCode": "\nconst records = $input.first().json.value || [];\nreturn records.map(r => ({ json: { ...r, docType: 'AR Invoice' } }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "c3a8fb76-ef6a-48d0-91f5-62dbc1a4ccb1",
      "name": "Check for New Documents",
      "type": "n8n-nodes-base.if",
      "position": [
        2736,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-docs",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.DocNum }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "605d0655-0e9c-4ef3-a7d6-e0af577f8bcb",
      "name": "Prepare WhatsApp Message",
      "type": "n8n-nodes-base.set",
      "position": [
        2976,
        -128
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "to",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__+1234567890__>"
            },
            {
              "id": "2",
              "name": "message",
              "type": "string",
              "value": "={{ \"\ud83d\udd14 *SAP B1 Alert*\\n\\n\" +\"\ud83d\udcc4 *\" + $json.docType + \"* #\" + $json.DocNum + \"\\n\" +\"\ud83d\udcc5 Date: \" + $json.DocDate + \"\\n\" +\"\ud83d\udc64 Customer: \" + $json.CardName + \" (\" + $json.CardCode + \")\\n\" +\"\ud83d\udcb0 Total: \" + $json.DocTotal.toLocaleString() + \" \" + $json.DocCurrency +($json.Comments ? \"\\n\ud83d\udcdd \" + $json.Comments : \"\") }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9ba82f5d-bfd4-42fd-a4c2-e8801a7392ba",
      "name": "Post WhatsApp Alert",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2976,
        48
      ],
      "parameters": {
        "url": "<__PLACEHOLDER_VALUE__https://graph.facebook.com/v19.0/YOUR_PHONE_NUMBER_ID/messages__>",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "jsonBody": "={{ { messaging_product: 'whatsapp', to: $json.to, type: 'text', text: { body: $json.message } } }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "f3c05e51-6b3f-44c3-afaa-b3ab1e16be11",
      "name": "Post SAP B1 Logout",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -16
      ],
      "parameters": {
        "url": "<__PLACEHOLDER_VALUE__https://your-sap-server:50000/b1s/v1/Logout__>",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Cookie",
              "value": "=B1SESSION={{ $('Post SAP B1 Login').item.json.SessionId }}; ROUTEID=.node1"
            }
          ]
        }
      },
      "executeOnce": true,
      "typeVersion": 4.3
    },
    {
      "id": "1f953376-2068-48a1-b73f-c9f10514d7a2",
      "name": "Store Last Checked Timestamp",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3456,
        -16
      ],
      "parameters": {
        "columns": {
          "value": {
            "mappingValues": [
              {
                "fieldId": "configKey",
                "fieldValue": "sap_b1_alert_last_checked"
              },
              {
                "fieldId": "lastCheckedAt",
                "fieldValue": "={{ $('Calculate Since Timestamp').item.json.now }}"
              }
            ]
          },
          "mappingMode": "defineBelow"
        },
        "filters": {
          "conditions": [
            {
              "keyName": "configKey",
              "keyValue": "sap_b1_alert_last_checked"
            }
          ]
        },
        "options": {},
        "matchType": "allConditions",
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "TysbEO2uPKsfHc4F"
        }
      },
      "executeOnce": true,
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "be98f57a-87a9-4e50-b639-d4adb3bff06a",
  "connections": {
    "Post SAP B1 Login": {
      "main": [
        [
          {
            "node": "Retrieve New Sales Orders",
            "type": "main",
            "index": 0
          },
          {
            "node": "Retrieve New AR Invoices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Invoice Data": {
      "main": [
        [
          {
            "node": "Combine Orders and Invoices",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Post SAP B1 Logout": {
      "main": [
        [
          {
            "node": "Store Last Checked Timestamp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post WhatsApp Alert": {
      "main": [
        [
          {
            "node": "Post SAP B1 Logout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Fetch Last Checked Timestamp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for New Documents": {
      "main": [
        [
          {
            "node": "Prepare WhatsApp Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Post SAP B1 Logout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Sales Orders Data": {
      "main": [
        [
          {
            "node": "Combine Orders and Invoices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare WhatsApp Message": {
      "main": [
        [
          {
            "node": "Post WhatsApp Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve New AR Invoices": {
      "main": [
        [
          {
            "node": "Parse Invoice Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Since Timestamp": {
      "main": [
        [
          {
            "node": "Post SAP B1 Login",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve New Sales Orders": {
      "main": [
        [
          {
            "node": "Parse Sales Orders Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Orders and Invoices": {
      "main": [
        [
          {
            "node": "Check for New Documents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Last Checked Timestamp": {
      "main": [
        [
          {
            "node": "Calculate Since Timestamp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}