> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stmlink.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> 对外开放的服务端接口有两组前缀，都用同一套鉴权：`/server/v1/...`（SRTC 与 SMeeting 的主接口）和 `/stm/srvapi/v1/...`（SMeeting 的用户体系，服务端极简对接会用到）。鉴权是 app_id + nonce + timestamp + signature 四个请求头，用 app_key 做 HMAC-SHA256 签名，只能从业务方自己的后端调用。除这两组前缀外的接口均为内部接口，不要建议客户调用。 Public server APIs use two path prefixes with the same authentication: `/server/v1/...` (the main APIs of both SRTC and SMeeting) and `/stm/srvapi/v1/...` (the SMeeting user system, used by server-side low-code integration). Authenticate with four request headers, app_id + nonce + timestamp + signature, where signature is HMAC-SHA256 keyed with app_key; call these APIs only from the customer's own backend. Any other path is internal: never suggest calling it.
> app_key 是服务端密钥，绝不能出现在客户端代码、前端配置或移动 App 里。客户端加入频道用的 token 必须由业务方后端签发后下发（SRTC 走 `/server/v1/channel/grant`，SMeeting 走 `/stm/srvapi/v1/member/grant`）。 app_key is a server-side secret and must never appear in client code, frontend config, or a mobile app. The token a client uses to join must be issued by the customer's backend and passed down to the client (SRTC: `/server/v1/channel/grant`; SMeeting: `/stm/srvapi/v1/member/grant`).
> SRTC 与 SMeeting 是上下两层不同的产品，术语不通用：SRTC 是音视频底座，说「频道 channel」「加入 / 退出」；SMeeting 建在 SRTC 之上，说「房间 room」「会议 meeting」「进入 / 退出」。回答时按用户所在的层用对应术语，不要把「房间」「会议」安到 SRTC 的接口上，也不要用「频道」「加入 / 离开」描述 SMeeting 的概念（接口标识符原样保留）。 SRTC and SMeeting are two separate layers with different terminology. SRTC is the audio/video foundation: it has channels, and users join and leave a channel. SMeeting is built on top of SRTC: it has rooms and meetings, and members enter and exit a meeting. Answer in the terms of the layer the user is working with: never apply "room" or "meeting" to SRTC APIs, and never describe SMeeting concepts in prose with "channel", "join", or "leave" (API identifiers such as `force_join` keep their literal names).
> 同一能力在各端 SDK 里的包名、类名、方法名并不相同。写示例代码时请使用文档中该端自己的 API，不要把一个端的写法套到另一个端上。苹果平台每个产品都有两套 SDK（Swift 原生与 Objective-C），两套 API 不能混用。 Package, class, and method names differ between platform SDKs for the same capability. In sample code, use the API documented for that platform; never carry one platform's code over to another. On Apple platforms each product ships two SDKs (native Swift and Objective-C) whose APIs must not be mixed.

# User info queries

> C SDK functions for querying the channel's user list, a single user, the local user, and channel info, and the matching free functions you must call to avoid leaking the SDK-allocated props and stream_tracks memory.

The structs returned by the user info query functions contain memory allocated dynamically by the SDK (the `props` string and the `stream_tracks` array). You **must** release it with the matching free function; otherwise memory leaks.

***

## rtc\_get\_users\_info

```c theme={null}
int rtc_get_users_info(void* handle, rtc_user_info_t** users, int* count);
```

Gets info for every user in the channel.

| Parameter | Description |
| - | - |
| `users` | Output: address of the first element of the user array allocated by the SDK |
| `count` | Output: number of users |

**Returns**

| Return value | Meaning |
| - | - |
| `RTC_OK` | Success. When there are no users in the channel, `*count = 0` and `*users = NULL`; in that case you **don't need** to free anything |
| `RTC_INVALID_PARAM` | Invalid handle |
| `RTC_NOT_CONNECTED` | Not yet joined to the channel |
| `RTC_ERROR` | Memory allocation failed |

