{
  "name": "Typeform to Sheets + Slack",
  "nodes": [
    {
      "parameters": {
        "content": "## Typeform Responses to Google Sheets + Slack\n\nTypeform collects the answers; this puts them where work happens - a spreadsheet your team can pivot and a Slack ping so nobody waits for the weekly export.\n\n**Setup time:** 10 minutes  ·  **Level:** Beginner\n\n**You'll need accounts/credentials for:** Typeform, Google Sheets, Slack\n\n**Replace these before running:**\n- `REPLACE_WITH_YOUR_FORM`\n- `REPLACE_WITH_YOUR_SPREADSHEET`\n- `REPLACE_WITH_CHANNEL`\n\n**Setup steps:**\n1. Connect Typeform with a personal access token and select your form in the trigger node.\n2. Connect Google Sheets and pick a spreadsheet - the Code node auto-maps answer titles to columns.\n3. Connect Slack and choose the channel.\n4. Activate the workflow and submit a test response.\n\nFree templates and updates: navkhan103.github.io",
        "height": 640,
        "width": 400,
        "color": 4
      },
      "id": "sticky-setup",
      "name": "Setup — read me first",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -440,
        -40
      ]
    },
    {
      "parameters": {
        "content": "### How it runs\n\nStarts from **New Typeform Response**.\n\n1. A Typeform Trigger receives each completed submission via webhook.\n2. A Code node flattens the answers into clean field: value pairs with a timestamp.\n3. A Google Sheets node appends the row.\n4. A Slack node posts a summary of the key answers to your channel.",
        "height": 348,
        "width": 400,
        "color": 7
      },
      "id": "sticky-how",
      "name": "How it runs",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -440,
        640
      ]
    },
    {
      "parameters": {
        "content": "### If something breaks\n\nThe read and AI nodes here **retry 3× automatically** (5s apart), which absorbs rate limits and brief timeouts.\n\nNodes that **send or write deliberately do not retry** — a send that reached the far end but lost its response would otherwise fire twice, so you'd get duplicate messages or duplicate rows.\n\nTo be told when one of those fails, build a workflow starting with an **Error Trigger** node that posts to Slack or email, then set it under **three-dot menu > Settings > Error Workflow**. Do it once and point every workflow at it.",
        "height": 400,
        "width": 400,
        "color": 3
      },
      "id": "sticky-fail",
      "name": "If something breaks",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -440,
        1028
      ]
    },
    {
      "parameters": {
        "formId": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_YOUR_FORM"
        }
      },
      "id": "e2f3a4b5-0001-4000-8000-000000000001",
      "name": "New Typeform Response",
      "type": "n8n-nodes-base.typeformTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const j = $input.first().json;\nconst row = { Timestamp: new Date().toISOString() };\nfor (const [key, value] of Object.entries(j)) {\n  if (key === 'submitted_at' || key === 'form_id' || key === 'token') continue;\n  row[key] = Array.isArray(value) ? value.join(', ') : String(value ?? '');\n}\nreturn [{ json: row }];"
      },
      "id": "e2f3a4b5-0002-4000-8000-000000000002",
      "name": "Flatten Answers",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_YOUR_SPREADSHEET"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Sheet1"
        },
        "columns": {
          "mappingMode": "autoMapInputData"
        }
      },
      "id": "e2f3a4b5-0003-4000-8000-000000000003",
      "name": "Append to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_CHANNEL"
        },
        "text": "=:inbox_tray: *New form response*\n{{ Object.entries($json).filter(([k]) => k !== 'Timestamp').slice(0, 6).map(([k, v]) => `*${k}:* ${String(v).slice(0, 80)}`).join('\\n') }}",
        "otherOptions": {}
      },
      "id": "e2f3a4b5-0004-4000-8000-000000000004",
      "name": "Post Summary to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        660,
        0
      ]
    }
  ],
  "connections": {
    "New Typeform Response": {
      "main": [
        [
          {
            "node": "Flatten Answers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flatten Answers": {
      "main": [
        [
          {
            "node": "Append to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append to Google Sheets": {
      "main": [
        [
          {
            "node": "Post Summary to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}