---
title: "Message a Reason"
method: POST
path: "/v3/organizations/{orgId}/reasons/{reasonId}/messages"
tags: ["Reasons"]
---

# Message a Reason

`POST /v3/organizations/{orgId}/reasons/{reasonId}/messages`

## Request

- Base URL: `https://api.reasonmachines.com`
- URL: `https://api.reasonmachines.com/v3/organizations/{orgId}/reasons/{reasonId}/messages`
- Auth: HTTP bearer

Messaging a Reason is the primary way to give it work. The message goes into the Reason's long-running conversation, which this call opens the first time (as openReasonConversation does); the Reason then decides how to do the work: it answers, starts Sessions, or sets up Loops. Sessions it starts are filed under it.

The message is delivered exactly as sendSessionMessage delivers a follow-up to the conversation Session: the same body, admission, usage and safeguards. A message sent while the Reason is working steers its running turn (or waits for the next one with `delivery` queue). The call returns once the message is accepted, not when the Reason answers: poll listReasonMessages with the last `end_cursor` for its reply, or follow the Session (`session_id`) with getSession and listSessionEvents. Send a `client_message_id` to make retries safe: resending with it returns the accepted message instead of adding another. Anyone the Reason is shared with may message it; each turn acts as the person who sent it.

<sub>Scope: `run`</sub>

## Headers

- `X-Reason-Agent` string

## Request body

- object
  - `message` string, required: The follow-up instruction for the agent. It reaches the running turn or starts the next one; the response's `delivery` says which.
  - `attachments` union[]: Up to 10 attachments; maximum 64 MiB per file and 80 MiB total decoded bytes. The endpoint's request-body limit also applies to the encoded payload.
    - union
      - object
        - `id` string: Caller-chosen identifier for this file, kept with the stored attachment. Omitted, the file is numbered by its position: att-1, att-2 and so on.
        - `name` string, required: File name shown with the attachment, for example screenshot.png. It also supplies the content type when none is given; an empty name becomes attachment-1, attachment-2 and so on.
        - `content_type` string: MIME type of the file, for example image/png. Omitted, the data URL's media type is used; when that is empty or application/octet-stream, the type is inferred from the name's extension.
        - `contentType` string: Legacy alias of content_type.
        - `data_url` string, required: Base64 data URL. Stored privately with the Session before dispatch.
      - object
        - `id` string: Caller-chosen identifier for this file, kept with the stored attachment. Omitted, the file is numbered by its position: att-1, att-2 and so on.
        - `name` string, required: File name shown with the attachment, for example screenshot.png. It also supplies the content type when none is given; an empty name becomes attachment-1, attachment-2 and so on.
        - `content_type` string: MIME type of the file, for example image/png. Omitted, the data URL's media type is used; when that is empty or application/octet-stream, the type is inferred from the name's extension.
        - `contentType` string: Legacy alias of content_type.
        - `dataUrl` string, required: Legacy alias of data_url.
  - `client_message_id` string: Caller-generated idempotency key. Reusing it for the same Session returns the original accepted turn.
  - `model` string: Optional model id from `GET /agent-auth/models` to retry this session on. A model override always starts a new continuation run, because a run's model is fixed at its first execution. Omit or use `auto` to keep the session's current model.
  - `reasoning_effort` 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max': How much reasoning the model should use for the follow-up turn. Higher effort trades time and token usage for more deliberation; supported levels depend on the selected model. Omit to retain the Session's effort setting.
  - `allow_model_rollover` boolean: Same as the create field, for this and later turns: the follow-up goes on with the provider's other accounts, then Reason, when the Session's subscription or key runs out. A Session that already rolls over keeps doing so.
  - `delivery` 'steer' | 'queue': While a turn runs: steer (the default) hands the message to the running turn; queue holds it until the turn ends and then runs it as the next one. A queued message can be sent at once or withdrawn with its message_id (POST .../messages/{messageId}/steer, DELETE .../messages/{messageId}/queued). Not combinable with target.
  - `target` union: Moves the Session: this message's turn, and every later one, runs on this target instead of the one the Session was on. Same shape and admission as the create `target` (`cloud`, a Device and root, or a named Machine). Only the conversation moves; files on the previous machine do not, unless they were pushed to the Session branch. A move starts a new continuation run, so the Session must have finished its current turn. Any target other than `cloud` needs the `devices:use` scope.
    - object
      - `kind` 'cloud', required: Run on Reason-managed cloud compute.
      - `computer_id` string, uuid: Start on this cloud computer of the Session's Reason (project_id, else your main Reason), from createReasonComputer. It keeps its own size. 409 computer_unavailable when that Reason's Sessions each keep their own computer; 404 computer_not_found when it is not one of the Reason's computers you may use.
    - object
      - `kind` 'mac', required: Run on a connected Mac.
      - `device_id` string, uuid, required: Connected Mac ID returned by listDevices.
      - `root_id` string, uuid: Optional: a Mac Device is the whole machine. Omit it to run in the Mac's own Reason workspace; older clients send the exact root id, which is still honored.
      - `cwd` string: Absolute folder on the Mac the Session works in, like a terminal's working directory. Nothing is granted or locked. It runs on the deepest folder the Mac granted Reason that holds it (an older Reason Device: only a folder inside its home folder); 409 local_device_cwd_not_granted when no granted folder holds it. Not combinable with root_id.
      - `isolation` 'worktree' | 'in_place': With cwd: worktree (the default for repository work) gives the Session its own git worktree at ~/.reason/workspace/sessions/<id>/<repository> on branch reason/<id>; in_place works directly in cwd.
    - object
      - `kind` 'headless', required: Run on a CLI-connected computer or externally provisioned worker.
      - `device_id` string, uuid, required: Connected computer or worker ID returned by listDevices.
      - `root_id` string, uuid: Optional on your own computer (`reason connect`): omit it to run on the deepest granted folder holding the cwd, else on its home grant (the folder you last used for the repository there, else a managed checkout), or on a worker's only folder. Required when the worker holds several folders, none is its home folder and no cwd is named (409 local_device_root_ambiguous otherwise).
      - `workspace_mode` 'managed' | 'project_root': Defaults to a stable Session-managed checkout. project_root explicitly operates the selected existing root.
      - `workspace_path` string: Absolute path of the project_root on the Device, as the client sees it (for example /app). Read only with workspace_mode project_root, as the working directory the default C2 harness names in its model prompt; ignored otherwise, and rejected when the request names experimental_runtime_profile runtime/reason-c2-tools-v7. It grants nothing and never selects a directory.
      - `cwd` string: Your own computer (`reason connect`) only: the absolute folder the Session works in, as for a Mac, in a folder the computer granted Reason (root_id when named, else the deepest one holding it; an older `reason connect`: only inside its home folder). Not combinable with workspace_mode or workspace_path.
      - `isolation` 'worktree' | 'in_place': With cwd: worktree (the default for repository work) gives the Session its own git worktree at ~/.reason/workspace/sessions/<id>/<repository> on branch reason/<id>; in_place works directly in cwd.
    - object
      - `kind` 'machine', required: Queue this Session on a worker belonging to a named Machine.
      - `name` string: Machine name (case-insensitive).
      - `id` string, uuid: Machine id.

