Skip to content

Event Protocol ​

AI Chat control events are sent through the TiRTC command channel. All payloads are UTF-8 JSON-RPC 2.0 messages, and the AI Chat command word is:

c
#define TIRTC_AI_SIGNALING 0x2100

Message Types ​

TypeSignalDescription
RequestHas an id fieldThe sender expects a success or error response, such as start_session.
NotificationHas no id fieldOne-way event, such as caption or interrupt.

Use a string value for id. JSON-RPC responses from the platform do not include method; match them with the original request by id.

Common request:

json
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "start_session",
  "params": {}
}

Common success response:

json
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {}
}

Common error response:

json
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "error": {
    "code": -32602,
    "message": "Invalid params"
  }
}

Event Summary ​

methodDirectionTypeDescription
start_sessionDevice -> PlatformRequestStarts an AI Chat session after TiRTC is connected.
captionPlatform -> DeviceNotificationSends ASR or TTS captions.
round_startPlatform -> DeviceNotificationMarks the start of one response audio round.
round_endPlatform -> DeviceNotificationMarks the end of one response audio round.
interruptDevice -> PlatformNotificationInterrupts the current platform response.
submit_speechDevice -> PlatformNotificationManually submits the current uplink speech.
update_configDevice -> PlatformRequestUpdates runtime session context. Currently only extra_params is supported.
device_actionPlatform -> DeviceRequestRequests the device to execute a declared capability.
end_sessionBothNotificationEnds the current session.

start_session ​

Direction: device -> Tange platform

The device must send start_session after the TiRTC connection is ready. The platform creates the session, loads device and role configuration, and returns the session ID and audio formats.

json
{
  "jsonrpc": "2.0",
  "id": "start-session-001",
  "method": "start_session",
  "params": {
    "device_id": "DEMO_DEVICE_01",
    "role_id": "role_xxx",
    "input_audio": {
      "codec": "opus",
      "sample_rate": 16000,
      "channels": 1
    },
    "output_audio": {
      "codec": "opus",
      "sample_rate": 16000,
      "channels": 1
    }
  }
}

Required fields:

FieldRequiredDescription
device_idYesDevice ID.
role_idYesRole ID from role configuration or device-role binding.
input_audioNoUplink audio format. Uses platform defaults (usually pcm) when omitted. We highly recommend passing this explicitly to ensure the server and device have a consistent understanding and avoid compatibility issues.
output_audioNoDownlink audio format. Uses platform defaults (usually pcm) when omitted. We highly recommend passing this explicitly to ensure the server and device have a consistent understanding and avoid compatibility issues.

Both input_audio and output_audio contain:

FieldTypeDescription
codecstringAudio codec.
sample_rateintSample rate in Hz.
channelsintNumber of channels.

Supported Audio Formats ​

codecSupported sample rates (Hz)Supported channels
opus160001
pcm16000, 80001
g711a16000, 80001
amr8000 (NB), 16000 (WB)1

Notes:

  • The platform ASR / LLM / TTS pipeline runs at 16 kHz mono internally. Uplink audio that does not match the internal format is resampled on the platform side.
  • The actual session formats are defined by the input_audio and output_audio fields in the start_session success response. The device must send and decode audio according to those values.
  • Unsupported codec, sample rate, or channel combinations return a JSON-RPC error with message set to Invalid audio format.

Success response:

json
{
  "jsonrpc": "2.0",
  "id": "start-session-001",
  "result": {
    "session_id": "aivoice-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "input_audio": {
      "codec": "opus",
      "sample_rate": 16000,
      "channels": 1
    },
    "output_audio": {
      "codec": "opus",
      "sample_rate": 16000,
      "channels": 1
    }
  }
}

caption ​

Direction: Tange platform -> device

caption sends ASR or TTS text.

json
{
  "jsonrpc": "2.0",
  "method": "caption",
  "params": {
    "text": "Hello, how can I help?",
    "emotion": "温和",
    "caption_type": 1,
    "is_final": true,
    "mode": 0,
    "utterance_id": 1001,
    "seq_num": 3,
    "begin_time_ms": 120,
    "end_time_ms": 1680
  }
}

params fields:

