Skip to main content
The WebSocket API is the streaming transport behind the Playground and the embedded chatbot. Use it directly when you build your own frontend and need token-by-token output, tool-call visibility, or Human-in-the-Loop approval cards — none of which the request/response /execute endpoint can deliver.
Endpoint: wss://api.artaios.ai/ws/amas/A session must already exist. Create one over REST first — see Connecting Your Agent.

Authentication

Pick whichever fits your client. The connection is rejected (close code 4003) when none of them authenticates.
Send your API key as a header on the upgrade request:
Browsers cannot set headers on a WebSocket, so this works only from a server-side client (Node, Python, Go …).

End-user identity (optional)

When the agent has External Auth configured, send your own end user’s JWT as a header on the handshake:
It is validated against the agent’s identity-provider configuration on connect, and must then be repeated on every user_message as ext_token — otherwise the message is rejected with missing_ext_token. See External Authentication.

Close codes


Message envelope

Every message in both directions carries type and client_type.
string
required
Message type discriminator.
string
required
Who is speaking: web_frontend, mobile_frontend, desktop_frontend, api, or backend. Server-to-client messages are always backend.
client_type is required on every client message. Omitting it fails with missing_client_type.

Connection flow

1

Create a session over REST

GET /api/v1/sessions/from-agent/?agent_key=… or POST /api/v1/sessions/. Keep the returned session_id.
2

Open the socket and subscribe

Send connect with the session_id. The session is validated against your credentials; an unknown or inaccessible session returns invalid_session.
3

Send user messages

Send user_message and read the event stream until ExecutionFinished.
4

Answer any approval card

If a HitlRequired event arrives, the run is suspended until you send hitl_response.

Client messages

connect

Subscribe to a session’s event stream.
string
required
The session to subscribe to.
array
default:"[\"*\"]"
Event types to receive. ["*"] subscribes to everything.

user_message

Run the agent on a user turn. This is also where variables are supplied.
string | array
required
The user turn. A plain string, or an array of multimodal content blocks (text, image, file) — the same shapes the REST endpoint accepts.
object
Variable values for this execution, keyed by variable name. Readable in prompts as {{ variables.key }}. See Variables over WebSocket.
array
ChatArtifact UUIDs attached to this message. The backend resolves them to presigned URLs and injects them into the agent context.
string
Echoed back on every event produced by this turn, so you can attribute events to the request that caused them.
string
IANA timezone of the end user, e.g. Europe/Berlin. Used to render the agent’s current time. Defaults to Europe/Berlin.
string
The end user’s external JWT. Required on every message when the session was created with an external identity.

hitl_response

Answer an open approval card. See Human-in-the-Loop over WebSocket.

cancel_execution

Stop the running execution for this session.

reconnect

Resubscribe after a dropped connection and replay what you missed.
string
required
The message_id of the last event you processed. Events are replayed from there.

disconnect

Unsubscribe gracefully.

Server messages

Three wrappers come back from the server, all with client_type: "backend".

event

The workhorse. Wraps one agent event.
object
The event itself. payload.type selects the shape — see Event types.
string
Stream entry id, monotonic per session. Use it to deduplicate replayed events after a reconnect, and as last_message_id on the next reconnect.
string
Entry id of the immediately preceding event, so a gap in the stream is detectable precisely. null for the first event of a turn.

error

execution_result

The terminal summary of a turn — the same shape the REST /execute endpoint returns.

Event types

payload.type is one of the following. Every event carries timestamp and an optional correlation_id.
ExecutionStarted · ExecutionFinished · ExecutionCancelled · ExecutionError · RunStarted · RunFinished · RunError · StepStarted · StepFinishedExecutionFinished carries finish_reason (stop, length, content_filter, tool_calls, error) and a usage object with token counts and a cost estimate.
TextMessageStart → TextMessageContent (repeated) → TextMessageEnd, plus MessageError.Each carries messageId and run_id; TextMessageContent.delta is the chunk to append. Group by messageId, not by arrival order.
ThinkingStart · ThinkingContent · ThinkingEnd · ProcessStep · AgentIntroductionStart · AgentIntroductionContent · AgentIntroductionEnd
ToolCallStart · ToolCallArgs · ToolCallEnd · ToolCallResult · ToolCallError
HitlRequired · HitlResolved
QueryGenerationStart · QueryGenerationResult · QueryGenerationEnd · QueryGenerationError · KnowledgeBaseCallStart · KnowledgeBaseCallArgs · KnowledgeBaseCallResult · KnowledgeBaseCallEnd · KnowledgeBaseCallError · MemoryCallStart · MemoryCallResult · MemoryCallEnd · MemoryCallError
LLMCallStarted · LLMCallCompleted · LLMCallError · StateSnapshot · StateDelta · MessagesSnapshot · ConditionChecked · ConditionError · Raw · Custom
Treat unknown payload.type values as ignorable. New event types are added without a protocol version bump.

Variables over WebSocket

Variables work exactly as they do over REST — the inputs object on user_message is the WebSocket equivalent of the REST body’s inputs. Keys must match variable names declared in the agent’s variables block. Given this configuration:
First turn — supply the session-level variable along with the request-level one:
Later turns — only the request-level variable is needed; current_user_id was checkpointed:
Rules worth repeating:
  • inputs overrides a variable’s default for that execution.
  • A variable with required: true must arrive on the first execution; it then persists while persistent_state: true.
  • A variable with require_every_execution: true ignores the checkpoint and must be sent every turn.
  • Values are coerced to the declared type: "70.5" for a float becomes 70.5. An uncoercible value fails the execution with 'score' expected float, got str.
  • Keys not declared in variables are ignored.

Variables System

Types, defaults, assignment from structured output, and template syntax

Human-in-the-Loop over WebSocket

WebSocket is the only transport that can answer an approval card. Configure the gate in YAML first — see Human-in-the-Loop.

1. The run pauses

A HitlRequired event arrives and the execution suspends. Nothing further happens until you respond.
The inner payload.kind discriminates the pause:
A session holds at most one open pause. Sending a user_message while a card is open is rejected with hitl_session_busy.

2. You respond

string
required
Must match the tool_call_id from the open HitlRequired payload. A mismatch is rejected with hitl_tool_call_mismatch — this is what stops stale and cross-session resumes.
object
required
The typed response, discriminated by kind.
Accept with edited arguments:
Decline with feedback:

3. The run resumes

A HitlResolved event reports the outcome — accepted, declined, validation_failed or answered — and the stream continues.
Do not retry a hitl_response on a timeout. A duplicate is rejected with hitl_resume_in_progress; wait for HitlResolved instead.

Error codes

Returned on the error message as error_code.

Full example


Human-in-the-Loop

Configure which tools pause for approval

Variables System

Declare and populate variables

Connecting Your Agent

Sessions, credentials and the REST path

Variables in REST API

The same inputs object over HTTP