Search that understands what you meant.

Index your notes, docs, or tickets and find them by meaning instead of exact words. Then hand the matches to an AI and let it answer from your own material. It runs on your project's Postgres, so there is no vector database to go set up.

— what your AI actually does

Two files. One is the index, one is the answer.

Every retrieval system is the same two halves: put text in so it can be found, then find it and use it. Here is what your agent writes for both, in the shape the platform teaches it.

you, to your AILet customers ask questions and answer them from our help articles
api/articles.js
import { db, knowledge } from 'hatchable';

export const methods = ['POST'];
export const access = 'member';

export default async function (req, res) {
  const docs = knowledge.base('help_docs', {
    dimensions: 1536,
  });
  const { title, body } = req.body;

  const { rows } = await db.query(
    `INSERT INTO articles (title, body)
          VALUES ($1, $2) RETURNING id`,
    [title, body]
  );

  // chunkText is your own helper
  await docs.add(chunkText(body).map((text, i) => ({
    id: `article:${rows[0].id}:chunk:${i}`,
    text,
    metadata: { articleId: rows[0].id, title },
  })));

  res.json({ id: rows[0].id });
}
api/ask.js
import { knowledge, ai } from 'hatchable';

export const methods = ['POST'];
export const access = 'member';

export default async function (req, res) {
  const docs = knowledge.base('help_docs', {
    dimensions: 1536,
  });

  // nearest six by meaning, not by keyword
  const hits = await docs.search(
    req.body.question, { topK: 6 }
  );

  const context = hits
    .map((h, i) => `[${i + 1}] ${h.metadata._text}`)
    .join('\n\n');

  const { text } = await ai.generateText({
    model: 'sonnet',
    system: `Answer using ONLY the context below.
              Cite sources by number.\n\n${context}`,
    prompt: req.body.question,
    purpose: 'rag-answer',
  });

  res.json({
    answer: text,
    sources: hits.map(h => h.metadata.title),
  });
}
add() embeds the text. search() embeds the question. you never handle a vector unless you want to.
— what you get

Retrieval, without the shopping list.

The pieces you would otherwise assemble from an embedding API, a vector store, and a chunking library. All three are already here and already talking to each other.

Meaning

Finds it when the words don't match

Results come back ranked by how close the meaning is, not by how many words overlap. Someone can ask in their own phrasing and still land on the right paragraph.

"How do I stop paying" finds the page titled "Cancelling your subscription".

RAG

An AI that answers from your material

Pull the closest passages, hand them to a model as context, get back an answer grounded in your own documents. Each hit carries its original text and metadata, so citing the source is a one-liner.

A support box that answers from your manual and links the section it used.

One database

It lives where your data lives

A knowledge base is a table in your project's own Postgres, with the search index built on it the first time you use it. One database to reason about, one place your rows live.

Nothing new to provision, and nothing new to keep in sync.

Embeddings

Text in, text out

Adding an item takes plain text and does the embedding for you. Searching takes a plain question and embeds that too. The vector maths is real, it just isn't yours to write.

docs.add([{ id, text }]) is the entire indexing step.

Filters

Search inside one person's content

Tag items with metadata when you index, then narrow by it at search time. The narrowing happens before ranking, so the best matches come from inside that slice rather than being filtered out after the fact.

Two customers, one knowledge base, and neither can retrieve the other's notes.

Console

Fill it without writing code

The project console has a Knowledge tab where you paste text or drop a .txt or .md file. The platform splits it into roughly 500-token pieces, embeds each one, and lists what it stored, chunk counts and all.

You paste the FAQ once. The app only ever calls search.

Declared

Written down in the project

Your agent declares each knowledge base in the project's config file with a name, a size, and who is expected to fill it. Deploy checks the declaration, and the console grows a card for every one so nothing sits half configured.

A template can ship with an empty base and a prompt telling the new owner to fill it.

Escape hatch

Plain SQL when you need it

