Skip to main content
The ClientBuilder provides a type-safe, fluent API for configuring and creating XMTP clients.

Structure

pub struct ClientBuilder<ApiClient, S, Db = xmtp_db::DefaultStore> {
    pub(crate) api_client: Option<ApiClient>,
    pub(crate) identity: Option<Identity>,
    pub(crate) store: Option<Db>,
    pub(crate) identity_strategy: IdentityStrategy,
    pub(crate) scw_verifier: Option<Box<dyn SmartContractSignatureVerifier>>,
    pub(crate) device_sync_worker_mode: DeviceSyncMode,
    pub(crate) fork_recovery_opts: Option<ForkRecoveryOpts>,
    pub(crate) version_info: VersionInfo,
    pub(crate) allow_offline: bool,
    pub(crate) disable_commit_log_worker: bool,
    pub(crate) mls_storage: Option<S>,
    pub(crate) sync_api_client: Option<ApiClient>,
    pub(crate) cursor_store: Option<Arc<dyn CursorStore>>,
    pub(crate) disable_workers: bool,
}

Creating a Builder

From Client

let builder = Client::builder(identity_strategy);
identity_strategy
IdentityStrategy
required
Strategy for initializing or loading the client’s identity. Options:
  • IdentityStrategy::new(inbox_id, account_identifier, nonce, legacy_identity) - Create new or restore existing
  • IdentityStrategy::CachedOnly - Load from local database only

Direct Construction

let builder = ClientBuilder::<(), ()>::new(identity_strategy);

Configuration Methods

Required Configuration

api_clients(api_client, sync_api_client)

Set both the main API client and sync API client.
api_client
impl XmtpApi + XmtpQuery + 'static
required
Client for network operations (publishing, querying)
sync_api_client
impl XmtpApi + XmtpQuery + 'static
required
Client for background sync operations
Returns: ClientBuilder<A, S, Db> with new API client type
builder.api_clients(grpc_client.clone(), grpc_client.clone())

store(db)

Set the encrypted message store.
db
impl XmtpDb
required
Database implementation (typically EncryptedMessageStore<NativeDb>)
Returns: ClientBuilder<ApiClient, S, NewDb>
let store = EncryptedMessageStore::new(db)?;
builder.store(store)

default_mls_store()

Use the default SQLite-based MLS key-value store. Must be called after store(). Returns: Result<ClientBuilder<ApiClient, SqlKeyStore, Db>, ClientBuilderError>
builder.default_mls_store()?

mls_storage(storage)

Provide a custom MLS storage implementation.
storage
impl XmtpMlsStorageProvider + 'static
required
Custom MLS storage provider
Returns: ClientBuilder<ApiClient, NewS, Db>

Smart Contract Verifier

One of these methods is required:
with_scw_verifier(verifier)
Provide a custom smart contract signature verifier.
verifier
impl SmartContractSignatureVerifier + 'static
required
Custom verifier implementation
builder.with_scw_verifier(my_verifier)
with_remote_verifier()
Use the default API-based verifier. Requires api_client to be set first. Returns: Result<ClientBuilder<ApiClient, S, Db>, ClientBuilderError>
builder.with_remote_verifier()?

Optional Configuration

identity(identity)

Provide a pre-initialized identity instead of using the identity strategy.
identity
Identity
Pre-configured identity object
builder.identity(existing_identity)

device_sync_worker_mode(mode)

Configure the device sync worker behavior.
mode
DeviceSyncMode
  • DeviceSyncMode::Enabled - Sync preferences across devices (default)
  • DeviceSyncMode::Disabled - No cross-device sync
builder.device_sync_worker_mode(DeviceSyncMode::Disabled)

with_device_sync_worker_mode(mode)

Same as above but accepts Option<DeviceSyncMode>.
builder.with_device_sync_worker_mode(Some(DeviceSyncMode::Enabled))

fork_recovery_opts(opts)

Configure fork recovery behavior for handling group state inconsistencies.
opts
ForkRecoveryOpts
Fork recovery configuration:
  • enable_recovery_requests: ForkRecoveryPolicy - When to request recovery
  • groups_to_request_recovery: Vec<String> - Specific groups to recover
  • disable_recovery_responses: bool - Don’t respond to recovery requests
  • worker_interval_ns: Option<u64> - Custom worker polling interval
