Skip to content

Gateway reference

Generated at build time from docs/api/asyncapi.yaml (Chat Server Gateway, version 0.1.1). Do not edit this page: change the document. Body schemas: Schemas.

WebSocket gateway for people, agents and apps. Frames are JSON text frames {"op": <name>, "d": <body>}. Authentication happens in-band with identify or resume (DPoP proof with the nonce from hello), not in the upgrade request. Protocol rules: docs/spec/04-sync-protocol.md. Body schemas are shared with openapi.yaml.

  • Default content type: application/json
  • x-protocol-versions: [1]

Servers

Server Host Path Protocol Description
instance {host} /api/v1 wss The instance origin.

Channels

Gateway

Address: /gateway

One connection per device (a newer one closes the older with 4017) or agent consumer (4015). Close codes are listed in x-close-codes. A failing op answers error with a problem code and keeps the connection.

Messages: op.identify, op.resume, op.heartbeat, op.reauth, op.focus, op.typing.start, op.typing.stop, op.ack, op.run.update, op.command.autocomplete_result, op.stats.subscribe, op.stats.unsubscribe, op.hello, op.ready, op.resumed, op.cursor, op.catchup.complete, op.resync_required, op.heartbeat_ack, op.reauth_required, op.error, event.message.created, event.message.updated, event.message.deleted, event.reaction.added, event.reaction.removed, event.pin.added, event.pin.removed, event.thread.created, event.thread.updated, event.thread.closed, event.thread.reopened, event.thread.moved, event.thread.moved_out, event.agent_session.changed, event.run.state_changed, event.space.created, event.space.updated, event.space.archived, event.channel.created, event.channel.updated, event.channel.settings_changed, event.channel.archived, event.command.changed, event.file.updated, event.user.updated, event.agent.updated, event.instance.updated, event.status.updated, event.grant.changed, event.group.changed, event.access.changed, event.access.expiring, event.device.pending, event.session.revoked, event.read_state.updated, event.draft.updated, event.draft.deleted, event.settings.updated, event.sidebar.updated, event.notification_pref.updated, event.quiet_hours.updated, event.gdpr.request_updated, event.gdpr.request_due, event.import.progress, event.agent.invocation, event.agent.followup, event.run.cancel_requested, event.interaction.created, event.command.invoked, event.agent.config_changed, event.typing.started, event.run.updated, event.agent.presence_changed, event.command.autocomplete_requested, event.notification.created, event.host.updated, event.terminal_session.changed, event.host.presence_changed, event.stats.live

Close codes:

Code Name Client action
1000 normal do not reconnect unless the app is in the foreground
1001 going_away reconnect with backoff, try resume
4000 unknown_error reconnect with backoff
4001 unknown_op report the error, reconnect
4002 decode_error report the error, reconnect
4003 not_identified an op was sent before identify; reconnect
4004 authentication_failed renew the token; if that fails, log out
4005 already_identified report the error, reconnect
4006 session_invalid identify instead of resume
4007 protocol_version_unsupported show the app update note, do not reconnect
4008 rate_limited only rl.gateway_connect at identify and resume: wait for the time given in the close reason
4009 heartbeat_timeout reconnect with backoff, try resume
4011 token_expired renew the token, then resume
4012 revoked session, device or principal revoked: delete local data, log out, do not reconnect
4013 server_restart reconnect at once with a random delay, try resume
4014 slow_consumer outbound queue full or write deadline gateway.write_timeout missed: reconnect, catch up from the cursor
4015 consumer_replaced agents only: another process took over the same consumer; do not reconnect
4016 device_pending device pending approval: do not reconnect until GET /api/v1/devices/{id} reports state active
4017 device_replaced a newer connection of the same device is active: do not reconnect on your own, only when the app returns to the foreground or on user action
4018 forbidden credentials are valid, but gateway.connect is denied (for example scope_missing of an app): show the error, do not renew the token, do not reconnect on your own
Bindings
{
  "ws": {
    "method": "GET",
    "bindingVersion": "0.1.0"
  }
}

Terminal stream of a device

Address: /terminal/sessions/{session_id}/stream

End-to-end encrypted terminal stream (Noise_NK_25519_ChaChaPoly_SHA256, docs/spec/24-terminal.md section 5). After hello and identify only binary frames, each one Noise message; the first is handshake message 1 with the session token. Not part of the gateway; nothing of it enters the event log.

Messages: terminal.hello, terminal.identify, terminal.error, noise.frame

Close codes:

