Skip to main content
im.chats manages direct and group conversations. im.groups handles group-specific operations like renaming, adding participants, and changing icons.

Chats

Create a chat

const { chat, sendReceipt } = await im.chats.create(["+1234567890"], {
  message: "Hey!",
  service: "iMessage",
  subject: "Optional subject",
  effectId: MessageEffect.confetti,
  clientMessageId: "my-idempotency-key",
});
sendReceipt is present only when an initial message was provided. CreateChatOptions
OptionTypeDescription
messagestringInitial message text to send
service"iMessage" | "SMS" | "RCS"Service to use
subjectstringSubject line
effectIdstringSend effect ID
clientMessageIdstringIdempotency key

Get a chat

const chat = await im.chats.get(chatGuid);
The returned Chat object contains:
interface Chat {
  readonly guid: ChatGuid;
  readonly chatIdentifier?: string;
  readonly displayName?: string;
  readonly groupId?: string;
  readonly participants: readonly AddressInfo[];
  readonly lastMessage?: Message;
  readonly isArchived: boolean;
  readonly isFiltered: boolean;
  readonly isGroup: boolean;
  readonly service: ChatServiceType;
  readonly unreadCount?: number;
  readonly _raw?: unknown;
}

Count chats

const total = await im.chats.count();
const totalWithArchived = await im.chats.count({ includeArchived: true });

Typing indicators

// Show typing bubble
await im.chats.startTyping(chatGuid);

// Stop typing bubble
await im.chats.stopTyping(chatGuid);

Share contact info

await im.chats.shareContactInfo(chatGuid);

Get participants

const participants = await im.chats.getParticipants(chatGuid);
// participants: AddressInfo[]

Leave a chat

await im.chats.leave(chatGuid);

Mark as read

await im.chats.markRead(chatGuid);

Real-time chat events

for await (const event of im.chats.subscribe()) {
  event.type;
  // "chat.created" | "chat.left" | "chat.readStatusChanged" | "chat.typingIndicator"
}

// Narrowed
for await (const event of im.chats.subscribe("chat.typingIndicator")) {
  console.log(event.isTyping, event.displayName);
}
Chat event types
TypeExtra fields
chat.createdchatGuid
chat.leftchatGuid
chat.readStatusChangedchatGuid, isRead
chat.typingIndicatorchatGuid, isTyping, displayName?

Groups

Rename a group

const updatedChat = await im.groups.setDisplayName(chatGuid, "New Group Name");

Manage participants

const updatedChat = await im.groups.addParticipant(chatGuid, "newperson@icloud.com");
const updatedChat2 = await im.groups.removeParticipant(chatGuid, "oldperson@icloud.com");

Group icons

// Get icon
const iconBytes = await im.groups.getIcon(chatGuid);
// iconBytes: Uint8Array | null

// Change icon (pass raw image bytes)
await im.groups.setIcon(chatGuid, imageBytes); // imageBytes: Uint8Array

// Remove icon
await im.groups.removeIcon(chatGuid);

Group backgrounds

// Get background
const bgInfo = await im.groups.getBackground(chatGuid);
// bgInfo: BackgroundInfo | null

// Set background (pass raw image bytes)
const bgInfo2 = await im.groups.setBackground(chatGuid, imageBytes); // imageBytes: Uint8Array
// bgInfo2: BackgroundInfo { channelGuid?, imageUrl?, backgroundId? }

// Remove background
await im.groups.removeBackground(chatGuid);

Group events

Group changes are emitted as a single group.changed event with a discriminated change field:
for await (const event of im.groups.subscribe()) {
  // event.type === "group.changed"
  switch (event.change.type) {
    case "renamed":
      console.log("New name:", event.change.name);
      break;
    case "participantAdded":
      console.log("Added:", event.change.address);
      break;
    case "participantRemoved":
      console.log("Removed:", event.change.address);
      break;
    case "iconChanged":
    case "iconRemoved":
    case "backgroundChanged":
    case "backgroundRemoved":
      // handle icon/background changes
      break;
  }
}