My agentic slop goes here. Not intended for anyone else!

JMAP Interface Usage Examples#

This document demonstrates how to construct and use all JMAP message types using only the module interfaces, without any manual JSON parsing or construction.

All examples use the accessor functions and constructors (v) provided by the module signatures.

Table of Contents#

  1. Core Protocol Examples
  2. Mail Protocol Examples
  3. Client Usage Examples
  4. Complete Workflow Examples

Core Protocol Examples#

Creating a JMAP Request#

open Jmap_core

(* Create capability URNs *)
let core_cap = Jmap_capability.core
let mail_cap = Jmap_capability.mail

(* Create a request with method calls *)
let request = Jmap_request.make
  ~using:[core_cap; mail_cap]
  [
    (* Method calls go here - see below *)
  ]

(* Accessing request fields *)
let caps = Jmap_request.using request
let calls = Jmap_request.method_calls request
let created = Jmap_request.created_ids request

Working with Primitives#

open Jmap_primitives

(* Create IDs *)
let account_id = Jmap_id.of_string "account123"
let mailbox_id = Jmap_id.of_string "mailbox456"

(* Create integers *)
let limit = UnsignedInt.of_int 50
let offset = Int53.of_int 0

(* Create dates *)
let now = UTCDate.now ()
let sent_date = Date.of_string "2024-01-15T10:30:00Z"

(* Access values *)
let limit_int = UnsignedInt.to_int limit
let date_str = UTCDate.to_string now

Building Filters#

open Jmap_filter

(* Simple condition filter *)
let simple_filter = condition { field = "value" }

(* Complex AND filter *)
let and_filter = and_ [
  condition { has_keyword = Some "$flagged" };
  condition { after = Some now };
]

(* Complex nested filter *)
let complex_filter = and_ [
  or_ [
    condition { from = Some "alice@example.com" };
    condition { from = Some "bob@example.com" };
  ];
  not_ (condition { has_keyword = Some "$seen" })
]

Building Sort Comparators#

open Jmap_comparator

(* Simple ascending sort *)
let sort_by_date = v ~property:"receivedAt" ()

(* Descending sort with collation *)
let sort_by_name = v
  ~property:"name"
  ~is_ascending:false
  ~collation:"i;unicode-casemap"
  ()

Standard Method Requests#

Get Request#

open Jmap_standard_methods.Get

(* Get specific objects *)
let get_req = v
  ~account_id
  ~ids:[mailbox_id; Jmap_id.of_string "mailbox789"]
  ~properties:["id"; "name"; "totalEmails"]
  ()

(* Get all objects with all properties *)
let get_all_req = v ~account_id ()

(* Access request fields *)
let acc_id = account_id get_req
let ids_opt = ids get_req
let props_opt = properties get_req

Query Request#

open Jmap_standard_methods.Query

(* Complex query *)
let query_req = v
  ~account_id
  ~filter:(Some complex_filter)
  ~sort:(Some [sort_by_date; sort_by_name])
  ~position:(Some (Int53.of_int 0))
  ~limit:(Some (UnsignedInt.of_int 50))
  ~calculate_total:(Some true)
  ()

Set Request#

open Jmap_standard_methods.Set

(* Create new objects *)
let set_create_req = v
  ~account_id
  ~create:(Some [
    (Jmap_id.of_string "temp1", new_obj1);
    (Jmap_id.of_string "temp2", new_obj2);
  ])
  ()

(* Update objects using PatchObject *)
let set_update_req = v
  ~account_id
  ~update:(Some [
    (existing_id, [
      ("name", Some (`String "New Name"));
      ("archived", Some (`Bool true));
    ]);
  ])
  ()

(* Destroy objects *)
let set_destroy_req = v
  ~account_id
  ~destroy:(Some [id1; id2; id3])
  ()

(* Combined create/update/destroy with state check *)
let set_combined_req = v
  ~account_id
  ~if_in_state:(Some "expectedState123")
  ~create:(Some [...])
  ~update:(Some [...])
  ~destroy:(Some [...])
  ()

Mail Protocol Examples#

Creating a Mailbox#

open Jmap_mail.Jmap_mailbox

(* Create mailbox rights *)
let full_rights = Rights.v
  ~may_read_items:true
  ~may_add_items:true
  ~may_remove_items:true
  ~may_set_seen:true
  ~may_set_keywords:true
  ~may_create_child:true
  ~may_rename:true
  ~may_delete:true
  ~may_submit:true