Code Name Client action
1000 normal session ended normally; do not reconnect
1001 going_away the node shuts down; attach again with terminal.attach within terminal.detach_grace
4002 decode_error text frame after identify, invalid JSON or frame over the Noise limit; report the error
4004 authentication_failed renew the token; if that fails, log out (also when identify does not arrive within terminal.open_timeout)
4008 rate_limited wait for the time given in the close reason
4012 revoked session, device or person revoked; delete local data, log out
4013 server_restart attach again with terminal.attach within terminal.detach_grace
4014 slow_consumer relay buffer full; attach again
4100 terminal_closed the session ended; the close reason holds close_reason; do not reconnect
4101 terminal_token_stale session token expired (token_expired) or already used (token_replayed); open or attach again with a fresh step-up
4102 terminal_host_unavailable the other side is missing (host offline or no counterpart on this node within terminal.open_timeout): an opening session ended open_failed (open again); a detached session stays detached (attach again later)
4103 terminal_replaced another device attached; do not reconnect on your own
4104 terminal_handshake_failed Noise handshake or prologue failed, token signature or binding (kid, typ, aud, sid, cek) invalid, profile unknown or working directory (wdi) not one of the session user, server host without owner, session user (osu) unknown or bound to another person, or unknown frame; show a hard warning, do not reconnect
  • x-protocol-versions: [1]
Bindings
{
  "ws": {
    "method": "GET",
    "bindingVersion": "0.1.0"
  }
}

Control connection of a host service

Address: /hosts/connect

Outgoing connection of bothy-host, one per host. A second connection with the same keys makes the server probe the older one: if the older does not answer it is closed with 4009; if it answers, both are closed with 4112 and the host is disabled (24, 3.4). Login with the Ed25519 key registered at pairing (24, 3.5); hello carries the server values auth.clock_leeway, terminal.idle_timeout and terminal.detach_grace (clock_leeway_ms, idle_timeout_ms, detach_grace_ms), which the host service applies to the session of each data connection from the hello of that connection; identify lists the sessions the host service still knows (live_sessions), and the server ends every other session of the host in active or detached with host_restarted (24, 6.2). The server sends ready after the login and again on every change of the declaration llm_credentials; the host service treats each ready as the current declaration and evaluates check HD6 with it (24, 2.2 and 7.4). After ready the host service sends report (profiles, session users, self-checks) before it accepts any session.open, and the server sends session.open only after that report (24, 2.2). An invalid report is answered with error (validation_failed), stores nothing and keeps the connection. When doctor refused the start, the host service sends that report and closes the connection without accepting session.open. heartbeat_ack carries jwks whenever the set of purpose terminal changed since the last set sent on the connection (24, 5.3). The server sends session.open per session; the host service answers with a data connection.

Messages: host.hello, host.identify, host.ready, host.heartbeat, host.heartbeat_ack, host.report, host.session.open, host.session.close, host.session.started, host.session.ended, host.error

Close codes:

Code Name Client action
1000 normal reconnect with backoff
1001 going_away reconnect with backoff
4000 unknown_error reconnect with backoff
4002 decode_error report the error, reconnect
4008 rate_limited wait for the time given in the close reason
4009 heartbeat_timeout reconnect with backoff
4013 server_restart reconnect at once with a random delay; keep PTYs until terminal.detach_grace
4014 slow_consumer relay buffer full; reconnect the data connection
4100 terminal_closed data connection only: the session ended; end the PTY
4110 host_unpaired keys unknown or replaced by a re-pairing; stop and run bothy-host pair again
4111 host_disabled the host is disabled or retired; end all PTYs, reconnect only after a long backoff
4112 host_duplicate a second live host service with the same keys was found; the host is disabled; stop, do not reconnect, an owner must re-pair (24, 3.4)
4113 host_auth_failed proof invalid or not sent within terminal.host_auth_timeout; check clock and keys, reconnect with backoff
4114 host_version_unsupported protocol version too old; update the host service, do not reconnect
  • x-protocol-versions: [1]
Bindings
{
  "ws": {
    "method": "GET",
    "bindingVersion": "0.1.0"
  }
}

Data connection of a host service for one session

Address: /hosts/sessions/{session_id}/stream

Opened by the host service after session.open, exactly one data connection per session.open. The clock_leeway_ms, idle_timeout_ms and detach_grace_ms of its hello apply to that session: the leeway to exp and the jti memory of the session token, idle and detach to the timers of the session. After hello and identify (with session_id) only binary Noise frames, forwarded unchanged to and from the device stream of the same session. The host service refuses a handshake by closing the connection with 4101 (token_expired, token_replayed) or 4104 (every other detail of 24 section 5.3) and the close_detail code as the close reason; the server hands the same code and reason to the device.

