Click or drag to resize

Chat Projects

Chat Projects

This topic covers the scripting API for chat projects. The chat-specific data lives on a separate object model accessed through ChatProjectItem, while the standard document loop still applies for iterating and committing changes.

Common Use Cases

Building custom chat connectors — ReadySuite has built-in connectors for common platforms, but scripts can import data from any chat format. A script reads the source data (CSV, JSON, database, proprietary export), creates participants, conversations, and events through the API, and produces a fully structured chat project. This is the primary way to add support for new or proprietary messaging formats.

Enriching and overlaying existing chat data — Scripts can iterate over an existing chat project to classify messages, flag content, assign importance, update metadata, or populate custom fields. Because each chat event is backed by a DocumentItem, you can combine standard field operations with chat-specific modifications in the same document loop.

Data cleanup and validation — Scripts can audit chat data for integrity issues like missing senders, orphaned threads, or incomplete metadata, and fix or report problems automatically.

Accessing the Chat Project

Use ProjectItem.GetChatProject() to access the chat API. Always check IsChatProject first — calling GetChatProject() on a document project throws a NotSupportedException:

C#
var project = script.GetProject();

if (project.IsChatProject)
{
    var chatProject = project.GetChatProject();
    // Work with chat data
}
Data Model

Chat projects are organized as a hierarchy:

C#
ChatProjectItem
├── Participants (ChatParticipantItem)
└── Sources (ChatSourceItem)
    └── Conversations (ChatConversationItem)
        └── Events (ChatEventItem) ── tied to a DocumentItem
            ├── Reactions (ChatEventReactionItem)
            ├── Attachments (ChatEventAttachmentItem)
            ├── Edits (ChatEventEditItem)
            └── Read Receipts (ChatEventReadReceiptItem)

Participants are global to the project — the same participant can appear in multiple conversations across multiple sources.

Sources are containers of conversations. A project may have one or many sources (e.g. one per data export or platform).

Conversations represent a channel or direct message thread. Each conversation has a type, platform, and its own set of participants.

Events are the individual messages, joins, leaves, or other activities. Each event is tied to a DocumentItem, so you can still use the document loop to iterate them.

Participants

Participants represent the people in a chat project. Each has a unique ID across the entire project.

Creating Participants
C#
var chatProject = script.GetProject().GetChatProject();

var alice = chatProject.CreateParticipant(
    id: "user-001",
    displayName: "Alice Smith",
    email: "alice@example.com");

var bob = chatProject.CreateParticipant(
    id: "user-002",
    displayName: "Bob Jones");

All parameters are optional — omitting id auto-generates a GUID.

Retrieving Participants
C#
var participants = chatProject.GetParticipants();
var alice = chatProject.GetParticipant("user-001");
ChatParticipantItem Properties
PropertyTypeDescription
IdstringUnique identifier
DisplayNamestring?Display name
Emailstring?Email address
AvatarPathstring?Path to avatar image
OtherPropertiesIDictionary<string, string>Custom metadata
Sources and Conversations
Creating a Source and Conversation
C#
var source = chatProject.CreateChatSource();

var conversation = source.CreateConversation(
    id: "general",
    displayName: "General",
    platform: "Slack",
    type: ChatConversationItemType.Channel);
Conversation Types
ValueDescription
ChannelA channel conversation (e.g. Slack channel, Teams channel)
DirectA direct or group message
OtherOther or unknown type
Adding Participants to a Conversation

Conversations track which participants are involved. Add participants to the Participants collection:

C#
conversation.Participants.Add(alice);
conversation.Participants.Add(bob);
Setting a Custodian

Each conversation can have a custodian — the person whose perspective the conversation is captured from:

C#
conversation.Custodian = alice;
ChatConversationItem Properties
PropertyTypeDescription
IdstringUnique within the source
DisplayNamestring?Conversation name
PlatformstringPlatform identifier (e.g. "Slack", "Teams")
TypeChatConversationItemTypeChannel, Direct, or Other
ParticipantsICollection<ChatParticipantItem>Conversation participants
CustodianChatParticipantItem?Custodian of the conversation
OtherPropertiesIDictionary<string, string>Custom metadata
Events

