agenticoutputs
Builds

Build a WISMO AI Agent with n8n and Supabase

mrmolsen · June 15, 2026 ·12 min read
Build a WISMO AI Agent with n8n and Supabase

You want to cut through the noise of repetitive customer support tickets, freeing your human agents for issues that actually need human insight. A mid-sized e-commerce company recently reported cutting “Where is my order?” inquiries by 60%, reallocating their support team to complex issues. They built the core of it in a day, for pennies per interaction, using tools anyone can access. This article details the exact architecture and step-by-step build.

Find the Right Problem Before You Touch a Tool

Building an agent that delivers real ROI starts with problem selection. The highest-value AI agents share four traits:

  1. High Volume: The problem generates a significant number of inbound requests.
  2. Proprietary Data Dependency: Resolving the issue requires accessing internal company data, not just general knowledge.
  3. API-Resolvable Outcomes: The agent can take a concrete action via an API to resolve the user’s need.
  4. Unambiguous User Intent: The user’s goal is clear, reducing the need for complex conversational turns.

“Where Is My Order?” (WISMO) inquiries are the canonical example of all four. For many e-commerce operations, WISMO represents 30-50% of total support volume. This makes it a prime candidate for automation.

The ROI formula for this kind of agent is simple and calculable within the first week: (autonomous resolutions × avg. handling time in minutes × fully-loaded hourly agent cost) − (LLM API cost + n8n runtime + vector DB cost)

Consider the difference: a generic chatbot might respond, “Our return policy is on our website.” A data-aware agent, however, can state, “Your order #12345 shipped yesterday and is expected on June 28th. Would you like me to initiate a return request if there’s an issue?” The latter provides a specific, actionable resolution, directly impacting customer satisfaction and operational cost.

The Architecture: Brain, Memory, Hands

A production-ready AI agent needs three core components working in concert: a “Brain” for reasoning, “Memory” for context, and “Hands” for action. This isn’t abstract; it’s a specific contract between services. The LLM acts as the Brain, outputting a structured JSON object. A vector database serves as the Memory, supplying retrieved context. Finally, n8n acts as the Hands, parsing that JSON and routing it to the correct API tool.

Our stack for this build is:

  • LLM (Brain): GPT-4o (used in this tutorial’s n8n nodes). Claude Sonnet 4.6 (claude-sonnet-4-6) is a direct drop-in via n8n’s HTTP Request node pointed at api.anthropic.com — the JSON contract and tool-call pattern below work identically.
  • Embeddings: OpenAI’s text-embedding-3-small for vectorizing context. Swap for gemini-embedding-2 or voyage-3 if you want to avoid OpenAI entirely.
  • Vector Database (Memory): Supabase with pgvector for scalable, managed vector storage.
  • Orchestration (Hands): n8n for workflow automation, API calls, and conditional logic.

When a user query hits the system, n8n first embeds it and queries Supabase for relevant context. This context, along with the user query and a prompt detailing available tools, is sent to GPT-4o. The LLM then decides whether to answer directly or call a tool, returning a structured JSON object.

The explicit JSON contract GPT-4o must produce looks like this:

{
  "tool_call": {
    "name": "lookup_order_status",
    "arguments": {
      "order_id": "12345"
    }
  }
}

Or, if no tool is needed:

{
  "answer": "Our return window is 30 days from the purchase date."
}

For the vector store, we’ll chunk our internal data (e.g., FAQs, return policies) into fixed-size segments with overlap. While this can sometimes split context, it’s a pragmatic starting point. Always spot-check retrieval quality before going live; bad chunks lead to bad answers.

Build It: The WISMO Agent Step by Step

This section walks through building the n8n workflow. The goal is a working agent that can fetch order status via the Shopify API or create a support ticket if an order isn’t found.

1. Initial Setup: Webhook and Supabase Connection

Start with an n8n Webhook node. This is your agent’s entry point. Configure it to listen for POST requests.

Next, set up your Supabase credentials in n8n. You’ll need your Supabase Project URL and anon key.

2. Embed and Retrieve Context

Add an OpenAI node. Configure it for text-embedding-3-small. The input will be the user’s query from the Webhook.

{
  "model": "text-embedding-3-small",
  "input": "{{$json.body.text}}"
}

Connect the output of the embedding node to a Supabase node. Set the operation to “Query” on your documents table (assuming you’ve pre-loaded your product/policy data with embeddings). Use a vector_distance query to find the top k relevant chunks.

SELECT
  content,
  metadata
FROM
  documents
ORDER BY
  embedding <-> '{{$json.data[0].embedding}}'
LIMIT 3;

3. The LLM: Reasoning and Tool Selection

This is the core of the agent. Add another OpenAI node, configured for gpt-4o. Use the “Chat” operation and set the “Response Format” to JSON.

