OpenChat API Overview
The OpenChat Bot SDK provides a comprehensive wrapper around the OpenChat API, making it easy to interact with OpenChat groups, channels, and communities from your Motoko bot.
OCService High-level service for making OpenChat API calls
OCApi Type definitions for all OpenChat API structures
OCTypes Additional type definitions and response structures
OCService Module
The OCService class (backend/OC/OCService.mo) provides the main interface for interacting with OpenChat:
public class OCServiceImpl()
Bot Registration
public func registerBot(
userIndexCanister : Text,
{ username : Text; displayName : ?Text }
) : async* Result.Result<OCApi.InitializeBotResponse, Text>
Usage:
let res = await* ocService.registerBot(
"4bkt6-4aaaa-aaaaf-aaaiq-cai",
{ username = "my_bot"; displayName = ?"My Bot" }
);
Bot registration requires 10 XDR (10 trillion cycles) to be attached to the call using Cycles.add<system>(BOT_REGISTRATION_FEE).
Response Handling
switch(res) {
case(#ok(#Success)) {
// Bot registered successfully
};
case(#ok(#AlreadyRegistered)) {
// Bot was already registered
};
case(#ok(#InsufficientCyclesProvided(required))) {
// Not enough cycles provided
};
case(#err(msg)) {
// API call failed
};
};
User Index Canister
The User Index Canister is the entry point for bot operations on OpenChat:
Canister ID: 4bkt6-4aaaa-aaaaf-aaaiq-cai
Key Operations
Registers a canister as an OpenChat bot. c2c_register_bot : ({
username : Text;
display_name : ?Text
}) -> async InitializeBotResponse
Fee: 10 XDR in cycles
Sets the bot’s avatar image. c2c_set_avatar : ({
avatar_id: ?Nat
}) -> async SetAvatarResponse
Max Size: 800 KB
Queries user information by ID or username. user : query ({
user_id : ?UserId;
username : ?Text
}) -> async UserSummaryResponse
Message Operations
Sending Messages to Groups
public func sendGroupMessage(
groupCanisterId : Text,
sender : Text,
senderDisplayName : ?Text,
content : OCApi.MessageContentInitial,
messageId : Nat,
threadIndexId : ?Nat32
) : async* Result.Result<OCApi.SendMessageResponse, Text>
Example:
let content = #Text({ text = "Hello from bot!" });
let messageId = generateUniqueId(); // Use Prng for random IDs
let res = await* ocService.sendGroupMessage(
"group-canister-id",
"my_bot",
?"My Bot",
content,
messageId,
null // Not in a thread
);
Sending Messages to Channels
public func sendChannelMessage(
communityCanisterId : Text,
channelId : Nat,
sender : Text,
senderDisplayName : ?Text,
content : OCApi.MessageContent,
messageId : Nat,
threadIndexId : ?Nat32
) : async* Result.Result<OCApi.SendMessageResponse, Text>
Example:
let res = await* ocService.sendChannelMessage(
"community-canister-id",
42, // Channel ID
"my_bot",
?"My Bot",
#Text({ text = "Channel message" }),
messageId,
null
);
The SDK automatically generates unique message IDs using the Prng library with time-based seeds to prevent collisions.
Message Content Types
The SDK supports various message content types:
public type MessageContentInitial = {
#Text : TextContent;
#Image : ImageContent;
#Video : VideoContent;
#Audio : AudioContent;
#File : FileContent;
#Poll : PollContent;
#Crypto : CryptoContent;
#Giphy : GiphyContent;
#GovernanceProposal : ProposalContent;
#Custom : CustomMessageContent;
// ... and more
};
Text Messages
Governance Proposals
#GovernanceProposal({
my_vote : ?Bool;
governance_canister_id : CanisterId;
proposal : {
#NNS : NnsProposal;
#SNS : SnsProposal;
};
})
Image Messages
#Image({
height : Nat32;
width : Nat32;
mime_type : Text;
blob_reference : ?BlobReference;
thumbnail_data : Text;
caption : ?Text;
})
Editing Messages
Edit Group Message
public func editGroupMessage(
groupCanisterId : Text,
messageId : MessageId,
threadRootIndex : ?MessageIndex,
newContent : MessageContentInitial
) : async* Result.Result<EditMessageResponse, Text>
Example:
let res = await* ocService.editGroupMessage(
"group-canister-id",
12345, // Message ID
null, // Not in a thread
#Text({ text = "Updated message" })
);
Edit Channel Message
public func editChannelMessage(
communityCanisterId : Text,
channelId : Nat,
messageId : MessageId,
threadRootIndex : ?MessageIndex,
newContent : MessageContentInitial
) : async* Result.Result<EditChannelMessageResponse, Text>
Message ID Tracking Use BotService’s saveMessageId() and getMessageId() methods to track message IDs for later editing or reference.
Group and Community Management
Joining Groups
public func joinGroup(
groupCanisterId : Text,
args : JoinGroupArgs
) : async* Result.Result<JoinGroupResponse, Text>
JoinGroupArgs Structure:
public type JoinGroupArgs = {
chat_id : Principal;
invite_code : ?Nat64;
correlation_id : Nat64;
};
Joining Communities
public func joinCommunity(
communityCanisterId : Text,
args : JoinCommunityArgs
) : async* Result.Result<JoinCommunityResponse, Text>
JoinCommunityArgs Structure:
public type JoinCommunityArgs = {
community_id : CommunityId;
user_id : UserId;
principal : Principal;
invite_code : ?Nat64;
is_platform_moderator : Bool;
is_bot : Bool;
diamond_membership_expires_at : ?Int;
verified_credential_args : ?VerifiedCredentialGateArgs;
};
Joining Channels
public func joinChannel(
communityCanisterId : Text,
args : JoinChannelArgs
) : async* Result.Result<JoinChannelResponse, Text>
Retrieving Messages
Get Messages by Index
public func messagesByMessageIndex(
groupCanisterId : Text,
args : MessagesByMessageIndexArgs
) : async* Result.Result<MessagesByMessageIndexResponse, Text>
Example:
let res = await* ocService.messagesByMessageIndex(
"group-canister-id",
{
thread_root_message_index = null;
messages = [10, 11, 12]; // Message indices
latest_known_update = null;
}
);
switch(res) {
case(#ok(#Success(response))) {
for (message in response.messages.vals()) {
// Process each message
};
};
case(_) { };
};
Group and Community Summaries
Get Public Group Summary
public func publicGroupSummary(
groupCanisterId : Text,
args : { invite_code : ?Nat64 }
) : async* Result.Result<PublicSummaryResponse, Text>
Returns:
Group metadata (name, description, avatar)
Member count
Latest message index
Local user index canister ID (needed for joining)
Access gate information
public func publicCommunitySummary(
communityCanisterId : Text,
args : { invite_code : ?Nat64 }
) : async* Result.Result<CommunitySummaryResponse, Text>
Returns:
Community metadata
Channel list
Member count
Local user index canister ID
Permissions and access gates
Local User Index Lookup
Many operations require knowing the Local User Index canister ID:
func lookupLocalUserIndex(group : Text) : async* Result.Result<Principal, Text> {
let res = await* ocService.publicGroupSummary(group, { invite_code = null });
switch(res) {
case(#ok(#Success(response))) {
#ok(response.summary.local_user_index_canister_id)
};
case(_) { #err("Failed to get local user index") };
};
};
The Local User Index canister ID is required for joining groups and communities. It’s obtained by querying the group/community summary first.
Error Handling
Common Response Variants
public type SendMessageResponse = {
#Success : { event_index : Nat32; message_index : Nat32 };
#ChannelNotFound;
#ThreadMessageNotFound;
#MessageEmpty;
#TextTooLong : Nat32;
#NotAuthorized;
#UserNotInCommunity;
#UserNotInChannel;
#UserSuspended;
#CommunityFrozen;
#RulesNotAccepted;
};
Example Error Handling
switch(await* ocService.sendGroupMessage(...)) {
case(#ok(#Success(data))) {
logService.logInfo("Message sent successfully", null);
};
case(#ok(#NotAuthorized)) {
logService.logError("Bot not authorized in group", null);
};
case(#ok(#TextTooLong(maxLen))) {
logService.logError("Message too long. Max: " # Nat32.toText(maxLen), null);
};
case(#err(msg)) {
logService.logError("API call failed: " # msg, null);
};
case(_) {
logService.logError("Unknown error", null);
};
};
Avatar Management
Bots can have custom avatars:
public type Document = {
id : Nat;
data : Blob;
mime_type : Text;
};
Setting an Avatar:
let avatar = {
id = 1;
data = myImageBlob;
mime_type = "image/png";
};
let res = await* ocService.setAvatar(
"4bkt6-4aaaa-aaaaf-aaaiq-cai",
{ avatar_id = ?avatar.id }
);
Avatar Size Limit Avatar images must be smaller than 800 KB. The SDK validates this before uploading.
Best Practices
Rate Limiting Implement delays between bulk operations to avoid overwhelming OpenChat infrastructure
Error Retry Logic Add retry logic for transient failures with exponential backoff
Message ID Uniqueness Always use unique message IDs to prevent duplicate messages
Log All API Calls Use LogService to track all OpenChat API interactions for debugging