BotClarify
BotClarify

API reference

Version 1 · Base URL https://app.botclarify.com/v1

The API lets your own systems ask questions about your documents and get the same answers, with the same sources, as the chatbot in the browser. It is read-only by design: a key can ask and can list what it is allowed to ask about, and can do nothing else.

Before you start

The API is included in the Business plan. Create a key in the console under API, in the left-hand menu of your organisation admin pages. The key is shown once, at the moment you create it, and never again — we store only a one-way hash of it, so nobody can recover it for you afterwards. If you lose one, revoke it and make another.

Keys belong on your server. A key can read your documents. Never put one in a web page, a mobile app, or anything else a visitor can view the source of — the API deliberately sends no CORS headers, so a browser refuses to use a key even if one leaks into your front-end code. If you want a chat widget on a public site, call this API from your own backend and let your page talk to that.

Authentication

Send the key as a bearer token:

Authorization: Bearer bck_xxxxxxxxxxxxxxxxxxxx

An X-API-Key: bck_... header is accepted too, for tools where a custom header is easier than an Authorization one. A key is never read from the query string, because URLs end up in server logs, proxy logs and Referer headers.

POST /v1/chat

Ask a question. This is the endpoint most integrations only ever need.

curl https://app.botclarify.com/v1/chat \
  -H "Authorization: Bearer $BOTCLARIFY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "How much notice do I have to give before taking annual leave?"
  }'
{
  "answer": "Annual leave has to be requested at least five working days in advance, and anything longer than five consecutive days needs your line manager's approval in writing.",
  "sources": [
    { "document_id": "6f1c...", "filename": "Employee handbook 2026.pdf" }
  ],
  "usage": { "calls_this_month": 412, "calls_included": 20000, "calls_remaining": 19588 }
}
FieldTypeNotes
questionstringRequired. Up to 2,000 characters.
folder_idsstring[]Optional. Search only these folders. They must be folders the key is already allowed to read — this narrows the search, it cannot widen it.

The answer is written in the language of the question. If nothing in the documents the key can reach is relevant, answer says so plainly and sources is empty — the model is not asked to fill the gap from its own knowledge.

GET /v1/folders

The folders this key may ask about, so you can discover the ids to pass as folder_ids rather than guessing them. scoped is true when the key was restricted to particular folders.

curl https://app.botclarify.com/v1/folders \
  -H "Authorization: Bearer $BOTCLARIFY_KEY"

GET /v1/documents

Metadata for the documents the key can reach — id, filename, folder, status, size, date. Never the file itself and never its text; the answer from /v1/chat is the only way content leaves the system through this API.

Takes folder_id, limit (up to 200, default 50) and offset.

GET /v1/me

What this key is, which folders it is scoped to, and how much of the month's allowance is left. The first thing to call when a key is not behaving the way you expected.

Folder scope

A key can be restricted to particular folders when you create it. This is what makes a key safe to put behind a public-facing help page: it reads the handbook and cannot reach the contracts, even if somebody later adds a contracts folder, because a scoped key names its folders explicitly rather than inheriting whatever exists.

A scoped key also stops seeing documents that are filed in no folder at all. If you want those included, either file them or leave the key unscoped.

Quota and rate headers

Calls are counted per calendar month across the whole organisation and reset on the 1st. Every response carries the state:

X-RateLimit-Limit: 20000
X-RateLimit-Remaining: 19588
X-RateLimit-Reset: 2026-10-01T00:00:00.000Z

Requests rejected for a bad key or a malformed body are not counted — debugging your integration does not spend the month's allowance. Once the allowance is gone, calls answer 429 until the 1st.

Errors

Every error has the same shape, with a stable code to branch on. The message is for a human reading a log and may be reworded; the code will not change.

{ "error": { "code": "quota_exceeded", "message": "This organization has used all 20,000 API calls included this month. The allowance resets on the 1st." } }
StatusCodeWhat happened
400missing_questionNo question field.
400question_too_longOver 2,000 characters.
401missing_keyNo key was sent.
401invalid_keyThe key is unknown or has been revoked. These are deliberately the same answer.
401expired_keyThe key was created with an end date that has passed.
402subscription_inactiveThe organisation's plan has lapsed.
403api_not_in_planThe plan does not include API access.
403folder_not_allowedA folder was requested that this key cannot read. The denied field lists which.
403no_folders_in_scopeEvery folder this key was scoped to has been deleted.
403organization_suspendedThe organisation is suspended.
404unknown_endpointNo such endpoint under /v1.
413payload_too_largeThe request body is bigger than we accept.
400malformed_requestThe body could not be read as JSON.
429quota_exceededThe month's allowance is spent.
500internal_errorSomething failed on our side. Safe to retry.

A worked example

A help-desk page that answers from the handbook folder only:

// Your server. The key never reaches the browser.
const res = await fetch('https://app.botclarify.com/v1/chat', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.BOTCLARIFY_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ question: userQuestion }),
});

if (res.status === 429) {
  return reply('Our document assistant has reached its limit for this month.');
}
if (!res.ok) {
  const { error } = await res.json();
  console.error('botclarify', error.code, error.message);
  return reply('Sorry — I could not check the documents just now.');
}

const { answer, sources } = await res.json();
return reply(answer, sources.map((s) => s.filename));

Versioning

The version is in the path. Anything that would break an existing integration — a field removed, a field's meaning changed, an error code retired — arrives as /v2, not as a change to this one. New optional fields may be added to responses within /v1, so parse leniently and ignore what you do not recognise.

Questions

Write to support@botclarify.com or use the contact form. If something in this page is wrong or missing, that is worth telling us too.