# SkillPatch skill: azure-communication-chat-java

This skill provides comprehensive guidance for building real-time chat applications using the Azure Communication Services Chat Java SDK. It covers client creation, thread management, sending and retrieving messages, managing participants, and working with read receipts and typing notifications. The skill includes ready-to-use Java code snippets and Maven dependency configuration.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/azure-communication-chat-java
curl -sSL https://skillpatch.dev/install_skill/azure-communication-chat-java | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (2)

- `SKILL.md`
- `references/examples.md`


### `SKILL.md`

````markdown
---
name: azure-communication-chat-java
description: Build real-time chat applications with Azure Communication Services Chat Java SDK. Use when implementing chat threads, messaging, participants, read receipts, typing notifications, or real-time chat features.
license: MIT
metadata:
  author: Microsoft
  version: "1.0.0"
  package: com.azure:azure-communication-chat
---

# Azure Communication Chat (Java)

Build real-time chat applications with thread management, messaging, participants, and read receipts.

## Installation

```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-communication-chat</artifactId>
    <version>1.6.0</version>
</dependency>
```

## Client Creation

```java
import com.azure.communication.chat.ChatClient;
import com.azure.communication.chat.ChatClientBuilder;
import com.azure.communication.chat.ChatThreadClient;
import com.azure.communication.common.CommunicationTokenCredential;

// ChatClient requires a CommunicationTokenCredential (user access token)
String endpoint = "https://<resource>.communication.azure.com";
String userAccessToken = "<user-access-token>";

CommunicationTokenCredential credential = new CommunicationTokenCredential(userAccessToken);

ChatClient chatClient = new ChatClientBuilder()
    .endpoint(endpoint)
    .credential(credential)
    .buildClient();

// Async client
ChatAsyncClient chatAsyncClient = new ChatClientBuilder()
    .endpoint(endpoint)
    .credential(credential)
    .buildAsyncClient();
```

## Key Concepts

| Class | Purpose |
|-------|---------|
| `ChatClient` | Create/delete chat threads, get thread clients |
| `ChatThreadClient` | Operations within a thread (messages, participants, receipts) |
| `ChatParticipant` | User in a chat thread with display name |
| `ChatMessage` | Message content, type, sender info, timestamps |
| `ChatMessageReadReceipt` | Read receipt tracking per participant |

## Create Chat Thread

```java
import com.azure.communication.chat.models.*;
import com.azure.communication.common.CommunicationUserIdentifier;
import java.util.ArrayList;
import java.util.List;

// Define participants
List<ChatParticipant> participants = new ArrayList<>();

ChatParticipant participant1 = new ChatParticipant()
    .setCommunicationIdentifier(new CommunicationUserIdentifier("<user-id-1>"))
    .setDisplayName("Alice");

ChatParticipant participant2 = new ChatParticipant()
    .setCommunicationIdentifier(new CommunicationUserIdentifier("<user-id-2>"))
    .setDisplayName("Bob");

participants.add(participant1);
participants.add(participant2);

// Create thread
CreateChatThreadOptions options = new CreateChatThreadOptions("Project Discussion")
    .setParticipants(participants);

CreateChatThreadResult result = chatClient.createChatThread(options);
String threadId = result.getChatThread().getId();

// Get thread client for operations
ChatThreadClient threadClient = chatClient.getChatThreadClient(threadId);
```

## Send Messages

```java
// Send text message
SendChatMessageOptions messageOptions = new SendChatMessageOptions()
    .setContent("Hello, team!")
    .setSenderDisplayName("Alice")
    .setType(ChatMessageType.TEXT);

SendChatMessageResult sendResult = threadClient.sendMessage(messageOptions);
String messageId = sendResult.getId();

// Send HTML message
SendChatMessageOptions htmlOptions = new SendChatMessageOptions()
    .setContent("<strong>Important:</strong> Meeting at 3pm")
    .setType(ChatMessageType.HTML);

threadClient.sendMessage(htmlOptions);
```

## Get Messages