Messages: host.hello, host.identify, host.error, noise.frame

Close codes:

Code Name Client action
1000 normal no expectation on this node or a paired host side exists: keep the PTY and wait for the next session.open
1001 going_away reconnect with backoff
4000 unknown_error reconnect with backoff
4002 decode_error report the error, reconnect
4008 rate_limited wait for the time given in the close reason
4009 heartbeat_timeout reconnect with backoff
4013 server_restart keep the PTY detached until terminal.detach_grace; wait for the next session.open
4014 slow_consumer relay buffer full: keep the PTY and wait for the next session.open
4100 terminal_closed data connection only: the session ended; end the PTY
4101 terminal_token_stale sent by the host service (M2-44): it refused the handshake because the session token expired (token_expired) or was already used (token_replayed); the relay hands the code to the device stream
4102 terminal_host_unavailable no counterpart on this node within terminal.open_timeout; do not reopen this data connection, wait for the next session.open
4104 terminal_handshake_failed sent by the host service (M2-44): it refused the token or the handshake for any other reason of 24 5.3, and ends the PTY of a started session; the relay hands the code to the device stream
4110 host_unpaired keys unknown or replaced by a re-pairing; stop and run bothy-host pair again
4111 host_disabled the host is disabled or retired; end all PTYs, reconnect only after a long backoff
4112 host_duplicate a second live host service with the same keys was found; the host is disabled; stop, do not reconnect, an owner must re-pair (24, 3.4)
4113 host_auth_failed proof invalid or not sent within terminal.host_auth_timeout; check clock and keys, reconnect with backoff
4114 host_version_unsupported protocol version too old; update the host service, do not reconnect
  • x-protocol-versions: [1]
Bindings
{
  "ws": {
    "method": "GET",
    "bindingVersion": "0.1.0"
  }
}

Operations

receive.identify

Action: receive · Channel: Gateway

Authenticate a new gateway session.

Messages: op.identify

Reply: op.ready

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

receive.resume

Action: receive · Channel: Gateway

Resume a gateway session after a short disconnect.

Messages: op.resume

Reply: op.resumed

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

receive.heartbeat

Action: receive · Channel: Gateway

Liveness signal with the last processed seq. Agents add their state; then agent.heartbeat is checked as well. Does not consume the rl.gateway_connect bucket, which counts only identify and resume (05 section 3). Errors of the agent part answer error and keep the connection.

Messages: op.heartbeat

Reply: op.heartbeat_ack

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

receive.reauth

Action: receive · Channel: Gateway

Hand over a new access token during the connection. Does not consume the rl.gateway_connect bucket (05 section 3).

Messages: op.reauth

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

receive.focus

Action: receive · Channel: Gateway

Set the threads currently on screen (live-box patches, typing). Unreadable threads are ignored silently.

Messages: op.focus

  • x-authz-action: gateway.focus
  • x-rate-limit-class: rl.read

receive.typing.start

Action: receive · Channel: Gateway

The person types in a thread. People only; agents show typing through their run (04 section 12).

Messages: op.typing.start

  • x-authz-action: typing.start
  • x-rate-limit-class: rl.typing

receive.typing.stop

Action: receive · Channel: Gateway

The person stopped typing. No event is sent; the op only resets the throttle of the connection.

Messages: op.typing.stop

  • x-authz-action: typing.start
  • x-rate-limit-class: rl.typing

receive.ack

Action: receive · Channel: Gateway

Agents only: acknowledge all events up to seq.

Messages: op.ack

  • x-authz-action: agent.events.consume
  • x-rate-limit-class: rl.agent_runtime

receive.run.update

Action: receive · Channel: Gateway

Agents only: live-box patch of an own run, same as PATCH /runs/{run_id}.

Messages: op.run.update

  • x-authz-action: run.update
  • x-rate-limit-class: rl.agent_runtime

receive.command.autocomplete_result

Action: receive · Channel: Gateway

Agents and apps: answer a command.autocomplete_requested, same as POST /command-autocompletions/{request_id}/answer.

Messages: op.command.autocomplete_result

  • x-authz-action: command.autocomplete.respond
  • x-rate-limit-class: rl.agent_runtime

receive.stats.subscribe

Action: receive · Channel: Gateway

Subscribe to the live frames stats.live of the instance or of one space (docs/spec/25-visualization.md, section 5). One subscription per connection; a new one replaces it.

