{
  "nodes": [
    {
      "id": "d6df5e4d-1ea2-41d4-b13b-08a0c062389a",
      "name": "On form submission1",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -1472,
        1296
      ],
      "parameters": {
        "options": {},
        "formTitle": "Content Approval Request",
        "formFields": {
          "values": [
            {
              "fieldType": "textarea",
              "fieldLabel": "Content Body"
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Approval Channel",
              "fieldOptions": {
                "values": [
                  {
                    "option": "slack"
                  },
                  {
                    "option": "email"
                  }
                ]
              }
            }
          ]
        },
        "formDescription": "Submit AI-generated content for multi-channel approval routing via Slack or Email"
      },
      "typeVersion": 2.5
    },
    {
      "id": "b2a3aa4d-ad53-4fa2-aeeb-758524f32675",
      "name": "AI Agent1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1248,
        1296
      ],
      "parameters": {
        "text": "=Enhance the following content for publishing. Make it more engaging and professional while keeping the original message intact. Return only the enhanced content, nothing else. Keep it short please.\n\nContent: {{ $json['Content Body'] }}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "316833b7-3a56-4c0c-8f18-b3fc9e5433f2",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -1184,
        1520
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "gpt-4o"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "9988eb6a-b3bc-4caf-9583-9d95d82982e9",
      "name": "Route by Channel",
      "type": "n8n-nodes-base.switch",
      "position": [
        -896,
        1296
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Slack",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "fd55a0f7-397f-480b-97f2-b4311713d7bf",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('On form submission1').item.json['Approval Channel'] }}",
                    "rightValue": "slack"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Email",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e0cf2764-9264-4f84-b789-7e6491f2b7bf",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('On form submission1').item.json['Approval Channel'] }}",
                    "rightValue": "email"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "cad71c5b-918c-40a1-ada5-4baf93396386",
      "name": "Slack Approval Request",
      "type": "n8n-nodes-base.slack",
      "position": [
        -672,
        1104
      ],
      "parameters": {
        "user": {
          "__rl": true,
          "mode": "list",
          "value": "U0AB7QP5H1B",
          "cachedResultName": "elvissaravia"
        },
        "message": "=*Content Approval Required*\n\n*Original:*\n{{ $('On form submission1').item.json['Content Body'].substring(0,300) }}\n\n*AI Enhanced:*\n{{ $json.output.substring(0,300) }}",
        "options": {
          "limitWaitTime": {
            "values": {
              "resumeUnit": "minutes",
              "resumeAmount": 2
            }
          }
        },
        "operation": "sendAndWait",
        "authentication": "oAuth2",
        "approvalOptions": {
          "values": {
            "approvalType": "double",
            "disapproveLabel": "Reject"
          }
        }
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5efdfe87-6d20-4fef-ba90-0c224e15b815",
      "name": "Slack Decision",
      "type": "n8n-nodes-base.switch",
      "position": [
        -448,
        1088
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Timed Out",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "77d67b45-6916-4cab-986b-cecc0ece5bfd",
                    "operator": {
                      "type": "string",
                      "operation": "empty",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.data }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Approved",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "557bf7ff-70e9-4732-8620-a7b0e22be3a5",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.data.approved }}",
                    "rightValue": "true"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Rejected",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "6621782e-1c23-4e2e-b125-e962e30fd0b8",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.data.approved }}",
                    "rightValue": "false"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {},
        "looseTypeValidation": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "b9f8c952-c7cc-405d-85f1-84e8d7eba3a9",
      "name": "Publish Content",
      "type": "n8n-nodes-base.code",
      "position": [
        -224,
        1104
      ],
      "parameters": {
        "jsCode": "// Content approved - publish\nconst items = $input.all();\nreturn items.map(item => ({\n  json: {\n    status: 'published',\n    content: item.json.ai_enhanced_content || 'Approved content',\n    published_at: new Date().toISOString()\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "8ac35e39-93c6-49bb-b569-a43b49d4064a",
      "name": "Handle Rejection",
      "type": "n8n-nodes-base.code",
      "position": [
        -224,
        1296
      ],
      "parameters": {
        "jsCode": "// Content rejected - route back for revision\nconst items = $input.all();\nreturn items.map(item => ({\n  json: {\n    status: 'rejected',\n    content: item.json.original_content || 'Rejected',\n    rejected_at: new Date().toISOString(),\n    action: 'Send back to content team for revision'\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "b4914384-c828-470c-bcc5-66890d5c581f",
      "name": "Escalate to Manager",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -224,
        1488
      ],
      "parameters": {
        "sendTo": "ellfae@gmail.com",
        "message": "=A content approval request received no response within 24 hours.\n\nOriginal content submitted:\n{{ $('On form submission1').item.json['Content Body'].substring(0,500) }}\n\nThe request was sent via {{ $('On form submission1').item.json['Approval Channel'] }} and no decision was made within the allowed timeframe.\n\nPlease review and take action.",
        "options": {},
        "subject": "=ESCALATION: Content Approval Timed Out",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "81b339e6-6344-42ea-bb30-40bfa69bbe09",
      "name": "Email Decision",
      "type": "n8n-nodes-base.switch",
      "position": [
        -448,
        1472
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Timed Out",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "06bbf56d-4498-4412-917d-688511b8de9f",
                    "operator": {
                      "type": "string",
                      "operation": "empty",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.data }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Approved",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "ae650af8-9e9e-41ca-a31d-65dfcc1ca3be",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.data.approved }}",
                    "rightValue": "true"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Rejected",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "8a5edea2-7dfc-43f9-99ab-827da9d9f2b6",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.data.approved }}",
                    "rightValue": "false"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {},
        "looseTypeValidation": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "23dbf2b0-b565-4b89-b628-ccb8d8bea5c7",
      "name": "Email Approval Request",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -672,
        1488
      ],
      "parameters": {
        "sendTo": "ellfae@gmail.com",
        "message": "=Content Approval Required\n\nOriginal:\n{{ $('On form submission1').item.json['Content Body'] }}\n\nAI Enhanced:\n{{ $json.output }}\n\nPlease review the content above and approve or reject.",
        "options": {
          "limitWaitTime": {
            "values": {
              "resumeUnit": "minutes"
            }
          }
        },
        "subject": "=Content Approval Required",
        "operation": "sendAndWait",
        "approvalOptions": {
          "values": {
            "approvalType": "double",
            "disapproveLabel": "Reject"
          }
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "090c7433-0d2b-4703-88b0-814eb18347e1",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2368,
        1040
      ],
      "parameters": {
        "width": 752,
        "height": 656,
        "content": "## Multi-Channel Content Approval with Slack and Email Escalation\n\n### How it works\n1. **Form Trigger** collects a content draft and preferred review channel (Slack or Email).\n2. **AI Agent** enhances the submitted content using the connected LLM.\n3. **Route by Channel** (Switch node) directs the approval request to Slack or Email based on the form selection.\n4. **Approval nodes** send the content with Approve/Reject buttons and pause until the reviewer responds or the timeout expires.\n5. **Decision nodes** handle three outcomes: approved routes to publish, rejected routes to handling, timed out escalates to a manager via email.\n\n### Setup\n- Connect your **LLM credentials** (e.g., OpenAI) to the Chat Model node\n- Connect your **Slack credentials** and set the target channel on the Slack Approval Request node\n- Connect your **Gmail credentials** on the Email Approval Request and Escalate to Manager nodes\n- Set timeout durations on each approval node (e.g., 2-4 hours for operational reviews)\n\n### Customization\n- Add more review channels (e.g., Microsoft Teams) by extending the Switch node\n- Replace the Publish Content and Handle Rejection code nodes with your actual publishing logic\n\n\nThis template is a learning companion to the Production AI Playbook, a series that explores strategies, shares best practices, and provides practical examples for building reliable AI systems in n8n.  \n\n[Link to blog](https://go.n8n.io/PAP-HO-Blog)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e629a92b-3259-49f6-8283-5527a834272a",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1536,
        1024
      ],
      "parameters": {
        "color": 7,
        "width": 544,
        "height": 704,
        "content": "## Submit & Enhance\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d4404a82-64ea-42d0-900b-64ccf968754b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        1024
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 704,
        "content": "## Route & Review"
      },
      "typeVersion": 1
    },
    {
      "id": "bee79d99-d6b7-45ab-98eb-b41d739a837e",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        1024
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 704,
        "content": "## Decide & Act"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "AI Agent1": {
      "main": [
        [
          {
            "node": "Route by Channel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Decision": {
      "main": [
        [
          {
            "node": "Escalate to Manager",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Publish Content",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Rejection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Decision": {
      "main": [
        [
          {
            "node": "Escalate to Manager",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Publish Content",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Rejection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Channel": {
      "main": [
        [
          {
            "node": "Slack Approval Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Email Approval Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "On form submission1": {
      "main": [
        [
          {
            "node": "AI Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Approval Request": {
      "main": [
        [
          {
            "node": "Email Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Approval Request": {
      "main": [
        [
          {
            "node": "Slack Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}