```java
import com.azure.core.util.paging.PagedIterable;

// List all messages
PagedIterable<ChatMessage> messages = threadClient.listMessages();

for (ChatMessage message : messages) {
    System.out.println("ID: " + message.getId());
    System.out.println("Type: " + message.getType());
    System.out.println("Content: " + message.getContent().getMessage());
    System.out.println("Sender: " + message.getSenderDisplayName());
    System.out.println("Created: " + message.getCreatedOn());
    
    // Check if edited or deleted
    if (message.getEditedOn() != null) {
        System.out.println("Edited: " + message.getEditedOn());
    }
    if (message.getDeletedOn() != null) {
        System.out.println("Deleted: " + message.getDeletedOn());
    }
}

// Get specific message
ChatMessage message = threadClient.getMessage(messageId);
```

## Update and Delete Messages

```java
// Update message
UpdateChatMessageOptions updateOptions = new UpdateChatMessageOptions()
    .setContent("Updated message content");

threadClient.updateMessage(messageId, updateOptions);

// Delete message
threadClient.deleteMessage(messageId);
```

## Manage Participants

```java
// List participants
PagedIterable<ChatParticipant> participants = threadClient.listParticipants();

for (ChatParticipant participant : participants) {
    CommunicationUserIdentifier user = 
        (CommunicationUserIdentifier) participant.getCommunicationIdentifier();
    System.out.println("User: " + user.getId());
    System.out.println("Display Name: " + participant.getDisplayName());
}

// Add participants
List<ChatParticipant> newParticipants = new ArrayList<>();
newParticipants.add(new ChatParticipant()
    .setCommunicationIdentifier(new CommunicationUserIdentifier("<new-user-id>"))
    .setDisplayName("Charlie")
    .setShareHistoryTime(OffsetDateTime.now().minusDays(7))); // Share last 7 days

threadClient.addParticipants(newParticipants);

// Remove participant
CommunicationUserIdentifier userToRemove = new CommunicationUserIdentifier("<user-id>");
threadClient.removeParticipant(userToRemove);
```

## Read Receipts

```java
// Send read receipt
threadClient.sendReadReceipt(messageId);

// Get read receipts
PagedIterable<ChatMessageReadReceipt> receipts = threadClient.listReadReceipts();

for (ChatMessageReadReceipt receipt : receipts) {
    System.out.println("Message ID: " + receipt.getChatMessageId());
    System.out.println("Read by: " + receipt.getSenderCommunicationIdentifier());
    System.out.println("Read at: " + receipt.getReadOn());
}
```

## Typing Notifications

```java
import com.azure.communication.chat.models.TypingNotificationOptions;

// Send typing notification
TypingNotificationOptions typingOptions = new TypingNotificationOptions()
    .setSenderDisplayName("Alice");

threadClient.sendTypingNotificationWithResponse(typingOptions, Context.NONE);

// Simple typing notification
threadClient.sendTypingNotification();
```

## Thread Operations

```java
// Get thread properties
ChatThreadProperties properties = threadClient.getProperties();
System.out.println("Topic: " + properties.getTopic());
System.out.println("Created: " + properties.getCreatedOn());

// Update topic
threadClient.updateTopic("New Project Discussion Topic");

// Delete thread
chatClient.deleteChatThread(threadId);
```

## List Threads

```java
// List all chat threads for the user
PagedIterable<ChatThreadItem> threads = chatClient.listChatThreads();

for (ChatThreadItem thread : threads) {
    System.out.println("Thread ID: " + thread.getId());
    System.out.println("Topic: " + thread.getTopic());
    System.out.println("Last message: " + thread.getLastMessageReceivedOn());
}
```

## Pagination

```java
import com.azure.core.http.rest.PagedResponse;

// Paginate through messages
int maxPageSize = 10;
ListChatMessagesOptions listOptions = new ListChatMessagesOptions()
    .setMaxPageSize(maxPageSize);

PagedIterable<ChatMessage> pagedMessages = threadClient.listMessages(listOptions);

pagedMessages.iterableByPage().forEach(page -> {
    System.out.println("Page status code: " + page.getStatusCode());
    page.getElements().forEach(msg -> 
        System.out.println("Message: " + msg.getContent().getMessage()));
});
```

## Error Handling

```java
import com.azure.core.exception.HttpResponseException;

try {
    threadClient.sendMessage(messageOptions);
} catch (HttpResponseException e) {
    switch (e.getResponse().getStatusCode()) {
        case 401:
            System.out.println("Unauthorized - check token");
            break;
        case 403:
            System.out.println("Forbidden - user not in thread");
            break;
        case 404:
            System.out.println("Thread not found");
            break;
        default:
            System.out.println("Error: " + e.getMessage());
    }
}
```

