We built a free AI chatbot for our electronics store using OpenAI, a Vercel proxy, and a hand-crafted knowledge base. It took longer than expected. Here is every failure along the way.
- We embedded an AI product assistant on okapius.com that knows every spec, price, and product in the catalog
- Direct browser-to-OpenAI calls do not work due to CORS — you need a proxy server, and deploying it took 6 attempts
- Final stack: OpenAI gpt-4o-mini + Vercel serverless proxy + vanilla JS widget embedded via Adobe Commerce CMS block
Why we built this
We run okapius.com — an electronics store selling phones, tablets, laptops, and solar products across America, Europe, Africa, and the Middle East. The catalog has 27+ products. Customers constantly ask the same questions: what is the cheapest phone, which one has the best camera, do you have 5G, where can I buy.
A traditional chatbot SaaS would cost $50-300 per month. We wanted to build something ourselves, keep costs near zero, and learn in the process. We also wanted it to run on our own infrastructure as much as possible — consistent with everything else we document in this series.
The goal was simple: a floating chat widget on the store that could answer product questions, recommend devices by budget, handle partnership and support enquiries, and escalate to a human via email when needed.
The plan — and why it immediately broke
Our first instinct was straightforward: build a JavaScript widget, call the OpenAI API directly from the browser, done. We wrote the widget, embedded it, and tested it. The chat opened. We asked a question. Nothing came back.
Attempt 1 — Local proxy on our homelab
We already had an Ubuntu server running at 192.168.20.10 with a full Bitcoin node, LND Lightning, and Ollama AI stack. We built a simple Flask proxy there and pointed the widget at it.
# proxy.py on our Ubuntu homelab
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests
app = Flask(__name__)
CORS(app, origins=["https://mcstaging.okapius.com"])
@app.route('/chat', methods=['POST'])
def chat():
response = requests.post(
'https://api.openai.com/v1/chat/completions',
headers={'Authorization': f'Bearer {OPENAI_API_KEY}'},
json=request.json
)
return jsonify(response.json())
Attempt 2 — Adobe Commerce CSP whitelist
We considered whitelisting our ngrok URL in Adobe Commerce's Content Security Policy. The CSP was logging violations for our proxy URL under connect-src. If we could add the URL to the allowed list it would unblock the connection.
Attempt 3 — Use Ollama locally instead of OpenAI
We already had Ollama running on our homelab with LLaMA 3 70B, Mistral 7B, and LLaMA 3 8B. Why pay for OpenAI when we have local models? We tried pointing the proxy at our local Ollama API.
Attempt 4 — OpenAI API quota exceeded
We created a fresh OpenAI API key, updated the proxy, and tested. Got a new error: insufficient_quota. The account had no credits. OpenAI requires a paid billing setup even for test usage.
The solution that actually worked — Vercel serverless
We stopped trying to expose our homelab to the internet and instead deployed a two-function serverless proxy on Vercel's free tier. No server to manage, no ngrok tunnels, permanent public URL, automatic HTTPS, free for our usage level.
export default async function handler(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') return res.status(200).end();
const response = await fetch(
'https://api.openai.com/v1/chat/completions',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(req.body)
}
);
const data = await response.json();
return res.status(200).json(data);
}
The knowledge base — what we actually put in the system prompt
The chatbot's intelligence is not the model — it is the system prompt. We baked in the complete product catalog: 27 products across phones, tablets, laptops, solar, and accessories, with full specs, pricing, availability by region, recommendation logic, and contact information.
The system prompt is approximately 3,000 tokens. At gpt-4o-mini pricing this adds about $0.0005 per conversation — negligible. The benefit is the model never hallucinates product specs because every spec is explicitly provided.
| Category | Products | Price range |
|---|---|---|
| Phones | 18 models | $30 - $1,000 |
| Tablets | 6 models | $112 - $350 |
| Computers | 2 models | $299 - $459 |
| Solar | 1 product | $45 |
| Accessories | 2 products | $19 - $29 |
The system prompt also includes a recommendation guide — "best phone under $100", "best for battery", "best 5G" — so the model gives confident answers instead of listing everything and saying "it depends".
Embedding in Adobe Commerce — one more surprise
We planned to paste the HTML widget into an Adobe Commerce CMS block and inject it globally via layout XML. The CMS block editor refused to save the content.
What we learned
Final architecture
What is next
The current setup works well for testing. Before going to production we will move the proxy to a dedicated subdomain like api.okapius.com, tighten the CORS origins to only allow okapius.com, and swap the OpenAI model to gpt-4o for more nuanced product comparisons.
Longer term we want to connect the chatbot to the actual Magento product API so specs and prices update automatically rather than requiring manual edits to the system prompt. That is a future post.
The full source code — widget HTML, Vercel proxy function, and system prompt template — is on our GitHub.
About Lemuntu
Crypto & AI editor
Get the weekly digest
New AI projects, launches, and homelab notes — every Friday in your inbox.
Subscribe free →
Join the discussion —
You must be logged in to post a comment.