# React Native (/en/api-reference/api-ref/signaling/react-native)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

The Signaling SDK API reference is divided into the following sections:

* [Setup](#setup)
* [User authentication](#user-authentication)
* [Components and hooks](#components-and-hooks)
* [Channels](#channels)
* [Topics](#topics)
* [Messages](#messages)
* [Presence](#presence)
* [Storage](#storage)
* [Lock](#lock)
* [Enumerated types](#enumerated-types)
* [Troubleshooting](#troubleshooting)

## Setup [#setup]

The RTM SDK API reference introduces the API definitions, methods, basic usage examples, and return values related to real-time messaging.

### RtmConfig [#rtmconfig]

#### Description [#description]

`RtmConfig` is used to configure additional properties during RTM initialization. These configuration properties take effect for the entire lifecycle of the RTM client and affect its behavior.

#### Method [#method]

You can create an `RtmConfig` instance as follows:

```typescript
new RtmConfig({
    userId: uid,
    appId: appId,
})
```

| Parameter           | Type                  | Required | Default  | Description                                                                                                                                                                                                                                                                                                                                           |
| ------------------- | --------------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `appId`             | string                | Required | -        | The App ID obtained when you create a project in the Agora Console.                                                                                                                                                                                                                                                                                   |
| `userId`            | string                | Required | -        | User ID used to identify a user or device. To distinguish users and devices, ensure that `userId` is globally unique and remains unchanged during the user or device lifecycle.                                                                                                                                                                       |
| `areaCode`          | `RtmAreaCode`         | Optional | `glob`   | Service area code. You can select based on your business deployment region. See [`RtmAreaCode`](#rtmareacode).                                                                                                                                                                                                                                        |
| `protocolType`      | `RtmProtocolType`     | Optional | `tcpUdp` | Message transport protocol type. RTM uses both TCP and UDP by default. You can change the protocol type as needed. See [`RtmProtocolType`](#rtmprotocoltype).                                                                                                                                                                                         |
| `presenceTimeout`   | number                | Optional | `300`    | Presence timeout in seconds. Range: \[5, 300]. After the RTM server determines the client is offline, it waits for this duration before sending a `remoteTimeout` event to other users. If the client reconnects and rejoins the channel within this time, the server does not notify others or delete temporary user data.                           |
| `heartbeatInterval` | number                | Optional | `5`      | SDK heartbeat interval in seconds. Range: \[5, 1800]. The interval at which the client sends heartbeat packets to the RTM server. If no heartbeat is sent within this time, the server considers the client timed out.<br /><br />**Info**: This parameter affects PCU counting and billing.                                                          |
| `useStringUserId`   | boolean               | Optional | `true`   | Whether to use `String` type for user ID:<br />- `true`: Use `String` type user ID. <br />- `false`: Use `Uint` type user ID. The SDK converts a `String`-type `userId` to `Uint`. In this case, you must pass a numeric string (e.g., `"1234567"`); otherwise, the method call fails.When using both Agora RTC and RTM, ensure `userId` is the same. |
| `eventHandler`      | `IRtmEventHandler`    | Required | -        | RTM event notification handler. See [Event Listener](#event-listener).                                                                                                                                                                                                                                                                                |
| `logConfig`         | `RtmLogConfig`        | Optional | -        | Configuration for local log storage such as size, location, and log level.                                                                                                                                                                                                                                                                            |
| `proxyConfig`       | `RtmProxyConfig`      | Optional | -        | Required when using RTM Proxy functionality.                                                                                                                                                                                                                                                                                                          |
| `encryptionConfig`  | `RtmEncryptionConfig` | Optional | -        | Required when using RTM client-side encryption.                                                                                                                                                                                                                                                                                                       |
| `privateConfig`     | `RtmPrivateConfig`    | Optional | -        | Required when using RTM private deployment.                                                                                                                                                                                                                                                                                                           |

##### RtmLogConfig [#rtmlogconfig]

An `RtmLogConfig` instance is used to configure and store the local log file `agora.log`. During debugging, logs help track app status and significantly improve efficiency. If complex issues occur, Agora support may ask for these logs. `RtmLogConfig` includes the following properties:

| Parameter      | Type          | Required | Default | Description                                                                                                                                                         |
| -------------- | ------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filePath`     | string        | Optional | -       | Log file storage path.                                                                                                                                              |
| `fileSizeInKB` | number        | Optional | `1024`  | Log file size in KB. Range: \[128, 1024].<br />- If the value is less than 128, SDK sets it to 128. <br />- If the value is greater than 1024, SDK sets it to 1024. |
| `level`        | `RtmLogLevel` | Optional | `info`  | Log output level. See [`RtmLogLevel`](#rtmloglevel).                                                                                                                |

##### RtmProxyConfig [#rtmproxyconfig]

An `RtmProxyConfig` instance configures proxy-related properties for the client. You may need this in restricted network environments.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    Keep your proxy username and password secure. RTM does not parse, store, or forward your credentials. If you change proxy settings during app runtime, you must restart the RTM client for changes to take effect.
  </CalloutDescription>
</CalloutContainer>

`RtmProxyConfig` includes the following properties:

| Property    | Type           | Required | Default | Description                                               |
| ----------- | -------------- | -------- | ------- | --------------------------------------------------------- |
| `proxyType` | `RtmProxyType` | Optional | `none`  | Proxy protocol type. See [`RtmProxyType`](#rtmproxytype). |
| `server`    | string         | Optional | -       | Proxy server domain or IP address.                        |
| `port`      | number         | Optional | -       | Proxy server port.                                        |
| `account`   | string         | Optional | -       | Proxy login username.                                     |
| `password`  | string         | Optional | -       | Proxy login password.                                     |

##### RtmEncryptionConfig [#rtmencryptionconfig]

An `RtmEncryptionConfig` instance configures encryption properties for the client. Once encryption mode and key are properly set, all messages and states are automatically encrypted and decrypted by the client.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    Once encryption is enabled, all users must use the same encryption mode and key. Otherwise, data cannot be exchanged.
  </CalloutDescription>
</CalloutContainer>

`RtmEncryptionConfig` includes the following properties:

| Property         | Type                | Required | Default | Description                                                                                          |
| ---------------- | ------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------- |
| `encryptionMode` | `RtmEncryptionMode` | Optional | `none`  | Encryption mode. See [`RtmEncryptionMode`](#rtmencryptionmode).                                      |
| `encryptionKey`  | string              | Optional | -       | Custom encryption key. No length limit. Agora recommends using a 32-byte key.                        |
| `encryptionSalt` | number\[]           | Optional | `null`  | Custom encryption salt. Must be 32 bytes. Agora recommends generating it with OpenSSL on the server. |

##### RtmPrivateConfig [#rtmprivateconfig]

An `RtmPrivateConfig` instance configures properties required for private deployment.

| Property                | Type             | Required | Default | Description                                                       |
| ----------------------- | ---------------- | -------- | ------- | ----------------------------------------------------------------- |
| `serviceType`           | `RtmServiceType` | Optional | `none`  | Service type. See [`RtmServiceType`](#rtmservicetype).            |
| `accessPointHosts`      | string\[]        | Optional | -       | Array of server addresses. Supports domain names or IP addresses. |
| `accessPointHostsCount` | number           | Optional | 0       | Number of server addresses.                                       |

#### Basic usage [#basic-usage]

```typescript
const rtmConfig = new RtmConfig({
    encryptionMode : EncryptionMode.AES_256_GCM,
    salt : yourSalt,
    cipherKey : "yourCipherKey",
    presenceTimeout : 300,
    logUpload : true,
    logLevel : "debug",
    cloudProxy : false,
    useStringUserId : false,
    privateConfig: {
        serviceType: ["MESSAGE", "STREAM"]
    },
    heartbeatInterval: 5
});
```

### createAgoraRtmClient [#createagorartmclient]

#### Description [#description-1]

Call the `createAgoraRtmClient` method to create and initialize an RTM client instance.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    * You must create and initialize the client instance before calling any other RTM APIs.
    * To distinguish users and devices, ensure that `userId` is globally unique and remains unchanged during the user or device lifecycle.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-1]

You can create and initialize an instance as follows:

```typescript
createAgoraRtmClient(config: RtmConfig): RTMClient
```

| Parameter | Type        | Required | Default | Description                                              |
| --------- | ----------- | -------- | ------- | -------------------------------------------------------- |
| `config`  | `RtmConfig` | Yes      | -       | RTM client configuration. See [`RtmConfig`](#rtmconfig). |

#### Basic usage [#basic-usage-1]

```typescript
const rtm = createAgoraRtmClient(
    new RtmConfig({
        userId: uid,
        appId: appId,
    })
);
```

#### Return value [#return-value]

Returns an RTM client instance for calling other RTM APIs.

## Event Listener [#event-listener]

#### Description [#description-2]

RTM supports seven types of event notifications, as shown in the table below:

| Event Type                 | Description                                                                                                                                                                 |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message`                  | Receives all message event notifications from the subscribed Message Channels and Topics.                                                                                   |
| `presence`                 | Receives all Presence event notifications from the subscribed Message Channels and joined Stream Channels.                                                                  |
| `topic`                    | Receives all Topic change event notifications from joined Stream Channels.                                                                                                  |
| `storate`                  | Receives all Channel Metadata event notifications from subscribed Message Channels and joined Stream Channels, and User Metadata event notifications from subscribed users. |
| `lock`                     | Receives all Lock event notifications from subscribed Message Channels and joined Stream Channels.                                                                          |
| `linkstate`                | Receives event notifications about client network connection state changes.                                                                                                 |
| `tokenPrivilegeWillExpire` | Receives event notifications when the client token is about to expire.                                                                                                      |

#### Add Listener [#add-listener]

You can add an event listener object during initialization or call the `addEventListener` method at any point during the app lifecycle to add one or more event listeners.

#### Add Listener [#add-listener-1]

You can call the `addEventListener` method to listen for specific event notifications. See the sample code below:

```typescript
 // Add the message event listener

 // Message
rtm.addEventListener("message", event => {
    const channelType = event.channelType; // Channel type: "STREAM", "MESSAGE", or "USER"
    const channelName = event.channelName; // The channel from which this message originates
    const topic = event.topicName; // The Topic from which this message originates, valid when channelType is "STREAM"
    const messageType = event.messageType; // Message type: "STRING" or "BINARY"
    const customType = event.customType; // User-defined type
    const publisher = event.publisher; // Message publisher
    const message = event.message; // Message payload
    const timestamp = event.timestamp; // Event timestamp
});

 // Presence
rtm.addEventListener("presence", event => {
    const action = event.eventType; // Action type: 'SNAPSHOT', 'INTERVAL', 'JOIN', 'LEAVE', 'TIMEOUT', 'STATE_CHANGED', 'OUT_OF_SERVICE'
    const channelType = event.channelType; // Channel type: "STREAM", "MESSAGE", or "USER"
    const channelName = event.channelName; // The channel from which this event originates
    const publisher = event.publisher; // The user who triggered this event
    const states = event.stateChanged; // User state payload, only for stateChanged event
    const interval = event.interval; // Interval payload, only for interval event
    const snapshot = event.snapshot; // Snapshot payload, only for snapshot event
    const timestamp = event.timestamp; // Event timestamp
});

 // Topic
rtm.addEventListener("topic", event => {
    const action = event.evenType; // Action type: 'SNAPSHOT', 'JOIN', 'LEAVE'
    const channelName = event.channelName; // The channel from which this event originates
    const publisher = event.userId; // The user who triggered this event
    const topicInfos = event.topicInfos; // Topic information payload
    const totalTopics = event.totalTopics; // Total number of topics
    const timestamp = event.timestamp; // Event timestamp
});

 // Storage
rtm.addEventListener("storage", event => {
    const channelType = event.channelType; // Channel type: "STREAM", "MESSAGE", or "USER"
    const channelName = event.channelName; // The channel from which this event originates
    const publisher = event.publisher; // The user who triggered this event
    const storageType = event.storageType; // Storage category: 'USER' or 'CHANNEL'
    const action = event.eventType; // Action type: "SNAPSHOT", "SET", "REMOVE", "UPDATE", or "NONE"
    const data = event.data; // Payload: 'USER_METADATA' or 'CHANNEL_METADATA'
    const timestamp = event.timestamp; // Event timestamp
});

 // Lock
rtm.addEventListener("lock", event => {
    const channelType = event.channelType; // Channel type: "STREAM", "MESSAGE", or "USER"
    const channelName = event.channelName; // The channel from which this event originates
    const publisher = event.publisher; // The user who triggered this event
    const action = event.evenType; // Action type: 'SET', 'REMOVED', 'ACQUIRED', 'RELEASED', 'EXPIRED', 'SNAPSHOT'
    const lockName = event.lockName; // The lock affected
    const ttl = event.ttl; // Time-to-live of this lock
    const snapshot = event.snapshot; // Snapshot payload
    const owner = event.owner; // Owner of this lock
    const timestamp = event.timestamp; // Event timestamp
});

 // Link State Change
rtm.addEventListener('linkState', event => {
    const currentState = event.currentState;
    const previousState = event.previousState;
    const serviceType = event.serviceType;
    const operation = event.operation;
    const reason = event.reason;
    const reasonCode = event.reasonCode;
    const affectedChannels = event.affectedChannels;
    const unrestoredChannels = event.unrestoredChannels;
    const timestamp = event.timestamp;
    const isResumed = event.isResumed;
});

 // Token Privilege Will Expire
rtm.addEventListener("tokenPrivilegeWillExpire", (channelName) => {
    const channelName = channelName; // The channel whose token will expire
});
```

### Remove Listener [#remove-listener]

If you no longer need to listen for a specific event but still want to keep other event listeners, you can call the `removeEventListener` method to remove the specified event listener.

```typescript
rtm.removeEventListener("tokenPrivilegeWillExpire", yourhandler);
```

RtmClient

### login [#login]

#### Description [#description-3]

After creating and initializing an RTM instance, you need to call the `login` operation to log in to the RTM service. A successful login establishes a persistent connection between the client and the RTM server, allowing the client to access RTM resources properly.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    Once a user successfully logs in to the RTM service, the application's PCU (Peak Concurrent Users) will increase, which affects your billing.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-2]

You can log in to the RTM system using the following method:

```typescript
login(options?: LoginOptions): Promise<LoginResponse>;
```

| Parameter | Type   | Required | Default | Description    |
| --------- | ------ | -------- | ------- | -------------- |
| `options` | object | Required | -       | Login options. |

The `options` object includes the following properties:

| Parameter | Type   | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                 |
| --------- | ------ | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`   | string | Optional | -       | The Token used to initialize the RTM client.<br />- If your project has enabled Token authentication, you can provide a Temporary Token or a server-generated RTM Token. See Client Authentication and Deploying an RTM Token Server for details. <br />- If your project has not enabled Token authentication, you may leave this field empty or provide the App ID of a project with RTM service enabled. |

#### Basic usage [#basic-usage-2]

```typescript
try {
    const result = await rtm.login({ token: "your_token" });
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-1]

If the method call succeeds, it returns a `LoginResponse` type:

```typescript
type LoginResponse {
    timestamp: number; // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### logout [#logout]

#### Description [#description-4]

When you no longer need to use the service, you can log out by calling the `logout` method. This operation affects the PCU metric in your billing.

#### Method [#method-3]

You can log out using the following method:

```typescript
logout(): Promise<LogoutResponse>;
```

#### Basic usage [#basic-usage-3]

```typescript
try {
    const result = await rtm.logout();
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-2]

If the method call succeeds, it returns a `LogoutResponse` type:

```typescript
type LogoutResponse {
    timestamp: number; // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

## User authentication [#user-authentication]

Authentication refers to the process of verifying a user's identity before they access your system. When users use Agora services, such as joining an audio/video call or logging into the signaling system, Agora uses a Token for authentication.

RTM provides three types of channels: Message Channel, User Channel, and Stream Channel. Each channel type requires a different kind of Token:

* For Message Channel or User Channel: You only need to pass an RTM Token (Token with RTM service enabled) when calling the `login` method to log in to the RTM system.
* For Stream Channel: In addition to using an RTM Token for authentication, you must also pass an RTC Token (Token with RTC service enabled) when calling the `join` method to join a Stream Channel.

The maximum validity period of a Token is 24 hours. Agora recommends that you renew the Token before it expires. This document describes how to renew a Token.

### renewToken [#renewtoken]

#### Description [#description-5]

Calls the `renewToken` method to renew a Token.

Different parameters are required depending on the type of Token being renewed:

* RTM Token: Only the `token` parameter is required.
* RTC Token: Both `token` and `channelName` parameters are required.

To ensure timely Token renewal, Agora recommends that you listen for the `tokenPrivilegeWillExpire` callback. See [Event Listener](configuration#event-listener) for details. Once the event listener is successfully added, the SDK triggers the `tokenPrivilegeWillExpire` callback 30 seconds before the RTM Token expires, notifying the user that the Token is about to expire. Upon receiving this callback, you can regenerate the RTM Token on your server and call the `renewToken` method to pass the new RTM Token to the SDK.

#### Method [#method-4]

You can call the `renewToken` method as follows:

```typescript
renewToken(
    token: string,
    options?: RenewTokenOptions
): Promise<RenewTokenResponse>;
```

| Parameter | Type   | Required | Default | Description                |
| --------- | ------ | -------- | ------- | -------------------------- |
| `token`   | string | Yes      | -       | The newly generated Token. |
| `options` | object | No       | -       | Token renewal options.     |

The `options` object includes the following properties:

| Property      | Type   | Required | Default | Description                                        |
| ------------- | ------ | -------- | ------- | -------------------------------------------------- |
| `channelName` | string | Optional | -       | Channel name. Required when renewing an RTC Token. |

#### Basic usage [#basic-usage-4]

```typescript
rtmClient.addEventListener('tokenPrivilegeWillExpire', async (channelName) => {
    if (!channelName){
 // RTM Token is about to expire
        const newToken = "<Your new token>";
        await rtmClient.renewToken(newToken);
    }
});
```

#### Return value [#return-value-3]

If the method call succeeds, it returns a `RenewTokenResponse` type:

```typescript
type RenewTokenResponse = {
    timestamp: number // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

## Components and hooks [#components-and-hooks]

## Components [#components]

### RTMProvider [#rtmprovider]

This component [provides Context](https://react.dev/learn/passing-data-deeply-with-context) to its child components, allowing all components within `children` to access the `client` prop you pass in.

#### Props [#props]

| Prop       | Type              | Default  | Description                                                                                                                                 |
| ---------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `client`   | `RTMClient`       | Required | The `RTMClient` object created using the React Native SDK's [`createAgoraRtmClient`](toc-configuration/configuration#createagorartmclient). |
| `children` | `ReactNativeNode` | None     | The React Native nodes to render.                                                                                                           |

#### Basic usage [#basic-usage-5]

```jsx
function App({ children }) {
    const [client] = useState(() => createAgoraRtmClient(
        new RtmConfig({
            userId: uid,
            appId: appId,
        })
    ));
    return <RTMProvider client={client}>{children}</RTMProvider>;
}
```

## Hooks [#hooks]

### useLogin [#uselogin]

Logs in to the RTM service. Logs in when the component is ready and automatically logs out when the component is unmounted.

#### Parameters [#parameters]

| Name           | Type           | Required | Description                                                                                                                                 |
| -------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `client`       | `RTMClient`    | Yes      | The `RTMClient` object created using the React Native SDK's [`createAgoraRtmClient`](toc-configuration/configuration#createagorartmclient). |
| `loginOptions` | `LoginOptions` | Yes      | Login options. See [`login`](toc-configuration/configuration#login) for details.                                                            |

#### Basic usage [#basic-usage-6]

```jsx
useLogin(client, { token: "your_token" });
```

### useRtm [#usertm]

Retrieves the `RTMClient` object.

#### Parameters [#parameters-1]

| Name     | Type                  | Required | Description                                                                                                                                 |
| -------- | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `client` | `RTMClient` \| `null` | No       | The `RTMClient` object created using the React Native SDK's [`createAgoraRtmClient`](toc-configuration/configuration#createagorartmclient). |

#### Return value [#return-value-4]

Returns an `RTMClient` object.

#### Basic usage [#basic-usage-7]

```jsx
const rtmClient = useRtm();
```

### useRtmEvent [#usertmevent]

Registers a listener for a specific event on the `RTMClient` object.

* Registers the specified event listener when the component is mounted.
* Removes the event listener when the component is unmounted.

#### Parameters [#parameters-2]

| Name       | Type        | Required | Description                                                                                                                                 |
| ---------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `client`   | `RTMClient` | Yes      | The `RTMClient` object created using the React Native SDK's [`createAgoraRtmClient`](toc-configuration/configuration#createagorartmclient). |
| `event`    | string      | Yes      | The name of the event. For supported events, see [Add Listener](toc-configuration/configuration#add-listener).                              |
| `listener` | Function    | Yes      | Callback function that is invoked when the specified event is triggered.                                                                    |

#### Basic usage [#basic-usage-8]

```jsx
useRtmEvent(client, 'message', (message: MessageEvent) => {
 // your code
});
```

## Channels [#channels]

A channel is a management mechanism for data transmission in the RTM real-time network. Any user who subscribes to or joins a channel can receive messages and events transmitted in the channel within 100 milliseconds. RTM allows clients to subscribe to hundreds or even thousands of channels. Most RTM APIs perform sending, receiving, encryption, and other operations based on channels.

Based on Agora's capabilities, RTM channels are categorized into three types to match different application scenarios:

* Message Channel: Follows the industry-standard Pub/Sub (publish/subscribe) model. Channels do not need to be created in advance. You can send and receive messages in a channel by subscribing to it. There is no limit to the number of publishers and subscribers in a channel.
* User Channel: Enables point-to-point messaging based on the Pub/Sub model. Users do not need to subscribe to a channel. You can send a message by specifying the user ID, and receive messages by simply listening to the `message` event.
* Stream Channel: Follows a room-based observer pattern similar to that used in the industry. Users must create and join a channel before sending and receiving messages. Channels can contain multiple Topics, and messages are organized and managed through Topics.

### subscribe [#subscribe]

#### Description [#description-6]

RTM provides capabilities for message, status, and event change notifications. By setting up event listeners, you can receive messages and events from the channels you have subscribed to. For how to add and configure event listeners, see the [Event Listener](configuration#event-listener) section.

By calling the `subscribe` method, the client can subscribe to a Message Channel and start receiving messages and event notifications from that channel. After the method is successfully called, users who subscribe to the channel and have enabled Presence event listening will receive a `presence` event of type `remoteJoinChannel`. See [Event Listener](configuration#event-listener) for details.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    This method applies to Message Channels only.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-5]

You can call the `subscribe` method as follows:

```typescript
subscribe(
    channelName: string,
    options?: SubscribeOptions
): Promise<SubscribeResponse>;
```

| Parameter     | Type   | Required | Default | Description                           |
| ------------- | ------ | -------- | ------- | ------------------------------------- |
| `channelName` | string | Yes      | -       | The name of the channel.              |
| `options`     | object | No       | -       | Subscription options for the channel. |

The `options` object includes the following properties:

| Parameter      | Type    | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| -------------- | ------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `withMessage`  | boolean | Optional | `true`  | Whether to subscribe to message event notifications in the channel.                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `withPresence` | boolean | Optional | `true`  | Whether to subscribe to Presence event notifications in the channel.                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `beQuiet`      | boolean | Optional | `false` | Whether to subscribe to the channel silently. When set to `true`, the behavior is as follows: <br />- You can still receive event notifications from other users in the channel. <br />- Other users in the channel will not receive notifications when you subscribe/unsubscribe to the channel or set/get/delete temporary states. <br />- You will not appear in the result of the `getOnlineUsers` method. <br />- Channels you silently subscribe to will not appear in the result of the `getUserChannels` method. |
| `withMetadata` | boolean | Optional | `false` | Whether to subscribe to Storage event notifications in the channel.                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `withLock`     | boolean | Optional | `false` | Whether to subscribe to Lock event notifications in the channel.                                                                                                                                                                                                                                                                                                                                                                                                                                                         |

#### Basic usage [#basic-usage-9]

```typescript
const options ={
    withMessage : true,
    withPresence : true,
    beQuiet : false,
    withMetadata : false,
    withLock : false
};
try {
    const result = await rtm.subscribe("chat_room", options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-5]

If the method call succeeds, the SDK returns a `SubscribeResponse` type:

```typescript
type SubscribeResponse = {
    timestamp : number // Reserved field
    channelName : string // Name of the channel in this operation
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### unsubscribe [#unsubscribe]

#### Description [#description-7]

If you no longer need to follow a channel, you can call the `unsubscribe` method to unsubscribe from it. After a successful unsubscription, other users who have subscribed to the channel and enabled event listening will receive a `presence` event of type `remoteLeaveChannel`. See [Event Listener](configuration#event-listener) for details.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    This method applies to Message Channels only.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-6]

You can call the `unsubscribe` method as follows:

```typescript
unsubscribe(channelName: string): Promise<UnsubscribeResponse>;
```

| Parameter     | Type   | Required | Default | Description              |
| ------------- | ------ | -------- | ------- | ------------------------ |
| `channelName` | string | Yes      | -       | The name of the channel. |

#### Basic usage [#basic-usage-10]

```typescript
try {
    const result = await rtm.unsubscribe("chat_room");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-6]

If the method call succeeds, the SDK returns a `UnsubscribeResponse` type:

```typescript
type UnsubscribeResponse = {
    timestamp : number // Reserved field
    channelName : string // Name of the channel in this operation
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### createStreamChannel [#createstreamchannel]

#### Description [#description-8]

Before using a Stream Channel, you need to call the `createStreamChannel` method to create an `RTMStreamChannel` instance. After the instance is successfully created, you can call its related methods such as joining the channel, leaving the channel, sending Topic messages, and subscribing to Topic messages.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    This method applies to Stream Channels only.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-7]

You can call the `createStreamChannel` method as follows:

```typescript
createStreamChannel(channelName: string): Promise<RTMStreamChannel>;
```

| Parameter     | Type   | Required | Default | Description              |
| ------------- | ------ | -------- | ------- | ------------------------ |
| `channelName` | string | Yes      | -       | The name of the channel. |

#### Basic usage [#basic-usage-11]

```typescript
try{
    const Loc_stChannel = await rtm.createStreamChannel( "Location");
    console.log("Create Stream Channel success!: ");
} catch (status){
    console.log(status);
}
```

#### Return value [#return-value-7]

An `RTMStreamChannel` instance.

RTMStreamChannel

### join [#join]

#### Description [#description-9]

After successfully creating a Stream Channel, you can call the `join` method to join the Stream Channel.

You must join the channel before performing any channel-related operations. Users who subscribe to the channel and have added event listeners will receive the following event notifications:

* Local user:
  * A `presence` event of type `snapshot`.
  * A `topic` event of type `snapshot`.
* Remote users: A `presence` event of type `remoteJoinChannel`.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    This method applies to Stream Channels only.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-8]

You can call the `join` method as follows:

```typescript
join(options?: JoinChannelOptions): Promise<JoinChannelResponse>;
```

| Parameter | Type   | Required | Default | Description                      |
| --------- | ------ | -------- | ------- | -------------------------------- |
| `options` | object | No       | -       | Options for joining the channel. |

The `options` object includes the following properties:

| Parameter      | Type    | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| -------------- | ------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `token`        | string  | Optional | -       | The token used to join the Stream Channel. <br />- If your project has token authentication enabled, you can provide an RTC temporary token or a server-generated RTC token. <br />- If your project does not use token authentication, you can leave this blank or use your App ID that has both RTC and RTM services enabled.                                                                                                                                                            |
| `withPresence` | boolean | Optional | `true`  | Whether to subscribe to Presence event notifications in the channel.                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `beQuiet`      | boolean | Optional | `false` | Whether to join the channel silently. When set to `true`, the behavior is as follows: <br />- You can still receive event notifications from other users in the channel. <br />- Other users in the channel will not receive notifications when you join/leave the channel or set/get/delete temporary states. <br />- You will not appear in the result of the `getOnlineUsers` method. <br />- Channels you silently join will not appear in the result of the `getUserChannels` method. |
| `withMetadata` | boolean | Optional | `false` | Whether to subscribe to Storage event notifications in the channel.                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `withLock`     | boolean | Optional | `false` | Whether to subscribe to Lock event notifications in the channel.                                                                                                                                                                                                                                                                                                                                                                                                                           |

#### Basic usage [#basic-usage-12]

```typescript
const options ={
    token : "yourToken",
    withPresence : true,
    beQuiet : false,
    withMetadata : false,
    withLock : false
};
try {
    const result = await stChannel.join(options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-8]

If the method call succeeds, the SDK returns a `JoinChannelResponse` type:

```typescript
type JoinChannelResponse = {
    timestamp : number, // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### leave [#leave]

#### Description [#description-10]

If you no longer need to stay in a channel, you can call the `leave` method to leave it. After leaving, you will no longer receive any messages, status updates, or event notifications from the channel. Your publisher role and all Topic subscriptions in that channel will be automatically removed. To restore your previous publisher role and subscriptions, you must call the `join` method again and manually call `joinTopic` and `subscribeTopic`.

After successfully leaving the channel, remote users in the channel will receive a `presence` event of type `remoteLeaveChannel`. See [Event Listener](configuration#event-listener) for details.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    This method applies to Stream Channels only.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-9]

You can call the `leave` method as follows:

```typescript
abstract leave(): Promise<LeaveChannelResponse>;
```

#### Basic usage [#basic-usage-13]

```typescript
try{
    const result = await streamChannel.leave();
    console.log(result);
} catch (status){
    console.log(status);
}
```

#### Return value [#return-value-9]

If the method call succeeds, the SDK returns a `LeaveChannelResponse` type:

```typescript
export type LeaveChannelResponse = {
    timestamp : number // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

## Topics [#topics]

A Topic is a data stream management mechanism within a Stream Channel. You can use Topics to subscribe to, distribute, and receive event notifications for data streams in a Stream Channel.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    Topics exist only in Stream Channels. Before using any related features, you must first create an instance of the `RTMStreamChannel` object.
  </CalloutDescription>
</CalloutContainer>

RTMStreamChannel

### joinTopic [#jointopic]

#### Description [#description-11]

Calling `joinTopic` registers the user as a publisher to the specified Topic, allowing them to send messages to it. This operation does not affect whether the user is a subscriber to the Topic.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    * RTM currently supports a single client joining up to 8 Topics in the same Stream Channel.
    * Before calling this method, you must create an `RTMStreamChannel` instance and call the join method to join the channel.
  </CalloutDescription>
</CalloutContainer>

After successfully joining a Topic, users who have subscribed to that Topic and enabled event listening will receive a `topic` event of type `remoteJoinTopic`. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-10]

You can call the `joinTopic` method as follows:

```typescript
joinTopic(
    topicName: string,
    options?: JoinTopicOptions
): Promise<JoinTopicResponse>;
```

| Parameter   | Type   | Required | Default | Description         |
| ----------- | ------ | -------- | ------- | ------------------- |
| `topicName` | string | Yes      | -       | Name of the Topic.  |
| `options`   | object | No       | -       | Reserved parameter. |

#### Basic usage [#basic-usage-14]

```typescript
try {
    const result = await stChannel.joinTopic("gesture", options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-10]

If the method call succeeds, it returns a JoinTopicResponse type:

```typescript
type JoinTopicResponse = {
    timestamp: number, // Reserved field
    topicName: string // Name of the Topic joined
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### publishTopicMessage [#publishtopicmessage]

#### Description [#description-12]

The `publishTopicMessage` method sends a message to a Topic. Users in the channel who have subscribed to the Topic and the publisher will receive the message within 100 milliseconds. To use this method, you must first join the Stream Channel and register as a publisher of the Topic by calling `joinTopic`.

Messages sent by the user are encrypted with TLS during transmission. Data link encryption is enabled by default and cannot be disabled. You can also enable end-to-end encryption during initialization for higher data security. See [Initial Configuration](configuration) for details.

#### Method [#method-11]

You can call the `publishTopicMessage` method as follows:

```typescript
publishTopicMessage(
    topicName: string,
    message: string | Uint8Array,
    options?: TopicMessageOptions
): Promise<PublishTopicMessageResponse>;
```

| Parameter   | Type                 | Required | Default | Description                                            |
| ----------- | -------------------- | -------- | ------- | ------------------------------------------------------ |
| `topicName` | string               | Yes      | -       | Name of the Topic.                                     |
| `message`   | string \| Uint8Array | Yes      | -       | Message payload. Supports string and Uint8Array types. |
| `options`   | object               | No       | -       | Message options.                                       |

The `options` object includes the following properties:

| Property      | Type           | Required | Default | Description                                                                                                                   |
| ------------- | -------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `messageType` | RtmMessageType | No       | -       | RTM message type. Supports `binary` and `string`.                                                                             |
| `sentTs`      | string         | No       | -       | Timestamp used to sync with media stream. Only effective if `syncWithMedia` is set to `true` when joining the Stream Channel. |
| `customType`  | string         | No       | -       | Custom user-defined field. Only supports string.                                                                              |

#### Basic usage [#basic-usage-15]

Example 1: Send a string message to a specific Topic

```typescript
try {
    const result = await stChannel.publishTopicMessage("Gesture", "message");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

Example 2: Send a Uint8Array message to a specific Topic

```typescript
try {
    const result = await stChannel.publishTopicMessage("Gesture", new Uint8Array(Buffer.from(your_message)));
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-11]

If the method call succeeds, it returns a `PublishTopicMessageResponse` type:

```typescript
type PublishTopicMessageResponse = {
    timestamp: number, // Reserved field
    topicName: string // Name of the Topic the message was sent to
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### leaveTopic [#leavetopic]

#### Description [#description-13]

If you no longer need to publish messages to a Topic, you can call the `leaveTopic` method to unregister as a publisher, thereby releasing resources. This method does not affect your subscription to the Topic or any operations by other users.

After a successful call, users who subscribe to the channel and have enabled event listening will receive a `topic` event of type `remoteLeaveTopic`. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-12]

You can call the leaveTopic method as follows:

```typescript
leaveTopic(topicName: string): Promise<LeaveTopicResponse>;
```

| Parameter   | Type   | Required | Default | Description        |
| ----------- | ------ | -------- | ------- | ------------------ |
| `topicName` | string | Yes      | -       | Name of the Topic. |

#### Basic usage [#basic-usage-16]

```typescript
try {
    const result = await stChannel.leaveTopic("gesture");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-12]

If the method call succeeds, it returns a `LeaveTopicResponse` type:

```typescript
type LeaveTopicResponse = {
    timestamp: number, // Reserved field
    topicName: string // Name of the Topic
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### subscribeTopic [#subscribetopic]

#### Description [#description-14]

After joining a channel, you can call the `subscribeTopic` method to subscribe to publishers of a Topic in that channel.

`subscribeTopic` is incremental. For example, if the first call subscribes to `[UserA, UserB]` and the second call subscribes to `[UserB, UserC]`, the final result is `[UserA, UserB, UserC]`.

Each user can subscribe to up to 50 Topics in the same channel, and up to 64 publishers per Topic.

#### Method [#method-13]

You can call the `subscribeTopic` method as follows:

```typescript
subscribeTopic(
    topicName: string,
    options?: TopicOptions
): Promise<SubscribeTopicResponse>;
```

| Parameter   | Type   | Required | Default | Description           |
| ----------- | ------ | -------- | ------- | --------------------- |
| `topicName` | string | Yes      | -       | Name of the Topic.    |
| `options`   | object | No       | -       | Subscription options. |

The options object includes the following properties:

| Property    | Type      | Required | Default | Description                                                                                                |
| ----------- | --------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `users`     | string\[] | No       | -       | List of user IDs of publishers to subscribe to. If not set, the SDK randomly subscribes to up to 64 users. |
| `userCount` | number    | No       | 0       | Number of publishers to subscribe to.                                                                      |

#### Basic usage [#basic-usage-17]

Example 1: Subscribe to specific publishers in a Topic

```typescript
var UIDs = ["zhangsan", "lisi", "wangwu"];
try {
    const result = await rtm.subscribeTopic("Gesture", { users: UIDs });
    console.log(result);
} catch (status) {
    console.log(status);
}
```

Example 2: Randomly subscribe to 64 publishers in a Topic

```typescript
try {
    const result = await stChannel.subscribeTopic("Gesture");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-13]

If the method call succeeds, it returns a `SubscribeTopicResponse` type:

```typescript
type SubscribeTopicResponse = {
    succeedUsers: string[], // List of users successfully subscribed to
    failedUsers: string[], // List of users failed to subscribe to
    timestamp: number, // Reserved field
    topiclName: string // Name of the Topic subscribed to
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### unsubscribeTopic [#unsubscribetopic]

#### Description [#description-15]

If you are no longer interested in a Topic or no longer need to subscribe to one or more publishers in a Topic, you can call the `unsubscribeTopic` method to unsubscribe from the Topic or specific publishers.

#### Method [#method-14]

You can call the `unsubscribeTopic` method as follows:

```typescript
unsubscribeTopic(
    topicName: string,
    options?: TopicOptions
): Promise<UnsubscribeTopicResponse>;
```

| Parameter   | Type   | Required | Default | Description                                                                |
| ----------- | ------ | -------- | ------- | -------------------------------------------------------------------------- |
| `topicName` | string | Yes      | -       | Name of the Topic.                                                         |
| `options`   | object | No       | -       | Options for unsubscribing. If not set, unsubscribes from the entire Topic. |

The `options` object includes the following properties:

| Property    | Type      | Required | Default | Description                                                                                         |
| ----------- | --------- | -------- | ------- | --------------------------------------------------------------------------------------------------- |
| `users`     | string\[] | No       | -       | List of user IDs of publishers to unsubscribe from. If not set, unsubscribes from the entire Topic. |
| `userCount` | number    | No       | 0       | Number of publishers to unsubscribe from.                                                           |

#### Basic usage [#basic-usage-18]

Example 1: Unsubscribe from specific publishers in a Topic

```typescript
try {
    const result = await rtm.unsubscribeTopic("Gesture", { users: ["Tony", "Bo"] });
    console.log("unsubscribe Topic success: ", result);
} catch (status) {
    console.log("unsubscribe Topic failed: ", result);
}
```

Example 2: Randomly unsubscribe from 64 publishers in a Topic

```typescript
try {
    const result = await rtm.unsubscribeTopic("Gesture");
    console.log("unsubscribe topic success: ", result);
} catch (status) {
    console.log("unsubscribe topic failed: ", result);
}
```

#### Return value [#return-value-14]

If the method call succeeds, it returns an `UnsubscribeTopicResponse` type:

```typescript
type UnsubscribeTopicResponse = {
    timestamp: number // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

## Messages [#messages]

Sending and receiving messages is one of the core features of the RTM service. Any message sent using RTM will be delivered to any online subscribed user within 100 ms. You can send a message to a single user or broadcast messages to multiple users, depending on your use case.

RTM supports three types of channels: Message Channel, User Channel, and Stream Channel. The main differences between these channel types are as follows:

* Message Channel: A real-time channel with strong scalability where messages are transmitted through the channel. A local user can call the `publish` method, set the `channelType` parameter to `message`, and provide the `channelName` to send a message. Remote users can call the `subscribe` method to subscribe to the channel and receive messages.
* User Channel: Sends point-to-point messages to a specified user. A local user can call the `publish` method, set the `channelType` to `user`, and set `channelName` to the target user's `userId` to send a message. The target user receives the message via the `message` event notification.
* Stream Channel: A stream-based channel where users must first join the channel and then join a Topic. Messages are transmitted through Topics. A local user can call the `publishTopicMessage` method to send messages to a Topic. Remote users can call the `subscribeTopic` method to subscribe to the Topic and receive messages.

This document describes how to send and receive messages in Message Channels and User Channels.

### publish [#publish]

#### Description [#description-16]

You can directly call the `publish` method to send a message to all online users subscribed to the specified channel. You can still send messages to a channel even if you haven't subscribed to it.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    The following practices can help improve message delivery reliability:

    * Keep the message payload under 32 KB. Otherwise, the message will fail to send.
    * The maximum message sending rate to a single channel is 60 QPS. If the rate exceeds this limit, some messages may be dropped. Lower rates are preferred when possible.
  </CalloutDescription>
</CalloutContainer>

After the method is successfully called, the SDK triggers a `message` event notification. Users who have subscribed to the channel and enabled event listening will receive this notification. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-15]

You can call the `publish` method as follows:

```typescript
publish(
    channelName: string,
    message: string | Uint8Array,
    options?: PublishOptions
): Promise<PublishResponse>;
```

| Parameter     | Type                | Required | Default | Description                                                                                                                            |
| ------------- | ------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `message`     | string / Uint8Array | Yes      | -       | The message payload. Supports both `string` and `Uint8Array` types.                                                                    |
| `channelName` | string              | Yes      | -       | - To send a message to a Message Channel, provide the channel name. <br /> - To send a message to a User Channel, provide the user ID. |
| `options`     | object              | No       | -       | Message options.                                                                                                                       |

The `options` object includes the following properties:

| Property         | Type             | Required | Default   | Description                                                                                                                         |
| ---------------- | ---------------- | -------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `channelType`    | `RtmChannelType` | No       | `message` | Channel type. See [Channel Type](enum#channeltype).                                                                                 |
| `messageType`    | `RtmMessageType` | No       | `binary`  | Message type. See [Message Type](enum#messagetype).                                                                                 |
| `customType`     | string           | No       | -         | Custom user-defined field. Only supports `string`.                                                                                  |
| `storeInHistory` | boolean          | No       | `false`   | Whether to store the message in history. If `true`, the message will be stored and can be retrieved using the `getMessages` method. |

#### Basic usage [#basic-usage-19]

Example 1: Send a `string` message to a specified Message Channel and store it in history

```typescript
try {
    const result = await rtm.publish("my_channel", "Hello world", { channelType: "MESSAGE", storeInHistory: true });
    console.log(result);
} catch (status) {
    console.log(status);
}
```

Example 2: Send a `Uint8Array` message to a specified Message Channel and store it in history

```typescript
try {
    const result = await rtm.publish("my_channel", new Uint8Array(Buffer.from(your_message)), { channelType: "MESSAGE", storeInHistory: true });
    console.log(result);
} catch (status) {
    console.log(status);
}
```

Example 3: Send a `string` message to a specified User Channel

```typescript
try {
    const result = await rtm.publish("user_b", "Hello world", { channelType: "USER" });
    console.log(result);
} catch (status) {
    console.log(status);
}
```

Example 4: Send a `Uint8Array` message to a specified User Channel

```typescript
try {
    const result = await rtm.publish("user_b", new Uint8Array(Buffer.from(your_message)), { channelType: "USER" });
    console.log(result);
} catch (status) {
    console.log(status);
}
```

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    After the method is successfully called, the SDK triggers a `message` event notification. Users who have subscribed to the channel and enabled event listening will receive this notification. See [Event Listener](configuration#event-listener) for details.
  </CalloutDescription>
</CalloutContainer>

#### Return value [#return-value-15]

If the method call succeeds, it returns a `PublishResponse` type:

```typescript
type PublishResponse = {
    timestamp: number, // Reserved field
    channelName: string // Channel name
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

RtmHistory

### getMessages [#getmessages]

#### Description [#description-17]

If you need to retrieve historical messages from a specified channel, call the `getMessages` method on the client.

#### Method [#method-16]

You can call the `getMessages` method as follows:

```typescript
getMessages(
    channelName: string,
    channelType: RtmChannelType,
    options?: GetHistoryMessagesOptions
): Promise<GetMessagesResponse>;
```

| Parameter     | Type           | Required | Default | Description                                           |
| ------------- | -------------- | -------- | ------- | ----------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                         |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [RtmChannelType](enum#channeltype). |
| `options`     | object         | Yes      | -       | Query options. Type: `GetHistoryMessagesOptions`.     |

The `GetHistoryMessagesOptions` type includes the following properties:

| Property       | Type   | Required | Default | Description                                                                                                                                                           |
| -------------- | ------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `messageCount` | number | No       | `100`   | Maximum number of messages to retrieve in a single query. If the number of messages in the time range exceeds this value, you must call `getMessages` multiple times. |
| `start`        | long   | No       | `0`     | Start timestamp for historical messages.                                                                                                                              |
| `end`          | long   | No       | `0`     | End timestamp for historical messages.                                                                                                                                |

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    The RTM SDK provides `start` and `end` parameters that you can set based on your needs:

    * If only `start` is set, messages older than the `start` timestamp are returned.
    * If only `end` is set, messages between the current time and the `end` timestamp (inclusive) are returned.
    * If both `start` and `end` are set, messages between `start` and `end` (inclusive) are returned.
  </CalloutDescription>
</CalloutContainer>

#### Basic usage [#basic-usage-20]

```typescript
const options = { messageCount: 50, start: 0, end: 0 };
try {
    const result = await rtm.history.getMessages("my_channel", "MESSAGE", options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-16]

If the method call succeeds, it returns a `GetMessagesResponse` type:

```typescript
type GetMessagesResponse = BaseResponse & {
    messageList: HistoryMessage[]; // List of historical messages
    count: number; // Total number of historical messages
    newStart: number; // Timestamp of the next message. If 0, there are no more messages
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### Receiving messages [#receiving-messages]

RTM provides event notifications for messages, status updates, and other changes. By setting up event listeners, you can receive messages and events from subscribed channels. The following example shows how to receive messages from a User Channel:

```typescript
rtm.addEventListener("message", event => {
    const channelType = event.channelType; // Channel type: "STREAM", "MESSAGE", or "USER"
    const channelName = event.channelName; // Channel name where the message comes from
    const topic = event.topicName; // Topic name, valid only if channelType is "STREAM"
    const messageType = event.messageType; // Message type: "STRING" or "BINARY"
    const customType = event.customType; // User-defined type
    const publisher = event.publisher; // Message publisher
    const message = event.message; // Message payload
    const timestamp = event.timestamp; // Message timestamp
});
```

For details on how to add and configure event listeners, see the [Event Listener](configuration#event-listener) section.

## Presence [#presence]

Presence provides the ability to monitor user online and offline status as well as historical state change notifications. With Presence, you can retrieve the following information in real time:

* Real-time event notifications when users join or leave a specified channel
* Real-time event notifications when temporary user states are set or changed
* Query which channels a specific user has joined or subscribed to
* Query which users have joined a specific channel and their temporary state data

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    Presence is available for both Message Channels and Stream Channels.
  </CalloutDescription>
</CalloutContainer>

### getOnlineUsers [#getonlineusers]

#### Description [#description-18]

Call the `getOnlineUsers` method to get real-time information about the number of online users, the list of online users, and their temporary states in a specified channel.

#### Method [#method-17]

You can call the `getOnlineUsers` method as follows:

```typescript
getOnlineUsers(
    channelName: string,
    channelType: RtmChannelType,
    options?: PresenceOptions
): Promise<getOnlineUsersResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |
| `options`     | object         | No       | -       | Additional query options.                           |

The `options` object includes the following properties:

| Property         | Type    | Required | Default | Description                                                                                                                |
| ---------------- | ------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
| `includedUserId` | boolean | No       | `true`  | Whether to include user IDs of online members in the result.                                                               |
| `includedState`  | boolean | No       | `false` | Whether to include temporary state data of online users in the result.                                                     |
| `page`           | string  | No       | -       | Page bookmark. If not provided, the SDK returns the first page. Check the return value to see if more pages are available. |

#### Basic usage [#basic-usage-21]

```typescript
const options = {
    includedUserId : true,
    includedState : true,
    page : "yourBookMark"
}
try {
    const result = await rtm.presence.getOnlineUsers("chat_room", "MESSAGE", options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-17]

If the method call succeeds, it returns a `getOnlineUsersResponse` type:

```typescript
type getOnlineUsersResponse = {
    timestamp: number, // Reserved field
    totalOccupancy: number, // Total number of online users in the channel
    occupants: [{
        states: StateItem[{}],
        userId: string,
        statesCount: number
    }], // List of online users and their temporary state information
    nextPage: string // Bookmark for the next page
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### getUserChannels [#getuserchannels]

#### Description [#description-19]

In scenarios such as data analysis or app debugging, you may need to know all the channels a specific user has joined or subscribed to. Call the `getUserChannels` method to retrieve a list of channels the user is currently in.

#### Method [#method-18]

You can call the `getUserChannels` method as follows:

```typescript
getUserChannels(userId: string): Promise<getUserChannelsResponse>;
```

| Parameter | Type   | Required | Default | Description                                                                      |
| --------- | ------ | -------- | ------- | -------------------------------------------------------------------------------- |
| `userId`  | string | Yes      | -       | User ID. If set to an empty string `""`, the SDK uses the local user's `userId`. |

#### Basic usage [#basic-usage-22]

```typescript
try {
    const result = await rtm.presence.getUserChannels("Tony");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-18]

If the method call succeeds, it returns a `getUserChannelsResponse` type:

```typescript
type getUserChannelsResponse = {
    timestamp: number, // Reserved field
    totalChannel: number, // Number of channels the user is in
    channels: [{
        channelName: string, // Name of the channel
        channelType: RtmChannelType // Type of the channel
    }]
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### setState [#setstate]

#### Description [#description-20]

To meet various business needs for user status, RTM provides the `setState` method to set custom temporary user states. Users can define states such as score, game status, location, mood, mic status, etc.

Once set successfully, the custom state persists in the channel as long as the user remains subscribed and online. The `setState` method sets a temporary user state. When the user leaves the channel or disconnects from RTM, the state is lost. To restore the state after rejoining or reconnecting, you must cache the data locally. To persist user state permanently, Agora recommends using the Storage feature's `setUserMetadata` method instead.

If a user updates their temporary state, RTM triggers a `presence` event of type `remoteStateChanged` in real time. You can receive this event by subscribing to the channel and enabling the corresponding event listener.

#### Method [#method-19]

You can call the `setState` method as follows:

```typescript
setState(
    channelName: string,
    channelType: RtmChannelType,
    state: StateItem
): Promise<SetStateResponse>;
```

| Parameter     | Type           | Required | Default | Description                                                                  |
| ------------- | -------------- | -------- | ------- | ---------------------------------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                                                |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype).                          |
| `state`       | object         | Yes      | -       | A JSON object in key-value format. Keys and values must be of type `string`. |

#### Basic usage [#basic-usage-23]

```typescript
var newState = { "mood": "pumped", "isTyping": false };

try {
    const result = await rtm.Presence.setState("chat_room", "MESSAGE", newState);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-19]

If the method call succeeds, it returns a `SetStateResponse` type:

```typescript
type SetStateResponse = {
    timestamp: number // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### getState [#getstate]

#### Description [#description-21]

To get the temporary state of a specified user in a specified channel, call the `getState` method.

#### Method [#method-20]

You can call the `getState` method as follows:

```typescript
getState(
    userId: string,
    channelName: string,
    channelType: RtmChannelType
): Promise<GetStateResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `userId`      | string         | Yes      | -       | User ID.                                            |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |

#### Basic usage [#basic-usage-24]

```typescript
try {
    const result = await rtm.presence.getState("Tony", "chat_room", "MESSAGE");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-20]

If the method call succeeds, it returns a `GetStateResponse` type:

```typescript
type GetStateResponse = {
    timestamp: number, // Reserved field
    userId: string, // User ID
    states: object, // Key-value pairs of user state
    statesCount: number // Number of user states
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### removeState [#removestate]

#### Description [#description-22]

When a temporary user state is no longer needed, you can call the `removeState` method to delete one or more of your own temporary states. After successful deletion, users who subscribe to the channel and have enabled Presence event listening will receive a `presence` event of type `remoteStateChanged`. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-21]

You can call the `removeState` method as follows:

```typescript
removeState(
    channelName: string,
    channelType: RtmChannelType,
 // Pass in state keys; if omitted, all states will be removed
    options?: RemoveStateOptions
): Promise<RemoveStateResponse>;
```

| Parameter     | Type           | Required | Default | Description                                                                               |
| ------------- | -------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                                                             |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype).                                       |
| `options`     | object         | No       | -       | Options for removing temporary states. If omitted, all states will be removed by default. |

The `options` object includes the following properties:

| Property | Type      | Required | Default | Description                                                                             |
| -------- | --------- | -------- | ------- | --------------------------------------------------------------------------------------- |
| `states` | string\[] | No       | -       | List of state keys to remove. If omitted or an empty array, all states will be removed. |

#### Basic usage [#basic-usage-25]

```typescript
const options = {
    states: ["mode", "Typing"]
}
try {
    const result = await rtm.Presence.removeState("chat_room", "MESSAGE", options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-21]

If the method call succeeds, it returns a `RemoveStateResponse` type:

```typescript
type RemoveStateResponse = {
    timestamp: number // Reserved field
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

## Storage [#storage]

The Storage feature in RTM provides a dynamic database mechanism that allows developers to dynamically set, store, update, and delete data such as Channel Metadata and User Metadata.

### setChannelMetadata [#setchannelmetadata]

#### Description [#description-23]

The `setChannelMetadata` method sets Channel Metadata for a channel (Message Channel or Stream Channel). A channel can only have one set of metadata, but each set can contain one or more Metadata Items. If you call this method multiple times, the SDK will iterate through the Metadata Item `key` values and process them as follows:

* If the `key` values are different, new items are added in the order of the calls.
* If the `key` values are the same, the `value` from the most recent call overwrites the previous one.

After setting the Channel Metadata successfully, users who have subscribed to the channel and enabled event listening will receive a `storage` event of type `channel`. See [Event Listener](configuration#event-listener) for details.

Channel Metadata also supports CAS (Compare And Set) version control. This method provides two independent version control fields, and you can choose one or both depending on your business needs:

* Use the `majorRevision` property in `options` to enable version validation for the entire set of Channel Metadata.
* Use the `revision` property in a single Metadata Item in `data` to enable version validation for that specific item.

When setting Channel Metadata or Metadata Items, you can control whether version validation is enabled using the version properties:

* If the version property is `-1` (default), CAS validation is disabled. If the Channel Metadata or Metadata Item exists, it will be overwritten with the new value; if it does not exist, the SDK will create it.
* If the version property is a positive integer, CAS validation is enabled. The SDK will update the value only if the version matches; otherwise, it will return an error.

#### Method [#method-22]

You can call the `setChannelMetadata` method as follows:

```typescript
setChannelMetadata(
    channelName: string,
    channelType: RtmChannelType,
    data: Metadata,
    options?: IMetadataOptions
): Promise<SetChannelMetadataResponse>;
```

| Parameter     | Type                  | Required | Default | Description                                                                                                          |
| ------------- | --------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `channelName` | string                | Yes      | -       | Channel name.                                                                                                        |
| `channelType` | RtmChannelType        | Yes      | -       | Channel type. See [Channel Type](enum#channeltype).                                                                  |
| `data`        | Array\&lt;object\&gt; | Yes      | -       | Array of Metadata Items. Each item is a JSON object with predefined properties. Custom properties are not supported. |
| `options`     | object                | No       | -       | Channel Metadata configuration options.                                                                              |

The `data` object includes the following properties:

| Parameter       | Type         | Required | Default     | Description                                                                                                                                                                                                                                                                                       |
| --------------- | ------------ | -------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `majorRevision` | number       | Optional | `-1`        | <br />- Returned as the actual version number during read operations. <br />- Used as a version control switch during write operations: <br />- `-1`: Disable version validation. <br />- > `0`: Enable version validation. The operation only proceeds if the target version matches this value. |
| `items`         | MetadataItem | Optional | Empty array | Array of Metadata Items.                                                                                                                                                                                                                                                                          |
| `itemCount`     | number       | Optional | 0           | Number of Metadata Items.                                                                                                                                                                                                                                                                         |

Each Metadata Item includes the following properties:

```typescript
class MetaDataItem = {
    key: string, // Metadata Item key
    value: string, // Metadata Item value
    revision: number, // Metadata Item version
    updatedTs: string, // Last updated timestamp
    authorUserId: string, // User ID of the last editor
}
```

The `options` object includes the following properties:

| Parameter       | Type    | Required | Default           | Description                                                                                                                                                                       |
| --------------- | ------- | -------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `majorRevision` | number  | Optional | `-1`              | Version control switch: <br />- `-1`: Disable version validation. <br />- > `0`: Enable version validation. The operation only proceeds if the target version matches this value. |
| `lockName`      | string  | Optional | Empty string `''` | Lock name. Only the user who acquires this lock via the `acquireLock` method can perform the operation.                                                                           |
| `addTimeStamp`  | boolean | Optional | `false`           | Whether to record the edit timestamp.                                                                                                                                             |
| `addUserId`     | boolean | Optional | `false`           | Whether to record the user ID of the editor.                                                                                                                                      |

#### Basic usage [#basic-usage-26]

```typescript
const data = [
    {
        key : "Apple",
        value : "100",
        revision : 174298200
    },
    {
        key : "Banana",
        value : "200",
        revision : 174298100
    }
];
const options = {
    majorRevision : 174298270,
    lockName: "lockName",
    addTimeStamp : true,
    addUserId : true
};
try {
    const result = await rtm.storage.setChannelMetadata("channel_name", "MESSAGE", data, options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-22]

If the method call succeeds, it returns a `SetChannelMetadataResponse` type:

```typescript
type SetChannelMetadataResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType // Channel type
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### getChannelMetadata [#getchannelmetadata]

#### Description [#description-24]

The `getChannelMetadata` method retrieves the Metadata of a specified channel.

#### Method [#method-23]

You can call the `getChannelMetadata` method as follows:

```typescript
getChannelMetadata(
    channelName: string,
    channelType: RtmChannelType
): Promise<GetChannelMetadataResponse>;
```

| Parameter     | Type           | Required | Default | Description                                          |
| ------------- | -------------- | -------- | ------- | ---------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                        |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enumv#channeltype). |

#### Basic usage [#basic-usage-27]

```typescript
try {
    const result = await rtm.storage.getChannelMetadata("channel_name", "MESSAGE");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-23]

If the method call succeeds, it returns a `GetChannelMetadataResponse` type:

```typescript
type GetChannelMetadataResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType, // Channel type
    majorRevision: number, // Version number
    items: MetadataItem[], // JSON object containing Metadata Item data
    itemCount: number // Number of Metadata Items
}
```

Each Metadata Item includes the following properties:

```typescript
class MetaDataItem = {
    key: string, // Metadata Item key
    value: string, // Metadata Item value
    revision: number, // Metadata Item version
    updatedTs: string, // Last updated timestamp
    authorUserId: string // User ID of the last editor
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### removeChannelMetadata [#removechannelmetadata]

#### Description [#description-25]

The removeChannelMetadata method deletes Channel Metadata or an array of Channel Metadata Items.

When deleting, you can control whether version validation is enabled by using the version property as follows:

* If the version property is set to -1 (default), CAS validation is disabled. If the Channel Metadata or Metadata Item exists, the SDK deletes it; if it does not exist, the SDK returns an error code.
* If the version property is a positive integer, CAS validation is enabled. If the Channel Metadata or Metadata Item exists, the SDK deletes it only after the version number passes validation; otherwise, the SDK returns an error code.

After successfully deleting Channel Metadata or a Metadata Item, users who subscribe to the channel and have enabled event listening will receive a storage event of type channel. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-24]

You can call the `removeChannelMetadata` method as follows:

```typescript
removeChannelMetadata(
    channelName: string,
    channelType: RtmChannelType,
    options?: RemoveChannelMetadataOptions
): Promise<RemoveChannelMetadataResponse>;
```

| Parameter     | Type           | Required | Default | Description                                          |
| ------------- | -------------- | -------- | ------- | ---------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                        |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enumv#channeltype). |
| `options`     | object         | No       | -       | Channel Metadata configuration options.              |

The `options` object includes the following properties:

| Parameter       | Type                  | Required | Default           | Description                                                                                                                                                                       |
| --------------- | --------------------- | -------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `items`         | Array\&lt;object\&gt; | Required | -                 | Array of Metadata Items. Each Metadata Item is a JSON object with predefined properties. Custom properties are not supported.                                                     |
| `itemCount`     | number                | Optional | 0                 | Number of Metadata Items.                                                                                                                                                         |
| `majorRevision` | number                | Optional | `-1`              | Version control switch: <br />- `-1`: Disable version validation. <br />- > `0`: Enable version validation. The operation only proceeds if the target version matches this value. |
| `lockName`      | string                | Optional | Empty string `''` | Lock name. Only the user who acquires this lock via the `acquireLock` method can perform the operation.                                                                           |
| `addTimeStamp`  | boolean               | Optional | `false`           | Whether to record the edit timestamp.                                                                                                                                             |
| `addUserId`     | boolean               | Optional | `false`           | Whether to record the user ID of the editor.                                                                                                                                      |

Each Metadata Item includes the following properties:

```typescript
class MetaDataItem = {
    key: string, // Metadata Item key
    value: string, // Metadata Item value
    revision: number, // Metadata Item version
    updatedTs: string, // Last updated timestamp
    authorUserId: string, // User ID of the last editor
}
```

#### Basic usage [#basic-usage-28]

```typescript
const data = [
    {
        key : "Apple",
        revision : 174298200
    }
];
const options = {
    data : data,
    majorRevision : 174298270,
};
try {
    const result = await rtm.storage.removeChannelMetadata("channel_name", "MESSAGE", options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-24]

If the method call succeeds, it returns a `RemoveChannelMetadataResponse` type:

```typescript
type RemoveChannelMetadataResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: string // Channel type
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### updateChannelMetadata [#updatechannelmetadata]

#### Description [#description-26]

The `updateChannelMetadata` method updates existing Channel Metadata. Each call can update the entire Channel Metadata or one or more Metadata Items.

After a successful update, users who subscribe to the `channel` and have enabled event listening will receive a `storage` event of type channel. See [Event Listener](configuration#event-listener) for details.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    This method cannot operate on Metadata Items that do not exist.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-25]

You can call the `updateChannelMetadata` method as follows:

```typescript
updateChannelMetadata(
    channelName: string,
    channelType: RtmChannelType,
    data: Metadata,
    options?: IMetadataOptions
): Promise<UpdateChannelMetadataResponse>;
```

| Parameter     | Type                  | Required | Default | Description                                                                                                          |
| ------------- | --------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `channelName` | string                | Yes      | -       | Channel name.                                                                                                        |
| `channelType` | RtmChannelType        | Yes      | -       | Channel type. See \[Channel Type]]\(enumv#channeltype).                                                              |
| `data`        | Array\&lt;object\&gt; | Yes      | -       | Array of Metadata Items. Each item is a JSON object with predefined properties. Custom properties are not supported. |
| `options`     | object                | No       | -       | Channel Metadata configuration options.                                                                              |

The `data` object includes the following properties:

| Parameter       | Type         | Required | Default     | Description                                                                                                                                                                                                                                                                                       |
| --------------- | ------------ | -------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `majorRevision` | number       | Optional | `-1`        | <br />- Returned as the actual version number during read operations. <br />- Used as a version control switch during write operations: <br />- `-1`: Disable version validation. <br />- > `0`: Enable version validation. The operation only proceeds if the target version matches this value. |
| `items`         | MetadataItem | Optional | Empty array | Array of Metadata Items.                                                                                                                                                                                                                                                                          |
| `itemCount`     | number       | Optional | 0           | Number of Metadata Items.                                                                                                                                                                                                                                                                         |

Each Metadata Item includes the following properties:

```typescript
class MetaDataItem = {
    key: string, // Metadata Item key
    value: string, // Metadata Item value
    revision: number, // Metadata Item version
    updatedTs: string, // Last updated timestamp
    authorUserId: string, // User ID of the last editor
}
```

The options object includes the following properties:

| Parameter       | Type    | Required | Default           | Description                                                                                                                                                                       |
| --------------- | ------- | -------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `majorRevision` | number  | Optional | `-1`              | Version control switch: <br />- `-1`: Disable version validation. <br />- > `0`: Enable version validation. The operation only proceeds if the target version matches this value. |
| `lockName`      | string  | Optional | Empty string `''` | Lock name. Only the user who acquires this lock via the `acquireLock` method can perform the operation.                                                                           |
| `addTimeStamp`  | boolean | Optional | `false`           | Whether to record the edit timestamp.                                                                                                                                             |
| `addUserId`     | boolean | Optional | `false`           | Whether to record the user ID of the editor.                                                                                                                                      |

#### Basic usage [#basic-usage-29]

```typescript
const data = [
    {
        key : "Mute",
        value : "false",
        revision : 174298100
    }
];
const options = {
    userId : "Tony",
    majorRevision : 174298270,
    addTimeStamp : true,
    addUserId : true
};
try {
    const result = await rtm.storage.updateUserMetadata(data, options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-25]

If the method call succeeds, it returns an `UpdateChannelMetadataResponse` type:

```typescript
type UpdateChannelMetadataResponse = {
    timestamp: number, // Reserved field
    channelName : string, // Channel name
    channelType : RtmChannelType // Channel type
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### setUserMetadata [#setusermetadata]

#### Description [#description-27]

The `setUserMetadata` method sets User Metadata. If this method is called multiple times, the SDK will iterate over the `key` values of the User Metadata Items and handle them according to the following rules:

* If the `key` values of the Metadata Items are different, they are added sequentially in the order of the calls.
* If the `key` values are the same, the value of the last Metadata set will overwrite the previous one.

Once User Metadata is successfully set, users who subscribe to the Metadata of that user and have enabled event listening will receive an `event` notification of type `user`. See [Event Listening](configuration#event-listener) for details.

User Metadata also introduces CAS (Compare-And-Swap) version control logic. This method provides two independent version control fields, and you can configure either or both depending on your business needs:

* Use the `majorRevision` property in `options` to enable version verification for the entire group of User Metadata.
* Use the `revision` property in `data` to enable version verification for each Metadata Item in the array.

When setting User Metadata, the version attributes determine whether to enable version verification for the current call, following this logic:

* If the version attribute is set to `-1` (default), CAS verification is disabled. If the User Metadata or Metadata Item already exists, the value will be overwritten with the new value; if it does not exist, it will be created.
* If the version attribute is a positive integer, CAS verification is enabled. If the User Metadata or Metadata Item already exists, the SDK will update the value only if the version matches; otherwise, the SDK will return an error code.

#### Method Signature [#method-signature]

You can call the `setUserMetadata` method as follows:

```typescript
setUserMetadata(
    data: Metadata,
    options?: SetOrUpdateUserMetadataOptions
): Promise<SetUserMetadataResponse>;
```

| Parameter | Type                  | Required | Default | Description                                                                                                                                                              |
| --------- | --------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `data`    | Array\&lt;object\&gt; | Required | -       | Array of Metadata Items. A Metadata Item is a JSON-formatted object that includes predefined properties. Custom properties beyond the predefined ones are not supported. |
| `options` | object                | Optional | -       | Metadata configuration options.                                                                                                                                          |

The `data` object contains the following properties:

| Parameter       | Type         | Required | Default     | Description                                                                                                                                                                                                                                                                                         |
| --------------- | ------------ | -------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `majorRevision` | number       | Optional | `-1`        | <br />- Returned as the actual version number during read operations. <br />- Used as a version control flag during write operations: <br />- `-1`: Disable version verification. <br />- > `0`: Enable version verification. The operation only proceeds if the target version matches this value. |
| `items`         | MetadataItem | Optional | Empty array | Array of Metadata Items.                                                                                                                                                                                                                                                                            |
| `itemCount`     | number       | Optional | 0           | Number of Metadata Items.                                                                                                                                                                                                                                                                           |

Each Metadata Item contains the following properties:

```typescript
class MetaDataItem = {
    key: string, // Key of the Metadata Item
    value: string, // Value of the Metadata Item
    revision: number, // Version number of the Metadata Item
    updatedTs: string, // Timestamp of the last update
    authorUserId: string, // User ID of the last editor
}
```

The `options` object contains the following properties:

| Parameter       | Type    | Required | Default                 | Description                                                                                                                                                                         |
| --------------- | ------- | -------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `userId`        | string  | Optional | Current user's `userId` | User ID.                                                                                                                                                                            |
| `majorRevision` | number  | Optional | `-1`                    | Version control flag: <br />- `-1`: Disable version verification. <br />- > `0`: Enable version verification. The operation only proceeds if the target version matches this value. |
| `lockName`      | string  | Optional | Empty string `''`       | Lock name. When set, only users who acquire the lock using the `acquireLock` method can perform the operation.                                                                      |
| `addTimeStamp`  | boolean | Optional | `false`                 | Whether to record the timestamp of the edit.                                                                                                                                        |
| `addUserId`     | boolean | Optional | `false`                 | Whether to record the user ID of the editor.                                                                                                                                        |

#### Basic usage [#basic-usage-30]

```typescript
const data = [
    {
        key : "Name",
        value : "Tony"
        revision : 174298200
    },
    {
        key : "Mute",
        value : "true",
        revision : 174298100
    }
];
const options = {
    userId : "Tony",
    majorRevision : 174298270,
    addTimeStamp : true,
    addUserId : true
};
try {
    const result = await rtm.storage.setUserMetadata(data, options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-26]

If the method call succeeds, it returns a `SetUserMetadataResponse` type object:

```typescript
type SetUserMetadataResponse = {
    timestamp: number, // Reserved field
    channelName: string;
    channelType: RtmChannelType
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### getUserMetadata [#getusermetadata]

#### Description [#description-28]

The `getUserMetadata` method retrieves the Metadata and User Metadata Items of a specified user.

#### Method Signature [#method-signature-1]

You can call the `getUserMetadata` method as follows:

```typescript
getUserMetadata(
    options?: GetUserMetadataOptions
): Promise<GetUserMetadataResponse>;
```

| Parameter | Type   | Required | Default | Description                     |
| --------- | ------ | -------- | ------- | ------------------------------- |
| `options` | object | Optional | -       | Metadata configuration options. |

The `options` object contains the following properties:

| Property | Type   | Required | Default                 | Description |
| -------- | ------ | -------- | ----------------------- | ----------- |
| `userId` | string | Optional | Current user's `userId` | User ID.    |

#### Basic usage [#basic-usage-31]

```typescript
try {
    const result = await rtm.storage.getUserMetadata({ userId: "Tony" });
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-27]

If the method call succeeds, it returns a `GetUserMetadataResponse` type object:

```typescript
type GetUserMetadataResponse = {
    timestamp: number, // Reserved field
    userId : string, // User ID
    itemCount : number, // Number of Metadata Items
    majorRevision : number, // Version number
    items : MetadataItem[] // JSON object containing MetadataItem data
}
```

Each Metadata Item contains the following properties:

```typescript
class MetaDataItem = {
    key: string, // Key of the Metadata Item
    value: string, // Value of the Metadata Item
    revision : number, // Version number of the Metadata Item
    updatedTs : string, // Timestamp of the last update
    authorUserId : string, // User ID of the last editor
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### removeUserMetadata [#removeusermetadata]

#### Description [#description-29]

The `removeUserMetadata` method deletes User Metadata or specific User Metadata Items.

After successful deletion, users who subscribe to the Metadata of that user and have enabled event listening will receive a `storage` event of type `user`. See [Event Listening](configuration#event-listener) for details.

#### Method Signature [#method-signature-2]

You can call the `removeUserMetadata` method as follows:

```typescript
removeUserMetadata(
    options?: RemoveUserMetadataOptions
): Promise<RemoveUserMetadataResponse>;
```

| Parameter | Type   | Required | Default | Description                                                                                               |
| --------- | ------ | -------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `options` | object | Optional | -       | Metadata configuration options. If not provided, all user properties of the current user will be deleted. |

The `options` object contains the following properties:

| Parameter       | Type              | Required | Default                 | Description                                                                                                                                                                                                                        |
| --------------- | ----------------- | -------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `userId`        | string            | Optional | Current user's `userId` | User ID.                                                                                                                                                                                                                           |
| `data`          | Array\&lt;object> | Optional | -                       | Array of Metadata Items. A Metadata Item is a JSON object with predefined properties. <br />Custom properties beyond the predefined ones are not supported. <br />If this parameter is not provided, all Metadata will be deleted. |
| `majorRevision` | number            | Optional | `-1`                    | Version control flag: <br />- `-1`: Disable version verification. <br />- > `0`: Enable version verification. The operation only proceeds if the target version matches this value.                                                |
| `lockName`      | string            | Optional | Empty string `''`       | Lock name. When set, only users who acquire the lock using the `acquireLock` method can perform the operation.                                                                                                                     |
| `addTimeStamp`  | boolean           | Optional | `false`                 | Whether to record the timestamp of the edit.                                                                                                                                                                                       |
| `addUserId`     | boolean           | Optional | `false`                 | Whether to record the user ID of the editor.                                                                                                                                                                                       |

The `data` object contains the following properties:

| Parameter       | Type         | Required | Default     | Description                                                                                                                                                                                                                                                                                         |
| --------------- | ------------ | -------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `majorRevision` | number       | Optional | `-1`        | <br />- Returned as the actual version number during read operations. <br />- Used as a version control flag during write operations: <br />- `-1`: Disable version verification. <br />- > `0`: Enable version verification. The operation only proceeds if the target version matches this value. |
| `items`         | MetadataItem | Optional | Empty array | Array of Metadata Items.                                                                                                                                                                                                                                                                            |
| `itemCount`     | number       | Optional | `0`         | Number of Metadata Items.                                                                                                                                                                                                                                                                           |

Each Metadata Item contains the following properties:

```typescript
class MetaDataItem = {
    key: string, // Key of the Metadata Item
    value: string, // Value of the Metadata Item
    revision: number, // Version number of the Metadata Item
    updatedTs: string, // Timestamp of the last update
    authorUserId: string // User ID of the last editor
}
```

#### Basic usage [#basic-usage-32]

```typescript
const data = [
    {
        key : "Mute",
        revision : 174298100
    }
];
const options = {
    userId: "Tony",
    data : data,
    majorRevision : 174298270
};
try {
    const result = await rtm.storage.removeUserMetadata(options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-28]

If the method call succeeds, it returns a `RemoveUserMetadataResponse` type object:

```typescript
type RemoveUserMetadataResponse = {
    timestamp: number, // Reserved field
    userId : string, // User ID
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### updateUserMetadata [#updateusermetadata]

#### Description [#description-30]

The updateUserMetadata method updates existing User Metadata or Metadata Items. After a successful update, users who subscribe to the User Metadata and have enabled event listening will receive a storage event of type user. See [Event Listener](configuration#event-listener) for details.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    This method cannot operate on Metadata Items that do not exist.
  </CalloutDescription>
</CalloutContainer>

#### Method [#method-26]

You can call the `updateUserMetadata` method as follows:

```typescript
updateUserMetadata(
    data: Metadata,
    options?: SetOrUpdateUserMetadataOptions
): Promise<UpdateUserMetadataResponse>;
```

| Parameter | Type                  | Required | Default | Description                                                                                                          |
| --------- | --------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `data`    | Array\&lt;object\&gt; | Yes      | -       | Array of Metadata Items. Each item is a JSON object with predefined properties. Custom properties are not supported. |
| `options` | object                | No       | -       | Metadata configuration options.                                                                                      |

The `data` object includes the following properties:

| Parameter       | Type         | Required | Default     | Description                                                                                                                                                                                                                                                                                              |
| --------------- | ------------ | -------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `majorRevision` | number       | Optional | `-1`        | <br />- Returned as the actual version number during read operations. <br />- Acts as a version control switch during write operations: <br />- `-1`: Disable version validation. <br />- > `0`: Enable version validation. The operation only proceeds if the target version number matches this value. |
| `items`         | MetadataItem | Optional | Empty array | Array of Metadata Items.                                                                                                                                                                                                                                                                                 |
| `itemCount`     | number       | Optional | `0`         | Number of Metadata Items.                                                                                                                                                                                                                                                                                |

Each Metadata Item includes the following properties:

```typescript
class MetaDataItem = {
    key: string, // Metadata Item key
    value: string, // Metadata Item value
    revision: number, // Metadata Item version number
    updatedTs: string, // Last updated timestamp
    authorUserId: string, // User ID of the last editor
}
```

The options object includes the following properties:

| Parameter       | Type    | Required | Default                 | Description                                                                                                                                                                                      |
| --------------- | ------- | -------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `userId`        | string  | Optional | Current user's `userId` | User ID.                                                                                                                                                                                         |
| `majorRevision` | number  | Optional | `-1`                    | <br />- Version control switch: <br />- `-1`: Disable version validation. <br />- > `0`: Enable version validation. The operation only proceeds if the target version number matches this value. |
| `lockName`      | string  | Optional | Empty string `''`       | Lock name. Only the user who acquires this lock via the `acquireLock` method can perform the operation.                                                                                          |
| `addTimeStamp`  | boolean | Optional | `false`                 | Whether to record the edit timestamp.                                                                                                                                                            |
| `addUserId`     | boolean | Optional | `false`                 | Whether to record the user ID of the editor.                                                                                                                                                     |

#### Basic usage [#basic-usage-33]

```typescript
const data = [
    {
        key : "Mute",
        value : "false",
        revision : 174298100
    }
];
const options = {
    userId : "Tony",
    majorRevision : 174298270,
    addTimeStamp : true,
    addUserId : true
};
try {
    const result = await rtm.storage.updateUserMetadata(data, options);
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-29]

If the method call succeeds, it returns an `UpdateUserMetadataResponse` type:

```typescript
type UpdateUserMetadataResponse = {
    timestamp: number, // Reserved field
    userId : string // User ID
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### subscribeUserMetadata [#subscribeusermetadata]

#### Description [#description-31]

The subscribeUserMetadata method subscribes to the Metadata of a specified user. After a successful subscription and enabling event listening, you will receive a storage event of type user whenever that user's Metadata changes. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-27]

You can call the `subscribeUserMetadata` method as follows:

```typescript
subscribeUserMetadata(
    userId: string
): Promise<SubscribeUserMetaResponse>;
```

| Parameter | Type   | Required | Default | Description |
| --------- | ------ | -------- | ------- | ----------- |
| `userId`  | string | Yes      | -       | User ID.    |

#### Basic usage [#basic-usage-34]

```typescript
try {
    const result = await rtm.storage.subscribeUserMetadata("Tony");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-30]

If the method call succeeds, it returns a `SubscribeUserMetaResponse` type:

```typescript
type SubscribeUserMetaResponse = {
    timestamp: number, // Reserved field
    userId : string // User ID
}
```

### unsubscribeUserMetadata [#unsubscribeusermetadata]

#### Description [#description-32]

If you no longer need to receive updates for a user's User Metadata, call the `unsubscribeUserMetadata` method to unsubscribe.

#### Method [#method-28]

You can call the `unsubscribeUserMetadata` method as follows:

```typescript
unsubscribeUserMetadata(
    userId: string
): Promise<UnsubscribeUserMetaResponse>;
```

| Parameter | Type   | Required | Default | Description |
| --------- | ------ | -------- | ------- | ----------- |
| `userId`  | string | Yes      | -       | User ID.    |

#### Basic usage [#basic-usage-35]

```typescript
try {
    const result = await rtm.storage.unsubscribeUserMetadata("Tony");
    console.log("result");
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-31]

If the method call succeeds, it returns an `UnsubscribeUserMetaResponse` type:

```typescript
type UnsubscribeUserMetaResponse = {
    timestamp: number, // Reserved field
    userId : string // User ID
}
```

## Lock [#lock]

A critical resource can only be used by one process at a time. If multiple processes share a critical resource, they must use mutual exclusion to avoid interfering with each other. RTM provides a complete Lock solution that allows you to manage distributed processes and resolve user contention for shared resources.

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    The client SDK supports setting, deleting, and revoking locks. We recommend that you control client-side permissions for lock operations based on your business needs.
  </CalloutDescription>
</CalloutContainer>

### setLock [#setlock]

#### Description [#description-33]

You need to configure properties such as the lock name and expiration time (TTL), then call the `setLock` method. After successful configuration, all users in the channel receive a `lockSet` type `lock` event notification. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-29]

You can call the `setLock` method as follows:

```typescript
setLock(
    channelName: string,
    channelType: RtmChannelType,
    lockName: string,
    options?: SetLockOptions
): Promise<SetLockResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |
| `lockName`    | string         | Yes      | -       | Lock name.                                          |
| `options`     | object         | No       | -       | Lock configuration options.                         |

The `options` object includes the following properties:

| Property | Type   | Required | Default | Description                                                                                                                                                                                                                                                                 |
| -------- | ------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ttl`    | number | No       | `10`    | Lock expiration time in seconds. Range: \[10, 300]. If the user holding the lock goes offline and returns to the channel within the TTL, they can still use the lock. Otherwise, the lock is released and users listening for `lock` events receive a `lockReleased` event. |

#### Basic usage [#basic-usage-36]

```typescript
try {
    const result = await rtm.Lock.setLock(
        channel: "my_channel",
        channelType: "STREAM",
        lockName: "my_lock",
        { ttl: 30 }
    );
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-32]

If the method call succeeds, it returns a `SetLockResponse` type:

```typescript
type SetLockResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType, // Channel type
    lockName: string // Lock name
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### acquireLock [#acquirelock]

#### Description [#description-34]

After configuring a Lock, you can call the `acquireLock` method on the client to obtain ownership of the lock. Once acquired, other users in the channel receive a `lockAcquired` type `lock` event notification. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-30]

You can call the `acquireLock` method as follows:

```typescript
acquireLock(
    channelName: string,
    channelType: RtmChannelType,
    lockName: string,
    options?: AcquireLockOptions
): Promise<AcquireLockResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |
| `lockName`    | string         | Yes      | -       | Lock name.                                          |
| `options`     | object         | No       | -       | Options for acquiring the lock.                     |

The `options` object includes the following properties:

| Property | Type    | Required | Default | Description                                                                          |
| -------- | ------- | -------- | ------- | ------------------------------------------------------------------------------------ |
| `retry`  | boolean | No       | `false` | Whether to retry acquiring the lock until successful or the user leaves the channel. |

#### Basic usage [#basic-usage-37]

```typescript
try {
    const result = await rtm.Lock.acquireLock(
        "chat_room",
        "STREAM",
        "my_lock",
        { retry: false }
    );
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-33]

If the method call succeeds, it returns an `AcquireLockResponse` type:

```typescript
type AcquireLockResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType, // Channel type
    lockName: string // Lock name
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### releaseLock [#releaselock]

#### Description [#description-35]

When a user no longer needs to hold a lock, they can call the `releaseLock` method to release it. After the lock is released, other users in the channel receive a `lockReleased` type `lock` event. See [Event Listener](configuration#event-listener) for details.

At this point, if another user wants to acquire the lock, they can call `acquireLock` to compete for it. New users and those with `retry` enabled have equal priority.

#### Method [#method-31]

You can call the `releaseLock` method as follows:

```typescript
releaseLock(
    channelName: string,
    channelType: RtmChannelType,
    lockName: string
): Promise<ReleaseLockResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |
| `lockName`    | string         | Yes      | -       | Lock name.                                          |

#### Basic usage [#basic-usage-38]

```typescript
try {
    const result = await rtm.lock.releaseLock(
        "chat_room",
        "STREAM",
        "my_lock"
    );
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-34]

If the method call succeeds, it returns a `ReleaseLockResponse` type:

```typescript
type ReleaseLockResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType, // Channel type
    lockName: string // Lock name
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### revokeLock [#revokelock]

#### Description [#description-36]

If a lock is held and you need to ensure your business logic proceeds, you may need to revoke the lock to give other users a chance to acquire it. Call `revokeLock` to revoke the held lock. After revocation, all users in the channel receive a `lockReleased` type `lock` event. See [Event Listener](configuration#event-listener) for details.

At this point, if another user wants to acquire the lock, they can call `acquireLock` to compete for it. New users and those with `retry` enabled have equal priority.

#### Method [#method-32]

You can call the `revokeLock` method as follows:

```typescript
revokeLock(
    channelName: string,
    channelType: RtmChannelType,
    lockName: string,
    owner: string
): Promise<RevokeLockResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |
| `lockName`    | string         | Yes      | -       | Lock name.                                          |
| `owner`       | string         | Yes      | -       | User ID of the current lock owner.                  |

#### Basic usage [#basic-usage-39]

```typescript
try {
    const result = await rtm.lock.revokeLockLock(
        "chat_room",
        "STREAM",
        "my_lock",
        "Tony"
    );
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-35]

If the method call succeeds, it returns a `RevokeLockResponse` type:

```typescript
type RevokeLockResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType, // Channel type
    lockName: string // Lock name
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### getLock [#getlock]

#### Description [#description-37]

If you need to query information such as the number of locks in a channel, their names, owners, and expiration times, call the `getLock` method on the client.

#### Method [#method-33]

You can call the `getLock` method as follows:

```typescript
getLock(
    channelName: string,
    channelType: RtmChannelType
): Promise<GetLockResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |

#### Basic usage [#basic-usage-40]

```typescript
try {
    const result = await rtm.lock.getLock("chat_room", "STREAM");
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-36]

If the method call succeeds, it returns a `GetLockResponse` type:

```typescript
type GetLockResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType, // Channel type
    totalLocks: string, // Number of locks
    lockDetails: [{
        lockName: string, // Lock name
        owner: string, // Lock owner
        ttl: number // Lock expiration time
    }]
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

### removeLock [#removelock]

#### Description [#description-38]

If you no longer need a specific lock in a channel, you can call the `removeLock` method to delete it. After successful deletion, all users in the channel receive a `lockRemoved` type `lock` event notification. See [Event Listener](configuration#event-listener) for details.

#### Method [#method-34]

You can call the `removeLock` method as follows:

```typescript
removeLock(
    channelName: string,
    channelType: RtmChannelType,
    lockName: string
): Promise<RemoveLockResponse>;
```

| Parameter     | Type           | Required | Default | Description                                         |
| ------------- | -------------- | -------- | ------- | --------------------------------------------------- |
| `channelName` | string         | Yes      | -       | Channel name.                                       |
| `channelType` | RtmChannelType | Yes      | -       | Channel type. See [Channel Type](enum#channeltype). |
| `lockName`    | string         | Yes      | -       | Lock name.                                          |

#### Basic usage [#basic-usage-41]

```typescript
try {
    const result = await rtm.Lock.removeLockLock(
        "chat_room",
        "STREAM",
        "my_lock"
    );
    console.log(result);
} catch (status) {
    console.log(status);
}
```

#### Return value [#return-value-37]

If the method call succeeds, it returns a `RemoveLockResponse` type:

```typescript
type RemoveLockResponse = {
    timestamp: number, // Reserved field
    channelName: string, // Channel name
    channelType: RtmChannelType, // Channel type
    lockName: string // Lock name
}
```

If the method call fails, it returns an ErrorInfo type object:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

You can look up the value of the errorCode field in the error code list to understand the cause and find the corresponding solution.

## Enumerated types [#enumerated-types]

### RtmAreaCode [#rtmareacode]

Access region, indicating the region of the server the SDK connects to.

| Enum Value | Description                         |
| ---------- | ----------------------------------- |
| `cn`       | `1`: Mainland China.                |
| `na`       | `2`: North America.                 |
| `eu`       | `4`: Europe.                        |
| `as`       | `8`: Asia excluding Mainland China. |
| `jp`       | `16`: Japan.                        |
| `in`       | `32`: India.                        |
| `glob`     | `4294967295`: Global.               |

### RtmChannelType [#rtmchanneltype]

Channel type.

| Enum Value | Description                |
| ---------- | -------------------------- |
| `none`     | `0`: Unknown channel type. |
| `message`  | `1`: Message Channel       |
| `stream`   | `2`: Stream Channel        |
| `user`     | `3`: User Channel          |

### RtmConnectionChangeReason [#rtmconnectionchangereason]

Reasons for SDK connection state changes.

| Enum Value                                         | Description                                                                                          |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `rtmConnectionChangedConnecting`                   | `0`: Connecting to the network.                                                                      |
| `rtmConnectionChangedJoinSuccess`                  | `1`: Successfully joined the channel.                                                                |
| `rtmConnectionChangedInterrupted`                  | `2`: Network connection interrupted.                                                                 |
| `rtmConnectionChangedBannedByServer`               | `3`: Connection blocked by the server.                                                               |
| `rtmConnectionChangedJoinFailed`                   | `4`: SDK failed to join the channel for 20 minutes and stopped retrying.                             |
| `rtmConnectionChangedLeaveChannel`                 | `5`: Left the channel.                                                                               |
| `rtmConnectionChangedInvalidAppId`                 | `6`: Invalid App ID.                                                                                 |
| `rtmConnectionChangedInvalidChannelName`           | `7`: Invalid channel name.                                                                           |
| `rtmConnectionChangedInvalidToken`                 | `8`: Invalid Token.                                                                                  |
| `rtmConnectionChangedTokenExpired`                 | `9`: Token expired.                                                                                  |
| `rtmConnectionChangedRejectedByServer`             | `10`: Connection rejected by the server.                                                             |
| `rtmConnectionChangedSettingProxyServer`           | `11`: Reconnecting due to proxy server settings.                                                     |
| `rtmConnectionChangedRenewToken`                   | `12`: Token renewal caused connection state change.                                                  |
| `rtmConnectionChangedClientIpAddressChanged`       | `13`: Client IP changed due to network type or carrier. Reconnecting.                                |
| `rtmConnectionChangedKeepAliveTimeout`             | `14`: Keep-alive timeout. SDK is reconnecting.                                                       |
| `rtmConnectionChangedRejoinSuccess`                | `15`: Successfully rejoined the channel.                                                             |
| `rtmConnectionChangedLost`                         | `16`: Lost connection to the server.                                                                 |
| `rtmConnectionChangedEchoTest`                     | `17`: Connection state changed due to echo test.                                                     |
| `rtmConnectionChangedClientIpAddressChangedByUser` | `18`: Client IP changed by user. Reconnecting.                                                       |
| `rtmConnectionChangedSameUidLogin`                 | `19`: Same user ID logged in from another device.                                                    |
| `rtmConnectionChangedTooManyBroadcasters`          | `20`: Too many broadcasters in the channel.                                                          |
| `rtmConnectionChangedLicenseValidationFailure`     | `21`: License validation failed.                                                                     |
| `rtmConnectionChangedCertificationVerifyFailure`   | `22`: Server certificate verification failed.                                                        |
| `rtmConnectionChangedStreamChannelNotAvailable`    | `23`: Stream Channel does not exist.                                                                 |
| `rtmConnectionChangedInconsistentAppid`            | `24`: App ID does not match Token.                                                                   |
| `rtmConnectionChangedLoginSuccess`                 | `10001`: Successfully logged in to RTM system.                                                       |
| `rtmConnectionChangedLogout`                       | `10002`: Logged out of RTM system.                                                                   |
| `rtmConnectionChangedPresenceNotReady`             | `10003`: Presence service not ready. You need to call `login` again and reinitialize SDK operations. |

### RtmConnectionState [#rtmconnectionstate]

SDK connection state.

| Enum Value     | Description                       |
| -------------- | --------------------------------- |
| `disconnected` | `1`: Disconnected from server.    |
| `connecting`   | `2`: Connecting to server.        |
| `connected`    | `3`: Connected to server.         |
| `reconnecting` | `4`: Reconnecting to server.      |
| `failed`       | `5`: Failed to connect to server. |

### RtmEncryptionMode [#rtmencryptionmode]

Encryption mode.

| Enum Value  | Description                      |
| ----------- | -------------------------------- |
| `none`      | `0`: No encryption.              |
| `aes128Gcm` | `1`: AES 128-bit GCM encryption. |
| `aes256Gcm` | `2`: AES 256-bit GCM encryption. |

### RtmLockEventType [#rtmlockeventtype]

Lock event types.

| Enum Value     | Description                                  |
| -------------- | -------------------------------------------- |
| `none`         | `0`: Unknown state.                          |
| `snapshot`     | `1`: Lock snapshot when joining the channel. |
| `lockSet`      | `2`: Lock set.                               |
| `lockRemoved`  | `3`: Lock removed.                           |
| `lockAcquired` | `4`: Lock acquired.                          |
| `lockReleased` | `5`: Lock released.                          |
| `lockExpired`  | `6`: Lock expired.                           |

### RtmLogLevel [#rtmloglevel]

Log output level.

| Enum Value | Description                                                                 |
| ---------- | --------------------------------------------------------------------------- |
| `none`     | `0x0000`: No logs.                                                          |
| `info`     | `0x0001`: Logs at `fatal`, `error`, `warn`, and `info` levels. Recommended. |
| `warn`     | `0x0002`: Logs at `fatal`, `error`, and `warn` levels.                      |
| `error`    | `0x0004`: Logs at `fatal` and `error` levels.                               |
| `fatal`    | `0x0008`: Logs only `fatal` level.                                          |

### RtmMessagePriority [#rtmmessagepriority]

Message priority.

| Enum Value | Description            |
| ---------- | ---------------------- |
| `highest`  | `0`: Highest priority. |
| `high`     | `1`: High priority.    |
| `normal`   | `4`: Normal priority.  |
| `low`      | `8`: Low priority.     |

### RtmMessageQos [#rtmmessageqos]

QoS level for Topic messages.

| Enum Value  | Description                    |
| ----------- | ------------------------------ |
| `unordered` | `0`: Messages are not ordered. |
| `ordered`   | `1`: Messages are ordered.     |

### RtmMessageType [#rtmmessagetype]

Message type.

| Enum Value | Description       |
| ---------- | ----------------- |
| `binary`   | `0`: Binary type. |
| `string`   | `1`: String type. |

### RtmPresenceEventType [#rtmpresenceeventtype]

Presence event type.

| Enum Value           | Description                                                                                                      |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `none`               | `0`: Unknown status.                                                                                             |
| `snapshot`           | `1`: Presence snapshot when joining the channel.                                                                 |
| `interval`           | `2`: When the number of users in the channel reaches a threshold, real-time notifications become interval-based. |
| `remoteJoinChannel`  | `3`: Remote user joined the channel.                                                                             |
| `remoteLeaveChannel` | `4`: Remote user left the channel.                                                                               |
| `remoteTimeout`      | `5`: Remote user timed out.                                                                                      |
| `remoteStateChanged` | `6`: Remote user’s temporary state changed.                                                                      |
| `errorOutOfService`  | `7`: Presence not enabled when joining the channel.                                                              |

### RtmLinkOperation [#rtmlinkoperation]

Operation type.

| Enum Value      | Description                |
| --------------- | -------------------------- |
| `login`         | `0`: Login.                |
| `logout`        | `1`: Logout.               |
| `join`          | `2`: Join Stream Channel.  |
| `leave`         | `3`: Leave Stream Channel. |
| `serverReject`  | `4`: Rejected by server.   |
| `autoReconnect` | `5`: Auto reconnect.       |
| `reconnected`   | `6`: Reconnected.          |
| `heartbeatLost` | `7`: Heartbeat timeout.    |
| `serverTimeout` | `8`: Server timeout.       |
| `networkChange` | `9`: Network change.       |

### RtmLinkState [#rtmlinkstate]

Link connection state.

| Enum Value     | Description         |
| -------------- | ------------------- |
| `idle`         | `0`: Initial state. |
| `connecting`   | `1`: Connecting.    |
| `connected`    | `2`: Connected.     |
| `disconnected` | `3`: Disconnected.  |
| `suspended`    | `4`: Suspended.     |
| `failed`       | `5`: Failed.        |

### RtmLinkStateChangeReason [#rtmlinkstatechangereason]

Reason for link state change.

| Enum Value                        | Description                            |
| --------------------------------- | -------------------------------------- |
| `unknown`                         | `0`: Unknown reason.                   |
| `login`                           | `1`: Logging in.                       |
| `loginSuccess`                    | `2`: Login successful.                 |
| `loginTimeout`                    | `3`: Login timed out.                  |
| `loginNotAuthorized`              | `4`: Login not authorized.             |
| `loginRejected`                   | `5`: Login rejected.                   |
| `relogin`                         | `6`: Relogin.                          |
| `logout`                          | `7`: Logout.                           |
| `autoReconnect`                   | `8`: Auto reconnect.                   |
| `reconnectTimeout`                | `9`: Reconnect timed out.              |
| `reconnectSuccess`                | `10`: Reconnect successful.            |
| `join`                            | `11`: Joining channel.                 |
| `joinSuccess`                     | `12`: Joined channel successfully.     |
| `joinFailed`                      | `13`: Failed to join channel.          |
| `rejoin`                          | `14`: Rejoin channel.                  |
| `leave`                           | `15`: Left channel.                    |
| `invalidToken`                    | `16`: Invalid Token.                   |
| `tokenExpired`                    | `17`: Token expired.                   |
| `inconsistentAppId`               | `18`: App ID mismatch.                 |
| `invalidChannelName`              | `19`: Invalid channel name.            |
| `invalidUserId`                   | `20`: Invalid user ID.                 |
| `notInitialized`                  | `21`: SDK not initialized.             |
| `rtmServiceNotConnected`          | `22`: RTM service not connected.       |
| `channelInstanceExceedLimitation` | `23`: Channel instance limit exceeded. |
| `operationRateExceedLimitation`   | `24`: Operation rate limit exceeded.   |
| `channelInErrorState`             | `25`: Channel in error state.          |
| `presenceNotConnected`            | `26`: Presence service not connected.  |
| `sameUidLogin`                    | `27`: Same user ID logged in.          |
| `kickedOutByServer`               | `28`: Kicked out by server.            |
| `keepAliveTimeout`                | `29`: Keep-alive timeout.              |
| `connectionError`                 | `30`: Connection error.                |
| `presenceNotReady`                | `31`: Presence service not ready.      |
| `networkChange`                   | `32`: Network change.                  |
| `serviceNotSupported`             | `33`: Service not supported.           |
| `streamChannelNotAvailable`       | `34`: Stream Channel not available.    |
| `storageNotAvailable`             | `35`: Storage service not available.   |
| `lockNotAvailable`                | `36`: Lock service not available.      |
| `loginTooFrequent`                | `37`: Login operation too frequent.    |

### RtmProtocolType [#rtmprotocoltype]

Message transport protocol type.

| Enum Value | Description                               |
| ---------- | ----------------------------------------- |
| `tcpUdp`   | `0`: TCP and UDP.                         |
| `tcpOnly`  | `1`: TCP only. Both environments use TCP. |

### RtmServiceType [#rtmservicetype]

Service type.

| Enum Value | Description                                                                           |
| ---------- | ------------------------------------------------------------------------------------- |
| `none`     | Basic services, including Message Channel, User Channel, Presence, Storage, and Lock. |
| `message`  | Message Channel service.                                                              |
| `stream`   | Stream Channel service.                                                               |

<CalloutContainer type="info">
  <CalloutTitle>
    Info
  </CalloutTitle>

  <CalloutDescription>
    To use all RTM services, you can use bitwise operations to combine multiple service types.
  </CalloutDescription>
</CalloutContainer>

### RtmProxyType [#rtmproxytype]

Proxy type.

| Enum Value | Description                   |
| ---------- | ----------------------------- |
| `none`     | `0`: Proxy disabled.          |
| `http`     | `1`: HTTP proxy enabled.      |
| `cloudTcp` | `2`: TCP Cloud Proxy enabled. |

### RtmStorageEventType [#rtmstorageeventtype]

Storage event type.

| Enum Value | Description                                                                                                            |
| ---------- | ---------------------------------------------------------------------------------------------------------------------- |
| `none`     | `0`: Unknown event type.                                                                                               |
| `snapshot` | `1`: Triggered when a user first subscribes to Channel Metadata or User Metadata, or joins a channel.                  |
| `set`      | `2`: Triggered when calling `setChannelMetadata` or `setUserMetadata`. Only returned in incremental update mode.       |
| `update`   | `3`: Returned when calling methods that add, update, or delete Channel Metadata or User Metadata.                      |
| `remove`   | `4`: Triggered when calling `removeChannelMetadata` or `removeUserMetadata`. Only returned in incremental update mode. |

### RtmStorageType [#rtmstoragetype]

Storage type.

| Enum Value | Description                  |
| ---------- | ---------------------------- |
| `none`     | `0`: Unknown type.           |
| `user`     | `1`: User Metadata event.    |
| `channel`  | `2`: Channel Metadata event. |

### RtmTopicEventType [#rtmtopiceventtype]

Topic event type.

| Enum Value         | Description                                     |
| ------------------ | ----------------------------------------------- |
| `none`             | `0`: Unknown event type.                        |
| `snapshot`         | `1`: Snapshot of Topics when joining a channel. |
| `remoteJoinTopic`  | `2`: Remote user joined the Topic.              |
| `remoteLeaveTopic` | `3`: Remote user left the Topic.                |

## Troubleshooting [#troubleshooting]

Refer to the following information for troubleshooting API calls.

### ErrorInfo [#errorinfo]

When you call an RTM React Native API and an error occurs, the SDK throws an object of type `ErrorInfo`, which contains the following properties:

```typescript
type ErrorInfo = {
    error: boolean;        // Whether the operation failed
    reason: string;        // Name of the API that triggered the error
    operation: string;     // Operation code
    errorCode: number;     // Error code
};
```

The `errorCode` and `reason` fields report the error code and the API name, respectively.

### Error code tables [#error-code-tables]

Refer to the table below for possible causes and solutions:

| Code     | Name                                   | Description                                                                                                                                                                                                                |
| -------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`      | `ok`                                   | Call succeeded.                                                                                                                                                                                                            |
| `-10002` | `notLogin`                             | The user is not logged in to the RTM system, timed out, or logged out before calling the API. Please log in to the RTM system first.                                                                                       |
| `-10003` | `invalidAppId`                         | Invalid App ID:<br />- Check whether the App ID is correct. <br />- Check whether the App ID has RTM service enabled.                                                                                                      |
| `-10004` | `invalidEventHandler`                  | Invalid event handler.                                                                                                                                                                                                     |
| `-10005` | `invalidToken`                         | Invalid Token. Check whether the Token Provider generates an RTM Token.                                                                                                                                                    |
| `-10006` | `invalidUserId`                        | Invalid user ID:<br />- Check if the username is empty. <br />- Check if the username contains illegal characters.                                                                                                         |
| `-10008` | `invalidChannelName`                   | Invalid channel name:<br />- Check if the channel name is empty. <br />- Check if the channel name contains illegal characters.                                                                                            |
| `-10009` | `tokenExpired`                         | The Token has expired. You need to call the `logout` method to log out of RTM, create an RTM instance with a new Token, and call `login` to log in again.                                                                  |
| `-10010` | `loginNoServerResources`               | Server resource limit reached. Try logging in again.                                                                                                                                                                       |
| `-10011` | `loginTimeout`                         | Login timed out. Check if the network is stable and switch to a stable network environment. If you're on an internal network, verify that your firewall allows the correct domain names and ports.                         |
| `-10012` | `loginRejected`                        | SDK login rejected by the server:<br />- Check if the App ID has RTM service enabled. <br />- Check if the Token or `userId` is banned.                                                                                    |
| `-10013` | `loginAborted`                         | SDK login interrupted due to unknown issues:<br />- Check if the network is stable and switch to a stable environment. <br />- The same `userId` may have logged in from another device.                                   |
| `-10015` | `loginNotAuthorized`                   | No RTM service permission. Possible reasons:<br />- RTM service not enabled in the console. <br />- Service is overdue. <br />- Account is banned.                                                                         |
| `-10016` | `inconsistentAppid`                    | App ID used for login or channel join is inconsistent.                                                                                                                                                                     |
| `-10017` | `duplicateOperation`                   | Duplicate operation.                                                                                                                                                                                                       |
| `-10018` | `instanceAlreadyReleased`              | RTMClient or RTMStreamChannel instance already released.                                                                                                                                                                   |
| `-10019` | `invalidChannelType`                   | Invalid channel type. The SDK supports the following types only:<br />- `MESSAGE`: Message Channel <br />- `STREAM`: Stream Channel <br />- `USER`: User Channel                                                           |
| `-10020` | `invalidEncryptionParameter`           | Invalid encryption parameters:<br />- Check if the encryption key is a string. <br />- Check if the salt is a Uint8Array of 32 bytes. <br />- Check if the encryption mode matches the key and salt.                       |
| `-10021` | `operationRateExceedLimitation`        | API call rate for Channel Metadata or User Metadata exceeded. Keep it under 10 calls per second.                                                                                                                           |
| `-10022` | `serviceNotSupported`                  | Unsupported service type. Check if the service type set in `RtmServiceType` is correct. This error is only for private deployment features.                                                                                |
| `-10023` | `loginCanceled`                        | Login operation canceled. Possible reasons:<br />- You called `login` again before the previous call completed. <br />- You called `logout` before login completed.                                                        |
| `-10025` | `notConnected`                         | RTM server not connected. This error occurs when calling message subscription, Metadata, or Lock APIs without a connection.                                                                                                |
| `-10026` | `renewTokenTimeout`                    | Token renewal timed out.                                                                                                                                                                                                   |
| `-11001` | `channelNotJoined`                     | User has not joined the channel:<br />- The user is offline, has disconnected, or has not joined the channel. <br />- Verify that the `userId` is spelled correctly.                                                       |
| `-11002` | `channelNotSubscribed`                 | User has not subscribed to the channel:<br />- The user is offline, has disconnected, or has not joined the channel. <br />- Verify that the `userId` is spelled correctly.                                                |
| `-11003` | `channelExceedTopicUserLimitation`     | The number of users subscribed to the Topic exceeds the limit.                                                                                                                                                             |
| `-11005` | `channelInstanceExceedLimitation`      | The number of created or subscribed channels exceeds the limit.                                                                                                                                                            |
| `-11006` | `channelInErrorState`                  | The channel is unavailable. Please recreate the Stream Channel or resubscribe to the Message Channel.                                                                                                                      |
| `-11007` | `channelJoinFailed`                    | Failed to join the channel:<br />- Check if the number of joined channels exceeds the limit. <br />- Check if the channel name is invalid. <br />- Check if the network is disconnected.                                   |
| `-11008` | `channelInvalidTopicName`              | Invalid Topic name:<br />- Check whether the Topic name contains illegal characters. <br />- Check whether the Topic name is empty.                                                                                        |
| `-11009` | `channelInvalidMessage`                | Invalid message. Check whether the message type is valid. RTM only supports string and binary message types.                                                                                                               |
| `-11010` | `channelMessageLengthExceedLimitation` | Message length exceeds the limit. Check whether the message payload size exceeds the threshold:<br />- Message Channel: each message must be less than 32 KB. <br />- Stream Channel: each message must be less than 1 KB. |
| `-11012` | `channelNotAvailable`                  | The Stream Channel feature is not enabled and is currently unavailable. Check whether the Stream Channel feature is enabled in the Console.                                                                                |
| `-11013` | `channelTopicNotSubscribed`            | The Topic is not subscribed.                                                                                                                                                                                               |
| `-11014` | `channelExceedTopicLimitation`         | The number of Topics exceeds the limit.                                                                                                                                                                                    |
| `-11015` | `channelJoinTopicFailed`               | Failed to join the Topic. Check whether the number of joined Topics exceeds the limit.                                                                                                                                     |
| `-11016` | `channelTopicNotJoined`                | The Topic is not joined. You must join the Topic before sending messages to it.                                                                                                                                            |
| `-11017` | `channelTopicNotExist`                 | The Topic does not exist. Check whether the Topic name is correct.                                                                                                                                                         |
| `-11018` | `channelInvalidTopicMeta`              | The `meta` parameter in the Topic is invalid. Check whether the `meta` parameter exceeds the 256-byte limit.                                                                                                               |
| `-11019` | `channelSubscribeTimeout`              | Channel subscription timed out. Check if the connection is lost.                                                                                                                                                           |
| `-11020` | `channelSubscribeTooFrequent`          | Channel subscription operations are too frequent. Make sure you do not subscribe to the same channel more than twice within 5 seconds.                                                                                     |
| `-11021` | `channelSubscribeFailed`               | Channel subscription failed. Check whether the number of subscribed channels exceeds the limit.                                                                                                                            |
| `-11023` | `channelEncryptMessageFailed`          | Message encryption failed:<br />- Check whether the encryption key is valid. <br />- Check whether the encryption salt is valid. <br />- Check whether the encryption mode matches the key and salt.                       |
| `-11024` | `channelPublishMessageFailed`          | Message publishing failed. Check if the connection is lost.                                                                                                                                                                |
| `-11026` | `channelPublishMessageTimeout`         | Message publishing timed out. Check if the connection is lost.                                                                                                                                                             |
| `-11028` | `channelLeaveFailed`                   | Failed to leave the channel. Check if the connection is lost.                                                                                                                                                              |
| `-11029` | `channelCustomTypeLengthOverflow`      | The `customType` field exceeds the maximum length. The length of `customType` must be within 32 characters.                                                                                                                |
| `-11030` | `channelInvalidCustomType`             | The `customType` field is invalid. Check whether it contains illegal characters.                                                                                                                                           |
| `-11033` | `channelReceiverOffline`               | The remote user is offline when sending a user message:<br />- Verify that the user ID set in the method is correct. <br />- Check whether the remote user is logged in and online.                                        |
| `-12001` | `storageOperationFailed`               | Storage operation failed.                                                                                                                                                                                                  |
| `-12002` | `storageMetadataItemExceedLimitation`  | The number of Storage Metadata Items exceeds the limit.                                                                                                                                                                    |
| `-12003` | `storageInvalidMetadataItem`           | Invalid Metadata Item.                                                                                                                                                                                                     |
| `-12004` | `storageInvalidArgument`               | Invalid argument.                                                                                                                                                                                                          |
| `-12005` | `storageInvalidRevision`               | Invalid Revision parameter.                                                                                                                                                                                                |
| `-12006` | `storageMetadataLengthOverflow`        | Metadata length exceeds the limit.                                                                                                                                                                                         |
| `-12007` | `storageInvalidLockName`               | Invalid Lock name.                                                                                                                                                                                                         |
| `-12008` | `storageLockNotAcquired`               | The Lock has not been acquired.                                                                                                                                                                                            |
| `-12009` | `storageInvalidKey`                    | Invalid Metadata key.                                                                                                                                                                                                      |
| `-12010` | `storageInvalidValue`                  | Invalid Metadata value.                                                                                                                                                                                                    |
| `-12011` | `storageKeyLengthOverflow`             | Metadata key length exceeds the limit.                                                                                                                                                                                     |
| `-12012` | `storageValueLengthOverflow`           | Metadata value length exceeds the limit.                                                                                                                                                                                   |
| `-12014` | `storageOutdatedRevision`              | Outdated Revision parameter.                                                                                                                                                                                               |
| `-12015` | `storageNotSubscribe`                  | The channel is not subscribed.                                                                                                                                                                                             |
| `-12017` | `storageSubscribeUserExceedLimitation` | The number of subscribed user Metadata exceeds the limit.                                                                                                                                                                  |
| `-12018` | `storageOperationTimeout`              | Storage operation timed out.                                                                                                                                                                                               |
| `-12019` | `storageNotAvailable`                  | Storage service is not available.                                                                                                                                                                                          |
| `-13001` | `presenceNotConnected`                 | User is not connected to the system.                                                                                                                                                                                       |
| `-13003` | `presenceInvalidArgument`              | Invalid argument.                                                                                                                                                                                                          |
| `-13004` | `presenceCachedTooManyStates`          | The number of cached temporary user states before joining the channel exceeds the limit.                                                                                                                                   |
| `-13005` | `presenceStateCountOverflow`           | The number of temporary state key-value pairs exceeds the limit.                                                                                                                                                           |
| `-13006` | `presenceInvalidStateKey`              | Invalid state key.                                                                                                                                                                                                         |
| `-13008` | `presenceStateKeySizeOverflow`         | State key length exceeds the limit.                                                                                                                                                                                        |
| `-13010` | `presenceStateDuplicateKey`            | Duplicate state key.                                                                                                                                                                                                       |
| `-13009` | `presenceStateValueSizeOverflow`       | State value length exceeds the limit.                                                                                                                                                                                      |
| `-13011` | `presenceUserNotExist`                 | The user does not exist.                                                                                                                                                                                                   |
| `-13012` | `presenceOperationTimeout`             | Presence operation timed out.                                                                                                                                                                                              |
| `-13013` | `presenceOperationFailed`              | Presence operation failed.                                                                                                                                                                                                 |
| `-14001` | `lockOperationFailed`                  | Lock operation failed.                                                                                                                                                                                                     |
| `-14002` | `lockOperationTimeout`                 | Lock operation timed out.                                                                                                                                                                                                  |
| `-14003` | `lockOperationPerforming`              | Lock operation in progress.                                                                                                                                                                                                |
| `-14004` | `lockAlreadyExist`                     | The Lock already exists.                                                                                                                                                                                                   |
| `-14005` | `lockInvalidName`                      | Invalid Lock name.                                                                                                                                                                                                         |
| `-14006` | `lockNotAcquired`                      | The Lock has not been acquired.                                                                                                                                                                                            |
| `-14007` | `lockAcquireFailed`                    | Failed to acquire the Lock.                                                                                                                                                                                                |
| `-14008` | `lockNotExist`                         | The Lock does not exist.                                                                                                                                                                                                   |
| `-14009` | `lockNotAvailable`                     | Lock service is not available.                                                                                                                                                                                             |
| `-15001` | `historyOperationFailed`               | History message operation failed.                                                                                                                                                                                          |
| `-15003` | `historyOperationTimeout`              | History message operation timed out.                                                                                                                                                                                       |
| `-15005` | `historyNotAvailable`                  | History message service is not available.                                                                                                                                                                                  |

If the above measures do not resolve your issue, or if you need assistance with a solution, please compile your request and send it via email to: [rtm-support@agora.io](mailto\:rtm-support@agora.io). We will contact you as soon as we receive your message.
