---
title: Connecting n8n and Letta | Letta Docs
description: Integrate Letta agents with n8n workflows for intelligent automation.
---

Letta’s stateful AI agents can enrich n8n workflows. This guide demonstrates how to create a routing system for customer support tickets by integrating n8n automation with a Letta agent. The agent remembers customer history, learns routing patterns, and provides intelligent recommendations.

Both n8n and Letta can be self-hosted if you need to run them on your own infrastructure (we’ll cover self-hosting in a future guide).

## What you’ll build

You’ll create the following customer support workflow, and in doing so, learn to add Letta agents to n8n automations:

```
graph LR
    A[Google Sheets<br/>New row] --> B[Letta agent<br/>Intelligence layer]
    B --> C[Slack<br/>Notification]
```

The Letta agent sits between your trigger and action, analyzing each ticket with full context from previous interactions. You’ll build this visually in the n8n canvas by connecting nodes — no complex configuration required.

## Prerequisites

To follow along, you need:

- **A [Letta API key](https://app.letta.com/api-keys):** For accessing the Letta API
- **A [n8n Cloud](https://n8n.io) account:** For workflow automation (14-day free trial with paid plans available)
- **[Google Sheets](https://sheets.google.com):** For the ticket submission trigger
- **[Slack](https://slack.com) (or any messaging app n8n supports):** For team notifications
- **Python** (version 3.8 or later) or **Node.js** (version 18 or later)
- **A code editor**

### Get your API key

You need an API key for Letta.

Get your Letta API key

1. **Create a Letta account**

   If you don’t have one, sign up for a free account at [letta.com](https://www.letta.com).

2. **Navigate to API keys**

   Once logged in, click on **API keys** in the sidebar. ![Letta API key navigation](/images/letta-n8n/letta-api-key-nav.png)

3. **Create and copy your key**

   Click **+ Create API key**, give it a descriptive name (such as `n8n Integration`), and click **Confirm**. Copy the key and save it somewhere safe.

Once you have these credentials, create a `.env` file in your project directory and add your API key as an environment variable:

Terminal window

```
LETTA_API_KEY=your_letta_api_key_here
```

### Prepare the dependencies

1. If you don’t have an account, sign up at [n8n.io](https://n8n.io) to get a 14-day free trial of n8n Cloud.

2. Create a new Google Sheet called `Support Tickets` with the following columns:

   - `Ticket ID` (Column A)
   - `Customer Name` (Column B)
   - `Customer Email` (Column C)
   - `Subject` (Column D)
   - `Description` (Column E)
   - `Priority` (Column F)

3. Make sure you have access to a Slack workspace where you can receive notifications (you’ll connect the workspace to n8n in Step 2).

## Step 1: Create the Letta agent

First, configure a Letta agent with memory blocks for customer history and routing knowledge.

### Install the dependencies

- [Python](#tab-panel-407)
- [TypeScript](#tab-panel-408)

Create a `requirements.txt` file that contains the following dependencies:

```
letta-client
python-dotenv
```

Run the following code to install the dependencies:

Terminal window

```
pip install -r requirements.txt
```

Create a `package.json` file with the following contents:

```
{
  "name": "letta-n8n-integration",
  "version": "1.0.0",
  "description": "TypeScript code for integrating Letta agents with n8n workflows",
  "type": "module",
  "scripts": {
    "create-agent": "tsx create-agent.ts",
    "test-ticket": "tsx test-ticket.ts"
  },
  "dependencies": {
    "@letta-ai/letta-client": "latest",
    "dotenv": "^16.3.1"
  },
  "devDependencies": {
    "@types/node": "^20.10.0",
    "tsx": "^4.7.0",
    "typescript": "^5.3.0"
  }
}
```

Create a `tsconfig.json` file with the following contents:

```
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "strict": true,
    "outDir": "./dist",
    "rootDir": "."
  },
  "include": [
    "*.ts"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}
```

Install the dependencies:

Terminal window

```
npm install
```

### Create the agent script

Create a script to initialize your Letta agent with three memory blocks:

- A `persona` block to define the agent’s role and behavior
- A `routing_knowledge` block to track ticket-routing patterns
- A `customer_history` block to maintain the memory of customer interactions

* [Python](#tab-panel-409)
* [TypeScript](#tab-panel-410)

Create a file named `create-agent.py` and add the following code to it:

```
import os
from letta_client import Letta
from dotenv import load_dotenv


# Load environment variables
load_dotenv()


def create_support_agent():
    """Create and configure a support ticket routing agent."""


    # Initialize the Letta client
    api_key = os.getenv("LETTA_API_KEY")
    if not api_key:
        raise ValueError("LETTA_API_KEY environment variable not set. Please add it to your .env file.")


    client = Letta(api_key=api_key)
    print("✓ Client initialized")


    # Create the agent with memory blocks
    agent = client.agents.create(
        name="Support Ticket Router",
        description="Intelligent support ticket routing assistant with customer memory and learning capabilities",
        memory_blocks=[
            {
                "label": "persona",
                "value": """I am an intelligent support ticket routing assistant. My role is to:
- Analyze incoming support tickets with full context
- Remember customer history across all interactions
- Learn which routing decisions lead to fastest resolutions
- Provide detailed routing recommendations with reasoning
- Continuously improve my routing accuracy over time


I always provide structured responses with routing recommendations, priority levels, and context."""
            },
            {
                "label": "routing_knowledge",
                "value": """I track patterns about ticket routing and resolutions:
- Which teams handle which types of issues most effectively
- Average resolution times for different issue categories
- Patterns in urgent vs routine tickets
- Success rates of different routing decisions


I learn from each ticket to improve future routing decisions."""
            },
            {
                "label": "customer_history",
                "value": """I maintain memory of customer interactions:
- Previous tickets and their resolutions
- Communication preferences (technical level, urgency patterns)
- Account-specific issues and patterns
- Customer satisfaction indicators


I use this history to provide personalized, context-aware routing."""
            }
        ]
    )


    print(f"Agent created: {agent.id}")
    print(f"\nAdd this to your .env file:")
    print(f"LETTA_AGENT_ID={agent.id}")


    return agent


if __name__ == "__main__":
    try:
        agent = create_support_agent()
    except Exception as e:
        print(f"\nError creating agent: {e}")
        raise
```

Create a file named `create-agent.ts` and add the following code to it:

```
import Letta from '@letta-ai/letta-client';
import * as dotenv from 'dotenv';


// Load environment variables
dotenv.config();


async function createSupportAgent() {
    /**
     * Create and configure a support ticket routing agent.
     */


    // Initialize the Letta client
    const apiKey = process.env.LETTA_API_KEY;
    if (!apiKey) {
        throw new Error("LETTA_API_KEY environment variable not set. Please add it to your .env file.");
    }


    const client = new Letta({ apiKey: apiKey });
    console.log("✓ Client initialized");


    // Create the agent with memory blocks
    const agent = await client.agents.create({
        name: "Support Ticket Router",
        description: "Intelligent support ticket routing assistant with customer memory and learning capabilities",
        memory_blocks: [
            {
                label: "persona",
                value: `I am an intelligent support ticket routing assistant. My role is to:
- Analyze incoming support tickets with full context
- Remember customer history across all interactions
- Learn which routing decisions lead to fastest resolutions
- Provide detailed routing recommendations with reasoning
- Continuously improve my routing accuracy over time


I always provide structured responses with routing recommendations, priority levels, and context.`
            },
            {
                label: "routing_knowledge",
                value: `I track patterns about ticket routing and resolutions:
- Which teams handle which types of issues most effectively
- Average resolution times for different issue categories
- Patterns in urgent vs routine tickets
- Success rates of different routing decisions


I learn from each ticket to improve future routing decisions.`
            },
            {
                label: "customer_history",
                value: `I maintain memory of customer interactions:
- Previous tickets and their resolutions
- Communication preferences (technical level, urgency patterns)
- Account-specific issues and patterns
- Customer satisfaction indicators


I use this history to provide personalized, context-aware routing.`
            }
        ]
    });


    console.log(`Agent created: ${agent.id}`);
    console.log(`\nAdd this to your .env file:`);
    console.log(`LETTA_AGENT_ID=${agent.id}`);


    return agent;
}


// Run the script
createSupportAgent().catch((error) => {
    console.error(`\nError creating agent: ${error.message}`);
    process.exit(1);
});
```

### Run the agent creation script

- [Python](#tab-panel-403)
- [TypeScript](#tab-panel-404)

Terminal window

```
python create-agent.py
```

Terminal window

```
npx tsx create-agent.ts
```

It should return an output similar to the following:

```
Client initialized
Agent created: agent-abc123def456


Add this to your .env file:
LETTA_AGENT_ID=agent-abc123def456
```

### Save the agent ID

Copy your agent ID from the output and add it to your `.env` file as follows:

Terminal window

```
LETTA_API_KEY=your_letta_api_key_here
LETTA_AGENT_ID=agent-abc123def456
```

### Test your agent

Before connecting to n8n, test whether your agent analyzes tickets correctly.

Create the test script

This script sends sample support tickets to your agent and displays the routing recommendations.

- [Python](#tab-panel-411)
- [TypeScript](#tab-panel-412)

Create a file named `test-ticket.py` and add the following code to it:

```
import os
import json
from letta_client import Letta
from dotenv import load_dotenv


# Load environment variables
load_dotenv()


# Sample test tickets
SAMPLE_TICKETS = [
    {
        "customer_name": "John Doe",
        "customer_email": "john.doe@example.com",
        "subject": "Cannot log in to dashboard",
        "description": "I'm getting a 500 error when trying to log in. This is the third time this month.",
        "priority": "High"
    },
    {
        "customer_name": "Sarah Smith",
        "customer_email": "sarah.smith@company.com",
        "subject": "Question about billing",
        "description": "I was charged twice for my subscription this month. Can someone look into this?",
        "priority": "Medium"
    },
    {
        "customer_name": "Mike Johnson",
        "customer_email": "mike.j@startup.io",
        "subject": "Feature request",
        "description": "Would love to see dark mode added to the app. Many users have been asking for this.",
        "priority": "Low"
    }
]


def format_ticket_message(ticket):
    """Format a ticket into a message for the Letta agent."""
    return f"""New support ticket received:


Customer: {ticket['customer_name']} ({ticket['customer_email']})
Subject: {ticket['subject']}
Description: {ticket['description']}
Priority: {ticket['priority']}


Please analyze this ticket and provide:
1. Recommended team to route to
2. Adjusted priority level (if needed)
3. Your reasoning based on customer history and patterns
4. A suggested response message
5. Estimated resolution time"""


def send_ticket_to_agent(client, agent_id, ticket):
    """Send a ticket to the Letta agent and get routing recommendation."""


    # Send message to agent
    message_content = format_ticket_message(ticket)


    response = client.agents.messages.create(
        agent_id=agent_id,
        messages=[
            {
                "role": "user",
                "content": message_content
            }
        ]
    )


    # Extract and display the agent's response
    for message in response.messages:
        if message.message_type == 'assistant_message':
            print(message.content)
            print()


    return response


def main():
    """Main test function."""


    # Get credentials from environment
    api_key = os.getenv("LETTA_API_KEY")
    agent_id = os.getenv("LETTA_AGENT_ID")


    if not api_key:
        raise ValueError("LETTA_API_KEY not set. Add it to your .env file.")


    if not agent_id:
        raise ValueError("LETTA_AGENT_ID not set. Run create-agent.py first and add the ID to your .env file.")


    # Initialize client
    client = Letta(api_key=api_key)
    print("Client initialized")
    print(f"Using agent: {agent_id}\n")


    # Test with sample tickets
    for i, ticket in enumerate(SAMPLE_TICKETS, 1):
        print(f"Processing ticket {i}/{len(SAMPLE_TICKETS)}: {ticket['subject']}")
        try:
            send_ticket_to_agent(client, agent_id, ticket)
        except Exception as e:
            print(f"Failed: {e}")
            continue


if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"Error: {e}")
        raise
```

Create a file named `test-ticket.ts` and add the following code to it:

```
import Letta from '@letta-ai/letta-client';
import * as dotenv from 'dotenv';


// Load environment variables
dotenv.config();


interface Ticket {
    customer_name: string;
    customer_email: string;
    subject: string;
    description: string;
    priority: string;
}


// Sample test tickets
const SAMPLE_TICKETS: Ticket[] = [
    {
        customer_name: "John Doe",
        customer_email: "john.doe@example.com",
        subject: "Cannot log in to dashboard",
        description: "I'm getting a 500 error when trying to log in. This is the third time this month.",
        priority: "High"
    },
    {
        customer_name: "Sarah Smith",
        customer_email: "sarah.smith@company.com",
        subject: "Question about billing",
        description: "I was charged twice for my subscription this month. Can someone look into this?",
        priority: "Medium"
    },
    {
        customer_name: "Mike Johnson",
        customer_email: "mike.j@startup.io",
        subject: "Feature request",
        description: "Would love to see dark mode added to the app. Many users have been asking for this.",
        priority: "Low"
    }
];


function formatTicketMessage(ticket: Ticket): string {
    /**
     * Format a ticket into a message for the Letta agent.
     */
    return `New support ticket received:


Customer: ${ticket.customer_name} (${ticket.customer_email})
Subject: ${ticket.subject}
Description: ${ticket.description}
Priority: ${ticket.priority}


Please analyze this ticket and provide:
1. Recommended team to route to
2. Adjusted priority level (if needed)
3. Your reasoning based on customer history and patterns
4. A suggested response message
5. Estimated resolution time`;
}


async function sendTicketToAgent(client: Letta, agentId: string, ticket: Ticket) {
    /**
     * Send a ticket to the Letta agent and get routing recommendation.
     */


    // Send message to agent
    const messageContent = formatTicketMessage(ticket);


    const response = await client.agents.messages.create(agentId, {
        messages: [
            {
                role: "user",
                content: messageContent
            }
        ]
    });


    // Extract and display the agent's response
    for (const message of response.messages) {
        if (message.message_type === 'assistant_message') {
            console.log((message as any).content);
            console.log();
        }
    }


    return response;
}


async function main() {
    /**
     * Main test function.
     */


    // Get credentials from environment
    const apiKey = process.env.LETTA_API_KEY;
    const agentId = process.env.LETTA_AGENT_ID;


    if (!apiKey) {
        throw new Error("LETTA_API_KEY not set. Add it to your .env file.");
    }


    if (!agentId) {
        throw new Error("LETTA_AGENT_ID not set. Run create-agent.ts first and add the ID to your .env file.");
    }


    // Initialize client
    const client = new Letta({ apiKey: apiKey });
    console.log("Client initialized");
    console.log(`Using agent: ${agentId}\n`);


    // Test with sample tickets
    for (let i = 0; i < SAMPLE_TICKETS.length; i++) {
        const ticket = SAMPLE_TICKETS[i];
        console.log(`Processing ticket ${i + 1}/${SAMPLE_TICKETS.length}: ${ticket.subject}`);


        try {
            await sendTicketToAgent(client, agentId, ticket);
        } catch (error: any) {
            console.error(`Failed: ${error.message}`);
            continue;
        }
    }
}


// Run the script
main().catch((error) => {
    console.error(`Error: ${error.message}`);
    process.exit(1);
});
```

Run the test script

- [Python](#tab-panel-405)
- [TypeScript](#tab-panel-406)

Terminal window

```
python test-ticket.py
```

Terminal window

```
npx tsx test-ticket.ts
```

You should see intelligent routing recommendations for each ticket:

```
Client initialized
Using agent: agent-abc123def456


Processing ticket 1/3: Cannot log in to dashboard
Based on the ticket analysis:


Recommended Team: Engineering - Backend
Priority Level: High
Reasoning: Customer John Doe has reported login issues...


Estimated Resolution Time: 2-4 hours


Processing ticket 2/3: Question about billing
...
```

If you see context-aware responses, your agent is working correctly.

View your agent in the ADE

You can also view your agent in Letta’s Agent Development Environment (ADE), a visual interface for managing and inspecting agents.

1. Go to <https://app.letta.com> and sign in with your Letta account.

2. You’ll see your agents list. Find your **Support Ticket Router** agent.

   ![Agent list](/images/letta-n8n/n8n-agent-list.png)

3. Click on the agent to open it in the ADE.

   ![Agent in ADE](/images/letta-n8n/n8n-agent-ade-view.png)

4. In the ADE, you can inspect your agent’s configuration, including its:

   - **Memory blocks:** In the **Core Memory** panel on the right (**persona**, **routing\_knowledge**, and **customer\_history**)
   - **System instructions:** In the **Agent Settings** panel on the left (and other behavior settings)
   - **Chat interface:** In the center, where the agent processes tickets

5. If you ran the test script, you can see the conversation history and responses in the chat interface.

   ![Agent chat history in ADE](/images/letta-n8n/n8n-agent-chat-history.png)

The ADE provides full visibility into your agent’s configuration and state. When your n8n workflow runs, you can return to the ADE to see how the agent’s memory evolves as it processes real support tickets.

[Learn more about the Agent Development Environment →](/guides/ade/overview/index.md)

## Step 2: Configure n8n

Now we’ll connect your Letta agent to n8n to create the automated workflow.

### Create the Google Sheets trigger

1. **Create a new workflow**

   In your n8n instance, click **Create workflow** or navigate to an existing workflow.

   ![Create n8n workflow](/images/letta-n8n/n8n-create-workflow.png)

2. **Add the Google Sheets trigger**

   - Click the **+** button on the canvas.
   - Search for and select **Google Sheets**.
   - In the **Trigger On** dropdown, select **Row Added**.
   - In the **Credential to connect with** dropdown, select the dropdown, then select **+ Create new credential**, and click **Sign in with Google**.
   - In the **Document** dropdown, select your **Support Tickets** spreadsheet.
   - In the **Sheet** dropdown, select **Sheet1** (or whatever you’ve named your sheet).
   - Navigate to the **Settings** tab and toggle **Always Output Data** on.

   ![Google Sheets trigger setup](/images/letta-n8n/n8n-sheets-trigger-setup.png)

3. **Test the trigger**

   - Add a sample row to your Google Sheet with test data.
   - Click **Fetch Test Event** in n8n.
   - n8n should find your sample row and display the data.
   - Verify that the field names match your column headers.

   ![Test Google Sheets trigger](/images/letta-n8n/n8n-test-sheets-trigger.png)

### Add the Letta agent action

Now you’ll add the next step to your workflow. This step sends the ticket data to your Letta agent for analysis.

1. **Add an HTTP request node**

   - Click the **+** button on the canvas, after the Google Sheets node.
   - Search for and select **HTTP Request**.

2. **Configure the request method and URL**

   - In the **Method** dropdown, select **POST**.
   - In the **URL** field, enter the following:

   ```
   https://api.letta.com/v1/agents/YOUR_AGENT_ID_HERE/messages
   ```

   Replace `YOUR_AGENT_ID_HERE` with the agent ID from Step 1.

3. **Add the headers**

   - Toggle **Send Headers** on.
   - Add the Authorization header by entering `Authorization` for the **Name** and `Bearer YOUR_LETTA_API_KEY` for the **Value**. (Replace `YOUR_LETTA_API_KEY` with your actual Letta API key.)
   - Click **Add Parameter** to add another header, and enter `Content-Type` for the **Name** and `application/json` for the **Value**.

   ![HTTP request headers](/images/letta-n8n/n8n-http-headers.png)

4. **Configure the request body**

   - Toggle **Send Body** on.

   - In the **Body Content Type** dropdown, select **Using JSON**

   - In the **Specify Body** dropdown, select **Using JSON**

   - Click into the **JSON** field, then copy and paste the following contents of the request body:

   ```
   {
     "messages": [
       {
         "role": "user",
         "content": "New support ticket received:\n\nCustomer: {{ $json['Customer Name'] }} ({{ $json['Customer Email'] }})\nSubject: {{ $json.Subject }}\nDescription: {{ $json.Description }}\nPriority: {{ $json.Priority }}\n\nPlease analyze this ticket and provide:\n1. Recommended team to route to\n2. Adjusted priority level (if needed)\n3. Your reasoning based on customer history and patterns\n4. A suggested response message\n5. Estimated resolution time"
       }
     ]
   }
   ```

   n8n uses `{{ $json.FieldName }}` syntax to reference data from the previous node. If your column headers have spaces, use bracket notation like `{{ $json['Customer Name'] }}`. Fields without spaces can use dot notation like `{{ $json.Subject }}`.

   ![HTTP request body configuration](/images/letta-n8n/n8n-http-body.png)

5. **Test the HTTP request**

   - Click **Execute step**.
   - Wait for the response (it may take a few seconds).
   - You should see a response with a `messages` array.
   - Expand the response to verify that the agent’s routing recommendation is present in `messages[0].content`.

   You can also verify whether the test worked by checking your agent in the ADE at <https://app.letta.com>. You should see the test ticket in the conversation history.

   ![HTTP request test response](/images/letta-n8n/n8n-http-test-response.png)

### Add the Slack notification action

1. **Add a Slack node**

   - Click the **+** button after the HTTP Request node.
   - Search for and select **Slack**.
   - Choose **Send a Message** as the action.

2. **Configure Slack authentication**

   - In the **Credential to connect with** dropdown, select **+ Create new credential**.
   - Click **Connect my account**.
   - Follow the prompts to authorize n8n to access your Slack workspace.

3. **Configure the Slack message**

   - In the **Resource** dropdown, select **Message**.
   - In the **Operation** dropdown, select **Send**.
   - In the **Send Message To** dropdown, select **Channel**.
   - Under **Channel**, select or type the name of your channel (for example, `#support`).
   - Click into the **Message Text** field and build your message as follows:

   ```
   *New Support Ticket*


   *Customer:* {{ $('Google Sheets Trigger').item.json['Customer Name'] }} ({{ $('Google Sheets Trigger').item.json['Customer Email'] }})
   *Subject:* {{ $('Google Sheets Trigger').item.json.Subject }}
   *Description:* {{ $('Google Sheets Trigger').item.json.Description }}
   *Priority:* {{ $('Google Sheets Trigger').item.json.Priority }}


   ---


   *Letta AI Recommendation:*


   {{ $json.messages[0].content }}


   ---


   View Ticket in Sheets
   ```

   To reference data from the Google Sheets trigger, use `{{ $('Google Sheets Trigger').item.json.FieldName }}`. To reference the Letta agent’s response from the current node input, use `{{ $json.messages[0].content }}`.

4. **Test the Slack message**

   - Click **Execute step**.

   ![Slack n8n test](/images/letta-n8n/n8n-execute.slack-test.png)

   - Check your Slack channel.
   - You should see a formatted message with the ticket details and agent recommendation.

   ![Slack test message](/images/letta-n8n/n8n-slack-test.png)

### Activate your workflow

1. **Save your workflow**

   Click **Save** in the top right corner and name your workflow (for example, `Intelligent Support Ticket Routing`).

2. **Activate the workflow**

   Toggle the workflow to **Active** using the toggle in the top right corner.

   ![Activate workflow](/images/letta-n8n/n8n-activate-workflow.png)

Your n8n workflow is now live and will process new support tickets automatically.

## Step 3: Test the complete flow

Add an example ticket to your Google Sheet:

| Ticket ID | Customer Name | Customer Email      | Subject               | Description                      | Priority |
| --------- | ------------- | ------------------- | --------------------- | -------------------------------- | -------- |
| 1         | James Doe     | <james@example.com> | Cannot access account | Getting an error when logging in | High     |

Wait one to two minutes for n8n to detect the new row (the polling interval varies). You should receive a Slack notification with the following information:

- The ticket details from Google Sheets
- Letta’s intelligent routing recommendation with reasoning
- A suggested response message
- An estimated resolution time

Try adding another ticket from the same customer. The agent will remember the first interaction and provide context-aware routing based on the customer’s history.

![Complete flow example](/images/letta-n8n/n8n-complete-flow.png)

## Next steps

Now that you’ve integrated a Letta agent into your n8n workflow, you can apply this same pattern to other use cases, such as lead qualification, content moderation, or expense approval.

Both n8n and Letta support self-hosted deployment, giving you full ownership of your automation infrastructure. We’ll cover how to self-host both platforms in a future guide.

Explore these guides to see how you can extend what you’ve built:

Custom tools

Learn how to create custom tools to extend your agent’s capabilities beyond message handling.

Memory management

Explore how to customize memory blocks for different workflow requirements.