Events are the core data in a chat project — messages, joins, leaves, and other activity. Each event is tied to a DocumentItem.

Reading Events from Documents

When iterating documents in a chat project, use GetChatEvent() to access the event tied to each document:

C#
while (script.ReadDocument())
{
    var document = script.Document;
    var chatEvent = document.GetChatEvent();

    if (chatEvent != null)
    {
        var message = chatEvent.Message;
        var sender = chatEvent.Sender;
        var timestamp = chatEvent.Timestamp;
    }
}

GetChatEvent() returns null if the document has no associated chat event.

Creating Events

To create a new event, call CreateChatEvent() on a document. The event is automatically tied to that document:

C#
var newDoc = script.GetNewDocument("MSG-0001");
script.CreateDocument(newDoc);

var chatEvent = newDoc.CreateChatEvent(
    conversation: conversation,
    sender: alice,
    timestamp: DateTime.UtcNow);

chatEvent.Message = "Hello, world!";
script.UpdateDocument(newDoc);

A document can only have one chat event — calling CreateChatEvent() on a document that already has one throws an InvalidOperationException.

Modifying Events

Event properties are mutable. Update them directly and call UpdateDocument():

C#
while (script.ReadDocument())
{
    var document = script.Document;
    var chatEvent = document.GetChatEvent();

    if (chatEvent != null && chatEvent.Type == ChatEventItemType.Message)
    {
        chatEvent.Importance = ChatEventItemImportance.High;
        script.UpdateDocument(document);
    }
}
ChatEventItem Properties
PropertyTypeDescription
IdstringUnique event identifier
ConversationChatConversationItemThe conversation this event belongs to
ParentChatEventItem?Parent event (for threaded replies)
Messagestring?Message content
TypeChatEventItemTypeMessage, Disclaimer, Join, Leave, or Other
DirectionChatEventItemDirectionIncoming, Outgoing, or Other
SenderChatParticipantItem?Who sent the event
RecipientsIReadOnlyCollection<ChatParticipantItem>All conversation participants
TimestampDateTimeWhen the event was sent
ImportanceChatEventItemImportanceNormal, High, or Other
IsDeletedboolWhether the event was deleted
Event Types
ValueDescription
MessageA chat message
DisclaimerA disclaimer event
JoinA participant joined
LeaveA participant left
OtherOther or unknown event
Threaded Replies

Events can be nested using the Parent property. This is used for threaded replies (e.g. Slack threads):

C#
var reply = newDoc.CreateChatEvent(conversation, bob, DateTime.UtcNow);
reply.Message = "This is a reply.";
reply.Parent = parentEvent;

The parent event must belong to the same conversation. Changing an event's conversation resets Parent to null.

Reactions

Events can have emoji reactions:

C#
var reaction = chatEvent.CreateReaction("👍");
reaction.Count = 3;
reaction.Participants.Add(alice);
reaction.Participants.Add(bob);
C#
var reactions = chatEvent.GetReactions();
chatEvent.RemoveReaction(reaction);
chatEvent.ClearReactions();
Attachments

Events can have file attachments or external resource links:

C#
// Local file attachment
var attachment = chatEvent.CreateAttachment("C:\\files\\report.pdf");

// External resource (URL or network path)
var extAttachment = chatEvent.CreateExternalResourceAttachment("https://example.com/file.pdf");

The copyFileToChatSource parameter (default true) controls whether the file is copied into the chat source directory:

C#
var attachment = chatEvent.CreateAttachment(filePath, copyFileToChatSource: false);
ChatEventAttachmentItem Properties
PropertyTypeDescription
IdstringUnique identifier
DisplayNamestring?Display name
Pathstring?Local file path (null for external resources)
IsExternalResourceboolWhether this is an external resource
ExternalResourcePathstring?External resource path

