---
title: "Post Thread Message"
method: POST
path: "/v1/threads/{thread_id}/messages"
tags: ["threads"]
---

# Post Thread Message

`POST /v1/threads/{thread_id}/messages`

## Request

- Base URL: `https://api.claims.zicron.ai`
- URL: `https://api.claims.zicron.ai/v1/threads/{thread_id}/messages`
- Auth: none declared

Send a claimant reply into a thread.

Channel honoring: the reply is routed to the channel of the most recent
agent message in the thread (sms / email / web_chat). Voice falls back
to web_chat — we cannot dynamically dial out from a synchronous HTTP
POST. The agent's response is then delivered via the corresponding
channel adapter (SMS via Twilio, email via Resend, web via the inbox
refresh) so the claimant continues in their preferred channel.

Async dispatch (item #16): the endpoint persists the new Conversation
row, then enqueues `process_turn` as a background asyncio task and
returns 202 Accepted immediately. The agent reply is delivered to
the client via the existing `/threads/{id}/stream` SSE endpoint
within ~1.5s. The previous synchronous code path is gone — keep one
path, not two. A task crash leaves the user message + Conversation
row durable; the orphan janitor in `conversations/background.py`
sweeps inbound conversations stuck in `scheduled` / `in_progress`
past `CONVERSATION_ORPHAN_THRESHOLD_SECONDS` (default 5 min) and
marks them `status='failed'` with the existing delivery_status
badge so the user sees the failure and can retry.

Auth model:
  - Claimants: may only post to their own threads (person_id match).
  - Staff: NOT permitted in v1. Staff impersonation / on-behalf-of
    posting is desirable but out of scope; returns 403. Track as a
    follow-up when staff takeover UX lands for the inbox.

## Path parameters

- `thread_id` string, required

## Request body

- PostMessageRequest
  - `content` string, required

## Response `202`

Successful Response

- PostMessageResponse: Response shape for POST /threads/{id}/messages. Item #16 of the holistic-agent follow-ups: the endpoint now enqueues `process_turn` as a background task and returns 202 Accepted immediately. The agent reply is delivered to the frontend via the SSE stream `/threads/{id}/stream`. Backwards-compat fields kept (and now always None / ""): - `message_id`: was the persisted human message id; the engine now runs after the response, so we can't return it. The frontend matches its optimistic message against the SSE-delivered canonical row by `(role, content, conversation_id, ts-window)`. - `agent_response_text`: was the synchronous agent reply; now delivered over SSE.
  - `conversation_id` string, required
  - `message_id` string
  - `agent_response_text` string, nullable
  - `accepted` boolean

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zicron/apis/zicron-api.md) · [All operations](https://skmtc.dev/zicron/apis/zicron-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zicron/apis/zicron-api/revisions/abe62e22aa46?raw)
