The public API is what the widget and the SDKs call. You can call it too, from a browser or a server. Every request needs a client key.

Base URL

https://app.kelu.dev
All paths below start with /api/v1/public. Self-hosted? Use your own server’s address. The paths are the same.

Endpoints

MethodPathWhat it does
POST/knowledge-bases/:knowledgeBaseId/chatStream an answer (SSE)
POST/threads/:threadId/chatAsk a follow-up
GET/knowledge-bases/:knowledgeBaseId/wsChat over WebSocket
POST/knowledge-bases/:knowledgeBaseId/searchSearch
POST/knowledge-bases/:knowledgeBaseId/form-deflectAnswer a support form
POST/deflections/:id/outcomeReport that the ticket was filed anyway
POST/feedbackRate an answer
DELETE/feedback/:messageIdWithdraw a rating
GET/knowledge-bases/:knowledgeBaseId/widget-configSettings the widget reads on load
GET/widget-integrations/:widgetId/configA saved widget’s settings
GET/form-deflectors/:deflectorId/configA saved form deflector’s settings
GET/knowledge-bases/:knowledgeBaseId/suggested-questionsStarter questions
POST/knowledge-bases/:knowledgeBaseId/relatedRelated articles
POST/knowledge-bases/:knowledgeBaseId/eventsRecord that the widget was opened: {"event": "opened", "end_user_id": "..."}. Returns 202
:knowledgeBaseId must be the knowledge base your key belongs to. Any other id returns 403.

Conventions

  • Request and response bodies are JSON. Chat streams Server-Sent Events.
  • Bodies are capped at 4 MB.
  • Questions on chat, search and WebSocket are capped at 4,000 characters.
  • Restricted documents are never returned.
  • group_ids (chat, search, form-deflect, WebSocket) limits answers to some source groups. Each id must be a source group of this knowledge base and, when the client key has allowed groups, one of them. Otherwise you get 403. Leave group_ids out and the key’s allowed groups are used.

Errors

Errors are JSON with a matching HTTP status:
{
  "error": "client key is not authorized for this knowledge base",
  "code": "FORBIDDEN",
  "error_code": "forbidden",
  "retryable": false,
  "request_id": "5f0c..."
}
  • code is what to branch on. error is a sentence you can log or show. One exception: a refused origin is 403 but carries code: "UNAUTHORIZED" (error_code: "unauthorized"), the same as a bad key. Tell the two apart by the HTTP status.
  • error_code is the same class in lower case. Most errors carry it.
  • retryable says whether the same request can work later.
  • request_id is also sent as the X-Request-ID header. Quote it when you contact support.
StatusWhen
400Bad body, missing field, question too long, a :threadId that is not a valid UUID
401Missing or wrong credentials (UNAUTHORIZED)
403Origin not allowed (UNAUTHORIZED), key for another knowledge base, CAPTCHA missing or failed (CAPTCHA_REQUIRED, CAPTCHA_FAILED), an internal knowledge base (KNOWLEDGE_BASE_INTERNAL), an archived knowledge base, a source group outside this knowledge base or the key’s allowed groups, a widget that is switched off (WIDGET_DISABLED) or a page outside its sites (ORIGIN_NOT_ALLOWED), or a form deflector that is off or a page outside its allowed domains
404Thread, message, deflector or widget not found
413Body over 4 MB
429Rate limit reached (RATE_LIMITED). See rate limits
Chat and WebSocket report failures that happen mid-answer inside the stream. See chat errors and WebSocket errors.