AutomationFlowsData & Sheets › Tutor English Chat Messages Using Openrouter with Postgres Memory and Log to…

Tutor English Chat Messages Using Openrouter with Postgres Memory and Log to…

Original n8n title: Tutor English Chat Messages Using Openrouter with Postgres Memory and Log to Supabase

ByKanishka Shrivastava @kanishka-shrivastava on n8n.io

This workflow receives English practice messages via a webhook, uses an OpenRouter chat model with Postgres-backed conversation memory to generate tutoring feedback, logs the exchange to Supabase, and returns the tutor’s reply as a JSON response. Receives a POST webhook request…

Webhook trigger★★★★☆ complexityAI-powered15 nodesAgentOpenRouter ChatMemory Postgres ChatSupabase
Data & Sheets Trigger: Webhook Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → OpenRouter Chat recipe pattern — see all workflows that pair these two integrations.

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": "FUpZPZE9KCnvVEti",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Tutor English messages from webhook using OpenRouter and Postgres memory",
  "tags": [],
  "nodes": [
    {
      "id": "bfa8e352-a943-4198-9cf5-2997a603a9a0",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -80,
        0
      ],
      "parameters": {
        "path": "e5626949-1022-4883-a045-3d118685694d",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "77cfcd9c-d5cb-4147-a517-1c5fce89749d",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1232,
        0
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"reply\": {{ JSON.stringify($json.message) }}\n}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "23a20a79-7d83-45a1-b081-0e0fb09a5f8e",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        240,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3d9a3593-0bae-45c7-a9e4-261f9aec0e00",
              "name": "user_id",
              "type": "string",
              "value": "={{$json.body.user_id}}"
            },
            {
              "id": "dc3f3d15-a058-4479-a11c-363acf3d24c1",
              "name": "message",
              "type": "string",
              "value": "={{$json.body.message}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ec854594-cea9-4e7c-8773-9f94f0de673d",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        496,
        0
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "options": {
          "systemMessage": "You are an advanced AI English tutor.\n\nRules:\n- remember previous conversations\n- help improve grammar\n- encourage confidence\n- explain mistakes simply\n- sound warm and natural"
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "1edfd4e2-5af4-4f9f-a03f-daf234fab7a9",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        384,
        240
      ],
      "parameters": {
        "model": "poolside/laguna-m.1:free",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3b424b31-876b-49ae-b424-3ace1979acab",
      "name": "Postgres Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
      "position": [
        752,
        240
      ],
      "parameters": {
        "sessionKey": "={{ $json.user_id }}",
        "sessionIdType": "customKey"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "51ae8112-8b54-4061-8273-7e934513a937",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        -304
      ],
      "parameters": {
        "width": 352,
        "height": 464,
        "content": "## \ud83c\udf93 AI English Tutor\n\nThis workflow creates a conversational AI English tutor that remembers \neach student's history across sessions.\n\n**How to use:**\nPOST to /webhook/ai-tutor with:\n{\n  \"user_id\": \"student_123\",\n  \"message\": \"I goed to the market yesterday.\"\n}\n\n**What you need:**\n- OpenRouter API key (free tier works)\n- PostgreSQL database (for memory)\n- Supabase project with a `conversations` table\n  (columns: user_id, role, message)\n\n**To customize:**\n- Change the model in the OpenRouter node\n- Edit the system prompt in the AI Agent node\n- Swap Supabase for any other database node"
      },
      "typeVersion": 1
    },
    {
      "id": "63696c52-4333-42dd-ba02-e5ed59594f07",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -112,
        -256
      ],
      "parameters": {
        "color": 6,
        "width": 224,
        "height": 240,
        "content": "## 1. Webhook\nListens for POST requests at /webhook/ai-tutor.\n\nExpected body:\n{\n  \"user_id\": \"string\",\n  \"message\": \"string\"\n}\n\nThe user_id is used to track memory per student."
      },
      "typeVersion": 1
    },
    {
      "id": "71fbef4d-dfa0-4b8b-af90-8426cac75ecc",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        -256
      ],
      "parameters": {
        "color": 6,
        "width": 224,
        "height": 240,
        "content": "## 2. Edit Fields\nExtracts user_id and message from the webhook body \nand passes them cleanly to the AI Agent.\n\n\u2699\ufe0f If you rename your input fields, update the \nexpressions here to match."
      },
      "typeVersion": 1
    },
    {
      "id": "0a57812b-0f47-45f6-9911-d77dd80d02c8",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        -368
      ],
      "parameters": {
        "color": 6,
        "width": 304,
        "height": 352,
        "content": "## 3. AI Agent\nThe core of the tutor. Sends the student's message \nto the LLM with a tutor system prompt.\n\nThe agent:\n- Corrects grammar with simple explanations\n- Remembers past conversations (via Postgres Memory)\n- Responds with encouragement and warmth\n\n\u270f\ufe0f Edit the system prompt here to change the \ntutor's subject, tone, or rules."
      },
      "typeVersion": 1
    },
    {
      "id": "24715753-6430-4c7a-ac6f-523b9b75b7f4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        384
      ],
      "parameters": {
        "color": 6,
        "width": 336,
        "height": 272,
        "content": "## 3a. OpenRouter Chat Model\nConnects the AI Agent to an LLM via OpenRouter.\n\nDefault model: poolside/laguna-m.1:free\n\n\u270f\ufe0f Swap the model slug to use GPT-4o, Claude, \nGemini, or any other OpenRouter-supported model.\n\n\ud83d\udd11 Add your OpenRouter API key in the credentials."
      },
      "typeVersion": 1
    },
    {
      "id": "c450eefa-3176-49af-9c0d-3a3724f3c997",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        384
      ],
      "parameters": {
        "color": 6,
        "width": 336,
        "height": 272,
        "content": "## 3b. Postgres Chat Memory\nStores conversation history per student in Postgres.\n\nMemory is keyed by user_id, so each student gets \ntheir own persistent session \u2014 even across days.\n\n\ud83d\udd11 Add your Postgres credentials.\nMake sure the database is accessible from your \nn8n instance."
      },
      "typeVersion": 1
    },
    {
      "id": "cd54a075-2640-4792-9847-9defd3b9c0df",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        -368
      ],
      "parameters": {
        "color": 6,
        "width": 272,
        "height": 352,
        "content": "## 4. Supabase \n Log Conversation\nSaves each AI reply to the `conversations` table \nin Supabase for record-keeping and analytics.\n\nRequired table columns:\n- user_id (text)\n- role (text)\n- message (text)\n\n\ud83d\udd11 Add your Supabase credentials.\n\u270f\ufe0f Swap this node for Airtable, Google Sheets, \nor any other DB if you prefer."
      },
      "typeVersion": 1
    },
    {
      "id": "713efb6f-2362-41d0-9536-25cd4fc2db54",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1136,
        -336
      ],
      "parameters": {
        "color": 6,
        "width": 304,
        "height": 320,
        "content": "## 5. Respond to Webhook\nReturns the tutor's reply as a JSON response \nto the original caller.\n\nResponse format:\n{\n  \"success\": true,\n  \"reply\": \"Tutor's message here\"\n}\n\n\u270f\ufe0f Edit the response body here if your frontend \nexpects a different format."
      },
      "typeVersion": 1
    },
    {
      "id": "448faf08-0be8-463b-9d0e-df9d9026add8",
      "name": "Log to Supabase",
      "type": "n8n-nodes-base.supabase",
      "position": [
        912,
        0
      ],
      "parameters": {
        "tableId": "conversations",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "user_id",
              "fieldValue": "={{ $('Webhook').item.json.body.user_id }}"
            },
            {
              "fieldId": "role",
              "fieldValue": "={{ $('Webhook').item.json.body.message }}"
            },
            {
              "fieldId": "message",
              "fieldValue": "={{ $json.output }}"
            }
          ]
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "binaryMode": "separate",
    "callerPolicy": "workflowsFromSameOwner",
    "timeSavedMode": "fixed",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "a6e49e32-9b41-4beb-9221-9ec350fe92d7",
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Log to Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Supabase": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "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 English practice messages via a webhook, uses an OpenRouter chat model with Postgres-backed conversation memory to generate tutoring feedback, logs the exchange to Supabase, and returns the tutor’s reply as a JSON response. Receives a POST webhook request…

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

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

• Multi-Layer Message Analysis - Every customer interaction passes through three specialized AI classifiers: privacy detection (identifies sensitive data and security requirements), intent recognition

OpenAI, OpenAI Chat, Tool Think +13
Data & Sheets

⚠️ Self-Hosted n8n Only

Agent, Gmail Tool, Google Sheets Tool +3
Data & Sheets

Splitout Filter. Uses toolWorkflow, lmChatOpenAi, outputParserStructured, manualTrigger. Event-driven trigger; 38 nodes.

Tool Workflow, OpenAI Chat, Output Parser Structured +3
Data & Sheets

Handles GDPR Article 15 (access) and Article 17 (erasure) requests end-to-end — from inbound email to legally-compliant response — with zero manual intervention and a full audit trail. 📬 Monitors Gmai

Gmail Trigger, Agent, OpenAI Chat +5
Data & Sheets

Postgres. Uses openAi, postgres, postgresTool, httpRequest. Webhook trigger; 19 nodes.

OpenAI, Postgres, Postgres Tool +2