---
title: "Add Message to Thread with Optional Attachments"
method: POST
path: "/threads/{thread_id}/messages"
tags: ["Threads"]
---

# Add Message to Thread with Optional Attachments

`POST /threads/{thread_id}/messages`

Add a user message to an existing thread with optional file attachments. Accepts both JSON (application/json) and form-data (multipart/form-data) payloads. Use JSON for text-only messages; use form-data when uploading file attachments. Can send text only, attachments only, or both. Documents must be indexed before further messages are allowed. To choose a model, set llm_provider and model_name. If omitted, defaults are llm_provider=openai and model_name=gpt-4o. When stream=true, the SSE feed emits `content_streaming` answer deltas and may also emit `reasoning_streaming` events when the selected provider exposes native live reasoning deltas; those events are live-only and are not persisted. Set thinking to a flat object such as `{"effort": "high"}` or `{"budget_tokens": 8192}` to request reasoning on supported models/providers. Use `{}` to enable provider defaults. Memory defaults to 'off'. Set memory='Auto' for workspace-scoped memory search and automatic memory operations, or memory='Readonly' for search-only. Supported attachment types: .pdf, .doc(x), .ppt(x), .xls(x), .txt, .csv, .md, .json(l), .xml, .py, .js, .ts, .jsx, .tsx, .html, .css, .cpp, .c, .h, .java, .go, .rs, .rb, .php, .sql, .png, .jpg, .jpeg, .webp, .gif, .bmp, .tiff, .tif, .mp3, .wav, .m4a, .ogg, .flac, .aac (audio), .mp4, .mov, .avi, .mkv, .mpeg, .mpg, .webm (video). Audio/video files are sent inline to models that natively accept them. See the Model Library page in the dashboard (model-library page) for a current list of supported models and providers.

## Path parameters

- `thread_id` string, uuid, required

## Headers

- `authorization` string, nullable
- `x_session_token` string, nullable

## Request body

- object
  - `content` string — Text content of the message
  - `llm_provider` string, nullable — LLM provider name. Default: openai.
  - `model_name` string, nullable — Model name. Default: gpt-4o.
  - `system_prompt` string, nullable — Per-run system prompt override. Not persisted on the assistant.
  - `image_generation` 'auto' | 'off' — Image generation: 'auto' enables the generate_image tool (requires image_model_provider and image_model_name); 'off' disables it.
  - `image_model_provider` string, nullable — Required when image_generation=auto. Provider for generate_image (e.g. openrouter). Ignored when image_generation=off.
  - `image_model_name` string, nullable — Required when image_generation=auto. Model for generate_image (e.g. google/gemini-2.5-flash-image). Ignored when image_generation=off.
  - `stream` boolean — Whether to stream the AI response.
  - `thinking` object, nullable — Flat reasoning controls inferred from the selected llm_provider/model. Use {} to enable provider defaults, or send only the fields supported by the selected model.
    - `effort` 'low' | 'medium' | 'high' | 'max' — Use for OpenAI, xAI, and Gemini 3 models.
    - `budget_tokens` integer — Use for Anthropic and Gemini 2.5 models.
    - `max_tokens` integer — Use for OpenRouter and Cerebras reasoning models.
    - `exclude_reasoning` boolean — Use for OpenRouter and Cerebras reasoning models.
  - `openrouter` object, nullable — OpenRouter-only routing options (ignored for other providers). OpenRouter serves each model from multiple upstream providers at different prices; omit this object to let it choose automatically. You are always billed the exact amount the serving provider charged.
    - `providers` string[] — Ranked upstream providers to try, e.g. ["Fireworks", "Together"]. See GET /billing/models/providers for the available names and their prices.
    - `allow_fallbacks` boolean — When false, the request runs only on `providers` and fails instead of falling back to another upstream (and another price).
    - `sort` 'price' | 'throughput' | 'latency' — Rank the available upstream providers by this criterion.
    - `ignore` string[] — Upstream providers to exclude for this request.
    - `max_price` object — Skip upstream endpoints priced above this ceiling.
      - `prompt` number — Max USD per 1M prompt tokens.
      - `completion` number — Max USD per 1M completion tokens.
    - `allowed_models` string[] — Auto Router only (model_name='openrouter/auto'): restrict selection with wildcard patterns, e.g. ["anthropic/*"].
    - `excluded_models` string[] — Auto Router only: exclude models by wildcard pattern.
    - `cost_tier` 'low' | 'medium' | 'high' | 'xhigh' | 'max' — Auto Router only: cost band to route within.
  - `tools` object[], nullable — Optional per-message tool override (OpenAI-style). Not persisted on the assistant.
  - `memory` string — Memory Lite mode (no reranking): 'Auto', 'Readonly', or 'off' (default). Cannot be used together with memory_pro.
  - `memory_response_citation` boolean — Whether the assistant should cite retrieved memories in its response text.
  - `memory_citation` boolean — Deprecated alias for memory_response_citation.
  - `memory_pro` string, nullable — Memory Pro mode (with reranking, higher cost): 'Auto', 'Readonly', or omit. Cannot be used together with memory.
  - `web_search` string — Web search mode: 'Auto' or 'off'.
  - `send_to_llm` string — Whether to send to LLM for a response.
  - `json_output` boolean — When true, request JSON object output from the model. Ignored when RAG, web search, or custom tools are active.
  - `custom_timestamp` string, date-time, nullable — Custom timestamp for the message (merged into metadata when stored).
  - `metadata` string — Optional metadata as JSON string.
  - `voice` object — Optional voice config object. Add `stt` to enable speech-to-text, add `tts` to enable text-to-speech.

