XMTP CLI
The XMTP CLI (xmtp) is a command-line tool for interacting with the XMTP decentralized messaging protocol. It enables:
- Sending and receiving encrypted messages
- Managing direct messages (DMs) and group conversations
- Managing identity across multiple wallets
- Setting consent preferences for spam control
Prerequisites
Initialize Configuration
Before using most commands, initialize the CLI with wallet and encryption keys:
bash
# generate keys and save backend configuration to ~/.xmtp/.env
xmtp init --backend-url http://127.0.0.1:5050
# output keys to console instead of writing to a file
xmtp init --backend-url http://127.0.0.1:5050 --stdout
# generate keys with a custom database label and output path
xmtp init --backend-url https://backend.example.com --env staging-a --output ./my-project/.env
# overwrite existing config file
xmtp init --backend-url http://127.0.0.1:5050 --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_BACKEND_URL - Required backend URLXMTP_ENV - Database label only (default: local)
Configuration Loading Priority
- CLI flags (highest priority)
- Explicit
--env-file <path> .env in the current working directory~/.xmtp/.env (global default)
Environment Variables
| 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_BACKEND_URL | Backend URL including http:// or https:// | Yes | |
XMTP_API_KEY | Backend API key, sent as a bearer token | For authenticated backends | |
XMTP_ENV | Database label only | No (default: local) | |
XMTP_DB_PATH | Custom database file path | No | |
\*Required for commands that need a client. If missing, the CLI will suggest running init to generate them.
Command Structure
All commands use kebab-case:
text
xmtp [TOPIC] [COMMAND] [ARGUMENTS] [FLAGS]
Topics
| Topic | Purpose | |
|---|
| (root) | Standalone utility commands | |
client | Identity and installation management | |
conversations | Manage multiple conversations | |
conversation | Single conversation operations | |
preferences | Consent and preferences | |
Output Modes
All commands support --json for machine-readable JSON output:
bash
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:
bash
xmtp client info --verbose
xmtp conversations list --json --verbose 2>/dev/null
Common Workflows
Check if an Address Can Receive Messages
bash
# 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
# check whether inboxes hold handles
xmtp can-message nick nick#2341 --identifier-kind handle
Get Client Information
Shows client properties (address, inboxId, handle, otherHandles, installationId, etc.) and client options (env, dbPath, etc.) in two sections. JSON output is nested as { properties, options }. handle is the current handle, or null when the inbox has none.
bash
# display client properties in human-readable format
xmtp client info
# output as JSON (nested as { properties, options })
xmtp client info --json
Choose a Handle
A handle is a public name, such as nick or nick#2341, that people on a backend use to find an inbox. Handles are public and permanent. An inbox can change its handle once every 7 days, up to 64 times in total. Its earlier handles stay with it and still find it.
bash
# list free handles for a name, the bare name first when it is free
xmtp client handle-available nick
# check one handle with a code
xmtp client handle-available nick#2341 --json
# claim a handle (--force skips the confirmation prompt)
xmtp client handle-claim nick#2341 --force
# switch back to one of your other handles (a new claim; every rule applies)
xmtp client handle-claim nick --force
The backend decides each claim. handle-claim exits with a nonzero code and a plain message when the handle is taken, not allowed on the backend, or claimed less than 7 days after the last change, or when the inbox has used all 64 changes. It also refuses the inbox's current handle before it asks the backend, because that claim changes nothing but counts as a change. It never claims a different handle: run handle-available again and pick another one. Do not claim a handle the user did not choose.
Sign and Verify Messages
bash
# 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
Send a Message
bash
# create a DM conversation with a wallet address
xmtp conversations create-dm <address>
# create a DM conversation with the inbox that holds a handle
xmtp conversations create-dm nick#2341 --identifier-kind handle
# 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!"
Send Optimistic Messages
bash
# 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 Other Message Types
bash
# 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
bash
# create a group with multiple members and a name
xmtp conversations create-group <address-1> <address-2> --name "My Team"
# create a group from handles
xmtp conversations create-group nick bo#0042 --identifier-kind handle
# 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 Conversations
bash
# list the 100 most recently active conversations from the local database
xmtp conversations list
# read every conversation: pages of up to 1000 in creation order, resuming
# after the last row's createdAtNs and id
xmtp conversations list --order-by created-at --limit 1000
xmtp conversations list --order-by created-at --limit 1000 \
--created-before <createdAtNs> --before-id <id>
# 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
Read Messages
bash
# 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
Filter Messages
bash
# 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
bash
# 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>
# look up a DM conversation by handle
xmtp conversations get-dm nick#2341 --identifier-kind handle
Stream in Real-Time