Messages: op.stats.subscribe

  • x-authz-action: stats.read
  • x-rate-limit-class: rl.read

receive.stats.unsubscribe

Action: receive · Channel: Gateway

End the stats.live subscription of the connection.

Messages: op.stats.unsubscribe

  • x-authz-action: stats.read
  • x-rate-limit-class: rl.read

send.hello

Action: send · Channel: Gateway

First frame after the upgrade.

Messages: op.hello

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.ready

Action: send · Channel: Gateway

Session ready, with snapshot. Catch-up follows when a cursor was sent.

Messages: op.ready

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.resumed

Action: send · Channel: Gateway

Resume accepted; catch-up from the cursor follows.

Messages: op.resumed

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.cursor

Action: send · Channel: Gateway

Advance the cursor without an event (skipped invisible events).

Messages: op.cursor

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.catchup.complete

Action: send · Channel: Gateway

Catch-up finished; the connection is live.

Messages: op.catchup.complete

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.resync_required

Action: send · Channel: Gateway

Catch-up impossible; load GET /sync/snapshot. The connection stays open and is live from head_seq.

Messages: op.resync_required

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.heartbeat_ack

Action: send · Channel: Gateway

Answer to heartbeat.

Messages: op.heartbeat_ack

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.reauth_required

Action: send · Channel: Gateway

The access token expires soon; send reauth.

Messages: op.reauth_required

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.error

Action: send · Channel: Gateway

Error for one op; the connection stays open.

Messages: op.error

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

send.dispatch

Action: send · Channel: Gateway

Deliver one event. Each message names its receive action in x-receive-action.

The server checks authorize(recipient, x-receive-action, resource) for every delivery (docs/spec/04-sync-protocol.md, section 7 and 8).

Messages: event.message.created, event.message.updated, event.message.deleted, event.reaction.added, event.reaction.removed, event.pin.added, event.pin.removed, event.thread.created, event.thread.updated, event.thread.closed, event.thread.reopened, event.thread.moved, event.thread.moved_out, event.agent_session.changed, event.run.state_changed, event.space.created, event.space.updated, event.space.archived, event.channel.created, event.channel.updated, event.channel.settings_changed, event.channel.archived, event.command.changed, event.file.updated, event.user.updated, event.agent.updated, event.instance.updated, event.status.updated, event.grant.changed, event.group.changed, event.access.changed, event.access.expiring, event.device.pending, event.session.revoked, event.read_state.updated, event.draft.updated, event.draft.deleted, event.settings.updated, event.sidebar.updated, event.notification_pref.updated, event.quiet_hours.updated, event.gdpr.request_updated, event.gdpr.request_due, event.import.progress, event.agent.invocation, event.agent.followup, event.run.cancel_requested, event.interaction.created, event.command.invoked, event.agent.config_changed, event.host.updated, event.terminal_session.changed, event.typing.started, event.run.updated, event.agent.presence_changed, event.host.presence_changed, event.stats.live, event.notification.created, event.command.autocomplete_requested

  • x-authz-action: gateway.connect
  • x-rate-limit-class: rl.gateway_connect

terminal.send.hello

Action: send · Channel: Terminal stream of a device

First frame with the DPoP nonce.

Messages: terminal.hello

  • x-authz-action: terminal.stream
  • x-rate-limit-class: rl.terminal

terminal.receive.identify

Action: receive · Channel: Terminal stream of a device

Authenticate the device; only the device of the current session token passes.

Messages: terminal.identify

  • x-authz-action: terminal.stream
  • x-rate-limit-class: rl.terminal

terminal.send.error

Action: send · Channel: Terminal stream of a device

Error before the handshake; a close code follows.

Messages: terminal.error

  • x-authz-action: terminal.stream
  • x-rate-limit-class: rl.terminal

terminal.receive.frame

Action: receive · Channel: Terminal stream of a device

Noise message from the device, forwarded to the host.

Messages: noise.frame

  • x-authz-action: terminal.stream
  • x-rate-limit-class: rl.terminal

terminal.send.frame

Action: send · Channel: Terminal stream of a device

Noise message from the host, forwarded to the device.

Messages: noise.frame

  • x-authz-action: terminal.stream
  • x-rate-limit-class: rl.terminal

hostControl.send.hello

Action: send · Channel: Control connection of a host service

First frame with challenge and JWKS.

Messages: host.hello

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.receive.identify

Action: receive · Channel: Control connection of a host service

Login of the host service.

Messages: host.identify

Reply: host.ready

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.send.ready

Action: send · Channel: Control connection of a host service