FieldAlways presentDescription
textYesPlain caption text without internal emotion control markers.
emotionNoEmotion of the current TTS caption segment. Returned when emotion recognition is enabled for the role. See the allowed values below.
caption_typeYesCaption source: 0 for ASR user caption, 1 for TTS reply caption.
is_finalYesWhether this is the final caption for the utterance.
modeYesDelivery mode: 0 for full text, 1 for incremental text.
utterance_idYesUtterance ID; captions from the same utterance share the same ID.
seq_numYesSequence number for ordering within the same utterance.
begin_time_msNoCaption segment start time in milliseconds; may be absent or 0.
end_time_msNoCaption segment end time in milliseconds; may be absent or 0.

Use caption_type + utterance_id as the caption group key. When mode=1, append text; when mode=0, replace the current text. Mark the group final when is_final=true.

Emotion handling:

  • Allowed emotion values are 中性, 开心, 兴奋, 温和, 安慰, 思考, 惊讶, 严肃, 困倦, and 困惑. These Chinese strings are the protocol values and must not be translated before comparison.
  • When emotion recognition is enabled for the role, sentence-level final TTS captions include emotion. If the emotion cannot be matched reliably, the current caption uses 中性.
  • ASR captions omit emotion. The field is also omitted when emotion recognition is disabled or not configured.
  • Treat emotion as optional and do not discard a caption when it is absent. Existing clients may ignore this field and continue to read text only.

round_start and round_end ​

Direction: Tange platform -> device

round_start marks the start of one response audio round:

json
{
  "jsonrpc": "2.0",
  "method": "round_start"
}

round_end marks the end:

json
{
  "jsonrpc": "2.0",
  "method": "round_end"
}

Use these events for UI state and playback state, but play audio according to the actual downlink audio stream.

interrupt ​

Direction: device -> Tange platform

json
{
  "jsonrpc": "2.0",
  "method": "interrupt"
}

The historical spelling interupt is also accepted by the platform and has the same effect as interrupt. New integrations should use interrupt.

After sending interrupt, the device should immediately stop playing buffered audio from the old response.

submit_speech ​

Direction: device -> Tange platform

submit_speech manually submits the current uplink speech. Typical scenarios include push-to-talk release, half-duplex talk, UI confirmation, or local device-side VAD deciding that the user has finished an utterance.

json
{
  "jsonrpc": "2.0",
  "method": "submit_speech"
}

Notes:

  • After submit_speech is sent, the platform treats the currently received uplink speech as submitted and continues the ASR / LLM / TTS flow.
  • submit_speech does not interrupt the current platform response. Use interrupt when the device needs to stop an in-progress response.
  • If the device relies only on cloud VAD to detect the end of speech, this event is usually not required.

update_config ​

Direction: device -> Tange platform

update_config updates small pieces of runtime context during an active session. Currently only extra_params is supported. It is intended for information such as location or business state that may change during the session. It does not update static settings such as welcome text, voice, or system prompt.

Request:

json
{
  "jsonrpc": "2.0",
  "id": "update-config-001",
  "method": "update_config",
  "params": {
    "extra_params": {
      "latitude": 39.9800718,
      "longitude": 116.309314,
      "coordinate_system": "WGS84"
    }
  }
}

Request with an NMEA-0183 RMC sentence:

json
{
  "jsonrpc": "2.0",
  "id": "update-config-002",
  "method": "update_config",
  "params": {
    "extra_params": {
      "nmea_rmc": "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A",
      "coordinate_system": "WGS84"
    }
  }
}

extra_params supports the following location fields:

FieldTypeDescription
longitudenumberWGS84 longitude in decimal degrees, in the range [-180, 180]. It must be provided together with latitude.
latitudenumberWGS84 latitude in decimal degrees, in the range [-90, 90]. It must be provided together with longitude.
nmea_rmcstringA complete raw NMEA-0183 GPRMC or GNRMC sentence.
coordinate_systemstringThe coordinate system. Currently fixed to WGS84; all other values are rejected. Required for RMC input, and new devices should also provide it with numeric input.

