Skip to content

Developers

Two endpoints. An afternoon. No SDK.

JSON over HTTP with one header. Send documents, ask questions, get answers with citations that resolve. Nothing to adopt and nothing to keep in sync with our releases.

  • 68 REST routes
  • 12 MCP tools
  • scoped keys
  • idempotent ingest

Quickstart

Four steps to a cited answer.

All of it on the free tier, with no card.

  1. Create an organisation

    Sign up and you get an organisation with one workspace on the free tier. No card, and no tier decision to make yet.

    the first workspace is always RAG

  2. Issue a key

    In the workspace settings, create an API key scoped to ingest, search, or both. Copy it — the plaintext is shown once.

    scopes: ingest · search

  3. POST a document

    Send text with your own identifier as the key. Every workspace already has a default source, so there is nothing to configure first.

    POST /v1/ingest

  4. Ask a question

    You get ranked passages, a cited answer, and the timing of every step that produced it. On the free tier that is one retrieval lane; on the graph tier it is three.

    POST /v1/search

POST /v1/ingest
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" }
POST /v1/search
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?" }'

The customer API

What you can call with an API key.

The full 68 routes include the console, tenancy, billing and platform-admin surfaces. These are the ones an integration uses.

Ingest

Push documents. Idempotent on your own identifier, so retries are safe.

  • POST/v1/ingestOne document. 200 indexed on the RAG tier; 202 extracting plus a status URL on the graph tier.
  • POST/v1/ingest/batchMany at once, up to your plan's batch limit.
  • GET/v1/ingest/:id/statusPipeline phase, timings, failure reason.
  • DELETE/v1/ingest/:externalIdRemoves the document and drops its support for every fact it asserted.

Search

Ranked passages, a cited answer, the graph evidence, and per-step timings.

  • POST/v1/searchThe main call. Optional prior turns, never stored.
  • POST/v1/search/streamThe same, as server-sent events.
  • POST/v1/search/testPer-step diagnostics: what each lane produced and how long it took.

Sources

Integration connections. Every document belongs to exactly one.

  • GET/v1/sourcesList. Every workspace is seeded with a default.
  • POST/v1/sourcesCreate, with an integration type that steers extraction.
  • PATCH/v1/sources/:idRename or retype.
  • DELETE/v1/sources/:idRefused while the source still holds documents.

Ontology and reconciliation

Read your vocabulary and the history of applies over it.

  • GET/v1/ontologyActive version, entity types, predicates.
  • GET/v1/reconciliation/runsApply sweeps: when they ran, what they touched.

Response shape

Everything the pipeline learned, in the payload.

POST /v1/search — response
{
  "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
  }
}

Authentication

One header, two scopes.

Keys belong to a workspace and carry the ingest scope, the search scope, or both. Issue and rotate them yourself; the plaintext is shown once at creation and never retrievable afterwards. A key used outside its scope gets a 403 that names the scope it lacked rather than a generic denial.

Errors

Predictable shapes.

  • 400validation_failedThe body did not match the schema. The response names the field.
  • 401invalid_api_keyMissing, malformed, revoked, or rotated key.
  • 403insufficient_scopeA search key tried to ingest, or the reverse.
  • 403payment_method_requiredCreating a graph-tier workspace without an entitlement.
  • 404not_foundNo document with that external identifier in this workspace.
  • 413text_too_largeDocument text above 500 KB. Split it on a meaningful boundary.
  • 429rate_limitedAbove your plan's ingest or search rate. Retry with backoff.

Model Context Protocol

12 tools, and a rule that keeps them honest.

An MCP client connects, authorises once as a person, and from then on can search and ingest into one workspace with exactly that person's access.

The rule: a tool exists if and only if the published documentation exposes that endpoint, and it requires exactly the scope and tier that endpoint requires. It is enforced by the build in both directions — an undocumented tool fails CI, and so does a documented endpoint with no tool.

  • search_knowledgeAsk a question and get the cited answer.search
  • search_diagnosticsThe same question, with the per-step trace.search
  • ingest_documentPush one document.ingest
  • ingest_documentsPush a batch.ingest
  • get_document_statusWhere a document is in the pipeline.read
  • delete_documentRemove a document by external identifier.ingest
  • list_sourcesThe workspace's integration connections.read
  • create_sourceAdd a connection.ingest
  • update_sourceRename or retype a connection.ingest
  • delete_sourceRemove an empty connection.ingest
  • list_reconciliation_runsOntology apply history.readgraph tier
  • list_ontologyEntity types and predicates in the active version.readgraph tier

FAQ

Frequently asked

The integration questions, answered by someone who has read the code.

Get a key and POST a document.

The free tier is enough to evaluate the whole retrieval path end to end.

  • no card required
  • first workspace is free
  • one POST to ingest