8 releases

0.4.1 Apr 13, 2026
0.4.0 Oct 19, 2025
0.3.3 Sep 29, 2025
0.3.2 Dec 29, 2023
0.1.0 Sep 9, 2022

#181 in Email

Download history 369/week @ 2026-02-19 270/week @ 2026-02-26 265/week @ 2026-03-05 173/week @ 2026-03-12 246/week @ 2026-03-19 253/week @ 2026-03-26 313/week @ 2026-04-02 289/week @ 2026-04-09 737/week @ 2026-04-16 937/week @ 2026-04-23 379/week @ 2026-04-30 231/week @ 2026-05-07 279/week @ 2026-05-14 1637/week @ 2026-05-21 1645/week @ 2026-05-28 1998/week @ 2026-06-04

5,574 downloads per month
Used in 2 crates

Apache-2.0 OR MIT

405KB
11K SLoC

jmap-client

crates.io build docs.rs crates.io

jmap-client is a JSON Meta Application Protocol (JMAP) library written in Rust. The library is a full implementation of the JMAP RFCs including:

Features:

  • Async and blocking support (use the cargo feature blocking to enable blocking).
  • WebSocket async streams (use the cargo feature websockets to enable JMAP over WebSocket).
  • EventSource async streams.
  • Helper functions to reduce boilerplate code and quickly build JMAP requests.
  • Fast parsing and encoding of JMAP requests.

Usage Example

// Connect to the JMAP server using Basic authentication.
// (just for demonstration purposes, Bearer tokens should be used instead)
let client = Client::new()
    .credentials(("john@example.org", "secret"))
    .connect("https://jmap.example.org")
    .await
    .unwrap();

// Create a mailbox.
let mailbox_id = client
    .mailbox_create("My Mailbox", None::<String>, Role::None)
    .await
    .unwrap()
    .take_id();

// Import a message into the mailbox.
client
    .email_import(
        b"From: john@example.org\nSubject: test\n\n test".to_vec(),
        [&mailbox_id],
        ["$draft"].into(),
        None,
    )
    .await
    .unwrap();

// Obtain all e-mail ids matching a filter.
let email_id = client
    .email_query(
        Filter::and([
            email::query::Filter::subject("test"),
            email::query::Filter::in_mailbox(&mailbox_id),
            email::query::Filter::has_keyword("$draft"),
        ])
        .into(),
        [email::query::Comparator::from()].into(),
    )
    .await
    .unwrap()
    .take_ids()
    .pop()
    .unwrap();

// Fetch an e-mail message.
let email = client
    .email_get(
        &email_id,
        [Property::Subject, Property::Preview, Property::Keywords].into(),
    )
    .await
    .unwrap()
    .unwrap();
assert_eq!(email.preview().unwrap(), "test");
assert_eq!(email.subject().unwrap(), "test");
assert_eq!(email.keywords(), ["$draft"]);

// Fetch only the updated properties of all mailboxes that changed
// since a state.
let mut request = client.build();
let changes_request = request.changes_mailbox("n").max_changes(0);
let properties_ref = changes_request.updated_properties_reference();
let updated_ref = changes_request.updated_reference();
request
    .get_mailbox()
    .ids_ref(updated_ref)
    .properties_ref(properties_ref);
for mailbox in request
    .send()
    .await
    .unwrap()
    .unwrap_method_responses()
    .pop()
    .unwrap()
    .unwrap_get_mailbox()
    .unwrap()
    .take_list()
{
    println!("Changed mailbox: {:#?}", mailbox);
}

// Delete the mailbox including any messages
client.mailbox_destroy(&mailbox_id, true).await.unwrap();

// Open an EventSource connection with the JMAP server.
let mut stream = client
    .event_source(
        [
            TypeState::Email,
            TypeState::EmailDelivery,
            TypeState::Mailbox,
            TypeState::EmailSubmission,
            TypeState::Identity,
        ]
        .into(),
        false,
        60.into(),
        None,
    )
    .await
    .unwrap();

// Consume events received over EventSource.
while let Some(event) = stream.next().await {
    let changes = event.unwrap();
    println!("-> Change id: {:?}", changes.id());
    for account_id in changes.changed_accounts() {
        println!(" Account {} has changes:", account_id);
        if let Some(account_changes) = changes.changes(account_id) {
            for (type_state, state_id) in account_changes {
                println!("   Type {:?} has a new state {}.", type_state, state_id);
            }
        }
    }
}

More examples available under the examples directory.

Testing

To run the testsuite:

 $ cargo test --all-features

Conformed RFCs

License

Licensed under either of

at your option.

Copyright (C) 2022, Stalwart Labs LLC

Dependencies

~23–50MB
~863K SLoC