## Message Types

| Type | Description |
|------|-------------|
| `TEXT` | Regular chat message |
| `HTML` | HTML-formatted message |
| `TOPIC_UPDATED` | System message - topic changed |
| `PARTICIPANT_ADDED` | System message - participant joined |
| `PARTICIPANT_REMOVED` | System message - participant left |

## Environment Variables

```bash
AZURE_COMMUNICATION_ENDPOINT=https://<resource>.communication.azure.com
AZURE_COMMUNICATION_USER_TOKEN=<user-access-token>
```

## Best Practices

1. **Token Management** - User tokens expire; implement refresh logic with `CommunicationTokenRefreshOptions`
2. **Pagination** - Use `listMessages(options)` with `maxPageSize` for large threads
3. **Share History** - Set `shareHistoryTime` when adding participants to control message visibility
4. **Message Types** - Filter system messages (`PARTICIPANT_ADDED`, etc.) from user messages
5. **Read Receipts** - Send receipts only when messages are actually viewed by user

## Trigger Phrases

- "chat application Java", "real-time messaging Java"
- "chat thread", "chat participants", "chat messages"
- "read receipts", "typing notifications"
- "Azure Communication Services chat"

````


### `references/examples.md`

````markdown
# Azure Communication Chat SDK for Java - Examples

Comprehensive code examples for the Azure Communication Chat SDK for Java.

