POST /v1/responses is the core call. You make it against your instance, not against api.agent37.com: every instance serves its own chat API at https://{instanceId}.agent37.app, the url of the default port in the create response. This page uses https://ab12cd34ef.agent37.app. Authenticate with the same sk_live_ key you use on the hosting API, sent as the X-Agent37-Key header.
The call is agentic by default: the agent can browse, run code, use a terminal, read and write files, call connected tools, and reason across many steps before answering.
Request body
Request bodies are capped at 2 MB; anything larger returns413 payload_too_large.
string
required
The message or task, a plain string. There is no image field; to attach files, upload them first and list their paths in
files. See Sessions and models for how history carries across turns.string[]
Paths of files on the instance to attach to this turn, typically the
path returned by PUT /v1/files/content. Each must name an existing file on the instance, or the call returns 400 validation_error. The paths are appended to the input, and the agent reads them from disk.string
Continue an existing conversation. Omit it to start a new one; the response returns the new session’s id. The harness owns sessions and creates one on first use, so an id it has not seen simply starts a fresh thread under that id rather than erroring.
boolean
default:"false"
true returns a Server-Sent Events stream; false returns the finished response as one JSON body. See Streaming.string
The LLM to run this turn on. Omit it to use the session’s current model (the instance default on a new session). List what the instance can run with
GET /v1/models; see Sessions and models.string
The model’s provider, for example
anthropic. Both model and provider are set per turn, and sending them on a continuation updates the session’s stored pair for the turns that follow.string
How hard the model thinks:
none, minimal, low, medium, high, or xhigh.object
Up to 16 key/value pairs, at most 64 KB serialized. Echoed back on the response object, never interpreted.
string
Which agent harness runs the turn,
hermes or openclaw. Omit it to use the instance’s configured default (hermes on agent37-hermes). Routing is per request: the gateway keeps no session-to-agent binding, so if a session runs on a non-default harness, send agent on every turn of it. Targeting a harness the instance was not provisioned with returns 503 agent_unavailable.string
default:"chat"
chat runs one turn and replies. goal is reserved: sending it returns 400 validation_error today.instance_id in the body is accepted and ignored. The URL names the instance: one gateway per instance, so there is nothing to route.Response
The response object. Ids are 32-character hex strings; timestamps from the gateway are epoch milliseconds.string
The response id. Use it to reconnect or cancel the turn.
string
The conversation this turn belongs to. Reuse it on the next call to continue the thread.
string
in_progress, then a terminal completed, failed, or cancelled.string
The agent that ran the turn,
hermes or openclaw.string | null
The model the turn ran on,
null when none was set.string | null
The model’s provider,
null when none was set.string
The agent’s final answer. Always a string, empty if the turn produced none.
object | null
Token counts and cost for the turn:
{ input_tokens, output_tokens, cost_usd }. cost_usd is absent or null when the provider did not report a cost.object | null
Your request metadata, echoed back verbatim.
number
When the turn started, epoch milliseconds.
A failed turn does not reject the HTTP call. The POST still returns 200 with
status: "failed" and error set. Branch on status, not on the HTTP code.On a non-streaming call the gateway sends the
200 and headers as soon as the turn starts, then keeps the connection alive with a whitespace tick every 25 seconds while the agent works. The JSON body arrives when the turn finishes, prefixed by that whitespace — leading whitespace is valid JSON, so standard parsers handle it unchanged. Don’t treat the early headers as the response being ready.Example
Continue a conversation
The first message omitssession_id and starts a session. The reply returns a session_id; pass it on the next message to continue the same thread. The session holds the full history, so you never resend a transcript: you send only the new input.
One active turn per session. A session runs one response at a time. Sending new input while one is in flight returns
409 session_busy, normally with the running response’s id in error.response_id. Use another session, reattach to the running turn, or cancel it first.Follow up on a response
Every response has an id you can use after the call returns.GET /v1/responses/{id}/stream replays every event so far in order, then stays attached live, so a dropped connection never loses the answer — including after the turn has finished, while the response is still retained. See Streaming for the replay window. Lost the id (page reload, new device)? GET /v1/sessions/{id} returns the running response as active_response_id.
POST /v1/responses/{id}/cancel takes no body and stops a running turn, best effort. It returns 200 with the current response object. Cancelling a finished response is a no-op that returns its terminal state, still 200.
Status values
A response moves fromin_progress to exactly one terminal status.