Attachments can be copied or moved:

C#
attachment.CopyTo("C:\\output\\report.pdf");
attachment.MoveTo("C:\\output\\report.pdf");
Edit History

Track message edits:

C#
var edit = chatEvent.CreateEdit(
    timestamp: DateTime.UtcNow,
    newMessage: "Updated message",
    oldMessage: "Original message",
    participant: alice);
C#
var edits = chatEvent.GetEdits();
chatEvent.ClearEdits();
Read Receipts

Track when participants read or received a message:

C#
var receipt = chatEvent.CreateReadReceipt(
    participant: bob,
    timestamp: DateTime.UtcNow,
    type: ChatEventReadReceiptItemType.Read);
Receipt TypeDescription
ReadThe message was read
DeliveredThe message was delivered but may not be read
OtherOther or unknown type
Custom Metadata

Most chat types expose an OtherProperties dictionary for storing platform-specific or custom metadata that doesn't fit the standard schema:

C#
participant.OtherProperties["sourceSystem"] = "LegacyChat v2.1";
conversation.OtherProperties["originalId"] = "slack-C123456";

OtherProperties is available on ChatParticipantItem, ChatConversationItem, ChatEventReactionItem, ChatEventAttachmentItem, ChatEventEditItem, and ChatEventReadReceiptItem.

Validation Rules

The chat API enforces several constraints:

  • When setting an event's Conversation, the Sender must be a participant of the target conversation
  • Changing an event's Conversation resets Parent to null
  • A Parent event must belong to the same conversation as the child
  • Participants referenced in reactions, edits, and read receipts must be participants of the event's conversation
  • A document can only have one chat event
Examples
Custom Connector (Importing Chat Data)

This example builds a chat project from scratch — the pattern used when writing a custom connector for a format ReadySuite doesn't natively support. The script reads source data, creates the project structure, and ties each message to a document:

C#
public override void Run(IScriptContext script)
{
    base.Run(script);

    var chatProject = script.GetProject().GetChatProject();

    // Create participants
    var alice = chatProject.CreateParticipant(
        id: "user-001", displayName: "Alice Smith", email: "alice@example.com");
    var bob = chatProject.CreateParticipant(
        id: "user-002", displayName: "Bob Jones");

    // Create source and conversation
    var source = chatProject.CreateChatSource();
    var conversation = source.CreateConversation(
        id: "general",
        displayName: "General",
        platform: "CustomChat",
        type: ChatConversationItemType.Channel);

    conversation.Participants.Add(alice);
    conversation.Participants.Add(bob);
    conversation.Custodian = alice;

    // Create a document and event for each message
    var newDoc = script.GetNewDocument("MSG-0001");
    script.CreateDocument(newDoc);

    var chatEvent = newDoc.CreateChatEvent(conversation, alice, DateTime.UtcNow);
    chatEvent.Message = "Hello, team!";
    chatEvent.Direction = ChatEventItemDirection.Outgoing;

    script.UpdateDocument(newDoc);
}
Enriching Existing Chat Data

This example iterates over an existing chat project and flags messages that match a keyword — a common overlay pattern for classification or review workflows:

C#
public override void Run(IScriptContext script)
{
    base.Run(script);

    var project = script.GetProject();
    if (!project.IsChatProject)
    {
        script.AddWarning("This script only works with chat projects.");
        return;
    }

    while (script.ReadDocument())
    {
        var document = script.Document;
        var chatEvent = document.GetChatEvent();

        if (chatEvent == null || chatEvent.Type != ChatEventItemType.Message)
            continue;

        if (!string.IsNullOrEmpty(chatEvent.Message)
            && chatEvent.Message.Contains(options.Keyword, StringComparison.OrdinalIgnoreCase))
        {
            chatEvent.Importance = ChatEventItemImportance.High;
            document.SetFieldData(options.FlagField, "true");
            script.UpdateDocument(document);
        }
    }
}