Send what the user typed into your support form. You get back an answer, a confidence score, and deflect: whether the answer is good enough to show. The answer is not streamed.
POST https://app.kelu.dev/api/v1/public/knowledge-bases/:knowledgeBaseId/form-deflect
Auth: client key. Public keys also need a CAPTCHA token when CAPTCHA is on. Don’t want to write code? Use the Support Form Deflector.

Request body

FieldTypeRequiredDescription
messagestringYesThe question or ticket description
subjectstringNoThe ticket subject
deflector_idstringNoA saved form deflector. Its settings apply (see below)
thread_idstringNoContinue an earlier deflection
fields{label, value}[]NoOther form fields, used as context. Max 12, the rest are dropped
group_idsstring[]NoAnswer only from these source groups
end_user_idstringNoAn anonymous id for the reader, so one person counts once
end_user_emailstringNoThe email typed into the form. Stored on the conversation. See user tracking
With deflector_id, the deflector’s confidence threshold, allowed domains, source groups, minimum question length and voice apply. Its source groups replace any group_ids you send. A disabled deflector, or a page origin outside its allowed domains, returns 403. An unknown id returns 404.
curl https://app.kelu.dev/api/v1/public/knowledge-bases/KB_ID/form-deflect \
  -H "X-Client-Id: kl_ci_..." \
  -H "X-Client-Secret: kl_cs_..." \
  -H "Content-Type: application/json" \
  -d '{"subject": "Password reset", "message": "I cannot log in after changing my account email"}'

Response

{
  "answer": "After you change your account email, sign in with the new address ... [1]",
  "citations": [{ "chunk_id": "7c1e...", "document_id": "a4d2...", "url": "https://docs.example.com/account", "title": "Account Settings", "heading_path": ["Account"], "excerpt": "When you change your account email ...", "index": 1 }],
  "confidence": 0.82,
  "deflect": true,
  "conversation_id": "9b1f...",
  "message_id": "c3a2...",
  "deflection_id": "e71d...",
  "powered_by": "Powered by Kelu",
  "powered_by_url": "https://kelu.dev"
}
FieldDescription
answerThe answer, with [n] citation markers
citationsThe sources the answer cites. Same fields as chat citations
confidence0 to 1. See How confidence works
deflecttrue when confidence is at or above the threshold. The default threshold is 0.55
conversation_idSend it as thread_id to chat or form-deflect for a follow-up
message_idOnly when deflect is true. Use it to rate the answer
deflection_idUse it to report the outcome
skip_reasonOnly when deflect is false: too_short, low_confidence or no_answer
handoff_linkOnly when the deflector has hand-off turned on and deflect is true. A dashboard link to this conversation, for your agents
A message shorter than 25 characters (or the deflector’s own minimum) is not answered. You get deflect: false and skip_reason: "too_short".

Report the outcome

When the user submits the ticket anyway, tell Kelu:
POST https://app.kelu.dev/api/v1/public/deflections/:deflection_id/outcome

{"outcome": "submitted"}
It returns {"success": true}. "submitted" is the only value. A user who reads the answer and leaves sends nothing, so the deflection rate is (answered − submitted) / answered.

How confidence works

Confidence is how well your content answers this question. 0.5 is the line between answerable and not. It is lowered in three cases:
  • The answer says it does not know. Confidence is at most 0.2.
  • Your knowledge base does not cover the question. Confidence is 0.
  • The answer makes claims its sources do not support. Confidence is 0.
If a check cannot run, the score is left as it is.

What to do with the result

  • deflect: true: show the answer, with a way to say it helped and a way to continue to the form.
  • deflect: false, or any error: let the form submit normally. Never block the user.
  • Rate the answer with message_id. The deflection rate only says a ticket was avoided, not that the answer was right.
Errors use the normal error format. For example 402 when the workspace used its monthly question allowance, 429 or 503 when the AI provider is busy.