An end-to-end pipeline
Upload, OCR, chunk, embed, retrieve, answer with citations — with retries and quality gates at the steps that fail on bad input rather than at the end.
AI SaaS case study
A platform that turns a PDF — including the scans nobody can search — into something people can ask questions of, over web chat, over SMS, or by pointing a phone camera at a QR code printed on the thing they are asking about.

TextGPT + IQR.Codes is two products on one pipeline, built for Modezilla LLC. TextGPT is the personal side: upload a document, pick a persona, ask it questions. IQR.Codes is the business side, where a company uploads its manuals and policies, prints a QR code, and every customer who scans it starts a conversation that already knows the answers.
The pipeline underneath is the same either way — upload, OCR, chunk, embed, retrieve, answer with citations. What made it hard is the state a PDF actually arrives in. A clean digital export is a solved problem; a photograph of a page from a fifteen-year-old manual is not, and those are the documents businesses actually have.
The rest of the architecture follows from two requirements that were fixed before the first line: an answer has to come back in under a second, and no organisation may ever see another's documents. The second one is enforced in Postgres by row-level security, rather than by the application remembering to filter.

A walkthrough of TextGPT and IQR.Codes, from a PDF dropped into the dashboard through to a customer scanning a printed code and getting the answer back as a text message.
PDF ingestion with OCR
Documents go in as they are, scans included. An OCR pass and a cleanup step run before anything reaches the parser, so a photographed page becomes searchable rather than being skipped.
Answers with citations
Retrieval-augmented chat that answers out of the uploaded documents and shows which one it came from, rather than out of whatever the model already believed.
QR and SMS channels
A scanned code opens a branded web chat or a text conversation. Nothing to install and no account to create, and over SMS the customer keeps the thread in their messages.
Multi-tenant by default
Every row and every file is scoped to an organisation in Postgres itself, through row-level security and signed URLs rather than an application-layer filter.
Subscriptions and billing
Stripe tiers decide which channels an account gets and how much of them it may use, with a customer portal for everything after the first payment.
The answer is in the manual. The manual is a scan. The scan is not searchable, and nobody is going to read forty pages to find one line.
Businesses were sitting on documents that already contained every answer their customers ask for — product manuals, policies, spec sheets, returns terms — in a format nobody can query. Where those documents were photographs of pages rather than digital exports, even a plain text search returned nothing. The workaround was a support queue: somebody re-reading the same PDF and retyping the same paragraph, all day, while a customer waited for it.
Unsearchable documents
The knowledge existed, in PDFs — and the worst of them were images of pages, where even a text search came back empty.
Friction at the front door
Anything that asked the customer to install an app or create an account lost them before the question was ever asked.
Cost and isolation at scale
The system had to stay fast and cheap across millions of documents while guaranteeing that no organisation could read another one's.
One pipeline: upload, OCR, chunk, embed, answer.
Every document takes the same path. It is uploaded, run through OCR when it needs it, split into chunks, embedded, and stored beside its vectors in Postgres. A question is embedded the same way and answered from what comes back, with the source cited. On top of that one pipeline sit two products — TextGPT for a person with a document, IQR.Codes for a business whose customers have the questions — and two ways in, web chat and SMS, either of them reachable from a code printed on the packaging.
Upload, OCR, chunk, embed, retrieve, answer with citations — with retries and quality gates at the steps that fail on bad input rather than at the end.
TextGPT for personal use and IQR.Codes for business, sharing the ingestion and retrieval layer instead of each carrying its own copy of it.
Supabase with row-level security and pgvector, on Vercel serverless functions. Scoped policies and signed URLs make isolation a property of the database.
The personal side of the platform: a chat window over whatever has been uploaded, with a persona that sets how the answers come back. The badge under the title is the one currently answering, and switching it is a sentence in the conversation rather than a trip to a settings page.

A team creates a product, drops in the PDF documentation, and optionally writes a system prompt that steers how the assistant answers about that one product. What comes out is a secure knowledge base and a QR code pointing at it — branded SMS or web chat, no support ticket in between.

Every product has its own row, with codes to view, edit and download. That download is what actually gets printed — on packaging, on an event flyer, on a sticker beside the machine — so the customer with the question is one camera scan away from the answer.

Web chat and SMS land in the same log, with the message, the time, the direction and the delivery status on a single line. It is what turns the chatbot from something a business hopes is working into something it can audit — and what shows which questions customers keep asking.

Three Stripe tiers, where the feature matrix is the product decision made visible. The entry plan is web chat reached from a printed code, the middle one adds SMS on a shared toll-free number, and the top one is a dedicated line for a brand that wants the number to be its own.

Fast enough to answer, strict enough to trust.
The platform shipped against explicit numbers rather than impressions: how long a document takes to become searchable, how long an answer takes to come back, how much of a bad scan survives OCR, and how much of one organisation's data another can reach.
Solution: An OCR pre-processing step and a heuristic cleanup pass before anything reaches the LLM parser, with retries and quality gates behind them. That combination is what moved accuracy on difficult, low-quality scans from unusable to dependable.
Solution: Tuned pgvector indexes, batched writes, and cosine similarity over filtered scopes rather than the whole corpus — with lightweight metadata caching in front of the queries that repeat.
Solution: An RLS-enforced schema with service-role segregation, and Supabase Storage reached only through signed URLs under scoped policies. Organisational data is separated by the database, not by the code that happens to be querying it.
Have documents nobody can search?
Let's turn them into answers.