A minimal-dependency client for the
Matrix Client-Server HTTP API, suitable for
talking to a Synapse or Conduit homeserver from R. Two Imports: curl
and jsonlite. No tidyverse.
Pairs with mx.crypto,
which handles Olm + Megolm; mx.api itself does no cryptography.
Install
# CRAN install.packages("mx.api") # GitHub (development version) remotes::install_github("cornball-ai/mx.api")
Quick start: log in, send, sync
library(mx.api) s <- mx_login( server = "https://matrix.example", user = "alice", password = "hunter2" ) room <- mx_room_join(s, "#general:matrix.example") mx_send(s, room, "hello from R") batch <- mx_sync(s, timeout = 0) str(batch$rooms$join) mx_logout(s)
Create a private room with invites
room <- mx_room_create( s, name = "project sync", topic = "weekly check-in", preset = "private_chat", invite = c("@bob:matrix.example", "@carol:matrix.example") ) mx_send(s, room, "kickoff in 5")
mx_room_create returns the new room id as a character string.
Presets: "private_chat", "trusted_private_chat", "public_chat".
Sending rich-text (code blocks, bold, etc.)
mx_send's body is plain text. Matrix clients that show rich text
(Element, Cinny, Fractal, etc.) render the optional formatted_body
field instead, fall back to body when it is absent. Markdown
fences in plain body show as literal backticks; you have to send
HTML in formatted_body to get a real code block.
Pass both via the extra argument:
plain <- "Build steps:\n\ndocker compose build\ndocker compose up -d" html <- paste0( "<p>Build steps:</p>", "<pre><code>docker compose build\n", "docker compose up -d</code></pre>" ) mx_send( s, room, plain, extra = list( format = "org.matrix.custom.html", formatted_body = html ) )
The format value "org.matrix.custom.html" is the only one the
Matrix spec defines. Supported tags are listed in the
Matrix client-server spec, section 11.2.1.7.
Code blocks use <pre><code>…</code></pre>; inline code is <code>…</code>.
What's covered
| Area | Functions |
|---|---|
| Session | mx_register, mx_login, mx_logout, mx_whoami, mx_session |
| Rooms | mx_rooms, mx_room_create, mx_room_join, mx_room_leave, mx_room_invite, mx_room_members, mx_room_name, mx_room_topic |
| Messages | mx_send, mx_send_event, mx_messages, mx_sync, mx_react, mx_redact, mx_read_receipt, mx_typing |
| Room state | mx_get_state, mx_set_state |
| Media | mx_upload (streaming), mx_download, mx_send_media (+ mx_send_file / mx_send_image / mx_send_audio / mx_send_video), mx_guess_mime, mx_media_config |
| Profile | mx_profile, mx_set_displayname, mx_set_avatar_url |
| Account data | mx_get_account_data, mx_set_account_data |
| Devices | mx_devices, mx_delete_device |
| E2EE transport | mx_keys_upload, mx_keys_query, mx_keys_claim, mx_send_to_device |
| E2EE signing helper | mx_canonical_json |
End-to-end cryptography is out of scope; pair with mx.crypto
(or another crypto library) to sign and verify the payloads these
endpoints carry. Helpful framing:
- mx.api speaks Matrix HTTP. It does no signing, no key management, no key validation, and holds no state between calls.
- mx.crypto speaks Olm + Megolm. It does no HTTP.
- mx.client is the stateful
layer that uses both: config persistence, sync cursors, and E2EE
orchestration (its
vignette("e2ee")walks the whole flow).
Canonical JSON
mx_canonical_json() is the byte-stable encoder Matrix's signing rules
require. It is hand-rolled (not a jsonlite wrapper) so the
spec-sensitive choices — key ordering by UTF-8 byte sequence, integer
range, NaN/Inf/NA rejection, duplicate-key rejection, control-char
escaping — are visible and unit-tested rather than hidden in another
package's defaults.
mx_canonical_json(list(b = 2, a = 1)) #> [1] "{\"a\":1,\"b\":2}" mx_canonical_json(1.5) #> Error: mx_canonical_json: non-integer number 1.5 disallowed
107 assertions exercise the encoder (see inst/tinytest/test_canonical_json.R).
Status
0.3.0. Relative to 0.2.0 the delta is additive:
- Generic event and state plumbing:
mx_send_event,mx_set_state,mx_get_state(needed form.room.encrypted/m.room.encryption). - Media messages:
mx_send_mediaand the file/image/audio/video wrappers. - Bot lifecycle:
mx_room_invite,mx_redact,mx_typing,mx_profile/mx_set_displayname/mx_set_avatar_url. - Account data:
mx_get_account_data,mx_set_account_data. - Devices:
mx_devices,mx_delete_device. - Classed error conditions (
mx_error_<ERRCODE>) for programmatic handling; streamed uploads.
See NEWS.md for the full changelog.
CI
GitHub Actions via r-ci; macOS and Ubuntu runners cover every commit + PR.
License
MIT. See LICENSE.