https://{instanceId}.agent37.app, with the same sk_live_ key sent as the X-Agent37-Key header. The platform edge authenticates the key and checks the instance belongs to your workspace, then the gateway answers. See Instance and preview URLs.
Endpoints
You never create a session directly. The first
POST /v1/responses without a session_id mints one and returns its id; reuse that id to continue the thread.Choosing the harness
Every read on this page takes an optional?agent= query — hermes or openclaw — that selects which harness on the instance answers. Omit it (or send it empty) and the instance’s configured default harness answers; the response echoes which agent it was. An unknown value is 400 validation_error. On the data reads (/v1/sessions, /v1/sessions/{id}, /v1/models), targeting a harness the instance was not provisioned with is 503 agent_unavailable; /v1/health instead reports an unreachable or unprovisioned harness as healthy: false with a 200. Your agent37-hermes Cloud instances serve Hermes, so you can leave ?agent= off.
The session object
There is no gateway-defined session shape.GET /v1/sessions passes each entry through from the harness’s own store, native fields untouched, so the exact fields depend on the harness and can evolve with it.
string
The only field the gateway guarantees across harnesses. It is the session id you pass back to
GET, PATCH, and DELETE /v1/sessions/{id}, and the session_id every response in the conversation carries. 32 hex characters, no prefix.title, model, message_count, started_at, last_active, and preview (see the example below). OpenClaw returns its own native fields. These come straight from the harness; the gateway does not normalize them, so field names, types, and timestamp units track the harness rather than this API (unlike a history message’s created_at, which the gateway does normalize to epoch milliseconds). Read what you need by name; don’t assume a field exists on every harness.
List sessions
GET /v1/sessions returns the harness’s sessions, newest first (Hermes orders by most recent activity), wrapped in { "agent": "...", "data": [...] }. The agent names which harness the list is for. Pass ?agent=hermes or ?agent=openclaw to pick a harness; omit it for the instance default. The list carries session metadata only, never history, so it stays cheap to poll for a sidebar. A harness without a list API returns data: [].
Retrieve a session with history
GET /v1/sessions/{id} returns { "id", "agent", "active_response_id", "history" }: history is the full transcript, in order, projected from the harness. You read it for display or audit; you never resend it, because the session already holds it. An unknown id returns an empty history rather than a 404 — the harness owns whether a session exists. Pass ?agent= to pick the harness.
string | null
The id of the response currently running on the session, or
null when it is idle. The harness writes a turn’s messages at turn end, so while this is set the running turn is normally not in history yet — follow it live with GET /v1/responses/{id}/stream. This is how a client that lost its state (page reload, new device) rediscovers a running turn and reattaches. Two timing edges: right at turn end, one read can briefly show the finished turn in history and its id still here — reattaching is still correct, the replay just ends immediately. And once it reads null the transcript is complete, with one exception: a cancelled OpenClaw turn is persisted by OpenClaw on its own schedule and can surface in history shortly after.history is a message:
string
The message id. Treat it as opaque; it uses a different format from session and response ids.
string
The session the message belongs to.
string
user, assistant, or system.string
The message text.
string
The assistant’s reasoning for that turn, when the agent recorded any. Absent otherwise.
number
When the message was created, in epoch milliseconds.
Rename a session
PATCH /v1/sessions/{id} sets a session’s title, writing it straight into the harness’s own store, and returns { "id", "agent", "renamed" }. renamed is false when no session matched the id. Send the new title in the body:
string
required
The new title. Cannot be empty. Hermes enforces a unique, length-capped title: a title already used by another session returns
409 title_conflict, and an over-long one returns 400 validation_error.405 rename_unsupported. Pass ?agent= to pick the harness.
Delete a session
DELETE /v1/sessions/{id} is a best-effort removal from the harness’s store and returns { "id": "...", "deleted": true|false }. deleted is true when a transcript was removed and false when nothing matched the id, so the call is idempotent: repeating it simply returns deleted: false rather than erroring. A harness with no delete route (OpenClaw) always returns deleted: false and keeps its copy. Pass ?agent= to pick the harness.
One turn at a time
A session runs one response at a time. Posting new input while a turn is in flight returns409 session_busy, normally with the running response’s id in error.response_id — reattach to it with GET /v1/responses/{id}/stream, cancel it with POST /v1/responses/{id}/cancel, or start the new input on another session. Two sessions on the same instance run independently.
List models
GET /v1/models lists the models a harness can run, in the OpenAI list shape — { "object": "list", "data": [...] } — so any OpenAI-compatible client works against it. It reports on one harness: the instance default, or the one named by ?agent= (e.g. ?agent=openclaw), and the response echoes which agent answered. The result is cached for about 60 seconds, so a newly available model can take up to a minute to appear.
string
Always
"list".string
Which harness this list is for,
hermes or openclaw.string | null
The model used when a turn does not name one.
string | null
The provider of the default model.
array
One entry per model. Each is an OpenAI-compatible model object plus a few additive fields a UI can group and label on:
id— the model id. Pass it asmodelon a turn.object— always"model".created— Unix seconds. We don’t track per-model creation time, so this is a stable placeholder (0).owned_by— the upstream provider, e.g.nousoranthropic.label— a human-readable name.source— where the entry comes from:current,catalog,custom, oralias.is_default—truefor the default model.
model and provider are dials you set per turn on POST /v1/responses: omit them to keep the session’s current model, or send them to switch. A continuation that sets them updates the session’s model for the turns that follow.
Health and version
GET /v1/health returns { "ok": true, "agent": "hermes", "healthy": true, "hermes": true }. ok is true whenever the gateway is up; agent is the harness that was probed, and healthy reports whether that harness behind it is reachable. By default it probes the instance’s configured harness; pass ?agent= to probe a specific one. When Hermes is probed, the body also carries a hermes field mirroring healthy, kept for backward compatibility; other harnesses omit it. Use it as a readiness probe after create or start.
GET /v1/version returns the gateway build, e.g. { "name": "agent37-gateway", "version": "0.1.3" }.