Skip to main content
The chat endpoint. Takes a user message, runs it through input sanitization, dispatches to the bot owner’s configured provider with the caller’s BYO key, sanitizes the reply, and returns it.

Request

Path params

Headers

The key is never placed in the JSON body. See BYO-key flow.

Body

Total body size cap

The route measures the raw byte length and rejects payloads over 16,384 bytes with 413 request_too_large, regardless of Content-Length.

Lifecycle

  1. Content-Type check - non-JSON → 415 unsupported_media_type
  2. Key read - missing x-llm-api-key400 missing_llm_key
  3. Body size cap - > 16,384 bytes → 413 request_too_large
  4. JSON parse - malformed → 400 invalid_json
  5. Zod validate - message out of bounds → 400 validation_failed
  6. Bot lookup - bots.id + is_active = true; miss → 404 bot_not_found
  7. Owner lookup - owner row missing → 404 bot_not_found
  8. Rate limit - 2-tier per-bot → 429 rate_limit
  9. Input sanitize - match → 400 blocked
  10. Provider dispatch - Azure requires x-llm-azure-endpoint; missing → 400 missing_llm_key
  11. Provider call → on error, map ProviderError.category to invalid_llm_key / provider_rate_limit / provider_unavailable
  12. Output sanitize - strip key shapes, system-prompt echo, PII, internal errors
  13. Return { reply }

Responses

200 OK

400 Bad Request - missing key

400 Bad Request - invalid JSON

400 Bad Request - validation failure

400 Bad Request - blocked by sanitizer

reason is a coarse category string (prompt_injection, role_override, credential_probe, system_prompt_extraction, jailbreak). The matched substring is not returned.

400 Bad Request - invalid LLM key

The provider rejected the key. The key value itself is never echoed.

404 Not Found

Returned both when the bot id doesn’t exist and when its owner record is missing - the two cases are deliberately indistinguishable to the caller.

413 Payload Too Large

415 Unsupported Media Type

429 Too Many Requests - local rate limit

429 Too Many Requests - provider rate limit

The bot owner’s LLM provider rate-limited the underlying call.

502 Bad Gateway

Provider was down, timed out, or returned an unknown error.

cURL

Anthropic / OpenAI / Google

Azure OpenAI

Notes

  • The route owner determines provider + model (users.llm_provider, users.llm_model). Callers cannot override them.
  • The system prompt is rebuilt per-request from the freshly-fetched bot row; there is no cache to invalidate.
  • The output sanitizer never returns an empty string - if the entire reply got stripped, the route returns a polite fallback string, not a 500.