Skip to main content
Use this endpoint to send prompt, control, and result events to an existing session. A request contains 1 to 100 events, and only one event may start a new turn. Control events and immediate custom tool results can be sent while a turn is running or paused for required action. Final long-running custom tool results are queued when necessary. Exceeding a limit — more than 100 events per request, more than 100 text blocks in a content array, or a text value over 20,000 characters — rejects the whole request with 400 validation_error. Nothing is truncated, and the error message names the failing field and limit. See Text content block. A user.message starts a new turn, and a session runs one turn at a time. A prompt sent while a turn is running is queued automatically — see Queue a message while a turn is running.

Send a message

metadata is optional turn context for the agent. It is persisted on the user.message event payload and returned by list/stream APIs, but it is not part of the visible content text your UI renders as the user’s message. Metadata values must be strings, numbers, booleans, or null.

Ralph Wiggim loop

Send /goal <objective> as a plain text message when you want Herm to keep working across turns until the objective is done. This is the Ralph Wiggim loop: the agent takes a turn, a goal judge checks whether the objective is complete, and Herm continues the same session if more work remains.
Use /goal for open-ended work where one model turn may not be enough, such as debugging a failing workflow, iterating until tests pass, or completing a multi-step internal operation. Write the objective like acceptance criteria: the clearer the finish line, the better the loop can decide when to stop. For the API to dispatch this as a goal command, the user.message must contain only text and must not include attachments or metadata. Messages such as /goal status, /goal pause, /goal resume, /goal clear, /goal stop, and /goal done are reserved control phrases, not new goal objectives.

Send a message with attachments

user.message accepts up to 10 turn-scoped file attachments. Attachments are sent as base64 data_url sources. The accepted event, including each source.dataUrl, is retained in session event history. Attachments are not written onto the subject’s sandbox volume; persist files with the Files API when later sessions should see them.
Supported attachment types are PDF, CSV, XLSX, TXT, Markdown, DOCX, PNG, JPEG, WebP, GIF, and BMP. The agent can inspect images and documents as part of the turn. PDFs default to document mode; set analysisMode: "visual" on a PDF attachment only when you want page-image analysis. Limits: each attachment is capped at 40 MB (PDF, CSV, XLSX, TXT, Markdown, DOCX, and images).

Queue a message while a turn is running

Sending a prompt while a turn is already running persists it with status: "queued" and returns 202. Herm starts it automatically when the running turn ends. The optional delivery: "when_idle" value remains supported for existing callers:
For queued prompts:
  • If the session is idle, the prompt starts a turn immediately — exactly like an immediate send.
  • Queued prompts start oldest-first, one per turn end, and each runs as its own turn.
  • The 202 response echoes the persisted events; data[].status === "queued" tells you the prompt was parked rather than started. The status moves to received when its turn starts, then processed like any other prompt.
  • Queued prompts remain durable until they are delivered once the session is idle again.
A when_idle request must contain only prompt-starting user.message events. Control and immediate result events (user.steer, user.approval_response, and so on) target the currently running turn and are never queued for a later one, so a when_idle request that includes them is rejected with 400 validation_error — send them in a separate request with the default delivery.

Rate-limit counting

The agent’s message limit defaults to 100 accepted inbound user.* events per stable subject (subjectId) per fixed 60-second window. Every event in a request consumes one point from this allowance.
  • Immediate events count when they are accepted for persistence.
  • A delivery: "when_idle" prompt counts when it is accepted into the durable queue. It is not counted again when the queue drains.
  • Validation, not-found, archived-session, and prompt-conflict failures rejected before persistence do not count. A control event that is persisted but cannot reach a live turn does count.
  • user.steer, user.interrupt, user.approval_response, user.clarify_result, and user.custom_tool_result each count.
The subject comes from the stored session; it cannot be changed on this route. Limited successful event requests include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset. See Rate limits and Errors.

Supported inbound events

See the Events Reference for the complete schema for each event.

Action responses

When the stream emits session.status_idle with payload.stop_reason.type: "requires_action", the event_ids array contains the IDs your application needs to answer.

Confirm a tool request

result is allow or deny. scope is optional and can be once, session, or always; omit it to allow once.

Answer a clarify request

Use an empty answer to skip the question and let the agent continue with its own default.

Return a custom tool result

Custom tools are executed by your application, not by the managed agent. When the stream emits agent.custom_tool_use, execute the named tool and return the result with user.custom_tool_result.
Set is_error: true when your application could not complete the tool. The text is still sent back to the model as tool output, so include a concise, model-readable error message. For long-running workflows, return a kickoff result within approximately five minutes:
The kickoff resolves the pending tool call and lets the original turn continue. When the workflow finishes, send a second result with the same tool_use_id:
When a matching running result exists, the completion replaces it in event history, emits session.tool_updated, and starts a new agent turn with the final text. If another turn is active, the continuation is queued automatically. See Long-running custom tools for the complete flow, UI reconciliation, and retry constraints.

Response

The endpoint returns the events persisted by the request. Additional agent events can be retrieved with List Session Events or streamed with Stream Events. A message parked behind an active turn is returned with status: "queued" instead of received.

Errors

Last modified on August 13, 2026