The prompt is critical. It defines the agent’s persona, its available tools, and the required JSON output format.

You are a helpful customer support agent for an e-commerce store.
Your goal is to assist users with their inquiries, primarily focusing on order status.
You have access to a tool to look up order information.
Always try to use the `lookup_order_status` tool if the user asks about an order.
If you cannot find an order number in the user's message, ask for it.
If the user's intent is not related to order status, or if the `lookup_order_status` tool fails, you can answer directly based on the provided context or create a support ticket.

Available tools:
- `lookup_order_status`:
  - Description: "Looks up the status of a customer order using the Shopify API."
  - Arguments:
    - `order_id` (string, required): "The unique identifier for the customer's order."
- `create_ticket`:
  - Description: "Creates a new support ticket in the internal system."
  - Arguments:
    - `user_query` (string, required): "The original query from the user."
    - `context` (string, required): "Any relevant context or error messages from previous steps."

When a tool is called, output a JSON object like this:
```json
{
  "tool_call": {
    "name": "tool_name",
    "arguments": {
      "arg1": "value1",
      "arg2": "value2"
    }
  }
}
```

If you decide to answer directly, output a JSON object like this:
```json
{
  "answer": "Your direct answer to the user."
}
```

User query: {{$json.body.text}}

Relevant context from our knowledge base:
{{$json.supabasedata.join('\n')}}

4. Route Based on LLM Output

Connect the OpenAI node to an IF node. This node checks the LLM’s JSON output for tool_call.

Condition 1 (Tool Call): {{$json.tool_call}} is not empty. Condition 2 (Direct Answer): {{$json.answer}} is not empty.

Route accordingly.

5. Execute Tool: Shopify API Lookup

If the LLM outputs a tool_call for lookup_order_status, connect to an HTTP Request node. Configure it for the Shopify API.

Method: GET URL: https://your-shop-name.myshopify.com/admin/api/2024-04/orders.json?name={{$json.tool_call.arguments.order_id}} Headers:

  • X-Shopify-Access-Token: YOUR_SHOPIFY_ACCESS_TOKEN

6. Error Handling and Fallback

After the Shopify HTTP Request node, add another IF node. This is critical for robustness.

Condition: {{$json.statusCode}} is equal to 200 (success path). Else Branch: Handle non-200 responses (error path).

On a non-200 response, route to a new OpenAI node to call the create_ticket tool.

You are a helpful customer support agent.
A user asked about an order, but the Shopify API returned an error or did not find the order.
You need to create a support ticket for this issue.

Available tools:
- `create_ticket`:
  - Description: "Creates a new support ticket in the internal system."
  - Arguments:
    - `user_query` (string, required): "The original query from the user."
    - `context` (string, required): "Any relevant context or error messages from previous steps."

Output a JSON object like this:
```json
{
  "tool_call": {
    "name": "tool_name",
    "arguments": {
      "arg1": "value1",
      "arg2": "value2"
    }
  }
}
```

User query: {{$json.body.text}}
Shopify API response: {{$json.errorMessage}}

Connect this create_ticket LLM call to an HTTP Request node that hits your internal ticketing system (e.g., Zendesk, HubSpot, custom API).

7. Synthesize Final Response

Whether the Shopify API call succeeded or a ticket was created, the agent needs to respond to the user. Add a final OpenAI node for synthesis.

You are a helpful customer support agent.
The Shopify API returned this data: [{{$json.shopify_response_data}}].
Summarize this information clearly and politely for the customer.
If the order was not found or an error occurred, state that a support ticket has been created and that a human agent will follow up shortly.

The output of this node goes back to the user via the Webhook response.

This complete workflow covers the happy path (order found, status returned) and the error path (order not found, ticket created).

What This Pattern Transfers To

The core pattern of Webhook → Structured LLM Call → Conditional Router → API Action → Error Fallback is highly adaptable. While we used n8n, tools like Make or Zapier can substitute as the orchestration layer, though n8n offers more granular control for complex logic.

Here are two immediate analogies for applying this pattern:

  1. SaaS Password Reset / Account Lookup: A user forgets their password or needs to look up account details. The agent receives a query, calls an LLM to extract user ID or email, then uses an internal API to initiate a password reset flow or fetch account data. If the user isn’t found, the agent creates a support ticket for manual verification. This offloads routine account management from human support.

  2. Appointment Rescheduling for Service Businesses: A customer wants to change their appointment time. The agent takes the user’s name and preferred new time, sends it to an LLM for parsing, then calls a scheduling API (e.g., Calendly, custom backend) to check availability and reschedule. If the new time is unavailable or the API returns an error, the agent offers alternative times or opens a ticket for a human to call back.

This pattern is a foundational block for automating many common, data-dependent customer interactions. It ships fast and provides immediate, measurable value.

Share Post on X LinkedIn