## Response `200`

Message accepted and delivered or queued.

- SendReasonMessageResponse
  - `ok` boolean, required: True: the message was accepted.
  - `delivery` string: How the message was delivered: steered_live (into the running turn), queued_for_next_turn or held_in_queue (waits for the turn to end), started_continuation_run or reattached_warm_session (started a new turn), applied_to_queued_run, saved_for_follow_up (saved; see held_reason), queue_cancelled or continuation_blocked.
  - `continuation_session_id` string, nullable: The stable logical Session id after an accepted follow-up. Continue polling this id; Reason may run the turn in a hidden execution Attempt.
  - `message_id` string: Set for a message sent with delivery queue: the id to send it at once or withdraw it while it waits.
  - `client_message_id` string: The message's idempotency key: the client_message_id sent, or the one the server gave it. Resending the message with it returns this accepted turn instead of adding another.
  - `held_reason` string: Present when delivery is saved_for_follow_up and the message is waiting instead of starting. session_machine_transition_pending: the Session's machine is changing; the message is delivered when the change settles, so do not send it again. If the change has not settled after 10 minutes the message is marked not delivered instead.
  - `reason_id` string, uuid, required: The Reason the message went to.
  - `session_id` string, required: The Reason's conversation Session the message was added to. getSession and listSessionEvents read it; listReasonMessages reads the replies.
  - `conversation_created` boolean, required: True when this message opened the Reason's conversation for the first time.

## Other responses

- `400`: The message was empty, a `model` / `reasoning_effort` override was not valid, or admission refused the conversation's create as invalid; the body names why.
- `401`: Missing, invalid, or expired key.
- `402`: Usage is unavailable for this message or for a new conversation; the body names why.
- `403`: A required scope or workspace permission is missing, or admission refused the message; the body names why.
- `404`: Resource not found.
- `409`: The conversation's Session is briefly unavailable or a `client_message_id` was used for a different message (as sendSessionMessage answers), or this is an ephemeral benchmark workspace (`reasons_unavailable_in_ephemeral_workspace`).
- `413`: Message limit exceeded (`message_too_large`), or the JSON request is over 116042412 bytes (`request_too_large`).
- `422`: Admission refused the create as unsupported here; the body names why.
- `429`: Too many requests. Retry after the number of seconds in the `Retry-After` response header.
- `503`: Session writes are disabled.

## Changes

- **2026-10-26** `c250866428e0`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/ara/apis/reason-api/changes/v3/organizations/:orgId/reasons/:reasonId/messages/post.md)

---

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