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.
/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.
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 withstatus: "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
202response echoes the persisted events;data[].status === "queued"tells you the prompt was parked rather than started. The status moves toreceivedwhen its turn starts, thenprocessedlike any other prompt. - Queued prompts remain durable until they are delivered once the session is idle again.
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 inbounduser.* 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, anduser.custom_tool_resulteach count.
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 emitssession.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
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 emitsagent.custom_tool_use, execute the named tool and return the
result with user.custom_tool_result.
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:
tool_use_id:
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 withstatus: "queued"
instead of received.