Control connection accepted.

Messages: host.ready

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.receive.heartbeat

Action: receive · Channel: Control connection of a host service

Liveness; missing terminal.host_heartbeat_miss_limit heartbeats close with 4009.

Messages: host.heartbeat

Reply: host.heartbeat_ack

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.send.heartbeat_ack

Action: send · Channel: Control connection of a host service

Answer to heartbeat.

Messages: host.heartbeat_ack

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.receive.report

Action: receive · Channel: Control connection of a host service

Profiles, session users and self-checks; a change of the server-host flag, a check result or the reported session users is audited.

Messages: host.report

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.send.session.open

Action: send · Channel: Control connection of a host service

Ask for the data connection of a session.

Messages: host.session.open

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.send.session.close

Action: send · Channel: Control connection of a host service

End the PTY of a session (revocation, owner, limits).

Messages: host.session.close

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.receive.session.started

Action: receive · Channel: Control connection of a host service

The PTY runs.

Messages: host.session.started

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.receive.session.ended

Action: receive · Channel: Control connection of a host service

A session ended on the host.

Messages: host.session.ended

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostControl.send.error

Action: send · Channel: Control connection of a host service

Error for one op.

Messages: host.error

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostData.send.hello

Action: send · Channel: Data connection of a host service for one session

First frame with challenge.

Messages: host.hello

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostData.receive.identify

Action: receive · Channel: Data connection of a host service for one session

Login with session_id; the session must belong to this host and not be closed.

Messages: host.identify

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostData.send.error

Action: send · Channel: Data connection of a host service for one session

Error before the first Noise frame.

Messages: host.error

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostData.receive.frame

Action: receive · Channel: Data connection of a host service for one session

Noise message from the host, forwarded to the device.

Messages: noise.frame

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

hostData.send.frame

Action: send · Channel: Data connection of a host service for one session

Noise message from the device, forwarded to the host.

Messages: noise.frame

  • x-authz-action: host.connect
  • x-rate-limit-class: rl.host

Messages

op.identify

Name: identify

Authenticate a new gateway session.

Payload:

Property Type Required Description
op string yes Values: identify.
d GatewayIdentify yes

op.resume

Name: resume

Resume a gateway session after a short disconnect.

Payload:

Property Type Required Description
op string yes Values: resume.
d GatewayResume yes

op.heartbeat

Name: heartbeat

Liveness signal with the last processed seq. Agents add their state; then agent.heartbeat is checked as well.

Payload:

Property Type Required Description
op string yes Values: heartbeat.
d GatewayHeartbeat yes

op.reauth

Name: reauth

Hand over a new access token during the connection.

Payload:

Property Type Required Description
op string yes Values: reauth.
d GatewayReauth yes

op.focus

Name: focus

Set the threads currently on screen (live-box patches, typing). Unreadable threads are ignored silently.

Payload:

Property Type Required Description
op string yes Values: focus.
d GatewayFocus yes

op.typing.start

Name: typing.start

The person types in a thread. People only.

Payload:

Property Type Required Description
op string yes Values: typing.start.
d GatewayTyping yes

op.typing.stop

Name: typing.stop

The person stopped typing. No event is sent; the op only resets the throttle of the connection.

Payload:

Property Type Required Description
op string yes Values: typing.stop.
d GatewayTyping yes

op.ack

Name: ack

Agents only: acknowledge all events up to seq.

Payload:

Property Type Required Description
op string yes Values: ack.
d GatewayAck yes

op.run.update

Name: run.update

Agents only: live-box patch of an own run, same as PATCH /runs/{run_id}.

Payload:

Property Type Required Description
op string yes Values: run.update.
d GatewayRunUpdate yes

op.stats.subscribe

Name: stats.subscribe

Subscribe to stats.live for the instance or one space. Needs stats.read; a denial answers error and keeps the connection.

Payload:

Property Type Required Description
op string yes Values: stats.subscribe.
d GatewayStatsSubscribe yes

op.stats.unsubscribe

Name: stats.unsubscribe

End the stats.live subscription of the connection.

Payload:

Property Type Required Description
op string yes Values: stats.unsubscribe.
d GatewayStatsUnsubscribe yes

op.command.autocomplete_result

Name: command.autocomplete_result

Agents and apps: suggestions for an autocomplete request (04, 15.7).

Payload:

Property Type Required Description
op string yes Values: command.autocomplete_result.
d GatewayAutocompleteResult yes

op.hello

Name: hello

First frame after the upgrade.

Payload:

Property Type Required Description
op string yes Values: hello.
d GatewayHello yes