## Response `200`

Successful Response

- MessageResponse — Response for message operations - includes content at top level for easy access
  - `message` string, required
  - `thread_id` string, uuid, required
  - `assistant_id` string, uuid, nullable
  - `content` string, nullable
  - `message_id` string, uuid, nullable
  - `role` 'user' | 'assistant' | 'tool'
  - `status` 'IN_PROGRESS' | 'REQUIRES_ACTION' | 'COMPLETED' | 'FAILED' | 'CANCELLED'
  - `tool_calls` object[], nullable
  - `run_id` string, nullable
  - `memory_operation_id` string, nullable
  - `retrieved_memories` RetrievedMemory[], nullable
    - `id` string, nullable
    - `memory` string, required
    - `score` number, nullable
  - `retrieved_files` string[], nullable
  - `retrieved_files_count` integer
  - `reasoning` string, nullable
  - `model_provider` string, nullable
  - `model_name` string, nullable
  - `input_tokens` integer, nullable
  - `output_tokens` integer, nullable
  - `total_tokens` integer, nullable
  - `created_at` string, date-time, nullable
  - `attachments` AttachmentInfo[], nullable
    - `document_id` string, uuid, required
    - `filename` string, required
    - `status` string, required
    - `file_size_bytes` integer, required
    - `summary` string, nullable
  - `generated_media` GeneratedMediaInfo[], nullable
    - `document_id` string, required
    - `media_type` string, required
    - `mime_type` string, required
    - `url` string, required
    - `file_size_bytes` integer, nullable
    - `transcript` string, nullable
  - `voice_records` VoiceRecord — Voice pipeline outcome for this turn: STT and/or TTS (artifacts + billing dimensions).
    - `stt` STTUsageInfo — STT result: transcript, input audio URL, billing dimensions, and raw provider output.
      - `provider` string, required
      - `model` string, required
      - `transcript` string, nullable
      - `audio_url` string, nullable — Presigned URL for the user's original audio input
      - `language` string, nullable
      - `duration_seconds` number, nullable
      - `input_tokens` integer, nullable
      - `output_tokens` integer, nullable
      - `audio_input_tokens` integer, nullable
      - `provider_output` object, nullable — Provider-specific response data (e.g. words, segments, entities, logprobs).
    - `tts` TTSUsageInfo — TTS result: synthesized audio URL, billing dimensions, and raw provider output.
      - `provider` string, required
      - `model` string, required
      - `audio_url` string, nullable — Presigned URL for synthesized speech output
      - `voice` string, nullable
      - `output_format` string, nullable
      - `characters` integer, nullable
      - `duration_seconds` number, nullable
      - `input_tokens` integer, nullable
      - `audio_output_tokens` integer, nullable
      - `provider_output` object, nullable — Provider-specific response data.
  - `timestamp` string, date-time, required
  - `context_usage` object, nullable

## Other responses

- `422` — Validation Error

---

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