Managed AI support for websites, apps, portals, CRM, ERP, and WordPress
Developers

Your content, your AI,
anywhere you need it.

The AskCybex API is OpenAI-compatible. Anything already built for that format works by changing a base URL and a key — no SDK, no rewrite. Connect a CMS, a custom web app, an internal tool, an ERP or a mobile app to a chatbot trained on your own documents.

Base URL https://askcybex.com Auth Bearer API key Format JSON & SSE

1 Get your API key

An API key maps to exactly one deployment — one chatbot trained on one set of documents. The key alone decides which documents an answer can come from, so you never pass a deployment id separately.

  1. Create a deployment and train it In your dashboard go to Document Training, create a deployment, upload your files (PDF, DOCX, TXT, CSV) and click Train. Wait for the status to reach Completed.
  2. Open the API key page Go to API key in the sidebar, or click here if you're signed in.
  3. Generate a key for that deployment Each deployment gets its own key. Copy it immediately — it is stored hashed, so it cannot be shown again. If you lose it, delete the key and generate a new one.
  4. Keep it server-side where you can A key spends your token balance. Our chat widget uses it in the browser by necessity — if you do the same, restrict the deployment by IP in Settings → IP Restriction and watch usage under Reports.
No account yet? Create one free — you get tokens to test with, and you only pay for what you use after that.

2 Authentication

Send your key whichever way suits the client. All three are equivalent:

Authorization: Bearer YOUR_API_KEY     # preferred, used by /api/v1/*
X-API-KEY: YOUR_API_KEY                # header alternative
?api_key=YOUR_API_KEY                  # query/form field — required for SSE,
                                       # because EventSource cannot set headers

Keys are stored as SHA-256 hashes. An invalid or disabled key, or a deployment that is not published, returns 401.

3 Quickstart

Ask your first question in one request. Pick a language:

curl https://askcybex.com/api/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "YOUR_DEPLOYMENT_ID",
    "messages": [
      { "role": "user", "content": "What is your refund policy?" }
    ]
  }'
// Works with the official OpenAI SDK — only the baseURL changes.
import OpenAI from "openai";

const client = new OpenAI({
  apiKey:  "YOUR_API_KEY",
  baseURL: "https://askcybex.com/api/v1",
});

const res = await client.chat.completions.create({
  model: "YOUR_DEPLOYMENT_ID",
  messages: [{ role: "user", content: "What is your refund policy?" }],
});

console.log(res.choices[0].message.content);
# pip install openai
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://askcybex.com/api/v1",
)

res = client.chat.completions.create(
    model="YOUR_DEPLOYMENT_ID",
    messages=[{"role": "user", "content": "What is your refund policy?"}],
)

print(res.choices[0].message.content)
$ch = curl_init('https://askcybex.com/api/v1/chat/completions');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer YOUR_API_KEY',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'messages'        => [
            ['role' => 'user', 'content' => 'What is your refund policy?'],
        ],
        'conversation_id' => 'visitor-123',
    ]),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);

echo $data['choices'][0]['message']['content']
     ?? $data['error']['message'];

Multi-turn conversations

Follow-up questions need the earlier turns. Pick one of two styles — don't mix them.

Watch the field you copy. Every response contains two ids: id starts with chatcmpl- and identifies that single completion. conversation_id starts with conv- and identifies the thread. Only conversation_id continues a conversation — passing the chatcmpl- id starts a brand new, empty thread, and the bot will appear to have forgotten everything.

Style A — resend the messages (OpenAI-native). Stateless, and what the OpenAI SDKs and Open WebUI do automatically:

{
  "model": "98",
  "messages": [
    { "role": "user",      "content": "What is Hogwarts?" },
    { "role": "assistant", "content": "Hogwarts is a school of witchcraft and wizardry..." },
    { "role": "user",      "content": "Who are the students there?" }
  ]
}

Style B — reuse the conversation id. Send only the new question; we rebuild the history server-side:

# Turn 1 — no id sent, so one is minted and returned
curl -X POST https://askcybex.com/api/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"98","messages":[{"role":"user","content":"What is Hogwarts?"}]}'

# response contains BOTH ids — copy the conv- one:
#   "id":              "chatcmpl-f616e6134ae341316ba712a9"   <- NOT this
#   "conversation_id": "conv-dc4d05c139d35b910ff4be69"       <- this one

# Turn 2 — reuse conversation_id, send only the new question
curl -X POST https://askcybex.com/api/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"98",
       "messages":[{"role":"user","content":"Who are the students there?"}],
       "conversation_id":"conv-dc4d05c139d35b910ff4be69"}'

If you send both, the messages history wins, so a turn is never counted twice.

Follow-ups that lean on context just work. Before searching your documents, the endpoint resolves the follow-up against the conversation — so “and who are they?” is searched as “who are the students at Hogwarts?”, and the answer still comes back in your original wording. The response carries search_query so you can see exactly what was searched.

This used to need a separate /chat/completions-contextual endpoint, while the default one searched for whatever the visitor literally typed. They are the same thing now, streamed or not.

4 Endpoint reference

