If you have started down the path of exploring DeltaChat you may have wondered about the difficulty of integrating any automations or bots with the platform. There do exist some nice libraries like the Python deltabot-cli-py, but what if you don't want to use Python? Requiring robust SMTP/IMAP and PGP libraries, handling account management, etc for your language of choice is a tall order, but fret not -- you don't need to do any of that.
DeltaChat's core is written in Rust and has everything you need because it comes with a JSON-RPC server which makes it simple to integrate with any language. You must build the deltachat-rpc-server
binary:
$ git clone https://github.com/chatmail/core && cd core
$ cargo build --locked -p deltachat-rpc-server --release
You can find the binary in the target/release
directory.
Using the JSON-RPC
📢 Remember, the "id" is an integer you control to identify the RPC call's response.
The deltachat-rpc-server
is designed to operate over stdio much like the language servers for IDEs. You simply execute the binary from your language of choice and communicate with it over stdio with JSON formatted newline terminated strings.
⚠️ I previously pretty-printed the JSON so the formatting was nice, but if you wanted to copy/paste these for testing purposes it would cause issues so I am displaying the JSON strings exactly as you'd send and receive them.
The basic operation is as follows:
Account management
get_all_accounts
List the accounts stored in the SQLite database. In this example there are no accounts yet.
Request:
{"id":5,"params":[],"method":"get_all_accounts","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":5,"result":[]}
add_account
If there are no accounts, you'll need to add one. This simply adds a stub into the SQLite database and returns the id of the account. It's probably going to return account id 1.
Request:
{"id":2147,"params":[],"method":"add_account","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":2147,"result":1}
It returned the id of the account: 1
batch_set_config
Now you need to set some values for the account.
Request:
{"id":2179,"params":[1,{"addr":"your-random-addr@nine.testrun.org","bot":"1","displayname":"DeltaChatBot","mail_pw":"your-random-password"}],"method":"batch_set_config","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":2179,"result":null}
configure, get_all_accounts again
The account has enough configuration to operate. Now we need to mark the account as 'configured' so it is ready to be used. This will test a login and return an error message if it fails, so you can catch account errors here before proceeding.
Request:
{"id":37,"params":[1],"method":"configure","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":37,"result":null}
Let's get the accounts again so we can see what we're working with.
Request:
{"id":69,"params":[],"method":"get_all_accounts","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":69,"result":[{"addr":"your-random-addr@nine.testrun.org","color":"#de00ac","displayName":"DeltaChatBot","id":1,"kind":"Configured","privateTag":null,"profileImage":null}]}
get_chat_securejoin_qr_code
You probably want to get the invite link for the account so you can add it as a contact and start sending it test messages.
Request:
{"id":322,"params":[1,null],"method":"get_chat_securejoin_qr_code","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":322,"result":"https://i.delta.chat/REDACTED-INVITE-LINK-PARAMETERS"}
📢 If you pass a value of a chat_id instead of
null
as the second parameter you will get an invite for that chat group.
The Read Loop
start_io_for_all_accounts
Start the IO for all configured accounts. This will begin the process of watching for new messages, downloading them into the local SQLite database, and automatically deleting them from the IMAP server.
Request:
{"id":450,"params":[],"method":"start_io_for_all_accounts","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":450,"result":null}
wait_next_msgs
Now tell it to asynchronously wait for messages for your account_id. You will receive a list of one or more integer message ids.
Request:
{"id":482,"params":[1],"method":"wait_next_msgs","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":482,"result":[24]}
📢 After every time you receive a list of message IDs you will have to issue the "wait_next_msgs" again.
get_messages
Take the list of IDs and fetch the messages
Request:
{"id":899,"params":[1,[36]],"method":"get_messages","jsonrpc":"2.0"}
Now you will receive the JSON encoded messages. An example looks like the following when fetching message id "36".
Response:
{
"jsonrpc": "2.0",
"id": 899,
"result": {
"36": {
"chatId": 10,
"dimensionsHeight": 0,
"dimensionsWidth": 0,
"downloadState": "Done",
"duration": 0,
"error": null,
"file": null,
"fileBytes": 0,
"fileMime": null,
"fileName": null,
"fromId": 10,
"hasDeviatingTimestamp": false,
"hasHtml": false,
"hasLocation": false,
"id": 36,
"isBot": false,
"isEdited": false,
"isForwarded": false,
"isInfo": false,
"isSetupmessage": false,
"kind": "message",
"originalMsgId": null,
"overrideSenderName": null,
"parentId": 35,
"quote": null,
"reactions": null,
"receivedTimestamp": 1742767997,
"savedMessageId": null,
"sender": {
"address": "az2g6a4rm@chat.feld.me",
"authName": "Mark",
"color": "#008b16",
"displayName": "Mark",
"e2eeAvail": true,
"id": 10,
"isBlocked": false,
"isBot": false,
"isProfileVerified": true,
"isVerified": true,
"lastSeen": 1742767997,
"name": "",
"nameAndAddr": "Mark (az2g6a4rm@chat.feld.me)",
"profileImage": "accounts/7c4e2fd6-6c51-4f63-bc08-aa9adbdfd107/dc.db-blobs/d5e20b3667354ce4f02d60b61a76bc5.jpg",
"status": "",
"verifierId": 1,
"wasSeenRecently": true
},
"setupCodeBegin": null,
"showPadlock": true,
"sortTimestamp": 1742767997,
"state": 10,
"subject": "Re: Message from Mark",
"systemMessageType": "Unknown",
"text": "test message",
"timestamp": 1742767997,
"vcardContact": null,
"videochatType": null,
"videochatUrl": null,
"viewType": "Text",
"webxdcHref": null,
"webxdcInfo": null
}
}
}
📢 The message is deleted from the IMAP server once you've fetched it. This is handled by DeltaChat automatically.
markseen_msgs
You need to mark the message ids as seen before issuing the next wait_next_msgs
or the message id you've already processed will be returned in the list:
Request:
{"id":2850,"params":[1,[36]],"method":"markseen_msgs","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":2850,"result":null}
📢 You're technically marking it as 'seen' in the local SQLite database.
That's basically all it takes to receive the messages.
Sending Messages
send_msg
To send a message for account id 1 to chat_id 10 you use this method. It will return the id of the message you sent.
Request:
{"id":612,"params":[1,10,{"text":"testing"}],"method":"send_msg","jsonrpc":"2.0"}
Response:
{"jsonrpc":"2.0","id":612,"result":45}
Additional Information
There are example bots in a few different languages here which may serve as a good starting point for your experiments.
If your account is not configured as a bot (bot: 1
), you must first issue an accept_chat
RPC call and specify the chat_id
or you will not be able to send it messages.
📢 An OpenRPC JSON schema file can be output with
deltachat-rpc-server --openrpc
so you can investigate all available methods. However, it does not include all of the optional paramters that methods accept so you will have to do a little digging into the DeltaChat / Chatmail Rust core codebase until there is a better documentation source for these parameters.
For documentation the C API docs are the most complete, but the doxygen search is not very good, so you may need to supplement your research with information on the DeltaChat CFFI page. There is also some information that can be gleaned from the Javascript client classes here.
This is just scratching the surface of what is possible with DeltaChat's JSON-RPC. All of the pain of email and key management has been abstracted away for you. Writing your own custom integrations for this E2EE messaging ecosystem is easier than you probably imagined.
Call For Help
The docs could definitely be improved. Want to help? Please reach out to the team!