> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blocks.team/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Messages

> Post a follow-up user message to an existing session.

```http theme={null}
POST /rest/v1/sessions/{session_id}/messages
```

Posts a follow-up user message to an existing session and starts a new agent turn. The response returns a fresh `chat_thread_id` — use it to build the polling URL for the assistant's reply.

<Note>
  Follow-ups can be sent at any time, including while the agent is still working on a previous turn. They will **interrupt** the in-flight turn.
</Note>

## Request

<CodeGroup>
  ```javascript JavaScript theme={null}
  const followup = await fetch(
    `https://api.blocks.team/rest/v1/sessions/${sessionId}/messages`,
    {
      method: "POST",
      headers: {
        Authorization: `ApiKey ${process.env.BLOCKS_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ message: "Cool — now tell me one about cuttlefish." }),
    },
  ).then((r) => r.json());

  // Poll followup._links.final_message.href to receive the assistant's reply.
  ```

  ```python Python theme={null}
  import os, requests

  followup = requests.post(
      f"https://api.blocks.team/rest/v1/sessions/{session_id}/messages",
      headers={
          "Authorization": f"ApiKey {os.environ['BLOCKS_API_KEY']}",
          "Content-Type": "application/json",
      },
      json={"message": "Cool — now tell me one about cuttlefish."},
  ).json()

  # Poll followup["_links"]["final_message"]["href"] to receive the assistant's reply.
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.blocks.team/rest/v1/sessions/$SESSION_ID/messages" \
    -H "Authorization: ApiKey $BLOCKS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"message": "Cool — now tell me one about cuttlefish."}'
  ```

  ```java Java theme={null}
  import java.net.URI;
  import java.net.http.*;

  HttpResponse<String> res = HttpClient.newHttpClient().send(
      HttpRequest.newBuilder(URI.create(
          "https://api.blocks.team/rest/v1/sessions/" + sessionId + "/messages"))
          .header("Authorization", "ApiKey " + System.getenv("BLOCKS_API_KEY"))
          .header("Content-Type", "application/json")
          .POST(HttpRequest.BodyPublishers.ofString(
              "{\"message\":\"Cool — now tell me one about cuttlefish.\"}"))
          .build(),
      HttpResponse.BodyHandlers.ofString());
  ```

  ```ruby Ruby theme={null}
  require "net/http"
  require "json"

  followup = JSON.parse(Net::HTTP.post(
    URI("https://api.blocks.team/rest/v1/sessions/#{session_id}/messages"),
    JSON.generate({ message: "Cool — now tell me one about cuttlefish." }),
    {
      "Authorization" => "ApiKey #{ENV['BLOCKS_API_KEY']}",
      "Content-Type" => "application/json",
    },
  ).body)
  ```

  ```go Go theme={null}
  body, _ := json.Marshal(map[string]string{
      "message": "Cool — now tell me one about cuttlefish.",
  })
  req, _ := http.NewRequest("POST",
      "https://api.blocks.team/rest/v1/sessions/"+sessionID+"/messages",
      bytes.NewReader(body))
  req.Header.Set("Authorization", "ApiKey "+os.Getenv("BLOCKS_API_KEY"))
  req.Header.Set("Content-Type", "application/json")
  res, _ := http.DefaultClient.Do(req)
  ```
</CodeGroup>

### Path parameters

<ParamField path="session_id" type="string (uuid)" required>
  The session to post the follow-up to.
</ParamField>

### Body

<ParamField body="message" type="string" required>
  The follow-up user message.
</ParamField>

<ParamField body="artifact_ids" type="string[] (uuid[])">
  Existing artifacts to attach to this message.
</ParamField>

<ParamField body="profile" type="object">
  Profile reference or inline profile to apply to this turn. Same shape as in [`Create Session`](/rest-api/sessions/create#body).
</ParamField>

## Response

```json theme={null}
{
  "id": "9c2f…",
  "chat_id": "a196cec6-49cb-4c48-8e4c-2707fb5d6709",
  "chat_thread_id": "b4e1…",
  "task_id": "1f8a…",
  "role": "user",
  "type": "message",
  "message": "Cool — now tell me one about cuttlefish.",
  "ts": null,
  "created_at": "2026-04-30T18:25:12.000Z",
  "updated_at": "2026-04-30T18:25:12.000Z",
  "_links": {
    "self": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/messages" },
    "thread": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/threads/b4e1…/messages" },
    "final_message": { "href": "https://api.blocks.team/rest/v1/sessions/a196cec6-…/threads/b4e1…/messages?type=final_message&role=assistant" }
  }
}
```

<ResponseField name="id" type="string (uuid)">
  Message ID for the user message you just posted.
</ResponseField>

<ResponseField name="chat_id" type="string (uuid)">
  The session this message belongs to.
</ResponseField>

<ResponseField name="chat_thread_id" type="string (uuid)">
  The new thread created for this turn. Prefer `_links.final_message.href` for polling — this field is exposed for cases where you need the thread ID directly.
</ResponseField>

<ResponseField name="task_id" type="string (uuid)">
  The task ID for the agent invocation triggered by this message.
</ResponseField>

<ResponseField name="role" type="string">
  Always `user` for this endpoint.
</ResponseField>

<ResponseField name="type" type="string">
  Always `message` for this endpoint.
</ResponseField>

<ResponseField name="message" type="string">
  Echo of the message body you posted.
</ResponseField>

<ResponseField name="ts" type="number | null">
  Provider-supplied timestamp (epoch seconds). Typically `null` for user messages.
</ResponseField>

<ResponseField name="created_at" type="string (ISO 8601)">
  When the message was created.
</ResponseField>

<ResponseField name="updated_at" type="string (ISO 8601)">
  When the message was last updated.
</ResponseField>

<ResponseField name="_links" type="object">
  HATEOAS links — mirror those returned by [`Create Session`](/rest-api/sessions/create) so you can reuse the same polling idiom for follow-ups.

  <Expandable title="properties">
    <ResponseField name="self" type="object">
      Link to the session's messages collection.
    </ResponseField>

    <ResponseField name="thread" type="object | null">
      Link to the new thread's messages list.
    </ResponseField>

    <ResponseField name="final_message" type="object | null">
      Pre-built polling URL for the assistant's reply on this thread — already filtered to `type=final_message&role=assistant`. Poll until `items` is non-empty.
    </ResponseField>
  </Expandable>
</ResponseField>

## Errors

| Status | Code          | Reason                                                                                                                                          |
| ------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `BAD_REQUEST` | The session has no prior messages, so no `task_id` can be resolved. Create the session via [`Create Session`](/rest-api/sessions/create) first. |
| `404`  | `NOT_FOUND`   | Session does not exist or belongs to a different workspace.                                                                                     |