op.ready

Name: ready

Session ready, with snapshot. Catch-up follows when a cursor was sent.

Payload:

Property Type Required Description
op string yes Values: ready.
d GatewayReady yes

op.resumed

Name: resumed

Resume accepted; catch-up from the cursor follows.

Payload:

Property Type Required Description
op string yes Values: resumed.
d GatewayResumed yes

op.cursor

Name: cursor

Advance the cursor without an event (skipped invisible events).

Payload:

Property Type Required Description
op string yes Values: cursor.
d GatewayCursor yes

op.catchup.complete

Name: catchup.complete

Catch-up finished; the connection is live.

Payload:

Property Type Required Description
op string yes Values: catchup.complete.
d GatewayCatchupComplete yes

op.resync_required

Name: resync_required

Catch-up impossible; load GET /sync/snapshot. The connection stays open and is live from head_seq.

Payload:

Property Type Required Description
op string yes Values: resync_required.
d GatewayResyncRequired yes

op.heartbeat_ack

Name: heartbeat_ack

Answer to heartbeat.

Payload:

Property Type Required Description
op string yes Values: heartbeat_ack.
d GatewayHeartbeatAck yes

op.reauth_required

Name: reauth_required

The access token expires soon; send reauth.

Payload:

Property Type Required Description
op string yes Values: reauth_required.
d GatewayReauthRequired yes

op.error

Name: error

Error for one op; the connection stays open.

Payload:

Property Type Required Description
op string yes Values: error.
d GatewayError yes

event.message.created

Name: message.created · Title: dispatch message.created

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d MessageCreatedEvent yes
  • x-event-kind: durable
  • x-receive-action: message.read

event.message.updated

Name: message.updated · Title: dispatch message.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d MessageUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: message.read

event.message.deleted

Name: message.deleted · Title: dispatch message.deleted

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d MessageDeletedEvent yes
  • x-event-kind: durable
  • x-receive-action: message.read

event.reaction.added

Name: reaction.added · Title: dispatch reaction.added

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ReactionAddedEvent yes
  • x-event-kind: durable
  • x-receive-action: message.read

event.reaction.removed

Name: reaction.removed · Title: dispatch reaction.removed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ReactionRemovedEvent yes
  • x-event-kind: durable
  • x-receive-action: message.read

event.pin.added

Name: pin.added · Title: dispatch pin.added

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d PinAddedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.pin.removed

Name: pin.removed · Title: dispatch pin.removed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d PinRemovedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.thread.created

Name: thread.created · Title: dispatch thread.created

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ThreadCreatedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.thread.updated

Name: thread.updated · Title: dispatch thread.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ThreadUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.thread.closed

Name: thread.closed · Title: dispatch thread.closed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ThreadClosedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.thread.reopened

Name: thread.reopened · Title: dispatch thread.reopened

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ThreadReopenedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.thread.moved

Name: thread.moved · Title: dispatch thread.moved

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ThreadMovedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.thread.moved_out

Name: thread.moved_out · Title: dispatch thread.moved_out

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ThreadMovedOutEvent yes
  • x-event-kind: durable
  • x-receive-action: channel.read

event.agent_session.changed

Name: agent_session.changed · Title: dispatch agent_session.changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AgentSessionChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: thread.read

event.run.state_changed

Name: run.state_changed · Title: dispatch run.state_changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d RunStateChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: run.read

event.space.created

Name: space.created · Title: dispatch space.created

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d SpaceCreatedEvent yes
  • x-event-kind: durable
  • x-receive-action: space.read

event.space.updated

Name: space.updated · Title: dispatch space.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d SpaceUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: space.read

event.space.archived

Name: space.archived · Title: dispatch space.archived

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d SpaceArchivedEvent yes
  • x-event-kind: durable
  • x-receive-action: space.read

event.channel.created

Name: channel.created · Title: dispatch channel.created

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ChannelCreatedEvent yes
  • x-event-kind: durable
  • x-receive-action: channel.read

event.channel.updated

Name: channel.updated · Title: dispatch channel.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ChannelUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: channel.read

event.channel.settings_changed

Name: channel.settings_changed · Title: dispatch channel.settings_changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ChannelSettingsChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: channel.read

event.channel.archived

Name: channel.archived · Title: dispatch channel.archived

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ChannelArchivedEvent yes
  • x-event-kind: durable
  • x-receive-action: channel.read

event.command.changed

Name: command.changed · Title: dispatch command.changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d CommandChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: command.read

event.file.updated

