with one click
xmtp-cli
// Use when working with XMTP messaging - sending messages, managing conversations, groups, consent, and identity operations via the xmtp CLI tool
// Use when working with XMTP messaging - sending messages, managing conversations, groups, consent, and identity operations via the xmtp CLI tool
| name | xmtp-cli |
| description | Use when working with XMTP messaging - sending messages, managing conversations, groups, consent, and identity operations via the xmtp CLI tool |
The XMTP CLI (xmtp) is a command-line tool for interacting with the XMTP decentralized messaging protocol. It enables:
Before using most commands, initialize the CLI with wallet and encryption keys:
# generate keys and save to default path (~/.xmtp/.env)
xmtp init
# output keys to console instead of writing to a file
xmtp init --stdout
# generate keys for production env and save to custom path
xmtp init --env production --output ./my-project/.env
# overwrite existing config file
xmtp init --force
This creates a .env file with:
XMTP_WALLET_KEY - Your wallet private key (identity)XMTP_DB_ENCRYPTION_KEY - Database encryption key (local data protection)XMTP_ENV - Network environment (local, dev, production)--env-file <path>.env in the current working directory~/.xmtp/.env (global default)| Variable | Description | Required |
|---|---|---|
XMTP_WALLET_KEY | Ethereum private key (hex, with or without 0x prefix) | Yes* |
XMTP_DB_ENCRYPTION_KEY | 32-byte encryption key (hex) | Yes* |
XMTP_ENV | Network: local, dev, or production | No (default: dev) |
XMTP_DB_PATH | Custom database file path | No (default: ~/.xmtp/xmtp-db) |
XMTP_GATEWAY_HOST | Custom gateway URL | No |
*Required for commands that need a client. If missing, the CLI will suggest running init to generate them.
All commands use kebab-case:
xmtp [TOPIC] [COMMAND] [ARGUMENTS] [FLAGS]
| Topic | Purpose |
|---|---|
| (root) | Standalone utility commands |
client | Identity and installation management |
conversations | Manage multiple conversations |
conversation | Single conversation operations |
preferences | Consent and preferences |
All commands support --json for machine-readable JSON output:
xmtp client info --json
Use --verbose to see detailed client initialization logs (env, db path, etc.). When combined with --json, verbose output goes to stderr so it doesn't interfere with JSON parsing:
xmtp client info --verbose
xmtp conversations list --json --verbose 2>/dev/null
# check if a single address can receive XMTP messages
xmtp can-message <address>
# check multiple addresses at once
xmtp can-message <address-1> <address-2>
# output result as JSON for scripting
xmtp can-message <address> --json
Shows client properties (address, inboxId, installationId, etc.) and client options (env, dbPath, etc.) in two sections. JSON output is nested as { properties, options }.
# display client properties in human-readable format
xmtp client info
# output as JSON (nested as { properties, options })
xmtp client info --json
# sign a message with the installation key, output signature as hex
xmtp client sign "Hello, World!"
# sign a message, output signature as base64 instead of hex
xmtp client sign "verify me" --base64
# verify a hex-encoded signature against the original message
xmtp client verify-signature "Hello, World!" --signature <hex-signature>
# verify a base64-encoded signature against the original message
xmtp client verify-signature "verify me" --signature <base64-signature> --base64
# create a DM conversation with a wallet address
xmtp conversations create-dm <address>
# send a text message to a conversation by its ID
xmtp conversation send-text <conversation-id> "Hello, world!"
# combined: create DM, extract the conversation ID via JSON, then send
DM_ID=$(xmtp conversations create-dm <address> --json | jq -r '.id')
xmtp conversation send-text "$DM_ID" "Hello!"
# queue message locally without publishing to the network
xmtp conversation send-text <conversation-id> "Quick message" --optimistic
# publish all queued optimistic messages for this conversation
xmtp conversation publish-messages <conversation-id>
# send a markdown-formatted message
xmtp conversation send-markdown <conversation-id> "**bold** and *italic*"
# send a plain text reply referencing another message by its ID
xmtp conversation send-reply <conversation-id> <message-id> "Replying to your message"
# send a markdown-formatted reply referencing another message
xmtp conversation send-reply <conversation-id> <message-id> "**Bold** reply" --markdown
# add a reaction to a message (action: add, content: emoji)
xmtp conversation send-reaction <conversation-id> <message-id> add "👍"
# remove a previously added reaction from a message
xmtp conversation send-reaction <conversation-id> <message-id> remove "👍"
# send a read receipt to mark conversation as read
xmtp conversation send-read-receipt <conversation-id>
# create a group with multiple members and a name
xmtp conversations create-group <address-1> <address-2> --name "My Team"
# create a group with full metadata and admin-only permissions
xmtp conversations create-group <address> \
--name "Project Team" \
--description "Discussion for our project" \
--image-url "https://example.com/team.png" \
--permissions admin-only
# list all conversations from local database
xmtp conversations list
# sync from network before listing to get latest data
xmtp conversations list --sync
# filter to only DMs (or use --type group for groups)
xmtp conversations list --type dm
# filter to only conversations with "allowed" consent state
xmtp conversations list --consent-state allowed
# filter by multiple consent states (repeatable flag)
xmtp conversations list --consent-state allowed --consent-state unknown
# sort by most recent activity and limit to 10 results
xmtp conversations list --order-by last-activity --limit 10
# filter by creation time (nanosecond timestamps)
xmtp conversations list --created-after 1700000000000000000
xmtp conversations list --created-before 1700000000000000000
# list messages in a conversation (default: descending order)
xmtp conversation messages <conversation-id>
# limit to the 10 most recent messages
xmtp conversation messages <conversation-id> --limit 10
# sync from network before reading to get latest messages
xmtp conversation messages <conversation-id> --sync
# sort in ascending order (oldest first)
xmtp conversation messages <conversation-id> --direction ascending
# include only specific content types (repeatable flag)
xmtp conversation messages <conversation-id> --content-type text --content-type markdown
# exclude specific content types (repeatable flag)
xmtp conversation messages <conversation-id> --exclude-content-type reaction
# filter by delivery status: unpublished, published, or failed
xmtp conversation messages <conversation-id> --delivery-status published
# filter by message kind: application (user messages) or membership-change
xmtp conversation messages <conversation-id> --kind application
# filter by sent timestamp range (nanoseconds)
xmtp conversation messages <conversation-id> --sent-after 1700000000000000000
xmtp conversation messages <conversation-id> --sent-before 1700000000000000000
# filter by local insertion timestamp range (nanoseconds)
xmtp conversation messages <conversation-id> --inserted-after 1700000000000000000
xmtp conversation messages <conversation-id> --inserted-before 1700000000000000000
# exclude messages from a specific sender (repeatable flag)
xmtp conversation messages <conversation-id> --exclude-sender <inbox-id>
# sort by local insertion time instead of sent time
xmtp conversation messages <conversation-id> --sort-by inserted-at
Content types: actions, attachment, custom, group-membership-change, group-updated, intent, leave-request, markdown, multi-remote-attachment, reaction, read-receipt, remote-attachment, reply, text, transaction-reference, wallet-send-calls
# look up a DM conversation by wallet address
xmtp conversations get-dm <address>
# look up a DM conversation by inbox ID
xmtp conversations get-dm <inbox-id>
# stream messages from all conversations indefinitely
xmtp conversations stream-all-messages
# stop streaming after 60 seconds
xmtp conversations stream-all-messages --timeout 60
# stop after receiving 10 messages
xmtp conversations stream-all-messages --count 10
# only stream messages from group conversations (or use --type dm)
xmtp conversations stream-all-messages --type group
# only stream from conversations with "allowed" consent
xmtp conversations stream-all-messages --consent-state allowed
# skip the initial sync before streaming starts
xmtp conversations stream-all-messages --disable-sync
# stream messages from a single conversation
xmtp conversation stream <conversation-id>
# stream new conversations as they are created
xmtp conversations stream
# only stream new DM conversations
xmtp conversations stream --type dm
# only stream new group conversations
xmtp conversations stream --type group
# stop after 60 seconds or 5 new conversations, whichever comes first
xmtp conversations stream --timeout 60 --count 5
# skip the initial sync before streaming starts
xmtp conversations stream --disable-sync
# list members of a conversation
xmtp conversation members <conversation-id>
# sync from network before listing to get latest membership
xmtp conversation members <conversation-id> --sync
# add members by their Ethereum addresses (space-separated)
xmtp conversation add-members <conversation-id> <address-1> <address-2>
# remove a member by their Ethereum address
xmtp conversation remove-members <conversation-id> <address>
# restrict adding members to admins only
xmtp conversation update-permission <conversation-id> --type add-member --policy admin
# restrict removing members to super-admins only
xmtp conversation update-permission <conversation-id> --type remove-member --policy super-admin
# restrict updating group name to admins (--metadata-field required for update-metadata type)
xmtp conversation update-permission <conversation-id> --type update-metadata --policy admin --metadata-field group-name
# restrict updating group description to admins
xmtp conversation update-permission <conversation-id> --type update-metadata --policy admin --metadata-field group-description
Permission types: add-member, remove-member, add-admin, remove-admin, update-metadata
Policies: allow, deny, admin, super-admin
Metadata fields: group-name, group-description, group-image-url, app-data
# get the consent state of a conversation
xmtp conversation consent-state <conversation-id>
# allow messages from this conversation
xmtp conversation update-consent <conversation-id> --state allowed
# block messages from this conversation
xmtp conversation update-consent <conversation-id> --state denied
# get consent state for an inbox ID
xmtp preferences get-consent --entity-type inbox_id --entity <inbox-id>
# get consent state for a conversation ID
xmtp preferences get-consent --entity-type conversation_id --entity <conversation-id>
# block an inbox ID (deny all messages from this sender)
xmtp preferences set-consent --entity-type inbox_id --entity <inbox-id> --state denied
# allow a conversation ID
xmtp preferences set-consent --entity-type conversation_id --entity <conversation-id> --state allowed
Entity types use snake_case: inbox_id, conversation_id
# request to be removed from a group conversation
xmtp conversation request-removal <conversation-id>
# sync conversation list from the network
xmtp conversations sync
# sync all conversations and their messages (more thorough, slower)
xmtp conversations sync-all
# sync only conversations with "allowed" consent state
xmtp conversations sync-all --consent-state allowed
# sync a single conversation from the network
xmtp conversation sync <conversation-id>
# sync user preferences (consent states, HMAC keys) from the network
xmtp preferences sync
# stream all preference changes (consent updates, HMAC key updates) indefinitely
xmtp preferences stream
# stop streaming after 60 seconds
xmtp preferences stream --timeout 60
# stop after receiving 5 preference update batches
xmtp preferences stream --count 5
# skip the initial preferences sync before streaming starts
xmtp preferences stream --disable-sync
# get your own inbox state from local database
xmtp preferences inbox-state
# fetch the latest inbox state from the network before displaying
xmtp preferences inbox-state --sync
# fetch inbox states from the network for specific inbox IDs (no local client needed)
xmtp inbox-states <inbox-id-1> <inbox-id-2>
# get cached inbox states from local database for specific inbox IDs
xmtp preferences inbox-states <inbox-id-1> <inbox-id-2>
# get HMAC keys for all conversations
xmtp conversations hmac-keys
These operations require --force to skip confirmation:
# add a new wallet to this inbox (--force skips confirmation prompt)
xmtp client add-account --new-wallet-key <wallet-key> --force
# remove a wallet from this inbox
xmtp client remove-account --identifier <address> --force
# change the recovery wallet address for this inbox
xmtp client change-recovery-identifier --identifier <address> --force
# verify the current inbox state after changes
xmtp preferences inbox-state
# check if a wallet address is authorized for an inbox (args: inbox-id, address)
xmtp address-authorized <inbox-id> <address>
# check if an installation is authorized for an inbox (args: inbox-id, installation-id)
xmtp installation-authorized <inbox-id> <installation-id>
# look up the inbox ID associated with a wallet address
xmtp client inbox-id --identifier <address>
# get detailed info about a conversation (type, metadata, members)
xmtp conversations get <conversation-id>
# get the permission policies for a group conversation
xmtp conversation permissions <conversation-id>
# get internal debug info for a conversation
xmtp conversation debug-info <conversation-id>
# fetch a specific message by its ID
xmtp conversations get-message <message-id>
# count messages in a conversation from local database
xmtp conversation count-messages <conversation-id>
# sync from network before counting to include latest messages
xmtp conversation count-messages <conversation-id> --sync
# count only application messages (exclude membership changes)
xmtp conversation count-messages <conversation-id> --kind application
# count only text and markdown messages
xmtp conversation count-messages <conversation-id> --content-type text --content-type markdown
# count messages sent after a timestamp (nanoseconds)
xmtp conversation count-messages <conversation-id> --sent-after 1700000000000000000
# promote a member to admin
xmtp conversation add-admin <conversation-id> <inbox-id>
# demote an admin to regular member
xmtp conversation remove-admin <conversation-id> <inbox-id>
# promote a member to super admin
xmtp conversation add-super-admin <conversation-id> <inbox-id>
# demote a super admin
xmtp conversation remove-super-admin <conversation-id> <inbox-id>
# list all admins in a group
xmtp conversation list-admins <conversation-id>
# list all super admins in a group
xmtp conversation list-super-admins <conversation-id>
# set the group name
xmtp conversation update-name <conversation-id> "New Name"
# set the group description
xmtp conversation update-description <conversation-id> "New description"
# set the group image URL
xmtp conversation update-image-url <conversation-id> "https://example.com/image.png"
# list all installations for this client's inbox
xmtp preferences inbox-state --json | jq '.installations'
# revoke specific installations from this client's inbox (--force skips confirmation)
xmtp client revoke-installations --installation-ids <installation-id> --force
# revoke all installations except the current one
xmtp client revoke-all-other-installations --force
# check key package status for specific installations
xmtp client key-package-status --installation-ids <installation-id>
# revoke installations from any inbox without local database (requires wallet key)
xmtp revoke-installations <inbox-id> -i <installation-id>
# revoke multiple installations (repeated -i flag)
xmtp revoke-installations <inbox-id> -i <id-1> -i <id-2>
# revoke multiple installations (comma-separated)
xmtp revoke-installations <inbox-id> -i <id-1>,<id-2>
Each device/app instance has a unique installation ID. This allows:
Unique identifier for each conversation (DM or group). Required for most conversation operations.
| State | Meaning |
|---|---|
allowed | Messages are welcome |
denied | Messages are blocked |
unknown | No decision made |
| Network | Use Case |
|---|---|
local | Local XMTP node |
dev | Development/testing (default) |
production | Production use |
xmtp init to generate keyscan-message to check firstconversations syncconversation permissions# 1. initialize with dev environment (first time only)
xmtp init --env dev
# 2. check if the recipient can receive XMTP messages
xmtp can-message <address>
# 3. create a DM conversation and extract the conversation ID
DM=$(xmtp conversations create-dm <address> --json)
DM_ID=$(echo "$DM" | jq -r '.id')
# 4. send a text message to the new DM
xmtp conversation send-text "$DM_ID" "Hello! This is my first message."
# 5. stream replies for up to 5 minutes
xmtp conversation stream "$DM_ID" --timeout 300
can-message before creating conversations--json flag when you need to extract data--sync flag when reading messages to ensure fresh data--timeout and --count flags for bounded operations--env flag explicitly when context requires specific networkadd-account, remove-account, revoke-installations, revoke-all-other-installations, and change-recovery-identifier prompt for confirmation unless --force is passedxmtp <command> --help for full flag documentation