n4nAI

Build a customer support chatbot with GPT-4o and n4n.ai

Step-by-step tutorial to build a streaming customer support chatbot with GPT-4o using Next.js, Vercel AI SDK, and n4n.ai's OpenAI-compatible gateway.

n4n Team3 min read669 words

Audio narration

Coming soon — every post will get a voice note here.

Building a customer support chatbot gpt-4o n4n.ai on Next.js takes less than an hour if you use the Vercel AI SDK. This tutorial ships a streaming chat route, a typed client, and a system prompt locked to your support policy. We assume you can run Node 18+ and read TypeScript.

Prerequisites

  • Node.js 18.18 or later
  • A Next.js 14+ project using the App Router
  • An API key from an OpenAI-compatible gateway (this tutorial uses n4n.ai)
  • The Vercel AI SDK (ai + @ai-sdk/openai)
  • Basic familiarity with React Server Components and Route Handlers

If you already have a Next.js app, skip the scaffold step. The only gateway-specific detail is pointing the SDK at a compatible base URL.

Scaffold the Next.js app

npx create-next-app@latest support-chat --ts --app --eslint --src-dir --import-alias "@/*"
cd support-chat

Use the App Router. We will place the API route under src/app/api/chat and the client UI at src/app/page.tsx. Do not use the Pages Router; the Vercel AI SDK’s useChat hook works cleanly with client components, but the streaming response helper expects modern Response semantics.

Install the Vercel AI SDK

npm install ai @ai-sdk/openai zod

@ai-sdk/openai gives you a createOpenAI factory that accepts a custom baseURL. That is the entire integration surface for swapping OpenAI for any compliant gateway.

Configure the gateway endpoint

Create .env.local at the project root:

OPENAI_API_KEY=sk-your-gateway-key
OPENAI_BASE_URL=https://api.n4n.ai/v1

The SDK reads these at runtime. Never expose the key to the browser; only the Route Handler should read process.env. If you deploy to Vercel, set the same vars in the project dashboard.

Build the streaming chat API route

Create src/app/api/chat/route.ts:

import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';

const gateway = createOpenAI({
  baseURL: process.env.OPENAI_BASE_URL,
  apiKey: process.env.OPENAI_API_KEY,
});

export const runtime = 'edge';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = await streamText({
    model: gateway('gpt-4o'),
    system: `You are a customer support agent for Acme Corp.
             Only answer questions about orders, returns, and product specifications.
             If the user needs human help, respond with "OPEN_TICKET".`,
    messages,
  });

  return result.toDataStreamResponse();
}

streamText returns a result object that serializes to the Vercel AI data stream protocol. The client hook consumes this without extra parsing. Running on the Edge runtime keeps cold starts low, but you can drop that line if you need Node APIs.

Wire the client component

Replace src/app/page.tsx with a client component:

'use client';

import { useChat } from 'ai/react';

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();

  return (
    <main style={{ maxWidth: 600, margin: '2rem auto' }}>
      <h1>Acme Support</h1>
      <div>
        {messages.map((m) => (
          <div key={m.id} style={{ margin: '0.5rem 0' }}>
            <strong>{m.role}:</strong> {m.content}
          </div>
        ))}
      </div>
      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask about your order..."
          style={{ width: '80%' }}
        />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
      </form>
    </main>
  );
}

useChat manages message state, input binding, and the fetch to /api/chat. It automatically appends the assistant’s streamed tokens.

Run and verify the first exchange

npm run dev

Open http://localhost:3000. In the input box, send:

Where is my order #12345?

Expected streamed response (truncated):

assistant: I can help with order #12345. It shipped on May 2 and is expected delivery May 5. Would you like the tracking number?

If you see a 401, check that OPENAI_API_KEY is loaded. If you see a 404 on the model, confirm the gateway passes gpt-4o to its upstream.

Add support-specific guardrails

The system prompt above is weak. Lock it down with explicit constraints and a fallback trigger:

system: `You are a customer support agent for Acme Corp.
  Scope: orders, returns, product specs for SKUs starting with "A-".
  Out of scope: billing disputes, legal, medical.
  If out of scope, reply exactly "OPEN_TICKET".
  Never invent order statuses. If unknown, say "I need to check" and reply "OPEN_TICKET".`

You can parse OPEN_TICKET on the client to route to a Zendesk or Linear integration. Because the model is GPT-4o, instruction adherence is high, but you should still validate with a simple string check before mutating any backend.

Handle multi-turn context and caching

The useChat hook sends the full message history each request. For long sessions, that grows tokens. Two concrete fixes:

  1. Trim history server-side before calling streamText.
  2. Forward provider cache-control hints if your gateway honors them. For example, prefix static system content with a cache point by sending the cache_control beta header through the SDK’s headers option:
const gateway = createOpenAI({
  baseURL: process.env.OPENAI_BASE_URL,
  apiKey: process.env.OPENAI_API_KEY,
  headers: { 'x-cache-control': 'prompt-cache' },
});

Not every gateway supports this; check the docs. The point is that the client code does not change when you flip routing directives.

Production concerns: fallback and metering

In production, provider degradation is a when-not-if event. A gateway that provides automatic fallback when a provider is rate-limited or degraded lets your gpt-4o call reroute without code changes. You should still set a timeout on streamText via maxTokens and temperature to bound cost.

Per-token usage metering is essential for support bots: you want to attribute spend to a customer session or tenant. Capture result.usage in the Route Handler and ship it to your analytics:

const result = await streamText({ model: gateway('gpt-4o'), messages });
const usage = await result.usage; // { promptTokens, completionTokens }
console.log('session_cost', usage);

If your gateway exposes per-token billing, this is the integration point.

Expected output at the checkpoint

After adding the stricter prompt, a scoped question:

User: What are the dimensions of A-99?
Assistant: A-99 is 12 x 8 x 3 inches and weighs 1.2 lbs.

An out-of-scope question:

User: I want a refund for a charge I dispute.
Assistant: OPEN_TICKET

The client can detect OPEN_TICKET and render a “Connecting you to a human” panel.

Deploy

npm run build
vercel deploy

Set OPENAI_API_KEY and OPENAI_BASE_URL in the Vercel project environment. The Edge function will stream from the gateway region closest to your user if the gateway supports it.

That is a complete, runnable customer support chatbot with GPT-4o on Next.js. The surface area is one route, one component, and one env file. Expand by adding tools for order lookup and ticket creation once the prompt guardrails hold in testing.

Tagschatbotgpt-4on4n-aicustomer-support

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All building chatbots with vercel ai sdk & next.js posts →