Name: file.updated · Title: dispatch file.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d FileUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: file.read

event.user.updated

Name: user.updated · Title: dispatch user.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d UserUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: user.read

event.agent.updated

Name: agent.updated · Title: dispatch agent.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AgentUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: agent.read

event.instance.updated

Name: instance.updated · Title: dispatch instance.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d InstanceUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: instance.read

event.status.updated

Name: status.updated · Title: dispatch status.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d StatusUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: status.read

event.grant.changed

Name: grant.changed · Title: dispatch grant.changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d GrantChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: grant.read

event.group.changed

Name: group.changed · Title: dispatch group.changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d GroupChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: group.read

event.access.changed

Name: access.changed · Title: dispatch access.changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AccessChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.access.expiring

Name: access.expiring · Title: dispatch access.expiring

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AccessExpiringEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.device.pending

Name: device.pending · Title: dispatch device.pending

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d DevicePendingEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.session.revoked

Name: session.revoked · Title: dispatch session.revoked

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d SessionRevokedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.read_state.updated

Name: read_state.updated · Title: dispatch read_state.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ReadStateUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.draft.updated

Name: draft.updated · Title: dispatch draft.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d DraftUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.draft.deleted

Name: draft.deleted · Title: dispatch draft.deleted

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d DraftDeletedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.settings.updated

Name: settings.updated · Title: dispatch settings.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d SettingsUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.sidebar.updated

Name: sidebar.updated · Title: dispatch sidebar.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d SidebarUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.notification_pref.updated

Name: notification_pref.updated · Title: dispatch notification_pref.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d NotificationPrefUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.quiet_hours.updated

Name: quiet_hours.updated · Title: dispatch quiet_hours.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d QuietHoursUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.gdpr.request_updated

Name: gdpr.request_updated · Title: dispatch gdpr.request_updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d GdprRequestUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: gdpr.request.read

event.gdpr.request_due

Name: gdpr.request_due · Title: dispatch gdpr.request_due

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d GdprRequestDueEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.import.progress

Name: import.progress · Title: dispatch import.progress

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d ImportProgressEvent yes
  • x-event-kind: durable
  • x-receive-action: import.read

event.agent.invocation

Name: agent.invocation · Title: dispatch agent.invocation

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AgentInvocationEvent yes
  • x-event-kind: durable
  • x-receive-action: agent.events.consume
  • x-audience: self

event.agent.followup

Name: agent.followup · Title: dispatch agent.followup

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AgentFollowupEvent yes
  • x-event-kind: durable
  • x-receive-action: agent.events.consume
  • x-audience: self

event.run.cancel_requested

Name: run.cancel_requested · Title: dispatch run.cancel_requested

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d RunCancelRequestedEvent yes
  • x-event-kind: durable
  • x-receive-action: agent.events.consume
  • x-audience: self

event.interaction.created

Name: interaction.created · Title: dispatch interaction.created

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d InteractionCreatedEvent yes
  • x-event-kind: durable
  • x-receive-action: agent.events.consume
  • x-audience: self

event.command.invoked

Name: command.invoked · Title: dispatch command.invoked

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d CommandInvokedEvent yes
  • x-event-kind: durable
  • x-receive-action: agent.events.consume
  • x-audience: self

event.agent.config_changed

Name: agent.config_changed · Title: dispatch agent.config_changed

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AgentConfigChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: agent.config.read
  • x-audience: self

event.typing.started

Name: typing.started · Title: dispatch typing.started

Ephemeral event, at most once.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d TypingStartedEvent yes
  • x-event-kind: ephemeral
  • x-receive-action: thread.read

event.run.updated

Name: run.updated · Title: dispatch run.updated

Ephemeral event, at most once.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d RunUpdatedEvent yes
  • x-event-kind: ephemeral
  • x-receive-action: run.read

event.agent.presence_changed

Name: agent.presence_changed · Title: dispatch agent.presence_changed

Ephemeral event, at most once.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d AgentPresenceChangedEvent yes
  • x-event-kind: ephemeral
  • x-receive-action: agent.read

event.command.autocomplete_requested

Name: command.autocomplete_requested · Title: dispatch command.autocomplete_requested

Ephemeral event, at most once.

Only to one gateway connection of the command owner (docs/spec/04-sync-protocol.md, section 15.7).

Payload:

Property Type Required Description
op string yes Values: dispatch.
d CommandAutocompleteRequestedEvent yes
  • x-event-kind: ephemeral
  • x-receive-action: agent.events.consume
  • x-audience: self

event.notification.created

