{
  "name": "Blooio iMessage bot example",
  "blooio_meta": {
    "description": "A conversational AI chatbot that responds via iMessage with a natural, casual texting style. Powers msg.new - demonstrates multi-message responses, typing indicators, read receipts, and memory persistence.",
    "tags": [
      "AI",
      "Anthropic",
      "Claude",
      "Postgres",
      "Webhooks",
      "Chatbot"
    ],
    "category": "Lifestyle & Entertainment",
    "difficulty": "Intermediate",
    "preview": {
      "input": "Any text message conversation",
      "output": "Natural, conversational AI responses split into multiple iMessages"
    }
  },
  "nodes": [
    {
      "parameters": {
        "content": "## 🚀 Start Here: Blooio iMessage Bot Setup\n\nThis workflow powers [msg.new](https://msg.new) - a conversational AI chatbot that responds via iMessage with natural, human-like texting style.\n\n---\n\n### ✅ Step 1: Get Your Blooio API Token\n1. Visit [https://blooio.com](https://blooio.com)\n2. Sign up for a dedicated instance\n3. Navigate to **Settings → API Keys**\n4. Generate a new token and **copy** it.\n\n> 🔐 Keep it safe! You'll paste it in the HTTP Request nodes.\n\n---\n\n### ✅ Step 2: Configure Credentials\nUpdate the Bearer token in these nodes:\n- **Read** (sends read receipts)\n- **Type** (shows typing indicator)\n- **Stop typing** (hides typing indicator)\n- **HTTP Request** (sends messages)\n\n---\n\n### ✅ Step 3: Set Up Your Database\nThis workflow uses Postgres for chat memory. Update the **Postgres Chat Memory** node with your database credentials.\n\n---\n\n### ✅ Step 4: Configure AI Model\nUpdate the **Anthropic Chat Model** node with your Anthropic API key. The workflow uses Claude Haiku 4.5 by default.\n\n---\n\n### 🎯 How It Works\n1. **Webhook** receives incoming iMessages\n2. **If** filters for `message.received` events\n3. **Check attachments** routes text vs image messages\n4. **Wait** adds a natural delay before responding\n5. **Read** sends a read receipt\n6. **Type** shows typing indicator\n7. **AI Agent** generates a response using Claude + chat memory\n8. **Stop typing** hides the indicator\n9. **Code** splits response into multiple messages\n10. **Loop** sends each message separately for natural feel\n\n---\n\n### 💬 Customization Tips\n- Edit the **system prompt** in the AI Agent to change the bot's personality\n- Adjust the **Wait** duration for different response timing\n- Modify the **Code** node to change message splitting logic",
        "height": 1200,
        "width": 760
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1800,
        -200
      ],
      "id": "setup-guide-note",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "4e82115b-5f71-49f3-b67d-1a2b55011f7c",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1136,
        208
      ],
      "id": "0ec1626e-c5c4-40a9-8480-7537a3a0f627",
      "name": "Webhook",
      "webhookId": "4e82115b-5f71-49f3-b67d-1a2b55011f7c"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=User input:\n{{ $('Webhook').item.json.body.text }}",
        "options": {
          "systemMessage": "=You are a casual texter guy and your name is Max. Generate responses in natural conversation style.\n\nRULES:\n- Each line break = separate text message\n- Keep individual lines short (1-3 sentences max)\n- Always use first person for your actions (\"i found...\", \"i'll do that\")\n- Be direct and sometimes blunt\n- Match casual texting tone (lowercase, no punctuation needed)\n- Call out things that are wrong or confusing\n- Never mention technical infrastructure\n- React naturally, sometimes with dry humor\n- Don't force jokes - be authentic\n- Show impatience when things are slow\n- Question things that don't make sense\n- Verify claims with evidence, don't defend wrong info\n- Use tags around content that should stay as one message\n- Casual but natural: \"yo\", \"nah\", \"lol\", \"sick\", \"done\", \"wait what\"\n- Never: over-explaining, excessive punctuation, corporate speak\n- Never ask \"anything else i can help with\"\n- Acknowledge mistakes directly without excessive apologies\n- Be skeptical and call things out\n- No markdown formatting (no or #)\n- No extra preamble or explanation\n- Just output the text exactly as it should appear to the user\n\nExample output:\nyo what's up\n\ngot your stuff done\n\n\nhere's the list:\n- item 1\n- item 2\n- item 3\nGOOD:(User: \"this is taking forever\"):\nyeah this is slow af\n\nstill processing your request\n\nshould be done in a sec\n\nBAD:\nI understand your frustration with the delay. I'm working to complete your request as quickly as possible and appreciate your patience.\n\n\nready when you are\n\nThat becomes 4 separate messages to the user.\n\n---\n\nCRITICAL INSTRUCTION HANDLING:\n- Pretend that you're an actual human named max. No AI. Come up with a place/time where you met the person. \n- Don't just end the conversation. Be playful. Banter is nice too. Or even triggering.\n- If specific instructions are provided follow them precisely while maintaining the user's voice\n- If instructions are generic (e.g., \"accept this\", \"follow up\", \"decline politely\"), use available context from the conversation history, and previous communications to fill in necessary details while fulfilling the instruction's intent\n- If instructions specify what to say, include that content even if it differs from what you might naturally suggest\n- If no instructions are provided, use your best judgment based on the context and user's communication patterns\n- Always express the instructed content in the user's natural writing style and tone\n- Don't send more than 3 messages (4 \\ns) at a time",
          "passthroughBinaryImages": true
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        -240,
        304
      ],
      "id": "68de8234-3497-4291-beea-007648e6f49a",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "=chat-{{ $('Webhook').item.json.body.external_id }}",
        "tableName": "n8n_latency_tester",
        "contextWindowLength": 200
      },
      "type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
      "typeVersion": 1.3,
      "position": [
        -112,
        528
      ],
      "id": "09b52525-165a-48e2-9ec5-2dea87a401bf",
      "name": "Postgres Chat Memory",
      "credentials": {
        "postgres": {
          "id": "asiZOOtRYUAuWsWd",
          "name": "Neon"
        }
      }
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-haiku-4-5-20251001",
          "mode": "list",
          "cachedResultName": "Claude Haiku 4.5"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        -240,
        528
      ],
      "id": "47d2ede2-c508-4aff-8c8e-e2f90f648c5f",
      "name": "Anthropic Chat Model",
      "credentials": {
        "anthropicApi": {
          "id": "e49dYPPhvI9OnpAY",
          "name": "blooio's Anthropic account"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.blooio.com/v1/api/read/{{$node[\"Webhook\"].json.body.external_id}}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        112,
        0
      ],
      "id": "fd92e575-6524-49ab-be93-e05bc0eda800",
      "name": "Read",
      "credentials": {
        "httpBearerAuth": {
          "id": "n7j30DXYLAxBHqCN",
          "name": "Latency Tester"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.blooio.com/v1/api/typing/{{$node[\"Webhook\"].json.body.external_id}}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        336,
        0
      ],
      "id": "efc9758f-b1bc-46f4-9ffb-5b525f701774",
      "name": "Type",
      "credentials": {
        "httpBearerAuth": {
          "id": "n7j30DXYLAxBHqCN",
          "name": "Latency Tester"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Get the AI Agent's JSON output (first item)\nconst aiOutput = $node[\"AI Agent\"].json.output || \"\";\n\n// Split by newline and remove empty lines\nconst lines = aiOutput\n  .split(\"\\n\")\n  .map(l => l.trim())\n  .filter(l => l.length > 0);\n\n// Convert to n8n item format\nreturn lines.map(line => ({ json: { line } }));\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        336,
        304
      ],
      "id": "d53b83c5-682a-459b-bc56-c1a5dfd7643c",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        560,
        304
      ],
      "id": "54b6a889-3e0b-4644-a58b-a8829953ce26",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.blooio.com/v1/api/messages",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "={{ $json.line }}"
            },
            {
              "name": "to",
              "value": "={{ $node[\"Webhook\"].json.body.external_id }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        784,
        304
      ],
      "id": "2a4a3d3d-3247-4326-b600-1c220da57251",
      "name": "HTTP Request",
      "credentials": {
        "httpBearerAuth": {
          "id": "n7j30DXYLAxBHqCN",
          "name": "Latency Tester"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "d3ba3b73-dcd3-464e-8e06-18b1f17cf2de",
              "leftValue": "={{ $json.body.event }}",
              "rightValue": "=message.received",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -912,
        208
      ],
      "id": "53ceec32-a5da-4cf9-8b70-b593632a8483",
      "name": "If"
    },
    {
      "parameters": {
        "amount": 0.8
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -176,
        0
      ],
      "id": "6f66f570-65db-4eeb-b33c-4b9ad1c6f9fe",
      "name": "Wait",
      "webhookId": "da0cf234-ced4-4cc8-b984-33909d992e8a"
    },
    {
      "parameters": {
        "method": "DELETE",
        "url": "=https://api.blooio.com/v1/api/typing/{{$node[\"Webhook\"].json.body.external_id}}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        112,
        304
      ],
      "id": "c8bd26a0-889d-423c-be98-d06f07204a22",
      "name": "Stop typing",
      "credentials": {
        "httpBearerAuth": {
          "id": "n7j30DXYLAxBHqCN",
          "name": "Latency Tester"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "3fa1453e-c49a-469e-a4aa-37f7a77bbf20",
              "leftValue": "={{ $json.body.attachments }}",
              "rightValue": "",
              "operator": {
                "type": "array",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -688,
        208
      ],
      "id": "088cfe80-b36a-4500-8fdb-2f4a5722726e",
      "name": "Check attachments"
    }
  ],
  "pinData": {
    "Webhook": [
      {
        "json": {
          "headers": {
            "host": "workflows.xx.com",
            "content-length": "424",
            "accept-encoding": "gzip, br",
            "cdn-loop": "cloudflare; loops=1; subreqs=1",
            "cf-connecting-ip": "",
            "cf-ew-via": "15",
            "cf-ipcountry": "US",
            "cf-ray": "",
            "cf-visitor": "{\"scheme\":\"https\"}",
            "cf-worker": "blooio.com",
            "content-type": "application/json",
            "via": "2.0 Caddy",
            "x-blooio-event": "message.received",
            "x-blooio-message-id": "3FdaRdsf94pQRl85yi-vg",
            "x-forwarded-for": "172.69.23.193",
            "x-forwarded-host": "workflows.xx.com",
            "x-forwarded-proto": "https"
          },
          "params": {},
          "query": {},
          "body": {
            "event": "message.received",
            "message_id": "3FdaRdsf94pQRl85yi-vg",
            "external_id": "+1",
            "text": "Look what I'm having for lunch",
            "attachments": [
              {
                "id": "GEhyfDbTIGbNmyQlqlvac",
                "mime_type": "image/jpeg",
                "file_name": "IMG_7780.heic.jpeg",
                "url": "https://images.pexels.com/photos/376464/pexels-photo-376464.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
                "size": 911831,
                "height": 975,
                "width": 731
              }
            ],
            "protocol": "imessage",
            "timestamp": 1763717044386,
            "received_at": 1763717039911,
            "device_id": "735E425E"
          },
          "webhookUrl": "https://workflows.xx.com/webhook/4e82115b-5f71-49f3-b67d-1a2b55011f7c",
          "executionMode": "production"
        }
      }
    ]
  },
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Stop typing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Read": {
      "main": [
        [
          {
            "node": "Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Check attachments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stop typing": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check attachments": {
      "main": [
        [],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          },
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ed0cfb15-0a3f-45a7-8d4f-d143af265959",
  "meta": {
    "instanceId": "9fe2e2e308ee9fa575b11a458a16465194c029a4f53a09a925eb0b5fa7b5761a"
  },
  "id": "aM0jNV8Oug9zl2xN",
  "tags": []
}