Skip to content

API & integrations

gStack exposes a small HTTP + JSON API over its documentation: you can search the docs and ask questions (RAG — retrieval-augmented answers with citations) from any external tool. This page documents the endpoints and shows how to connect a local Obsidian vault.

Only paths under /api/* are exposed at the public base URL (the service’s internal /healthz is not proxied). A quick smoke test is a search call.

Semantic (vector) search over the docs. Returns the top-k matching passages — no model generation, so it’s fast.

Query paramTypeDefaultMeaning
qstring—the search query (required)
kint5how many passages to return
Terminal window
curl "https://gstack.esempla.systems/api/search?q=lifecycle%20states&k=3"
{
"query": "lifecycle states",
"results": [
{
"score": 0.71,
"title": "Data model",
"heading_path": "Data model > Status",
"url": "https://gstack.esempla.systems/technical-specification/data-model/",
"path": "technical-specification/data-model.md",
"snippet": "Eleven states. Only ACTIV (and SUSPENDAT for history)…"
}
]
}

Full RAG answer as plain JSON (no streaming) — the easiest endpoint to call from a script or plugin. Retrieves context, then generates a cited answer.

// request body
{ "question": "What statuses can an interdiction have?", "k": 5 }
Terminal window
curl -X POST https://gstack.esempla.systems/api/ask \
-H 'Content-Type: application/json' \
-d '{"question":"What statuses can an interdiction have?"}'
{
"question": "What statuses can an interdiction have?",
"answer": "An interdiction moves through eleven states… Only ACTIV produces public legal effect [1].",
"sources": [
{ "n": 1, "title": "Data model", "heading_path": "Data model > Status",
"url": "https://gstack.esempla.systems/technical-specification/data-model/" }
]
}

The answer replies in the same language as the question (RO / RU / EN) and cites passages as [1], [2] matching the sources array.

Same as /api/ask but streams tokens via Server-Sent Events — use this for a live typing UI (it’s what the portal’s “Ask AI” widget uses). Events: sources, token (repeated), done, error. For most integrations prefer /api/ask.

The API is plain HTTP+JSON with open CORS, so anything in Obsidian that can make an HTTP request can reach it. The most reliable path is a small Templater (or QuickAdd) user script that POSTs to /api/ask and inserts the answer.

Templater user script — Ask gStack.md template:

<%*
const q = await tp.system.prompt("Ask the gStack docs a question");
if (!q) return;
const res = await tp.obsidian.requestUrl({
url: "https://gstack.esempla.systems/api/ask",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ question: q, k: 5 })
});
const { answer, sources } = res.json;
const cites = (sources || []).map(s => `> ${s.n}. [${s.title} — ${s.heading_path}](${s.url})`).join("\n");
tR += `## ❓ ${q}\n\n${answer}\n\n**Sources**\n${cites}\n`;
%>

tp.obsidian.requestUrl is Obsidian’s built-in HTTP helper exposed through Templater — it bypasses browser CORS, so no extra plugin or proxy is needed.

  • Auth: the read endpoints are currently open (no key). Fine for a demo or trusted network; an API-key / token guard can be added before wider exposure.
  • Answers depend on the AI backend (Ollama) being reachable; /api/search works without it (index only).
  • Freshness: answers reflect the last indexed content — the index refreshes on each admin Sync / Reindex.