The simple call covers most of it. When it doesn't, the knowledge base hands you its table name and a helper for the query vector, so you can join similarity ranking to your own tables in one query.

Rank by meaning, but only among the articles you have actually published.

— the numbers

The spec sheet, in plain terms.

Where it livesOne table per knowledge base, in your project's own Postgres. Columns are id, embedding, metadata and a timestamp, with the search index built on first use.
Declaring oneknowledge.base('name', { dimensions: 1536 }). Dimensions are required, on purpose. Names use lowercase letters, digits and underscores, up to 63 characters.
SimilarityCosine by default, with l2 and ip available. Fixed when the index is built, so choose once per collection.
Indexingadd([{ id, text, metadata }]) embeds and stores in one call, up to 1,000 items at a time. Adding the same id again updates it in place.
Searchingsearch(query, { topK, filter }) returns id, similarity and metadata, closest first. topK defaults to 10. Filters match metadata exactly and apply before ranking.
EmbeddingsComputed with OpenAI's text-embedding-3-small at 1,536 dimensions by default. Needs an OpenAI key set on the project or the account, and the bill goes to that key.
ChunkingAutomatic for console uploads: roughly 500-token pieces with about 50 tokens of overlap. Code that indexes its own content stores exactly the text it is handed, so splitting long documents is on you.
From the consoleA Knowledge tab appears once the project declares a knowledge base. Paste text or upload .txt and .md files, 5 MB per upload, and remove any source later. How the declaration works →
PacePer project, per minute: 200 searches, 100 index writes, 200 embedding calls. Ordinary app traffic never comes near it.
What it costsEach knowledge base is one table in your database and counts toward your table allowance, 50 on the free plan. Embedding calls bill to your own provider key and show up in the project's AI spend. Paid plans raise every number →

Numbers current as of August 2026 and read from the same limits table the pricing page renders from.

— things people ask for

Say it like this.

Nobody has to say the word "embedding". These are ordinary asks that turn into an index, a query, and an answer.

search"Let people search my notes by meaning, not just keywords"
answers"Add a box where customers ask questions and get answers from our manual"
scoping"Make sure each customer only searches their own documents"
related"Show three related articles at the bottom of every post"
freshness"Re-index an article whenever somebody edits it"
hybrid"Rank by relevance, but only show things we've published"
— common questions

Asked and answered.

Do I need a separate vector database?

No. The vectors live in your project's own Postgres, in a table sitting next to your ordinary ones. There is nothing extra to sign up for, no second connection string, and no second bill.

Does this need an AI key?

Yes. Turning text into something searchable by meaning is a paid call to an embedding provider, and Hatchable does not hold provider keys. Set an OpenAI key on the project or on your account and both indexing and searching work. Without one you get a plain error pointing at the console page that fixes it.

How is this different from a keyword search?

Keyword search looks for the words you typed. This looks for what those words mean, so "how do I stop paying" finds an article titled "Cancelling your subscription". Ordinary SQL is still there for exact matches, and you can combine the two in one query.

Do I have to prepare the documents first?

Not if you go through the console. Paste text or drop a .txt or .md file into the Knowledge tab and the platform splits it into roughly 500-token pieces, embeds each one, and stores them. When your app indexes content from its own code instead, splitting long documents into pieces is your side of the job.

Can each person search only their own content?

Yes. Attach metadata when you index, then pass a filter when you search. The filter is applied before ranking, so the results come from inside that person's content only. Worth wiring up on day one in any app with more than one customer.

What if I outgrow the simple search call?

Drop into SQL. A knowledge base hands you its table name and a helper for the query vector, so you can join similarity ranking to your own tables and add whatever conditions you want. Same rows, full control.

The full surface lives in the SDK reference, and the declaration format in the config reference.

— free to start, no card

Stop shopping for a vector database.

Connect the AI you already use, tell it what should be findable, and the embedding, the index, and the query all arrive with the project.