Docs

API reference

Ask, search and ingest from your own systems. Every call is scoped, rate-limited, cost-capped and recorded — so handing a key to a service stays a decision you can audit.

Quickstart

Create a key in Settings → API keys. It is shown once. Send it as a bearer token.

curl https://api.brainstack.space/v1/ask \
  -H "Authorization: Bearer bsk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"question": "What is our refund policy?"}'
{
  "answer": "Refunds are allowed within 30 days [1].",
  "sources": [
    { "n": 1, "title": "policies-2026", "page": 7,
      "text": "...", "score": 0.87, "source_type": "pdf" }
  ],
  "trace": [ { "n": 1, "kind": "knowledge", "label": "Searching knowledge" } ],
  "usage": { "input_tokens": 3200, "output_tokens": 180,
             "cost_usd": 0.0042, "latency_ms": 4100,
             "first_token_ms": 900, "model": "claude-haiku-4-5" },
  "conversation_id": "…", "trace_id": "…"
}

Authentication

Authorization: Bearer bsk_live_… on every request. Keys are workspace-scoped: the workspace is resolved from the key itself, never from anything you send, so a key can only ever reach its own tenant's data.

We store only a SHA-256 hash of your key. If you lose it, revoke it and mint another — we genuinely cannot recover it.

Environments

bsk_live_ and bsk_test_ keys get separate quotas, separate cost caps, and separate usage reporting, so test traffic can be excluded from your numbers. They read and write the same workspace data — this is not a sandbox.

Scopes

A key carries only the scopes you tick when you create it. Give each integration the least it needs.

ask:writeAsk questions and get grounded, cited answers.
search:readSearch the workspace's knowledge without calling a model.
documents:readList documents and read ingestion status.
documents:writeUpload, ingest and delete documents.
actions:writeLet the agent act in company systems over MCP.
analytics:readRead this key's own usage and cost.

actions:write is the one to think about: it gives the key's agent session the same company-system tools a manager has. Without it, those tools are never discovered — they do not exist for that key.

Endpoints

RouteScopeWhat
GET /v1/meIntrospect the calling key. Needs no scope.
POST /v1/askask:writeA grounded, cited answer. JSON or SSE.
POST /v1/searchsearch:readRetrieval only — no model call.
GET /v1/documentsdocuments:readList documents and their status.
GET /v1/documents/{id}documents:readPoll one document until ready.
POST /v1/documentsdocuments:writeUpload a PDF (multipart). 202.
POST /v1/documents/urldocuments:writeIngest a web page. 202.
DELETE /v1/documents/{id}documents:writeRemove a document and its vectors.
GET /v1/usageanalytics:readThis key's own calls, spend and errors.

The full machine-readable schema is at https://api.brainstack.space/openapi.json.

/v1/search runs the full hybrid retrieval stack — dense vectors, BM25, and reciprocal-rank fusion — and returns the passages. No LLM call, so it is fast, cheap and deterministic. Use it when you want to bring your own model, or to inspect what a question would retrieve.

curl https://api.brainstack.space/v1/search \
  -H "Authorization: Bearer bsk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"query": "ERR_4021", "top_k": 5}'

Streaming

Pass "stream": true (or send Accept: text/event-stream) to get server-sent events: trace, sources, delta, reset, done.

A resetevent means the agent's self-check rejected its own draft and is rewriting — discard the text you have rendered so far and keep reading.

Ingestion is asynchronous

Uploads return 202 with a document id. Poll GET /v1/documents/{id} until status is ready — it walks queued → extracting → chunking → embedding → ready, with chunks_done / chunk_count for progress. Webhooks are not available yet.

Limits and caps

Every key has an hourly request limit and, optionally, a monthly spend cap. Rate-limited responses carry X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and Retry-After. Back off and retry; do not spin.

Errors

Every error has the same shape. Branch on code, never on the prose.

{
  "code": "insufficient_scope",
  "message": "This key lacks the `documents:write` scope.",
  "request_id": "8f2c…",
  "required": "documents:write"
}
401invalid_api_keyMissing, malformed or unknown key.
401key_revokedThe key was revoked. It will never work again.
401key_expiredThe key passed its expiry date.
403insufficient_scopeThe key lacks the scope this route needs.
429rate_limitedOver the per-key or per-workspace hourly limit.
429cost_cap_exceededOver this key's monthly spend cap.
404document_not_foundNo such document in this workspace.
404conversation_not_foundNo such conversation for this key.
503model_not_configuredThe answer model isn't configured server-side.

Every response carries X-Request-Id. Quote it when something goes wrong — it is one lookup on our side.

What we record

Every call — including the ones we refuse — is written to a usage ledger with its route, status, latency and request id. Answers additionally record tokens, cost and the tools the agent used. All of it is visible to workspace admins per key, and to the key itself at /v1/usage.

Key creation, scope changes and revocation are written to an append-only audit log that nothing can delete.

scopedrate limitedcost cappedinstantly revocableaudited