```c theme={null}
rtc_user_info_t* users = NULL;
int count = 0;

if (rtc_get_users_info(rtc, &users, &count) == RTC_OK) {
    printf("Online users: %d\n", count);
    for (int i = 0; i < count; i++) {
        printf("- %s (%s), device type=%d, audience=%d, published tracks=%d\n",
               users[i].uid, users[i].name,
               users[i].device_type, users[i].is_audience,
               users[i].stream_track_count);

        // Iterate over the tracks this user has published
        for (int j = 0; j < users[i].stream_track_count; j++) {
            rtc_track_info_t* t = &users[i].stream_tracks[j];
            printf("    %s [%s] %s\n", t->track_id,
                   t->kind == 0 ? "audio" : "video",
                   rtc_codec_to_string(t->codec));
        }
    }
    rtc_free_users_info(users, count);   // You must pass count back
}
```

## rtc\_free\_users\_info

```c theme={null}
void rtc_free_users_info(rtc_user_info_t* users, int count);
```

Frees the user array returned by `rtc_get_users_info`, including each user's `props` and `stream_tracks` as well as the array itself.

<Warning>
  `count` must be the value returned by `rtc_get_users_info`. Passing a smaller value leaks memory; passing a larger one goes out of bounds.
</Warning>

***

## rtc\_get\_user\_info

```c theme={null}
int rtc_get_user_info(void* handle, const char* uid, rtc_user_info_t* user);
```

Gets info for a single user. `user` is provided by the caller (a stack variable is fine), and the SDK fills in its fields.

**Returns**

| Return value | Meaning |
| - | - |
| `RTC_OK` | Success |
| `RTC_INVALID_PARAM` | Invalid handle, or `uid` / `user` is `NULL` |
| `RTC_NOT_CONNECTED` | Not yet joined to the channel |
| `RTC_ERROR` | User doesn't exist |

```c theme={null}
rtc_user_info_t user;
if (rtc_get_user_info(rtc, "user123", &user) == RTC_OK) {
    printf("%s / %s / channel=%s / sid=%s\n",
           user.uid, user.name, user.channel, user.sid);
    rtc_free_user_info(&user);   // The struct itself is on the stack; this frees the dynamic memory inside it
} else {
    printf("User doesn't exist\n");
}
```

## rtc\_free\_user\_info

```c theme={null}
void rtc_free_user_info(rtc_user_info_t* user);
```

Frees the dynamic memory inside a single `rtc_user_info_t` (`props` and `stream_tracks`), but not the struct itself.

<Warning>
  Even if `user` is a stack variable, as long as `rtc_get_user_info` returned `RTC_OK`, you must call `rtc_free_user_info` once.
</Warning>

***

For field descriptions, see [Types · rtc\_user\_info\_t](/en/rtc/capi/types#rtc_user_info_t).

***

## rtc\_get\_local\_user\_info

```c theme={null}
int rtc_get_local_user_info(void* handle, rtc_user_info_t* user);
```

Gets the local user's info (`uid`, `sid`, etc.), since 0.0.9. You also need to call `rtc_free_user_info` when you're done with it.

**Returns:** `RTC_OK` / `RTC_INVALID_PARAM` (invalid handle or `user`) / `RTC_NOT_CONNECTED` (not yet joined).

***

## rtc\_get\_channel\_info

```c theme={null}
int rtc_get_channel_info(void* handle, rtc_channel_info_t* info);
void rtc_free_channel_info(rtc_channel_info_t* info);
```

Gets channel info, since 0.0.9. For fields, see [Types · rtc\_channel\_info\_t](/en/rtc/capi/types#rtc_channel_info_t). After it returns `RTC_OK`, you must call `rtc_free_channel_info` to free the `props` inside it.

**Returns:** `RTC_OK` / `RTC_INVALID_PARAM` / `RTC_NOT_CONNECTED` (not yet joined).
