Instruction file imported from xmtplabs/xmtp-agent-examples (
.cursor/rules/xmtp.mdc). Copyright stays with the author.
Identifiers Reference
XMTP Identifiers
Ethereum Address: 0x + 40 hex chars (e.g., 0xfb55CB623f2aB58Da17D8696501054a2ACeD1944)
Private Key: 0x + 64 hex chars (e.g., 0x11567776b95bdbed513330f503741e19877bf7fe73e7957bf6f0ecf3e267fdb8)
Encryption Key: 64 hex chars, no prefix (e.g., 11973168e34839f9d31749ad77204359c5c39c404e1154eacb7f35a867ee47de)
Inbox ID: 64 hex chars, no prefix (e.g., 1180478fde9f6dfd4559c25f99f1a3f1505e1ad36b9c3a4dd3d5afb68c419179)
Installation ID: 64 hex chars, no prefix (e.g., a83166f3ab057f28d634cc04df5587356063dba11bf7d6bcc08b21a8802f4028)
Example User Credentials Set
{
"accountAddress": "0xfb55CB623f2aB58Da17D8696501054a2ACeD1944",
"privateKey": "0x11567776b95bdbed513330f503741e19877bf7fe73e7957bf6f0ecf3e267fdb8",
"encryptionKey": "11973168e34839f9d31749ad77204359c5c39c404e1154eacb7f35a867ee47de",
"inboxId": "1180478fde9f6dfd4559c25f99f1a3f1505e1ad36b9c3a4dd3d5afb68c419179",
"installationId": "a83166f3ab057f28d634cc04df5587356063dba11bf7d6bcc08b21a8802f4028"
}
Working with Members
All conversations, both Groups and DMs, have a members() method that returns an array of GroupMember objects:
// Get members from any conversation type (DM or Group)
const members = await conversation.members();
// Find a specific member
const member = members.find(
(member) => member.inboxId.toLowerCase() === targetInboxId.toLowerCase(),
);
// Get member's Ethereum address
if (member) {
const ethIdentifier = member.accountIdentifiers.find(
(id) => id.identifierKind === IdentifierKind.Ethereum,
);
if (ethIdentifier) {
const ethereumAddress = ethIdentifier.identifier;
console.log(`Found Ethereum address: ${ethereumAddress}`);
}
// Get installation ID
if (member.installationIds.length > 0) {
const installationId = member.installationIds[0];
console.log(`Found installation ID: ${installationId}`);
}
}
Working with Conversations
XMTP provides two main conversation types:
Direct Messages (DMs)
// Create a new DM via Inboxid
const dm = await client.conversations.newDm("inboxId123");
// Or create using an Ethereum address
const dmByAddress = await agent.createDmWithIdentifier({
identifier: "0x7c40611372d354799d138542e77243c284e460b2",
identifierKind: IdentifierKind.Ethereum,
});
// Send a message
await dm.send("Hello!");
// Access peer's inbox ID
const peerInboxId = dm.peerInboxId;
Groups
// Create a new group
const group = await agent.createGroupWithAddresses(addresses, {
groupName: "My Group",
groupDescription: "Group description",
});
// Update group metadata
await group.updateName("New Group Name");
await group.updateDescription("Updated description");
// Manage members
await group.addMembers(["newMemberInboxId"]);
await group.removeMembers(["memberToRemoveInboxId"]);
// Manage permissions
await group.addAdmin("memberInboxId");
await group.addSuperAdmin("memberInboxId");
Example usage:
// Create a group with some initial settings
const group = await agent.createGroupWithAddresses(addresses, {
groupName: "Project Discussion",
groupDescription: "A group for our project collaboration",
groupImageUrlSquare: "https://example.com/image.jpg",
});
// Update group settings later
await group.updateName("Updated Project Name");
await group.updateDescription("Our awesome project discussion");
await group.updateImageUrl("https://example.com/new-image.jpg");
Retrieve all messages at once from the local database:
// First sync the conversations from the network to update the local db
await client.conversations.sync();
// Then get all messages as an array
const messages = await conversation.messages();
Key Type References
// Client Class
declare class Client {
constructor(client: Client$1, signer: Signer, codecs: ContentCodec[]);
static create(
signer: Signer,
encryptionKey: Uint8Array,
options?: ClientOptions,
): Promise<Client>;
get inboxId(): string;
get installationId(): string;
get conversations(): Conversations;
get preferences(): Preferences;
}
// Conversations Class
declare class Conversations {
constructor(client: Client, conversations: Conversations$1);
getConversationById(id: string): Promise<Dm | Group | undefined>;
newGroupWithIdentifiers(
identifiers: Identifier[],
options?: CreateGroupOptions,
): Promise<Group>;
newGroup(inboxIds: string[], options?: CreateGroupOptions): Promise<Group>;
newDmWithIdentifier(
identifier: Identifier,
options?: CreateDmOptions,
): Promise<Dm>;
newDm(inboxId: string, options?: CreateDmOptions): Promise<Dm>;
sync(): Promise<void>;
streamAllMessages(
callback?: StreamCallback<DecodedMessage>,
): Promise<AsyncStream<DecodedMessage<any>>>;
}
// Conversation Base Class
declare class Conversation {
client: Client;
constructor(
client: Client,
conversation: Conversation$1,
lastMessage?: Message | null,
);
get id(): string;
send<T>(content: T, options?: SendOptions): Promise<string>;
messages<T>(options?: PaginationOptions): Promise<Array<DecodedMessage<T>>>;
members(): Promise<GroupMember[]>;
}
// Dm Class
declare class Dm extends Conversation {
constructor(
client: Client,
conversation: Conversation$1,
lastMessage?: Message | null,
);
get peerInboxId(): string;
}
// Group Class
declare class Group extends Conversation {
constructor(
client: Client,
conversation: Conversation$1,
lastMessage?: Message | null,
);
get name(): string;
updateName(name: string): Promise<void>;
get imageUrl(): string;
updateImageUrl(imageUrl: string): Promise<void>;
get description(): string;
updateDescription(description: string): Promise<void>;
get admins(): string[];
get superAdmins(): string[];
isAdmin(inboxId: string): boolean;
isSuperAdmin(inboxId: string): boolean;
addMembersByIdentifiers(identifiers: Identifier[]): Promise<void>;
addMembers(inboxIds: string[]): Promise<void>;
removeMembers(inboxIds: string[]): Promise<void>;
addAdmin(inboxId: string): Promise<void>;
removeAdmin(inboxId: string): Promise<void>;
addSuperAdmin(inboxId: string): Promise<void>;
removeSuperAdmin(inboxId: string): Promise<void>;
}
// GroupMember Class
declare class GroupMember {
inboxId: string;
accountIdentifiers: Array<Identifier>;
installationIds: Array<string>;
permissionLevel: PermissionLevel;
consentState: ConsentState;
}
// DecodedMessage Class
declare class DecodedMessage<T = any> {
content: T;
contentType: ContentTypeId | undefined;
conversationId: string;
id: string;
senderInboxId: string;
sentAt: Date;
constructor(client: Client, message: Message);
}
// Identifier Interface
export interface Identifier {
identifier: string;
identifierKind: IdentifierKind;
}
export declare const enum IdentifierKind {
Ethereum = 0,
Passkey = 1,
}
// CreateGroupOptions Interface
export interface CreateGroupOptions {
groupName?: string;
groupImageUrlSquare?: string;
groupDescription?: string;
}
Group Permissions System
XMTP provides fine-grained control over group actions through a permissions system with configurable policies and member roles.
Core Types
export declare const enum PermissionUpdateType {
AddMember = 0, // Who can add new members
RemoveMember = 1, // Who can remove members
AddAdmin = 2, // Who can grant admin status
RemoveAdmin = 3, // Who can remove admin status
UpdateMetadata = 4, // Who can update group info
}
export declare const enum PermissionPolicy {
Allow = 0, // All members can perform action
Deny = 1, // No one can perform action
Admin = 2, // Only admins can perform action
SuperAdmin = 3, // Only super admins can perform action
DoesNotExist = 4, // Legacy - permission doesn't exist
Other = 5, // Reserved for custom policies
}
export interface CreateGroupOptions {
permissions?: GroupPermissionsOptions;
groupName?: string;
groupImageUrlSquare?: string;
groupDescription?: string;
customPermissionPolicySet?: PermissionPolicySet;
messageDisappearingSettings?: MessageDisappearingSettings;
}
Member Roles
- Member: Basic group participant
- Admin: Can perform admin-level actions based on group settings
- Super Admin: Has all permissions, can manage other admins
Default Permissions
New groups use the All_Members policy set:
AddMember: Allow (all members)RemoveMember: Admin onlyAddAdmin/RemoveAdmin: Super Admin onlyUpdateMetadata: Allow (all members)
Managing Permissions
// Check roles
const isAdmin = group.isAdmin(inboxId);
const isSuperAdmin = group.isSuperAdmin(inboxId);
// Get role lists
const admins = group.admins;
const superAdmins = group.superAdmins;
// Manage admin status
await group.addAdmin(inboxId);
await group.removeAdmin(inboxId);
await group.addSuperAdmin(inboxId);
await group.removeSuperAdmin(inboxId);
Custom Permission Examples
// Admin-controlled group
const adminOnlyPermissions = {
AddMember: PermissionPolicy.Admin,
RemoveMember: PermissionPolicy.Admin,
UpdateMetadata: PermissionPolicy.Admin,
AddAdmin: PermissionPolicy.SuperAdmin,
RemoveAdmin: PermissionPolicy.SuperAdmin,
};
// Locked group (super admin only)
const lockedPermissions = {
AddMember: PermissionPolicy.SuperAdmin,
RemoveMember: PermissionPolicy.SuperAdmin,
UpdateMetadata: PermissionPolicy.SuperAdmin,
AddAdmin: PermissionPolicy.SuperAdmin,
RemoveAdmin: PermissionPolicy.SuperAdmin,
};
Other Notes
If no dbPath is provided, the client will use the current working directory. You can also specify a custom path for the database.
// Create a client with db path
const client = await Client.create(signer, {
env: XMTP_ENV as XmtpEnv,
dbPath: dbPath: (inboxId) =>
process.env.RAILWAY_VOLUME_MOUNT_PATH ??
"." + `/${process.env.XMTP_ENV}-${inboxId.slice(0, 8)}.db3`,
});