Location reporting rules:

  • Each update must use exactly one input form: both numeric latitude and longitude, or nmea_rmc. Numeric coordinates and nmea_rmc are mutually exclusive.
  • Numeric coordinates must be provided as a pair; a latitude or longitude cannot be updated by itself. For backward compatibility, if numeric coordinates omit coordinate_system, the platform temporarily treats them as WGS84 and adds that field.
  • When using nmea_rmc, you must also provide coordinate_system: "WGS84". The platform accepts only complete RMC sentences beginning with $GPRMC or $GNRMC and whose positioning status is A.
  • The RMC sentence must end with * followed by a two-digit hexadecimal checksum. The platform XORs the ASCII characters between $ and * byte by byte (excluding $ and *) and rejects a missing, malformed, or mismatched checksum.
  • RMC latitude uses ddmm.m... with N/S, and longitude uses dddmm.m... with E/W. Minutes must be less than 60. The platform converts the value using degrees + minutes / 60; N/E is positive and S/W is negative.
  • If any field is invalid, the entire update is rejected and the current valid location is not partially overwritten.
  • The platform does not convert coordinates based on the type of a user-registered tool. Tools integrating navigation services such as Amap or Baidu Maps must convert WGS84 coordinates to the coordinate system required by their downstream API.

Success response:

json
{
  "jsonrpc": "2.0",
  "id": "update-config-001",
  "result": {
    "success": true,
    "message": "更新配置成功"
  }
}

Error response:

json
{
  "jsonrpc": "2.0",
  "id": "update-config-001",
  "error": {
    "code": -32602,
    "message": "update_config only supports extra_params, got \"voice\""
  }
}

Notes:

  • The response is a JSON-RPC response and does not include method: "config_updated".
  • Match the response to the update_config request by id.
  • Any field other than extra_params is rejected.

device_action ​

Direction: Tange platform -> device

When device capabilities are declared in the role configuration, the platform may send device_action to request the device to perform an action. This message is a JSON-RPC Request. The device must keep the id and return a response with the same id.

Request from platform:

json
{
  "jsonrpc": "2.0",
  "id": "device-action-001",
  "method": "device_action",
  "params": {
    "action": "set_light",
    "data": {
      "power": "on"
    }
  }
}

Success response from device:

json
{
  "jsonrpc": "2.0",
  "id": "device-action-001",
  "result": {
    "ok": true,
    "data": {
      "power": "on"
    }
  }
}

Error response from device:

json
{
  "jsonrpc": "2.0",
  "id": "device-action-001",
  "error": {
    "code": -32000,
    "message": "device is busy"
  }
}

end_session ​

Direction: both

end_session ends the current AI Chat session. It may be sent by the device or by the platform when the session should close.

Device to platform:

json
{
  "jsonrpc": "2.0",
  "method": "end_session"
}

Platform to device:

json
{
  "jsonrpc": "2.0",
  "method": "end_session"
}

After sending or receiving end_session, stop capture, stop playback, and release local session state. The cleanup flow should be idempotent.

Device Receive Example ​

c
#include <stdint.h>
#include <string.h>
#include "tiRTC.h"

#define TIRTC_AI_SIGNALING 0x2100

static void on_command(tirtc_conn_t hconn, uint32_t cmdw,
                       const void *data, uint32_t len)
{
    (void)hconn;
    if (cmdw != TIRTC_AI_SIGNALING || data == NULL || len == 0) {
        return;
    }

    /* data is a UTF-8 JSON-RPC string.
     * In production code, parse method / params with a JSON library,
     * then dispatch to caption, round_start, round_end, interrupt,
     * submit_speech, device_action, end_session, and other handlers. */
}

Self-Check List ​

  • Use the 0x2100 command word.
  • Ensure every payload is valid UTF-8 JSON.
  • Send start_session with an id, and handle both success and error responses.
  • Pass a valid role_id.
  • Handle incremental and full caption modes.
  • Handle round_start / round_end state changes.
  • Implement immediate local playback stop for interrupt.
  • If manual speech submission is needed, implement submit_speech.
  • If runtime config updates are needed, match the update_config JSON-RPC response by id.
  • If device capability calls are needed, handle device_action requests and responses by id.
  • Make end_session cleanup idempotent.

AI Chat