---
title: "Handle Send Chat Message"
method: POST
path: "/chat/send-chat-message"
tags: ["public"]
---

# Handle Send Chat Message

`POST /chat/send-chat-message`

This endpoint is used to send a new chat message.

Args:
    chat_message_req (SendMessageRequest): Details about the new chat message.
        - When stream=True (default): Returns StreamingResponse with SSE
        - When stream=False: Returns ChatFullResponse with complete data
    request (Request): The current HTTP request context.
    user (User): The current user, obtained via dependency injection.
    _ (None): Rate limit check is run if user/group/global rate limits are enabled.

Returns:
    StreamingResponse | ChatFullResponse: Either streams or returns complete response.

## Request body

- SendMessageRequest
  - `message` string, required
  - `llm_override` LLMOverride — Per-request LLM settings that override persona defaults. All fields are optional — only the fields that differ from the persona's configured LLM need to be supplied. Used both over the wire (API requests) and for multi-model comparison, where one override is supplied per model. Attributes: model_configuration_id: Exact model configuration to use. Preferred over the name-based fields — provider display names are not unique, so only the id routes unambiguously. model_provider: LLM provider display name. When ``None``, the persona's default provider is used. model_version: Specific model version string (e.g. ``"gpt-4o"``). When ``None``, the persona's default model is used. temperature: Sampling temperature in ``[0, 2]``. When ``None``, the persona's default temperature is used. display_name: Human-readable label shown in the UI for this model, e.g. ``"GPT-4 Turbo"``. Optional; falls back to ``model_version`` when not set.
    - `model_configuration_id` integer, nullable
    - `model_provider` string, nullable
    - `model_version` string, nullable
    - `temperature` number, nullable
    - `display_name` string, nullable
  - `llm_overrides` LLMOverride[], nullable — Two or three LLM overrides to run in parallel (multi-model mode), one entry per model. Requires `stream=true`: a request carrying more than one entry with `stream=false` is rejected with a 400 `INVALID_INPUT` error. A list with a single entry is ignored — use `llm_override` to change the model for an ordinary single-model request.
    - `model_configuration_id` integer, nullable
    - `model_provider` string, nullable
    - `model_version` string, nullable
    - `temperature` number, nullable
    - `display_name` string, nullable
  - `allowed_tool_ids` integer[], nullable
  - `forced_tool_id` integer, nullable
  - `file_descriptors` FileDescriptor[]
    - `id` string, required
    - `type` 'image' | 'document' | 'plain_text' | 'tabular', required
    - `name` string, nullable
    - `user_file_id` string, nullable
  - `internal_search_filters` BaseFilters
    - `source_type` DocumentSource[], nullable
    - `document_set` string[], nullable
    - `created_at_range` TimeRange — An inclusive [start, end] window; either bound may be None (open). Naive (timezone-less) bounds are treated as UTC.
      - `start` string, date-time, nullable
      - `end` string, date-time, nullable
    - `updated_at_range` TimeRange — An inclusive [start, end] window; either bound may be None (open). Naive (timezone-less) bounds are treated as UTC.
      - `start` string, date-time, nullable
      - `end` string, date-time, nullable
    - `tags` Tag[], nullable
      - `tag_key` string, required
      - `tag_value` string, required
    - `time_cutoff` string, date-time, nullable
  - `deep_research` boolean
  - `mcp_headers` object, nullable — Headers forwarded to MCP tool calls made while answering this message, e.g. `{"Authorization": "Bearer <user_jwt>", "X-User-ID": "user123"}`. Use this to pass end-user credentials through to MCP servers that require them.
  - `origin` 'webapp' | 'chrome_extension' | 'api' | 'slackbot' | 'widget' | 'discordbot' | 'mobile' | 'unknown' | 'unset' — Origin of a chat message for telemetry tracking.
  - `parent_message_id` integer, nullable
  - `chat_session_id` string, uuid, nullable
  - `chat_session_info` ChatSessionCreationRequest
    - `persona_id` integer
    - `description` string, nullable
    - `project_id` integer, nullable
    - `incognito` boolean
    - `incognito_session_id` string, uuid, nullable
  - `stream` boolean
  - `include_citations` boolean
  - `additional_context` string, nullable — A string of extra context injected into the LLM call for this request. The context is passed to the model but is not stored in the database and will not appear in the chat history. Use this to supply ephemeral, request-scoped information (e.g. the user's current page URL, session metadata, or any runtime context) without polluting the persistent conversation history. Pass null or omit the field to use no additional context.

## Response `200`

If `stream=true`, returns `text/event-stream`.
If `stream=false`, returns `application/json` (ChatFullResponse).

- ChatFullResponse — Complete non-streaming response with all available data.
  - `answer` string, required
  - `answer_citationless` string, required
  - `pre_answer_reasoning` string, nullable
  - `tool_calls` ToolCallResponse[]
    - `tool_name` string, required
    - `tool_arguments` object, required
    - `tool_result` string, required
    - `search_docs` SearchDoc[], nullable
      - `document_id` string, required
      - `chunk_ind` integer, required
      - `semantic_identifier` string, required
      - `link` string, nullable
      - `blurb` string, required
      - `source_type` 'ingestion_api' | 'slack' | 'web' | 'google_drive' | 'gmail' | 'github' | 'gitbook' | 'gitlab' | 'guru' | 'bookstack' | 'outline' | 'confluence' | 'jira' | 'slab' | 'productboard' | 'file' | 'coda' | 'canvas' | 'notion' | 'zulip' | 'linear' | 'hubspot' | 'document360' | 'gong' | 'google_sites' | 'zendesk' | 'loopio' | 'box' | 'dropbox' | 'sharepoint' | 'teams' | 'salesforce' | 'discourse' | 'axero' | 'clickup' | 'mediawiki' | 'wikipedia' | 'asana' | 's3' | 'r2' | 'google_cloud_storage' | 'oci_storage' | 'xenforo' | 'not_applicable' | 'discord' | 'freshdesk' | 'fireflies' | 'egnyte' | 'airtable' | 'highspot' | 'drupal_wiki' | 'imap' | 'bitbucket' | 'testrail' | 'braintrust' | 'lumapps' | 'mock_connector' | 'user_file' | 'craft_file', required
      - `boost` integer, required
      - `hidden` boolean, required
      - `metadata` object, required
      - `score` number, nullable
      - `is_relevant` boolean, nullable
      - `relevance_explanation` string, nullable
      - `match_highlights` string[], required
      - `updated_at` string, date-time, nullable
      - `primary_owners` string[], nullable
      - `secondary_owners` string[], nullable
      - `is_internet` boolean
      - `file_id` string, nullable
    - `generated_images` GeneratedImage[], nullable
      - `file_id` string, required
      - `url` string, required
      - `revised_prompt` string, required
      - `shape` string, nullable
    - `pre_reasoning` string, nullable
  - `top_documents` SearchDoc[], required
    - `document_id` string, required
    - `chunk_ind` integer, required
    - `semantic_identifier` string, required
    - `link` string, nullable
    - `blurb` string, required
    - `source_type` 'ingestion_api' | 'slack' | 'web' | 'google_drive' | 'gmail' | 'github' | 'gitbook' | 'gitlab' | 'guru' | 'bookstack' | 'outline' | 'confluence' | 'jira' | 'slab' | 'productboard' | 'file' | 'coda' | 'canvas' | 'notion' | 'zulip' | 'linear' | 'hubspot' | 'document360' | 'gong' | 'google_sites' | 'zendesk' | 'loopio' | 'box' | 'dropbox' | 'sharepoint' | 'teams' | 'salesforce' | 'discourse' | 'axero' | 'clickup' | 'mediawiki' | 'wikipedia' | 'asana' | 's3' | 'r2' | 'google_cloud_storage' | 'oci_storage' | 'xenforo' | 'not_applicable' | 'discord' | 'freshdesk' | 'fireflies' | 'egnyte' | 'airtable' | 'highspot' | 'drupal_wiki' | 'imap' | 'bitbucket' | 'testrail' | 'braintrust' | 'lumapps' | 'mock_connector' | 'user_file' | 'craft_file', required
    - `boost` integer, required
    - `hidden` boolean, required
    - `metadata` object, required
    - `score` number, nullable
    - `is_relevant` boolean, nullable
    - `relevance_explanation` string, nullable
    - `match_highlights` string[], required
    - `updated_at` string, date-time, nullable
    - `primary_owners` string[], nullable
    - `secondary_owners` string[], nullable
    - `is_internet` boolean
    - `file_id` string, nullable
  - `citation_info` CitationInfo[], required
    - `type` 'citation_info'
    - `citation_number` integer, required
    - `document_id` string, required
  - `message_id` integer, required
  - `chat_session_id` string, uuid, nullable
  - `incognito` boolean
  - `error_msg` string, nullable

## Other responses

- `422` — Validation Error

## Changes

- **2026-09-02** `fcb09658bc37` — 3 breaking, 29 info
  - removed the enum value `csv` of the request property `file_descriptors/items/type`
  - removed the enum value `requesttracker` of the request property `internal_search_filters/anyOf[subschema #1: BaseFilters]/source_type/anyOf[subschema #1]/items/`
  - the response's body type changed from no type to `object` for status `200`
  - added the new optional request property `chat_session_info/anyOf[subschema #1: ChatSessionCreationRequest]/incognito`
  - …28 more
- **2026-02-24** `8230a6a0119a` — 1 info
  - added the new optional request property `additional_context`
- **2026-01-29** `7750a15eed20` — 2 info
  - added the new optional request property `include_citations`
  - added the new `widget` enum value to the request property `origin`
- **2026-01-13** `7f4d7f7b70ee` — 1 info
  - added the new optional request property `origin`
- **2026-01-06** `2d941ea53eb3` — 1 info
  - endpoint added

[Full history](https://skmtc.dev/onyx-dot-app/apis/onyx-api/changes/chat/send-chat-message/post.md)

---

[API](https://skmtc.dev/onyx-dot-app/apis/onyx-api.md) · [All operations](https://skmtc.dev/onyx-dot-app/apis/onyx-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/onyx-dot-app/onyx-api/revisions/fcb09658bc37/schema)
