A webhook is an HTTP or HTTPS callback mechanism that enables a service to proactively push data. The RTC callback notification server uses webhooks to send event notifications to your server so you can process business logic as needed.
How to Use
Prerequisites
You have deployed an HTTP or HTTPS service to receive callback messages.
You have created an Alibaba Cloud account and completed identity verification. To register, go to the Alibaba Cloud website. For registration steps, see Create an Alibaba Cloud Account. For identity verification steps, see Personal Identity Verification or Enterprise and Individual Business Identity Verification.
You have activated RTC. For activation steps, see Activate the Service.
Workflow
Enable event callbacks for an AppID in the console.
Log on to the RTC console. In the navigation pane on the left, choose Configuration Management > Event Notifications > Select the Target AppID. Configure specific events as needed.
Trigger a callback event.
After you configure event notifications for an AppID, use the server-side API to start related tasks—such as starting recording or stream ingest—to trigger corresponding callback events.
Receive callback events.
After a callback event occurs—for example, when a recording file is generated—you can view the callback notification on your deployed callback-receiving server if the callback succeeds.
Callback Mechanism
Deploy an HTTP service to receive callback messages. Then configure the callback URL for your specific business in the console.
When an event occurs, the RTC callback notification server sends an HTTP POST request to that URL. The event notification content is included in the HTTP request body.
Your HTTP service must return an HTTP status code of 200 to confirm a successful callback. Any other status code or timeout counts as a failed callback.
After failure, the system retries every 10 seconds, up to three times.
After a successful callback, your configured callback URL receives the corresponding event notification content.
Callback Format
Callback messages are sent to your server as HTTP POST requests. The request body format is JSON. Character encoding is UTF-8.
The request header includes these fields:
Field |
Example Value |
Description |
Content-Type |
application/json |
Static field |
trace-id |
2401058********622012463d9 |
Used for troubleshooting. |
DingRTC-Signature |
z5jbvxxx.1718877424.xx3e7691142ffe4342e13e25dc317695b17827e34ec248a5cc35d3a7e1e1cd44 |
A signature value generated by the RTC callback service encryption algorithm. For details, see Verify the Signature. |
The request body includes these fields:
Name |
Type |
Required |
Example Value |
Description |
eventId |
string |
Yes |
12343aed********* |
Event ID |
eventType |
string |
Yes |
101 |
Event type. See the callback message list below for supported values. |
notifyTime |
long |
Yes |
1701056041128 |
Notification timestamp, in milliseconds |
eventData |
JSONObject |
Yes |
|
Specific callback message content. This varies by event type. See the callback message list below. |
The order in which your server receives notifications may not match the order in which events occur.
To ensure reliable delivery, each event may generate more than one notification. Your server must handle duplicate messages.
Verify the Signature
The RTC callback server uses a signature algorithm to verify the legitimacy of each request sent to your server.
The DingRTC-Signature in the message header consists of three parts, concatenated with .. The format is AppId.TimeStamp.Signature, and the meanings of the fields are as follows:
AppID: Application ID
TimeStamp: UTC timestamp (in seconds)
Signature: Computed from the raw HTTP request body, the timestamp, and the callback secret. The full algorithm is:
Signature=hexString(HmacSHA256(plain request body + TimeStamp, callbackSecret))
Retrieve the callback notification secret from the console.
Use this sample code to verify the signature:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class SignUtil {
public static final String HMAC_SHA_256 = "HmacSHA256";
public static String hmacSha256(String message, String secret) {
try {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA_256);
Mac mac = Mac.getInstance(HMAC_SHA_256);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
return bytesToHex(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() < 2) {
sb.append(0);
}
sb.append(hex);
}
return sb.toString();
}
public static void main(String[] args) {
String requestBody = "{\"eventData\":{\"channelId\":\"55\",\"timestamp\":1718877424674},\"eventId\":\"2133cc0c17188774246986428d0cb0\",\"eventType\":\"101\",\"notifyTime\":1718877424701}";
String secret = "your callback secret";
String signatureHeader = "z5jbvxxx.1718877424.b1a2d36af0f43023009d9ff1fb33cfcb075acb94132898bee6a53925fdd0d877";
String appId = signatureHeader.split("\\.")[0];
String timestamp = signatureHeader.split("\\.")[1];
String signature = signatureHeader.split("\\.")[2];
if (signature.equals(hmacSha256(requestBody + timestamp, secret))) {
System.out.println("DingRTC-Signature is valid");
} else {
System.out.println("DingRTC-Signature is invalid");
}
}
}
# !-*- coding: utf-8 -*-
import hashlib
import hmac
request_body='{"eventData":{"channelId":"55","timestamp":1718877424674},"eventId":"2133cc0c17188774246986428d0cb0","eventType":"101","notifyTime":1718877424701}'
secret = 'your callback secret'
signature_header = 'z5jbvxxx.1718877424.b1a2d36af0f43023009d9ff1fb33cfcb075acb94132898bee6a53925fdd0d877'
appId = signature_header.split('.')[0]
timestamp = signature_header.split('.')[1]
signature = signature_header.split('.')[2]
sign_body = request_body + timestamp
if (signature == hmac.new(secret.encode('utf-8'), sign_body.encode('utf-8'), hashlib.sha256).hexdigest()):
print("DingRTC-Signature is valid")
else:
print("DingRTC-Signature is invalid")
using System.Security.Cryptography;
using System.Text;
namespace Program
{
public class Program
{
public static string hmacSha256(string message,string secret)
{
using (HMACSHA256 mac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
byte[] signing = mac.ComputeHash(Encoding.UTF8.GetBytes(message));
return bytesToHex(signing);
}
}
private static string bytesToHex(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
public static void Main()
{
String requestBody = "{\"eventData\":{\"channelId\":\"55\",\"timestamp\":1718877424674},\"eventId\":\"2133cc0c17188774246986428d0cb0\",\"eventType\":\"101\",\"notifyTime\":1718877424701}";
String secret = "your callback secret";
String signatureHeader = "z5jbvxxx.1718877424.b1a2d36af0f43023009d9ff1fb33cfcb075acb94132898bee6a53925fdd0d877";
String appId = signatureHeader.Split(".")[0];
String timestamp = signatureHeader.Split(".")[1];
String signature = signatureHeader.Split(".")[2];
if(signature.Equals(hmacSha256(requestBody + timestamp,secret)))
{
Console.WriteLine("DingRTC-Signature is valid");
}
else
{
Console.WriteLine("DingRTC-Signature is invalid");
}
}
}
}
Callback Message List
This document omits the eventId and notifyTime fields from the JSON examples.
New fields may be added or field order may change. Parse responses according to your programming language.
Verification Events
001 Callback Verification
This event triggers only when you set or manually verify a callback URL in the console.
{
"eventType": "001",
"eventData":{
"appId": "12adxxxx2"
}
}
Channel Events
101 Channel Started
{
"eventType": "101",
"eventData":{
"channelId": "room**", // Channel ID
"timestamp": 1709696165584 // Occurrence time (ms)
}
}
Channel 102 Ends
{
"eventType": "102",
"eventData":{
"channelId": "room**", // Channel ID
"timestamp": 1709696165584 // Occurrence time (ms)
}
}
103 User Joined
{
"eventType": "103",
"eventData":{
"channelId": "room**", // Channel ID
"user":{
"userId":"123444"
},
"timestamp": 1709696165584 // Occurrence time (ms)
}
}
104 User Left
{
"eventType": "104",
"eventData":{
"channelId": "room**", // Channel ID
"reasonCode": 20003001, // Reason user left. See status code table.
"user":{
"userId":"123444"
},
"timestamp": 1709696165584 // Occurrence time (ms)
}
}
Stream Ingest Events
1000 Stream Ingest Started
{
"eventType": "1000",
"eventData": {
"channelId": "room**", // Channel ID
"liveState":{
"code": 20000000 // Status code. See status code table.
},
"taskId": "task-03061", // Task ID
"timestamp": 1709737037688 // Occurrence time (ms)
}
}
1001 Stream Ingest Completed
{
"eventType": "1001",
"eventData": {
"channelId": "room**", // Channel ID
"liveState":{
"code": 20000000 // Status code. See status code table.
},
"taskId": "task-03061", // Task ID
"timestamp": 1709737037688 // Occurrence time (ms)
}
}
1002 Stream Ingest Failed
{
"eventType": "1002",
"eventData": {
"channelId": "room**", // Channel ID
"liveState":{
"code": 50001001 // Status code. See status code table.
},
"taskId": "task-03061", // Task ID
"timestamp": 1709737037688 // Occurrence time (ms)
}
}
Recording Events
2000 Recording Started
{
"eventType": "2000",
"eventData": {
"channelId": "room**",
"recordState": {
"bucket":"rtc*******", // Object Storage Service bucket where recordings are stored
"vendor":1, // Object storage provider. See Start Recording API.
"region":1, // Object storage region. See Start Recording API.
"startTs":1709737037688, // Start timestamp, in milliseconds
"code": 20000000
},
"taskId": "task-0422",
"timestamp": 1709737037688
}
}
2001 Recording Succeeded
{
"eventType": "2001",
"eventData": {
"channelId": "room**",
"recordState": {
"bucket":"rtc*******", // Object Storage Service bucket where recordings are stored
"vendor":1, // Object storage provider. See Start Recording API.
"region":1, // Object storage region. See Start Recording API.
"startTs":1709737037688, // Start timestamp, in milliseconds
"code": 20000000, // Status code. See status code table.
"fileFailCount": 0,
"fileInfo": [
{
"fileDuration": 7859, // File duration, in milliseconds
"fileSize": 216777, // File size, in bytes
"filePath": "record/v980**/65e82ef000210**/1709737028486_1709737030532/1709737028486-1709737030532.mp4", // File path
"status": 0, // 0 = success. Other values = failure.
"timestamp": 1709737037679 // File generation timestamp (ms)
}
],
"fileCount": 1 // Total number of files
},
"taskId": "task-03061",
"timestamp": 1709737037688
}
}
2002 Recording Failed
{
"eventType": "2002",
"eventData": {
"channelId": "room**",
"recordState": {
"bucket":"rtc*******", // Object Storage Service bucket where recordings are stored
"vendor":1, // Object storage provider. See Start Recording API.
"region":1, // Object storage region. See Start Recording API.
"startTs":1709737037688, // Start timestamp, in milliseconds
"reason": "WritePlaylist failed",
"code": 50002001, // Status code. See status code table.
"fileFailCount": 2,
"fileInfo": [
{
"reason": "write flv file fail", // Failure reason
"status": 50002001,
"timestamp": 1709721091674
},
{
"reason": "WritePlaylist failed",
"fileDuration": 30437,
"fileSize": 123875456,
"filePath": "taskidtaskId-199-cid65e844**e000000001ac0000/playlist.m3u8",
"status": 50002001,
"timestamp": 1709721103666
}
],
"fileCount": 2
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
2003 Single-Stream Recording Succeeded
{
"eventType": "2003",
"eventData": {
"channelId": "room**",
"recordState": {
"bucket":"rtc*******", // Object Storage Service bucket where recordings are stored
"vendor":1, // Object storage provider. See Start Recording API.
"region":1, // Object storage region. See Start Recording API.
"startTs":1709737037688, // Start timestamp, in milliseconds
"fileInfo": [
{
"fileSize": 313074,
"filePath": "record/cu***p/112233/b07c****6/122221/mic_default_0_0_1757484943513.mp3",
"fileDuration": 19559,
"status": 0,
"timestamp": 1757484964292
}
],
"streamInfo": {
"type": "mic",
"deviceId": "default",
"userId": "122221"
}
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
2010 Recording Service Status Changed
This event does not trigger automatically. Subscribe via the console or OpenAPI.
{
"eventType": "2010",
"eventData": {
"channelId": "room**",
"recordState": {
"bucket":"rtc*******", // Object Storage Service bucket where recordings are stored
"vendor":1, // Object storage provider. See Start Recording API.
"region":1, // Object storage region. See Start Recording API.
"startTs":1709737037688, // Start timestamp, in milliseconds
"code": 20002002 // Status code. See status code table.
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
2011 Audio Stream Status Changed
This event does not trigger automatically. Subscribe via the console or OpenAPI.
{
"eventType": "2011",
"eventData": {
"channelId": "room**",
"recordState": {
"streamChangeInfo": { // Stream change info
"streamType": 3, // Stream type: 1 = camera, 2 = screen share, 3 = audio mix, 4 = video mix
"state": 1, // Recording state: 1 = receiving, 2 = not receiving
"direction": 2, // Stream direction: 1 = input, 2 = output
"timestamp": 1721112755076 // Unix timestamp (ms) of state change
}
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
2012 changes to recorded video streams
This event does not trigger automatically. Subscribe via the console or OpenAPI.
{
"eventType": "2012",
"eventData": {
"channelId": "room**",
"recordState": {
"streamChangeInfo": { // Stream change info
"uid": "user1", // UID. Empty if direction = output (mix stream).
"streamType": 1, // Stream type: 1 = camera, 2 = screen share, 3 = audio mix, 4 = video mix
"state": 1, // Recording state: 1 = receiving, 2 = not receiving
"direction": 1, // Stream direction: 1 = input, 2 = output
"timestamp": 1721112755076 // Unix timestamp (ms) of state change
}
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
Event Summary
Start of minutes 3000
{
"eventType": "3000",
"eventData": {
"channelId": "room**",
"asrState": {
"code": 20000000 // Status code. See status code table.
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
3001 Notes Succeeded
Note: Real-time subtitles use event 3003. They are delivered in real time and are not saved to files. Their behavior differs from event 3001.
{
"eventType": "3001",
"eventData": {
"asrState": {
"transcriptionFilePath": "cloudNote/6pz38941/1234_1234/transcription_1734069823271.json", // Transcription result
"serviceInspectionFilePath": "cloudNote/6pz38941/1234_1234/serviceInspection_1734069824007.json", // Service inspection result
"customPromptFilePath": "cloudNote/6pz38941/1234_1234/customPrompt_1734069824057.json", // Custom prompt result
"meetingAssistanceFilePath": "cloudNote/6pz38941/1234_1234/meetingAssistance_1734069823787.json", // Key points result
"summarizationFilePath": "cloudNote/6pz38941/1234_1234/summarization_1734069823845.json", // Summary result
"textPolishFilePath": "cloudNote/6pz38941/1234_1234/textPolish_1734069823903.json", // Speech-to-text polish result
"autoChaptersFilePath": "cloudNote/6pz38941/1234_1234/autoChapters_1734069823728.json", // Auto-chapter result
"vendor": 1, // Object storage provider
"region": 1, // Object storage region
"bucket": "rtc-qa-test" // Bucket name
},
"channelId": "room**",
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
Result file path format: cloudNote/{appId}/{channelId}_{taskId}/{biz}_{putTs}.json
3002 Notes Failed
{
"eventType": "3002",
"eventData": {
"channelId": "room**",
"asrState": {
"code": 50004001 // Status code. See status code table.
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
3003 Real-Time Subtitles
This event does not trigger automatically. Subscribe via the console or OpenAPI. Set the AsrCallback field to true in the StartCloudNote API.
{
"eventType": "3003",
"eventData": {
"channelId": "room**",
"asrState": {
"sentenceIndex": 14, # Global sentence index
"sentenceEnd": true, # Whether the sentence ends
"beginTime": 40680, # Start timestamp
"endTime": 53280, # End timestamp
"text": "I am a service expert.", # Subtitle text
"userId": "471812" # User ID
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
Agent Events
4,000 agents connected successfully
{
"eventType": "4000",
"eventData": {
"channelId": "room**",
"aiAgentState": {
"code": 20000000 // Status code. See status code table.
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
4001 Agent Failed to Join Channel
{
"eventType": "4001",
"eventData": {
"channelId": "room**",
"aiAgentState": {
"code": 50005001,
"reason": "join rtc channel failed"
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
4002 Agent Exited
{
"eventType": "4002",
"eventData": {
"channelId": "room**",
"aiAgentState": {
"code": 50005010,
"reason": "exit without user"
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
4003 Agent Internal Error
{
"eventType": "4003",
"eventData": {
"channelId": "room**",
"aiAgentState": {
"code": 50005050,
"reason": "asr internal error"
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
4004 Agent Status Notification
{
"eventType": "4004",
"eventData": {
"channelId": "room**",
"aiAgentState": {
"code": 50005020,
"reason": "agent long silence"
},
"taskId": "taskId-199",
"timestamp": 1709721103673
}
}
Status Code Table
Type |
Status Code |
Description |
Common |
20000000 |
Success |
50000000 |
Internal server error |
|
Stream Ingest |
50001001 |
Stream ingest failed |
Recording |
50002001 |
Writing to user storage failed, This may be caused by a network issue. |
50002002 |
Failed to start user storage. The input parameters AK, SK, Bucket, Region, or Vendor may have been entered incorrectly. |
|
50002003 |
Recording duration too short. No recording file generated. |
|
50002004 |
Invalid user storage key |
|
50002005 |
Bucket does not exist |
|
50002006 |
Access to user storage denied |
|
50002007 |
Unknown error accessing user storage |
|
50002008 |
Recording processing failed |
|
20002001 |
No cloud recording started |
|
20002002 |
Cloud recording initialization complete |
|
20002003 |
Recording component starting |
|
20002004 |
Recording component started |
|
20002005 |
Recording stopped |
|
20002006 |
Upload component started |
|
20002007 |
First file uploaded successfully |
|
User |
20003001 |
Client exited voluntarily |
20003002 |
Client keepalive failed |
|
20003003 |
User kicked out |
|
20003004 |
Same UID removed |
|
20003005 |
Unknown exit reason |
|
Meeting Notes |
50004001 |
Meeting notes server error |
50004002 |
Meeting notes task exceeded maximum time |
|
30006001 |
Invalid user AK/SK/Bucket configuration |
|
Agent |
50005001 |
join rtc channel failed |
50005002 |
join rtc task exceed limit |
|
50005003 |
join rtm channel failed |
|
50005010 |
exit without user |
|
50005011 |
exit rtc bye |
|
50005050 |
asr internal error |
|
50005051 |
llm intrtnal error |
|
50005052 |
tts internal error |
|
50005020 |
agent long silence |