The product
From what you've written to something you can act on.
You already have the content. This is what it becomes: one place to ask, answers in plain language, and the evidence behind every one of them.
- no crawler
- nothing of yours to connect
- nothing to migrate
End to end
Three moves, and none of them are yours to build.
What you send, what we make of it, and what your users get back.
1 · You send your content
Whatever you already have, with your own identifier on it. Most teams hook this into whatever already fires when their content changes, and never think about it again.
one call per document · safe to repeat
2 · We read it properly
Not just indexed for search — read for what it actually claims, and for which of your other documents say the same thing. That is what lets an answer join two of them up.
what it says · and who said it
3 · Your users get an answer
In plain language, with the passages behind it, how many sources agree, and a clear admission when your content does not cover the question.
the answer · and the receipts
How it finds things
Your question rarely matches the document that answers it.
So it searches three ways at once: for what you asked, for what you meant, and for the documents that back the answer up.
Direct
weight 1.0
always runsHybrid dense and keyword search on the query exactly as it was asked.
This lane is the floor. It runs on every search, at full weight, whatever the graph does or fails to do.
Expanded
weight 0.6
Entities in the query are resolved to graph nodes, traversed two hops, then the query is re-embedded together with the names it reached.
This is how a search reaches content whose wording the question never used. Ask about React and the traversal pulls in JSX and Meta.
Evidence
weight 0.6
The original question is asked again, restricted to the documents that provide provenance for the facts the traversal crossed.
Ranked by corroboration — how many distinct documents assert each fact. This is the lane that finds the passage supporting a point rather than the passage matching a phrase.
Reciprocal rank fusion
The three result sets are merged on rank rather than on score, so no lane can dominate because its similarity numbers happen to be on a different scale. The top documents are then hydrated to chunk text and synthesised into one cited answer.
When your content changes
Your answers keep up with your content.
Re-send a document and it replaces the old version. Unpublish one and it stops influencing answers — including any claim that was only holding up because of it.
- Every document that asserts a fact keeps its own confidence for it. Removing one document does not remove the fact if three others still say it.
- This is bookkeeping, not judgement: we do not decide which of two conflicting documents is right. Both are kept, with their sources, and the answer can say they disagree.
- The console has a contradictions surface for claim-against-claim review. Detection is specified and not yet built — the page ships as a declared placeholder, and we would rather tell you that here.
Asserted by
- 0.95 confidence
Q3 Retention Review
notion
- 0.88 confidence
Support Themes 2026
zendesk
- 0.81 confidence
Activation Playbook v4
lms_content
What comes back
Everything it learned on the way, not just the answer.
The passages, the answer, the connections it used and how long each step took — because the point is that you can check any of it.
{
"results": [
{ "documentId": "...", "externalId": "account-4471",
"title": "Account 4471 — Team Annual",
"integrationType": "salesforce", // which system it came from
"score": 0.0263,
"chunk": "Plan: Team Annual. Seats: 240. SSO entitlement: false.",
"summary": null,
"metadata": { "accountUrl": "https://crm.example/4471" } }
],
"answer": {
"text": "Not on your current plan — account 4471 is on Team Annual [1]...",
"citations": [
{ "marker": 1, "documentId": "...", "externalId": "account-4471",
"title": "Account 4471 — Team Annual" }
],
"tokensIn": 3184, "tokensOut": 96, "generationMs": 890
},
"graphContext": {
"facts": ["Team Annual —excludes→ SAML SSO (confidence 0.93, 2 sources)"],
"resolvedFromQuery": ["SSO"],
"expandedEntities": ["SAML SSO", "Team Annual", "Business"]
},
"metadata": {
"mode": "graph", "retrievalPath": "fused", "graphExpansion": true,
"searchTimeMs": 661, "vectorSearchMs": 142, "graphTraversalMs": 187,
"entitiesResolved": 1, "entitiesExpanded": 3
}
}- results
- Ranked passages with the score AND its unit. A 0.026 fusion rank and a 0.700 similarity are not comparable numbers, so we never print them as if they were.
- answer
- The cited prose, with token counts and generation latency. Null rather than absent if synthesis failed, so your code has one shape to handle.
- graphContext
- The facts the traversal crossed, rendered readably, plus which entities were resolved from the question and which were reached from them.
- metadata
- Per-step timings. This is what makes a slow search diagnosable from the caller side without asking us to look at a log.
Your systems
Records and prose, read differently, joined anyway.
Tell us which system something came from and we read it accordingly. Then the account in a billing row and the account in a support ticket resolve to one thing, which is what lets an answer span the two.
salesforce · billing · product analytics
Records — what is true of this account
Plans, entitlements, seats, renewal dates, usage. Serialise the row and send it as text; we read it as facts about that account, and it becomes joinable to everything else that mentions them.
zendesk
Support history — what went wrong before
Where the same problem is described five different ways. Resolution collapses those into one entity rather than five near-duplicates, so the fifth person to hit it finds the first four.
notion · help centre
Documentation — how any of it works
Wikis, runbooks, policies, decision records. The content that explains why, and that nobody can ever find at the moment they need it.
lms_content
Learning content — how to do it
Courses and modules, read for concepts, skills and prerequisites rather than for problems and resolutions. A course library is not read the same way as a ticket queue.
generic
Anything else
A source with no domain hints. Every workspace is seeded with one, so a first call works with nothing configured.
When something breaks
It degrades, it does not fall over.
Worth reading before you build on it, because it tells you how much error handling you actually need — which is less than you would expect.
| If this fails | What you still get |
|---|---|
| The graph store is unreachable | Vector search returns results as normal. The expanded and evidence lanes are skipped. |
| Entity extraction on the query finds nothing | Nothing to traverse, so the direct lane is the answer. No error, no empty response. |
| Traversal reaches no facts | Graph expansion contributes nothing and fusion has one lane to fuse. Results stand. |
| Answer synthesis fails | The response carries a null answer and the ranked documents intact. The caller loses the prose, never the sources. |
What you still get
- The graph store is unreachable
- Vector search returns results as normal. The expanded and evidence lanes are skipped.
- Entity extraction on the query finds nothing
- Nothing to traverse, so the direct lane is the answer. No error, no empty response.
- Traversal reaches no facts
- Graph expansion contributes nothing and fusion has one lane to fuse. Results stand.
- Answer synthesis fails
- The response carries a null answer and the ranked documents intact. The caller loses the prose, never the sources.
Connecting it
Two calls, and nothing to migrate.
No library to adopt, no framework to buy into, and nothing in your stack that depends on ours.
curl -X POST https://api.graas.ai/v1/ingest \
-H "x-api-key: $GRAAS_KEY" \
-H "content-type: application/json" \
-d '{
"sourceSlug": "billing",
"externalId": "account-4471",
"title": "Account 4471 — Team Annual",
"textContent": "Plan: Team Annual. Seats: 240.
SSO entitlement: false. Renews: 2027-03-01."
}'
# 202 Accepted
# { "documentId": "...", "externalId": "account-4471",
# "status": "extracting", "chunksCreated": 1,
# "statusUrl": "/v1/ingest/.../status" }curl -X POST https://api.graas.ai/v1/search \
-H "x-api-key: $GRAAS_KEY" \
-H "content-type: application/json" \
-d '{ "query": "does onboarding reduce churn?" }'FAQ
Frequently asked
The questions engineers ask in the first call.
Start with the free tier. Ship the first answer today.
RAG is free, with no card and no sales call. Add the graph when your corpus earns it.
- no card required
- first workspace is free
- one POST to ingest