Skip to main content
August 7, 2026

I Built ChatGPT a Backend

6 min read
n8nretrievalself-hostedchatgptcustom-gptmodel-routingautomationcost-optimization
open in claude ↗ chatgpt ↗ grok ↗

A customer was losing sales history on the road. I gave him a private assistant inside the ChatGPT app he already uses every day. No new app, no Telegram bot, no server for him to touch.

The n8n workflow behind the Custom GPT: one webhook routes on action_type into folder lookup, Drive and Gmail search, document extraction, and four document generators

TL;DR: A customer of mine sells technical products into industrial supply chains and is on the road most of the year, losing sales history between his recorder, his inbox, and his memory. I built him a private backend, n8n and Caddy on one Hetzner box, wired into a Custom GPT. He asks by voice, in the app he already knows, and gets answers grounded in his own Drive and inbox.

That last part is the whole design. The winning move was not a better chat interface, it was adding capability to the one he already opens twenty times a day. Adoption cost: zero.


The Problem, and Why the App Choice Was Easy

He records meetings with a pocket recorder, which transcribes and emails him the write-up, landing next to customer threads and Drive quotes. Capture was never the issue. Recall was: "what did this customer say about that spec" meant scrolling Gmail in an airport.

A Telegram bot or a bespoke app would have meant teaching a travelling salesman a new habit. A Custom GPT taught him nothing: same login, same voice mode, one new item in his GPT list.


The Part I Got Wrong First

The original plan was the obvious one: extract the text, chunk it, embed it, store the vectors in Qdrant, keep the index in sync. I wrote that plan out in full, then checked one detail and deleted all of it.

Google already indexes the inside of his files. The Drive API's fullText contains searches inside PDF, DOCX, PPTX and XLSX with no setup, and Drive OCRs scanned PDFs on upload. Gmail does the same for attachments, in place. A vector store would have been a staler copy of an index he already pays for.

So the system searches at question time. Dropped before shipping: the extraction service, the Qdrant collection, embeddings, chunking, ingestion, backfill, reconcile. Nothing to keep in sync means deleted and edited files are correct automatically.

The trade-off is real: keyword search is not semantic. Most of the engineering below is the cost of that sentence.


Then the Vendor Caught Up

Mid-build, the vendor shipped native Drive and Gmail connectors and my customer turned them on. A useful forcing function: it sharpened what the Custom GPT is actually for.

  • Connectors do not work inside a Custom GPT. A GPT uses Actions or Apps, never both, so inside Sales Brain my retrieval is the only retrieval there is.
  • The connector cannot see folders. He dumps arbitrarily-named files into a customer folder, then asks about it. Folder membership is the relevance test, and it is true the second a file lands.
  • A connector searches one app at a time, on his tier by keyword only. Drive and Gmail never land in one grounded answer.

Losing half your justification and writing down what is left beats defending all of it.


Architecture

One Hetzner box: Caddy for TLS, n8n for everything else, out to the Google APIs and OpenRouter. The screenshot above is that workflow, 75 nodes behind one webhook. Actions call in from the vendor's cloud, so the endpoint is public but locked down: header auth, key-only SSH, an Internal OAuth consent screen. The Custom GPT lives on his account; the box and every key stay on mine.

It parses the question in code, prefers a folder lookup, falls back to Drive and Gmail search, then reads the files through Google's convert/export path, which handles Office formats and OCR'd PDFs in one chain. It ranks on content, not filenames.


Three Bugs It Took to Get Right

It cited its own homework. The agent writes documents into his Drive. Search then found those documents and served them back as customer history, so its own output became its own evidence. Fixed by excluding the artifact folder from every search.

My precision filter had the connector's own weakness. Full-text search matches word tokens, so "Northern Composites" also returns every file containing "composites". I added a filter requiring the filename to match. Wrong: he dumps files under arbitrary names, so I was deleting exactly the documents he asks about. It now ranks instead of dropping, judging relevance on the extracted text.

The timeout was never the cause. A run took 320 seconds, so I bounded every HTTP node. It then failed at exactly 20.3 seconds, the new limit. That precision is the tell: the upload never completed at any deadline, it expanded to fill whichever one it got. The cause was a missing Content-Type on a binary upload. One header took that node from 20,277 ms to 993 ms.

The pattern: almost nothing threw an error. Everything returned plausible, valid, wrong output. A fourth bug returned empty email bodies for days without failing once. That is the failure mode I watch hardest.


Model Control Is the Real Unlock

Inside a chat subscription you get one vendor's model, on that vendor's rate limits, at that vendor's price. My backend picks the model per task, so each job gets the cheapest thing that does it well:

Job Model Why
Grounded Q&A over retrieved text DeepSeek V4 Flash grounding follows instructions, not brilliance
Fast structured extraction Haiku 4.5 quick and cheap at schema work
Google Docs and reports Terra it carries his name out the door

About 100 generated reports cost roughly $2.00. He is not rate-limited by anyone, not locked to one lab, and when a better or cheaper model lands I swap one node. He never notices, except in the bill.

Latency is the live constraint: 30 seconds before the frontend gives up, of which retrieval is a steady ~8. The model is the real cost, and it caps how many documents get read.


Status and What's Next

Live against his real data. Folder retrieval, honest negatives, coverage reporting, document reading and attachment reading are all verified end to end. Two tests are still unrun, and I would rather say so: a generically-named file mentioning the customer only in its body, and a head-to-head against the native connectors.

Next is a Monday digest addressed to him, then a local semantic rerank running CPU-only on the box. After that, an Apify actor built around his business, feeding leads and competitor signals into the same report pipeline.

That is where this is going. Personalized automation is the thing subscriptions cannot sell you: a workflow shaped around one person's actual job, with the models, the sources and the schedule chosen per task.


What I'd Tell Someone Building This

  • Build into the app they already open. Adoption is the hard part, and a Custom GPT costs the user nothing to learn.
  • Check what your data platform already indexes before you build one. I was one afternoon from maintaining a worse copy of a search index my customer already paid for.
  • Own the model choice. Per-task routing is where the cost and the flexibility both live.
  • A hang that grows to fill your timeout is not a timeout bug. Round, exact failure times mean something cut you off rather than finished.

Resources

Hetzner · n8n · Caddy · OpenRouter · Qdrant · My Apify profile