(* Create a mailbox *)
let inbox = v
  ~id:(Jmap_id.of_string "inbox123")
  ~name:"Inbox"
  ~role:(Some "inbox")
  ~sort_order:(UnsignedInt.of_int 10)
  ~total_emails:(UnsignedInt.of_int 1542)
  ~unread_emails:(UnsignedInt.of_int 127)
  ~total_threads:(UnsignedInt.of_int 890)
  ~unread_threads:(UnsignedInt.of_int 95)
  ~my_rights:full_rights
  ~is_subscribed:true
  ()

(* Create a sub-mailbox *)
let archive = v
  ~id:(Jmap_id.of_string "archive456")
  ~name:"Archive"
  ~parent_id:(Some (Jmap_id.of_string "inbox123"))
  ~role:(Some "archive")
  ~sort_order:(UnsignedInt.of_int 20)
  ~total_emails:(UnsignedInt.of_int 5231)
  ~unread_emails:(UnsignedInt.of_int 0)
  ~total_threads:(UnsignedInt.of_int 3452)
  ~unread_threads:(UnsignedInt.of_int 0)
  ~my_rights:full_rights
  ~is_subscribed:false
  ()

(* Access mailbox fields *)
let mailbox_name = name inbox
let parent = parent_id archive
let unread_count = UnsignedInt.to_int (unread_emails inbox)
let can_delete = Rights.may_delete (my_rights inbox)

Mailbox Query with Filters#

open Jmap_mail.Jmap_mailbox

(* Create mailbox filter *)
let filter = Filter.v
  ~parent_id:(Some inbox_id)
  ~has_any_role:(Some false)
  ~is_subscribed:(Some true)
  ()

(* Query with tree sorting *)
let query_req = Query.v
  ~account_id
  ~filter:(Some (Jmap_filter.condition filter))
  ~sort_as_tree:(Some true)
  ~filter_as_tree:(Some true)
  ()

Creating an Email#

open Jmap_mail.Jmap_email

(* Create email addresses *)
let from_addr = EmailAddress.v
  ~name:(Some "Alice Smith")
  ~email:"alice@example.com"

let to_addr = EmailAddress.v
  ~email:"bob@example.com"  (* name is optional *)

(* Create a simple text email *)
let text_body = BodyPart.v
  ~part_id:(Some "1")
  ~blob_id:(Some blob_id)
  ~size:(UnsignedInt.of_int 1234)
  ~headers:[]
  ~type_:"text/plain"
  ~charset:(Some "utf-8")
  ()

(* Create email with all common fields *)
let email = v
  ~id:(Jmap_id.of_string "email123")
  ~blob_id:(Jmap_id.of_string "blob456")
  ~thread_id:(Jmap_id.of_string "thread789")
  ~mailbox_ids:[(inbox_id, true); (drafts_id, true)]
  ~keywords:[("$seen", true); ("$flagged", true)]
  ~size:(UnsignedInt.of_int 15234)
  ~received_at:(UTCDate.now ())
  ~from:(Some [from_addr])
  ~to_:(Some [to_addr])
  ~subject:(Some "Important Meeting")
  ~sent_at:(Some sent_date)
  ~text_body:(Some [text_body])
  ~has_attachment:false
  ~preview:"This is a preview of the email..."
  ()

(* Access email fields *)
let subject = subject email
let sender = from email
let is_flagged = List.mem ("$flagged", true) (keywords email)

Email with Attachments#

open Jmap_mail.Jmap_email

(* Create multipart structure *)
let text_part = BodyPart.v
  ~part_id:(Some "1")
  ~size:(UnsignedInt.of_int 500)
  ~headers:[]
  ~type_:"text/plain"
  ~charset:(Some "utf-8")
  ()

let attachment = BodyPart.v
  ~part_id:(Some "2")
  ~blob_id:(Some attachment_blob_id)
  ~size:(UnsignedInt.of_int 45000)
  ~headers:[]
  ~name:(Some "document.pdf")
  ~type_:"application/pdf"
  ~disposition:(Some "attachment")
  ()

let multipart = BodyPart.v
  ~size:(UnsignedInt.of_int 45500)
  ~headers:[]
  ~type_:"multipart/mixed"
  ~sub_parts:(Some [text_part; attachment])
  ()

let email_with_attachment = v
  ~id
  ~blob_id
  ~thread_id
  ~mailbox_ids:[(inbox_id, true)]
  ~size:(UnsignedInt.of_int 45500)
  ~received_at:(UTCDate.now ())
  ~body_structure:(Some multipart)
  ~attachments:(Some [attachment])
  ~has_attachment:true
  ~preview:"Email with PDF attachment"
  ()