builder.fork_recovery_opts(ForkRecoveryOpts {
    enable_recovery_requests: ForkRecoveryPolicy::All,
    groups_to_request_recovery: vec![],
    disable_recovery_responses: false,
    worker_interval_ns: None,
})

version(version_info)

Set client version information.
version_info
VersionInfo
Version metadata for this client
builder.version(VersionInfo::default())

maybe_version(version)

Set version information if provided.
version
Option<VersionInfo>
Optional version metadata
builder.maybe_version(Some(version_info))

with_allow_offline(allow_offline)

Skip network calls during client construction.
allow_offline
Option<bool>
If true, don’t fetch identity updates from network during build
builder.with_allow_offline(Some(true))

with_disable_workers(disable_workers)

Disable background workers (sync, key rotation, etc.).
disable_workers
bool
If true, no background workers will be started
builder.with_disable_workers(false)

cursor_store(cursor_store)

Provide a custom cursor store for sync operations.
cursor_store
Arc<dyn CursorStore>
Custom cursor store implementation
builder.cursor_store(Arc::new(SqliteCursorStore::new(db)))

Debugging and Monitoring

enable_api_debug_wrapper()

Wrap the API client to print statistics on errors. Requires api_client to be set. Returns: Result<ClientBuilder<ApiDebugWrapper<ApiClient>, S, Db>, ClientBuilderError>
builder.enable_api_debug_wrapper()?

enable_api_stats()

Wrap the API client to track statistics. Requires api_client to be set. Returns: Result<ClientBuilder<TrackedStatsClient<ApiClient>, S, Db>, ClientBuilderError>
builder.enable_api_stats()?

Building the Client

build()

Asynchronously build the client, potentially making network calls. Returns: Result<Client<ContextParts<ApiClient, S, Db>>, ClientBuilderError>
let client = builder.build().await?;
Process:
  1. Validates all required parameters are set
  2. Wraps API clients with retry logic
  3. Initializes or loads identity based on strategy
  4. Loads identity updates from network (unless allow_offline is true)
  5. Creates shared context with all dependencies
  6. Registers and spawns background workers (unless disabled)
  7. Performs cleanup of old data

build_offline()

Build the client synchronously without network access. Returns: Result<Client<ContextParts<ApiClient, S, Db>>, ClientBuilderError> Fails with ClientBuilderError::OfflineBuildFailed if network access would be required.
let client = builder.build_offline()?;

Type Parameters

The builder uses three generic type parameters:
ApiClient
type
Type implementing XmtpApi + XmtpQuery + 'staticTypically XmtpApiClient or wrapped variants like ApiDebugWrapper<XmtpApiClient>
S
type
MLS storage provider type implementing XmtpMlsStorageProvider + 'staticTypically SqlKeyStore<DbQuery>
Db
type
Database type implementing XmtpDb + 'staticDefaults to xmtp_db::DefaultStore. Typically EncryptedMessageStore<NativeDb>

Error Types

pub enum ClientBuilderError {
    AddressValidation(IdentifierValidationError),
    MissingParameter { parameter: &'static str },
    ClientError(crate::client::ClientError),
    StorageError(StorageError),
    Identity(crate::identity::IdentityError),
    WrappedApiError(xmtp_api::ApiError),
    GroupError(Box<crate::groups::GroupError>),
    DeviceSync(Box<crate::worker::device_sync::DeviceSyncError>),
    OfflineBuildFailed,
}

Complete Example

use xmtp_mls::{
    Client, builder::DeviceSyncMode,
    identity::IdentityStrategy,
};
use xmtp_db::{EncryptedMessageStore, NativeDb};
use xmtp_id::associations::Identifier;

// Set up database
let db = NativeDb::builder().persistent("./db.sqlite").build()?;
let store = EncryptedMessageStore::new(db)?;

// Create API clients
let api_client = XmtpApiClient::create(...)?;

// Define identity
let inbox_id = "inbox_123";
let identifier = Identifier::Address("0x...".to_string());
let identity_strategy = IdentityStrategy::new(
    inbox_id.to_string(),
    identifier,
    1, // nonce
    None, // legacy_identity
);

// Build client
let client = Client::builder(identity_strategy)
    .api_clients(api_client.clone(), api_client.clone())
    .store(store)
    .default_mls_store()?
    .with_remote_verifier()?
    .device_sync_worker_mode(DeviceSyncMode::Enabled)
    .enable_api_stats()?
    .build()
    .await?;

See Also

Build docs developers (and LLMs) love