One endpoint answers questions. The rest are the things a chat widget needs around a conversation, and one for teaching a chatbot something new. Every one of them takes the same bearer key, and the key is what selects the chatbot — there is no deployment id to pass.

Chat

POST/api/v1/chat/completions 60 / min

Answers the last user message from your documents, in the standard OpenAI ChatCompletion shape. Earlier messages are used as history, and the document search is resolved against them. Set "stream": true for Server-Sent Events.

Beyond the OpenAI fields, the response carries sources (the documents behind the answer), search_query, conversation_id and chat_log_id. A stock OpenAI client ignores all four.

messagesthe conversation; the last user turn is the question
messagea plain string, instead of messages
conversation_idthread id; the server rebuilds history for it
streamtoken-by-token SSE instead of one JSON reply
include_chunksreturn the retrieved passages — tens of KB per turn
visitor_emaila captured lead, stored against this turn
GET/api/v1/chat/completions SSE60 / min

The same endpoint for browsers. EventSource cannot set an Authorization header, so the key travels in the query string.

const es = new EventSource(
  "https://askcybex.com/api/v1/chat/completions?stream=1" +
  "&api_key=" + YOUR_API_KEY +
  "&message=" + encodeURIComponent(question) +
  "&conversation_id=" + conversationId
);

es.onmessage = (e) => {
  if (e.data === "[DONE]") return;
  const chunk  = JSON.parse(e.data);
  const choice = chunk.choices[0];

  if (choice.delta.content) output.textContent += choice.delta.content;

  if (choice.finish_reason) {
    es.close();
    rate(chunk.chat_log_id);        // /widget/feedback
    remember(chunk.conversation_id);
  }
};
GET/api/v1/models 120 / min

Returns the one chatbot your key maps to, as an OpenAI model object. Clients such as Open WebUI call this to fill their model picker; it also makes a good connection test.

Widget

POST/api/v1/widget/config 120 / min

Everything a widget needs before its first question: the chatbot's name, the starter questions its owner wrote, any pre-generated Q&A pairs, and whether to offer a human when the bot cannot help.

POST/api/v1/widget/feedback 120 / min

Record whether an answer helped. Send the chat_log_id from the answer and feedback (1 = helpful, 0 = not). Thumbs-down answers appear on the owner's Corrections screen, where they can write a replacement that is then served instead — free, and even when the account is out of credit.

POST/api/v1/widget/handoff 10 / min

“The bot could not help — have a person email me.” Offer it after a refusal and not before, so it never competes with an answer the chatbot could have given. The enquiry is recorded as a lead and the owner is notified. Available only when handoff_enabled comes back true from the widget config.

Documents

POST/api/v1/documents 20 / min

Teach the chatbot something. Send a content string with a name, or a multipart file — pdf, csv, txt or docx. This is how the WooCommerce plugin keeps a catalogue in sync, and how you would push anything your own system generates.

Training runs by default. Send "train": false when pushing several documents and train on the last one. replace defaults to true, because a catalogue push is the whole catalogue — appending would leave last week's discontinued products answering questions for ever.

200stored and trained — the chatbot knows this now
202stored, training queued — it does not know it yet
201stored; training was not requested

5 Use it inside tools you already run

Because the API speaks the OpenAI format, most AI tooling connects without any code. For Open WebUI:

  1. Settings → Connections → OpenAI APIAdd a new connection.
  2. Base URLhttps://askcybex.com/api/v1
  3. API keyYour deployment key.
  4. Save, then pick your deployment from the model listIt appears by deployment id.

The same pattern works for LangChain, LlamaIndex, agent frameworks, ERP and CRM add-ons, and any internal tool that lets you set an OpenAI base URL.

6 Rate limits, errors & billing

EndpointLimit
/chat/completions60 requests / minute
/models, /widget/config, /widget/feedback120 requests / minute
/documents20 requests / minute
/widget/handoff10 requests / minute — it sends mail

Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. Exceeding a limit returns 429.

StatusMeaningWhat to do
401Invalid or disabled key, or the deployment isn't publishedCheck the key; publish the deployment
402Token balance exhaustedTop up your balance
404Deployment not foundConfirm the key belongs to a trained deployment
429Rate limitedBack off and retry
Errors are OpenAI-shaped, and the message is safe to show a visitor. A failure returns {"error": {"message", "type", "code"}} with a real HTTP status. Read error.message to display and error.code to branch on — the code is stable, the wording is not.

The message never reveals why in a way that embarrasses the site owner: an account out of credit tells the visitor the assistant is temporarily unavailable, not that somebody forgot to top up. error.code tells you the truth: no_tokens, account_disabled, ip_restricted, bad_key, not_trained, backend_down, bad_request.

Older endpoints answered HTTP 200 with a bare error string. They are gone; if you have code checking for that shape, it needs updating.

Billing. Answers consume tokens from your account balance. When a question cannot be grounded in your documents, the refusal is returned before the language model is called — so a “Sorry, I don’t know” costs you nothing.

Try every endpoint in the browser

The explorer is loaded with the live spec. Click Authorize, paste your key, and send real requests against your own deployment.

© 2024 AskCybex. All rights reserved.