Name: notification.created · Title: dispatch notification.created

Ephemeral event, at most once.

Only to connections of the recipient, instead of a push to this device (docs/spec/13-push-notifications.md, section 6.3). A hint without a thread (kinds security and admin) is checked with the recipient rule of R-AZ-30 instead of push.deliver (docs/spec/05-actions.md, section 6).

Payload:

Property Type Required Description
op string yes Values: dispatch.
d NotificationCreatedEvent yes
  • x-event-kind: ephemeral
  • x-receive-action: push.deliver
  • x-audience: self

terminal.hello

Name: hello

First frame on the device stream with the DPoP nonce.

Payload:

Property Type Required Description
op string yes Values: hello.
d TerminalStreamHello yes

terminal.identify

Name: identify

Authenticate the device stream with access token and DPoP proof.

Payload:

Property Type Required Description
op string yes Values: identify.
d TerminalStreamIdentify yes

terminal.error

Name: error

Error before the Noise handshake; the connection closes afterwards.

Payload:

Property Type Required Description
op string yes Values: error.
d GatewayError yes

noise.frame

Name: noise_frame

One Noise message as a binary frame; the relay forwards it unchanged.

Payload: NoiseFrame

host.hello

Name: hello

First frame on a host connection with the challenge and the JWKS of purpose terminal.

Payload:

Property Type Required Description
op string yes Values: hello.
d HostChannelHello yes

host.identify

Name: identify

Login of the host service with an Ed25519 proof over the challenge.

Payload:

Property Type Required Description
op string yes Values: identify.
d HostIdentify yes

host.ready

Name: ready

Control connection accepted.

Payload:

Property Type Required Description
op string yes Values: ready.
d HostReady yes

host.heartbeat

Name: heartbeat

Liveness of the host service.

Payload:

Property Type Required Description
op string yes Values: heartbeat.
d HostHeartbeat yes

host.heartbeat_ack

Name: heartbeat_ack

Answer to heartbeat.

Payload:

Property Type Required Description
op string yes Values: heartbeat_ack.
d HostHeartbeatAck yes

host.report

Name: report

Profiles, session user names, self-checks and server-host flag; codes and names only, never paths.

Payload:

Property Type Required Description
op string yes Values: report.
d HostReport yes

host.session.open

Name: session.open

Open the data connection for a session.

Payload:

Property Type Required Description
op string yes Values: session.open.
d HostSessionOpen yes

host.session.close

Name: session.close

End the PTY of a session.

Payload:

Property Type Required Description
op string yes Values: session.close.
d HostSessionClose yes

host.session.started

Name: session.started

The PTY of a session runs.

Payload:

Property Type Required Description
op string yes Values: session.started.
d HostSessionStarted yes

host.session.ended

Name: session.ended

A session ended on the host (exit, idle, refused handshake, refused PTY start, policy).

Payload:

Property Type Required Description
op string yes Values: session.ended.
d HostSessionEnded yes

host.error

Name: error

Error for one op; the connection stays open unless a close code follows.

Payload:

Property Type Required Description
op string yes Values: error.
d GatewayError yes

event.host.updated

Name: host.updated · Title: dispatch host.updated

Durable event, at least once, in seq order.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d HostUpdatedEvent yes
  • x-event-kind: durable
  • x-receive-action: host.read

event.terminal_session.changed

Name: terminal_session.changed · Title: dispatch terminal_session.changed

Durable event, at least once, in seq order.

Only to the person of the session. Never content (docs/spec/24-terminal.md, section 10.2).

Payload:

Property Type Required Description
op string yes Values: dispatch.
d TerminalSessionChangedEvent yes
  • x-event-kind: durable
  • x-receive-action: self
  • x-audience: self

event.host.presence_changed

Name: host.presence_changed · Title: dispatch host.presence_changed

Ephemeral event, at most once.

Payload:

Property Type Required Description
op string yes Values: dispatch.
d HostPresenceChangedEvent yes
  • x-event-kind: ephemeral
  • x-receive-action: host.read

event.stats.live

Name: stats.live · Title: dispatch stats.live

Ephemeral event, at most once.

Every stats.live_interval to connections with a stats.subscribe subscription; filtered to the channels visible to the recipient, cost members only with cost.read.aggregate. Loss of stats.read ends the subscription with one error (forbidden). Never in the event log (docs/spec/25-visualization.md, section 5).

Payload:

Property Type Required Description
op string yes Values: dispatch.
d StatsLiveEvent yes
  • x-event-kind: ephemeral
  • x-receive-action: stats.read