Direct Chat
Implement durable messages, online delivery, offline sync, unread state, and multi-device recovery between two users.
This tutorial uses alice and bob. The target outcome is that both endpoints connect with their own product identity, Alice publishes one durable message to Bob's person Channel, Bob can receive it online or recover it after reconnect, and Bob can manage unread state for their account.
Before you start
- Complete Start a Single-node Cluster or prepare a test cluster.
- Understand the current Beta limits in Authentication.
- Run HTTP examples only locally or inside a protected product-service network.
1. Establish product identities
The product service owns account login, friendship, bans, and content policy. Choose stable UIDs for the two test users; do not substitute a nickname, connection ID, or device ID for the UID.
If the Web two-user example is running, its BFF has already prepared Alice and Bob; continue at step 2. For your own integration, store development tokens from the trusted backend. This example uses the Web device category device_flag=1; the client must use the same category:
curl -sS http://127.0.0.1:5001/user/token \
-H 'Content-Type: application/json' \
-d '{"uid":"alice","token":"alice-local-only","device_flag":1,"device_level":1}'The built-in check is not a complete production identity system
The default v3 Beta Gateway requires the CONNECT token to exactly match the stored token for the same UID and device category. Before production, protect /user/token, implement expiry and rotation policy, and prove that invalid, revoked, and product-expired tokens cannot connect.
Store separate metadata for Bob, then connect both clients with their own UID, device identity, and token. If no SDK integration exists yet, use the embedded Chat Demo to validate two browser sessions first.
2. Send a person message
The client uses the peer UID as channel_id and sets channel_type=1. Do not construct or persist the internal canonical person Channel ID; the server normalizes it from sender and receiver UIDs.
A trusted product service can send with the same semantics:
This example uses the built-in SDK text format, matching the Web example. Before encoding, the JSON is:
{"type":1,"content":"hello Bob"}The payload below is the UTF-8 Base64 encoding of this JSON. Custom types and fields need a matching client decoder; see Custom Messages.
curl -sS http://127.0.0.1:5001/message/send \
-H 'Content-Type: application/json' \
-d '{
"from_uid":"alice",
"channel_id":"bob",
"channel_type":1,
"client_msg_no":"dm-alice-bob-0001",
"payload":"eyJ0eXBlIjoxLCJjb250ZW50IjoiaGVsbG8gQm9iIn0="
}'Retain a stable, unique client_msg_no. If the network outcome is unclear, retry the same logical send with the same number instead of creating a duplicate with a new number. HTTP reason=1 means the durable send reached durable Channel commit; it does not mean every Bob device received it.
3. Verify online and offline results
An online Bob should receive RECV and send RECVACK after processing it. Verify the durable log through the compatibility sync route; a person-Channel query still uses the peer UID:
curl -sS http://127.0.0.1:5001/channel/messagesync \
-H 'Content-Type: application/json' \
-d '{
"login_uid":"bob",
"channel_id":"alice",
"channel_type":1,
"start_message_seq":0,
"limit":20,
"pull_mode":1
}'The response maps channel_id back to alice; message_seq is ordered only within this person Channel. Merge online delivery and reconnect sync by message ID, client_msg_no, and Channel sequence while tolerating duplicate arrival.
4. Inspect conversation and unread state
curl -sS http://127.0.0.1:5001/conversation/list \
-H 'Content-Type: application/json' \
-d '{"uid":"bob","limit":20}'Bob's item uses channel_id=alice. unread is computed from Bob's read position and visible messages in the Channel; it is not the total number of deliveries across devices. After the user confirms the current conversation is read, call through the trusted boundary:
curl -sS http://127.0.0.1:5001/conversations/clearUnread \
-H 'Content-Type: application/json' \
-d '{"uid":"bob","channel_id":"alice","channel_type":1}'clearUnread advances Bob's read position (read_seq) to the newest committed ordinary message. Messages arriving during the operation can also have their unread badge cleared; this does not prove to Alice which messages Bob actually read. Other devices using the same UID see the update after synchronization.
5. Test multi-device recovery
- Connect a second Bob Session with a different
device_id. - Define the product's primary/secondary-device conflict policy; one UID does not imply one connection.
- Disconnect one device, then publish another durable message.
- Reconnect and recover the missing sequence through SDK sync or
/channel/messagesync. - Verify that online delivery, offline recovery, and account unread state are not treated as one completion signal.
Before launch, also test friendship removal, denylist policy, token revocation, disconnect retries, duplicate sends, direct-Channel distribution, and duplicate webhook consumption. Continue with Groups & Large Groups or Messaging.