Email Query with Complex Filters#

open Jmap_mail.Jmap_email

(* Create email filter *)
let email_filter = Filter.v
  ~in_mailbox:(Some inbox_id)
  ~after:(Some (UTCDate.of_string "2024-01-01T00:00:00Z"))
  ~has_keyword:(Some "$flagged")
  ~not_keyword:(Some "$seen")
  ~from:(Some "important@example.com")
  ~has_attachment:(Some true)
  ()

(* Create query with filter *)
let email_query = Query.v
  ~account_id
  ~filter:(Some (Jmap_filter.condition email_filter))
  ~sort:(Some [
    Jmap_comparator.v ~property:"receivedAt" ~is_ascending:false ()
  ])
  ~collapse_threads:(Some true)
  ~limit:(Some (UnsignedInt.of_int 25))
  ()

Email Import#

open Jmap_mail.Jmap_email.Import

(* Create import email object *)
let import_email = import_email_v
  ~blob_id:raw_message_blob_id
  ~mailbox_ids:[(inbox_id, true)]
  ~keywords:[("$seen", true)]
  ()

(* Create import request *)
let import_req = request_v
  ~account_id
  ~emails:[
    (Jmap_id.of_string "import1", import_email);
  ]
  ()

Creating an Identity#

open Jmap_mail.Jmap_identity

let signature_html = "<p>Best regards,<br>Alice Smith<br><i>CEO</i></p>"

let identity = v
  ~id:(Jmap_id.of_string "identity123")
  ~name:"Work Identity"
  ~email:"alice@company.com"
  ~reply_to:(Some [EmailAddress.v ~email:"noreply@company.com"])
  ~bcc:(Some [EmailAddress.v ~email:"archive@company.com"])
  ~text_signature:"Best regards,\nAlice Smith\nCEO"
  ~html_signature:signature_html
  ~may_delete:true
  ()

Email Submission#

open Jmap_mail.Jmap_email_submission

(* Create SMTP envelope *)
let envelope = Envelope.v
  ~mail_from:(Address.v ~email:"alice@example.com" ())
  ~rcpt_to:[
    Address.v ~email:"bob@example.com" ();
    Address.v ~email:"carol@example.com" ();
  ]

(* Create email submission *)
let submission = v
  ~id:(Jmap_id.of_string "submission123")
  ~identity_id
  ~email_id
  ~thread_id
  ~envelope:(Some envelope)
  ~send_at:(UTCDate.now ())
  ~undo_status:"pending"
  ()

(* Access submission state *)
let can_undo = (undo_status submission) = "pending"
let send_time = send_at submission

Vacation Response#

open Jmap_mail.Jmap_vacation_response

let vacation = v
  ~id:(Jmap_id.of_string "singleton")
  ~is_enabled:true
  ~from_date:(Some (UTCDate.of_string "2024-12-20T00:00:00Z"))
  ~to_date:(Some (UTCDate.of_string "2024-12-31T23:59:59Z"))
  ~subject:(Some "Out of Office")
  ~text_body:(Some "I'm away until January. Will respond when I return.")
  ~html_body:(Some "<p>I'm away until January. Will respond when I return.</p>")
  ()

(* Check if active *)
let is_active = is_enabled vacation

Client Usage Examples#

Creating a Client Connection#

open Jmap_client
open Jmap_connection

(* Create custom config *)
let config = config_v
  ~max_retries:5
  ~timeout:60.0
  ~user_agent:"MyApp/1.0"
  ()

(* Create connection with basic auth *)
let conn = v
  ~config
  ~auth:(basic "user@example.com" "password123")
  ()

(* Create connection with bearer token *)
let oauth_conn = v
  ~config
  ~auth:(bearer "oauth_token_here")
  ()

(* Access connection properties *)
let max_retries = config_max_retries (config conn)
let auth_method = auth conn

Complete Workflow Examples#

Example 1: Fetch and Display Mailboxes#

open Lwt.Syntax
open Jmap_core
open Jmap_mail.Jmap_mailbox

let display_mailboxes client account_id =
  (* Create get request *)
  let get_req = Get.v ~account_id ()

  (* Create JMAP request *)
  let request = Jmap_request.make
    ~using:[Jmap_capability.core; Jmap_capability.mail]
    [Jmap_invocation.Packed {
      (* Would need to properly construct invocation *)
    }]
  in

  (* Execute request *)
  let* response = Jmap_client.call client request in

  (* Process response *)
  let method_responses = Jmap_response.method_responses response in
  (* Extract mailboxes and display *)

  Lwt.return_unit

