FeaturesMessages

Send

Send a message to a session to create the next turn, and read that turn's result

One message is one turn. Messages in the same session run one after another in the same worker, the same working tree, and the same Claude Code conversation. The scope is sessions:write. The SDK examples use the werft client made in Install the SDK, and the HTTP API examples use the shell variables API (the server address) and KEY (the key). The GATE-SPEC {…} messages in the examples are scripts for the development server's fake model (Start).

Prepare a session

const created = await werft.sessions.create({
  profile_id: "claude-coding-local",
  repository_id: "sample-app",
  message: 'GATE-SPEC {"id":"m1","steps":[],"final":"ready"}',
});
const sessionId = created.session_id;

Send

const sent = await werft.sessions.messages.send(sessionId, {
  message:
    'GATE-SPEC {"id":"m2","steps":[{"tool":"Bash","input":{"command":"ls","description":"list files"}}],"final":"listed"}',
});
console.log(sent);
{
  turn_id: '2',
  receipt_id: '…',
  receipt_status: 'accepted'
}

The response is 202. If the previous turn is still running, the new turn waits as queued and runs when its turn comes.

  • message is up to 32,768 UTF-8 bytes. Over that is 413.
  • A session can hold 20 queued inputs by default. Over that is 429.
  • Idempotency-Key is required. If you did not receive a response, send it again with the same key and the same body. The turn is created only once. The SDK makes the key and uses the same key on retries.

Read the result

import { waitForTurn } from "@werft-ai/sdk";

const turn = await waitForTurn(werft, sessionId, sent.turn_id, {
  signal: AbortSignal.timeout(5 * 60_000),
});
console.log(`turn ${turn.turn_id}: ${turn.status}`);
turn 2: completed

Judge whether a turn has finished by this status alone. If it is one of the five values, it has finished. completed means the engine ended the turn normally, not that the requested work was done. It is completed even if the model calls only one tool and ends its answer, or if a tool receives an error. Decide whether the work was done by reading the assistant and tool_result events in Streaming, and if more work is needed, send a follow-up message to the same session.

When you cannot send

In the SDK, the responses below arrive as APIError, and you tell them apart like isApiError(error, "SESSION_PAUSED"). The SDK does not resend a 409.

ResponseMeaning
409 SESSION_PAUSEDThe session is paused. Resume it first (Waiting for approval and resuming)
409 RECOVERY_REQUIREDA recovery decision comes first (Close)
409 SESSION_CLOSING, SESSION_CLOSEDThe session is closing or closed
409 CHECKPOINT_UNAVAILABLEA checkpoint problem prevents accepting a new turn. The cause is in the response's message. If the transcript is over its limit, open a new session
409 PROFILE_BLOCKEDAn owner blocked the operator profile this session runs on. For a workspace profile, its base is blocked (details.reason_code is base_blocked). Nothing is queued. Once it is unblocked, the same session takes messages again
413The message is too long
429Queued inputs reached the limit

Next steps

On this page