## Table of Contents
- [Maven Dependency](#maven-dependency)
- [Client Creation](#client-creation)
- [Creating Chat Threads](#creating-chat-threads)
- [Sending Messages](#sending-messages)
- [Listing Messages](#listing-messages)
- [Adding and Removing Participants](#adding-and-removing-participants)
- [Updating Thread Topic](#updating-thread-topic)
- [Typing Notifications](#typing-notifications)
- [Read Receipts](#read-receipts)
- [Async Client Patterns](#async-client-patterns)
- [Error Handling](#error-handling)

## Maven Dependency

```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-communication-chat</artifactId>
    <version>1.6.4</version>
</dependency>

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-communication-common</artifactId>
    <version>1.3.8</version>
</dependency>
```

Using Azure SDK BOM:

```xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-sdk-bom</artifactId>
            <version>{bom_version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-communication-chat</artifactId>
    </dependency>
</dependencies>
```

## Client Creation

### Synchronous ChatClient

```java
import com.azure.communication.chat.ChatClient;
import com.azure.communication.chat.ChatClientBuilder;
import com.azure.communication.common.CommunicationTokenCredential;

String endpoint = "https://<RESOURCE_NAME>.communication.azure.com";
String userAccessToken = "<USER_ACCESS_TOKEN>";

CommunicationTokenCredential credential = new CommunicationTokenCredential(userAccessToken);

ChatClient chatClient = new ChatClientBuilder()
    .endpoint(endpoint)
    .credential(credential)
    .buildClient();
```

### Asynchronous ChatAsyncClient

```java
import com.azure.communication.chat.ChatAsyncClient;
import com.azure.communication.chat.ChatClientBuilder;

ChatAsyncClient chatAsyncClient = new ChatClientBuilder()
    .endpoint(endpoint)
    .credential(credential)
    .buildAsyncClient();
```

### Get ChatThreadClient

```java
import com.azure.communication.chat.ChatThreadClient;

String chatThreadId = "19:abc123...@thread.v2";
ChatThreadClient chatThreadClient = chatClient.getChatThreadClient(chatThreadId);
```

## Creating Chat Threads

```java
import com.azure.communication.chat.models.ChatParticipant;
import com.azure.communication.chat.models.CreateChatThreadOptions;
import com.azure.communication.chat.models.CreateChatThreadResult;
import com.azure.communication.common.CommunicationUserIdentifier;

String userId1 = "<USER_Id1>";
ChatParticipant firstParticipant = new ChatParticipant()
    .setCommunicationIdentifier(new CommunicationUserIdentifier(userId1))
    .setDisplayName("Display Name 1");

String userId2 = "<USER_Id2>";
ChatParticipant secondParticipant = new ChatParticipant()
    .setCommunicationIdentifier(new CommunicationUserIdentifier(userId2))
    .setDisplayName("Display Name 2");

CreateChatThreadOptions createOptions = new CreateChatThreadOptions("Topic")
    .addParticipant(firstParticipant)
    .addParticipant(secondParticipant);

CreateChatThreadResult result = chatClient.createChatThread(createOptions);
String chatThreadId = result.getChatThread().getId();

// Get thread client
ChatThreadClient chatThreadClient = chatClient.getChatThreadClient(chatThreadId);
```

### Using setParticipants

```java
import java.util.ArrayList;
import java.util.List;

List<ChatParticipant> participants = new ArrayList<>();
participants.add(firstParticipant);
participants.add(secondParticipant);

CreateChatThreadOptions createOptions = new CreateChatThreadOptions("Topic")
    .setParticipants(participants);

CreateChatThreadResult result = chatClient.createChatThread(createOptions);
```

## Sending Messages

### Send Text Message

```java
import com.azure.communication.chat.models.ChatMessageType;
import com.azure.communication.chat.models.SendChatMessageOptions;
import com.azure.communication.chat.models.SendChatMessageResult;

SendChatMessageOptions sendOptions = new SendChatMessageOptions()
    .setContent("Message content")
    .setType(ChatMessageType.TEXT)
    .setSenderDisplayName("Sender Display Name");

SendChatMessageResult sendResult = chatThreadClient.sendMessage(sendOptions);
String chatMessageId = sendResult.getId();
```

### Send Message with Metadata

```java
import java.util.HashMap;
import java.util.Map;

Map<String, String> metadata = new HashMap<>();
metadata.put("hasAttachment", "true");
metadata.put("attachmentUrl", "https://contoso.com/files/attachment.docx");

SendChatMessageOptions sendOptions = new SendChatMessageOptions()
    .setType(ChatMessageType.TEXT)
    .setContent("Please take a look at the attachment")
    .setSenderDisplayName("Sender")
    .setMetadata(metadata);

SendChatMessageResult sendResult = chatThreadClient.sendMessage(sendOptions);
```

## Listing Messages

### List All Messages

```java
import com.azure.communication.chat.models.ChatMessage;

chatThreadClient.listMessages().forEach(message -> {
    System.out.printf("Message id: %s%n", message.getId());
    System.out.printf("Content: %s%n", message.getContent().getMessage());
});
```

### List with Pagination

```java
import com.azure.core.http.rest.PagedIterable;

PagedIterable<ChatMessage> messages = chatThreadClient.listMessages();
messages.iterableByPage().forEach(page -> {
    System.out.printf("Status: %d%n", page.getStatusCode());
    page.getElements().forEach(message ->
        System.out.printf("Message: %s%n", message.getId()));
});
```

### Get Specific Message

```java
ChatMessage message = chatThreadClient.getMessage(chatMessageId);
System.out.printf("Content: %s%n", message.getContent().getMessage());
```

### Update Message

```java
import com.azure.communication.chat.models.UpdateChatMessageOptions;

UpdateChatMessageOptions updateOptions = new UpdateChatMessageOptions()
    .setContent("Updated message content");

chatThreadClient.updateMessage(chatMessageId, updateOptions);
```

### Delete Message

```java
chatThreadClient.deleteMessage(chatMessageId);
```

### Check Message Status

```java
chatThreadClient.listMessages().forEach(message -> {
    // Check if deleted
    if (message.getDeletedOn() != null) {
        System.out.println("Deleted at: " + message.getDeletedOn());
    }
    
    // Check if edited
    if (message.getEditedOn() != null) {
        System.out.println("Edited at: " + message.getEditedOn());
    }
    
    // Message type
    System.out.println("Type: " + message.getType());
});
```

## Adding and Removing Participants

### Add Participants

```java
import java.util.ArrayList;
import java.util.List;

List<ChatParticipant> participants = new ArrayList<>();

ChatParticipant newParticipant = new ChatParticipant()
    .setCommunicationIdentifier(new CommunicationUserIdentifier("<USER_Id3>"))
    .setDisplayName("Display Name 3");

participants.add(newParticipant);

chatThreadClient.addParticipants(participants);
```

### Add Participant with History Sharing

```java
import java.time.OffsetDateTime;

ChatParticipant newParticipant = new ChatParticipant()
    .setCommunicationIdentifier(new CommunicationUserIdentifier("<USER_ID>"))
    .setDisplayName("New Participant")
    .setShareHistoryTime(OffsetDateTime.MIN);  // Share from beginning

List<ChatParticipant> participants = new ArrayList<>();
participants.add(newParticipant);
chatThreadClient.addParticipants(participants);
```

### Remove Participant

```java
CommunicationUserIdentifier user = new CommunicationUserIdentifier("<USER_ID>");
chatThreadClient.removeParticipant(user);
```

### List Participants

```java
import com.azure.communication.chat.models.ChatParticipant;

chatThreadClient.listParticipants().forEach(participant -> {
    System.out.println("Participant: " + participant.getDisplayName());
});
```

## Updating Thread Topic

```java
chatThreadClient.updateTopic("New Topic");
```

## Typing Notifications

### Send Typing Notification

```java
chatThreadClient.sendTypingNotification();
```

### Send with Options

```java
import com.azure.communication.chat.models.TypingNotificationOptions;

TypingNotificationOptions options = new TypingNotificationOptions()
    .setSenderDisplayName("Sender Name");

chatThreadClient.sendTypingNotificationWithResponse(options, null);
```

## Read Receipts

### Send Read Receipt

```java
chatThreadClient.sendReadReceipt(chatMessageId);
```

### List Read Receipts

```java
import com.azure.communication.chat.models.ChatMessageReadReceipt;

chatThreadClient.listReadReceipts().forEach(receipt -> {
    System.out.printf("Message ID: %s, Read by: %s at %s%n",
        receipt.getChatMessageId(),
        receipt.getSenderCommunicationIdentifier().getRawId(),
        receipt.getReadOn());
});
```

## Async Client Patterns

### Create Thread Async

```java
chatAsyncClient.createChatThread(createOptions)
    .subscribe(
        result -> System.out.println("Thread ID: " + result.getChatThread().getId()),
        error -> System.err.println("Error: " + error.getMessage())
    );
```

### Send Message Async

```java
ChatThreadAsyncClient asyncThreadClient = chatAsyncClient.getChatThreadClient(chatThreadId);

asyncThreadClient.sendMessage(sendOptions)
    .subscribe(
        result -> System.out.println("Message ID: " + result.getId()),
        error -> System.err.println("Error: " + error.getMessage())
    );
```

### List Messages Async

```java
asyncThreadClient.listMessages()
    .subscribe(
        message -> System.out.println("Message: " + message.getId()),
        error -> System.err.println("Error: " + error.getMessage())
    );
```

### Chain Operations Async

```java
chatAsyncClient.createChatThread(createOptions)
    .flatMap(result -> {
        String threadId = result.getChatThread().getId();
        return chatAsyncClient.getChatThreadClient(threadId).sendMessage(sendOptions);
    })
    .subscribe(
        sendResult -> System.out.println("Message sent: " + sendResult.getId()),
        error -> System.err.println("Error: " + error.getMessage())
    );
```

## Error Handling

### Sync Error Handling

```java
import com.azure.core.exception.HttpResponseException;

try {
    chatThreadClient.sendMessage(sendOptions);
} catch (HttpResponseException e) {
    System.err.println("HTTP Status: " + e.getResponse().getStatusCode());
    System.err.println("Error: " + e.getMessage());
} catch (Exception e) {
    System.err.println("Unexpected error: " + e.getMessage());
}
```

### Async Error Handling

```java
asyncThreadClient.sendMessage(sendOptions)
    .subscribe(
        result -> System.out.println("Success"),
        error -> {
            if (error instanceof HttpResponseException) {
                HttpResponseException httpError = (HttpResponseException) error;
                System.err.println("HTTP error: " + httpError.getResponse().getStatusCode());
            } else {
                System.err.println("Error: " + error.getMessage());
            }
        }
    );
```

### Common Error Scenarios

| Status Code | Cause |
|-------------|-------|
| 401 | Invalid or expired token |
| 403 | User not in thread |
| 404 | Thread or message not found |
| 429 | Rate limited |

````