Example 2: Search and Display Emails#

open Jmap_mail.Jmap_email

let search_flagged_emails client account_id inbox_id =
  (* Build filter *)
  let filter = Filter.v
    ~in_mailbox:(Some inbox_id)
    ~has_keyword:(Some "$flagged")
    ~not_keyword:(Some "$seen")
    ()

  (* Build query *)
  let query = Query.v
    ~account_id
    ~filter:(Some (Jmap_filter.condition filter))
    ~sort:(Some [
      Jmap_comparator.v ~property:"receivedAt" ~is_ascending:false ()
    ])
    ~limit:(Some (UnsignedInt.of_int 20))
    ()

  (* Execute query and fetch results *)
  (* ... *)

Example 3: Create and Send Email#

open Jmap_mail

let send_email client account_id identity_id =
  (* Create email addresses *)
  let from = Jmap_email.EmailAddress.v
    ~name:(Some "Alice")
    ~email:"alice@example.com" in
  let to_ = Jmap_email.EmailAddress.v
    ~email:"bob@example.com" in

  (* Create email body *)
  let body = Jmap_email.BodyPart.v
    ~part_id:(Some "1")
    ~size:(UnsignedInt.of_int 100)
    ~headers:[]
    ~type_:"text/plain"
    ~charset:(Some "utf-8")
    ()

  (* Create draft email *)
  let email = Jmap_email.v
    ~id:(Jmap_id.of_string "draft1")
    ~blob_id:(Jmap_id.of_string "blob1")
    ~thread_id:(Jmap_id.of_string "thread1")
    ~mailbox_ids:[(Jmap_id.of_string "drafts", true)]
    ~keywords:[("$draft", true)]
    ~size:(UnsignedInt.of_int 100)
    ~received_at:(UTCDate.now ())
    ~from:(Some [from])
    ~to_:(Some [to_])
    ~subject:(Some "Hello")
    ~text_body:(Some [body])
    ~has_attachment:false
    ~preview:"Hello, how are you?"
    ()

  (* Create submission *)
  let submission = Jmap_email_submission.v
    ~id:(Jmap_id.of_string "sub1")
    ~identity_id
    ~email_id:(Jmap_email.id email)
    ~thread_id:(Jmap_email.thread_id email)
    ~send_at:(UTCDate.now ())
    ~undo_status:"pending"
    ()

  (* Execute send *)
  (* ... *)

Example 4: Update Mailbox Hierarchy#

open Jmap_mail.Jmap_mailbox

let reorganize_mailboxes client account_id =
  (* Create new archive folder *)
  let new_archive = v
    ~id:(Jmap_id.of_string "temp1")
    ~name:"Archive 2024"
    ~parent_id:(Some (Jmap_id.of_string "archive"))
    ~sort_order:(UnsignedInt.of_int 10)
    ~total_emails:(UnsignedInt.of_int 0)
    ~unread_emails:(UnsignedInt.of_int 0)
    ~total_threads:(UnsignedInt.of_int 0)
    ~unread_threads:(UnsignedInt.of_int 0)
    ~my_rights:full_rights
    ~is_subscribed:true
    ()

  (* Create set request *)
  let set_req = Jmap_standard_methods.Set.v
    ~account_id
    ~create:(Some [(Jmap_id.of_string "temp1", new_archive)])
    ()

  (* Execute *)
  (* ... *)

Key Patterns#

1. All Required Fields First#

let obj = v
  ~required1
  ~required2
  ~optional1:(Some value)
  ()

2. Optional Fields Default to None#

let obj = v ~id ~name ()  (* All other fields are None/[] *)

3. Response Field Access#

(* For request/response pairs *)
let req_id = account_id request
let resp_id = response_account_id response

4. Submodule Pattern#

let rights = Rights.v ~may_read_items:true ~may_delete:false (...)
let mailbox = v ~id ~name ~my_rights:rights ()

5. Filter Composition#

let filter = Jmap_filter.and_ [
  condition { field1 = value1 };
  or_ [
    condition { field2 = value2 };
    condition { field3 = value3 };
  ]
]

Summary#

This document shows that all JMAP message types can be constructed using only the module interfaces with:

  • ✅ Type-safe constructors with labeled arguments
  • ✅ Clear separation of required vs optional fields
  • ✅ Accessor functions for all fields
  • ✅ No manual JSON manipulation required
  • ✅ Composable filters and queries
  • ✅ Complete coverage of core and mail protocols

The interface design ensures compile-time safety while remaining flexible and ergonomic for all JMAP use cases.