Delta Chat Core C Interface
No Matches
Getting started

This document describes how to handle the Delta Chat core library. For general information about Delta Chat itself, see and

Let's start.

First of all, you have to create a context object bound to a database. The database is a normal SQLite file with a "blob directory" beside it. This will create "example.db" database and "example.db-blobs" directory if they don't exist already:

dc_context_t* context = dc_context_new(NULL, "example.db", NULL);
An object representing a single account.

After that, make sure you can receive events from the context. For that purpose, create an event emitter you can ask for events. If there is no event, the emitter will wait until there is one, so, in many situations, you will do this in a thread:

void* event_handler(void* context)
dc_event_emitter_t* emitter = dc_get_event_emitter(context);
dc_event_t* event;
while ((event = dc_get_next_event(emitter)) != NULL) {
// use the event as needed, e.g. dc_event_get_id() returns the type.
// once you're done, unref the event to avoid memory leakage:
static pthread_t event_thread;
pthread_create(&event_thread, NULL, event_handler, context);
Opaque object that is used to get events from a single context.
Opaque object describing a single event.

The example above uses "pthreads", however, you can also use anything else for thread handling.

Now you can configure the context:

// use some real test credentials here
dc_set_config(context, "addr", "");
dc_set_config(context, "mail_pw", "***");

dc_configure() returns immediately. The configuration itself runs in the background and may take a while. Once done, the DC_EVENT_CONFIGURE_PROGRESS reports success to the event_handler() you've defined above.

The configuration result is saved in the database. On subsequent starts it is not needed to call dc_configure() (you can check this using dc_is_configured()).

On a successfully configured context, you can finally connect to the servers:


Now you can send the first message:

// use a real testing address here
uint32_t contact_id = dc_create_contact(context, NULL, "");
uint32_t chat_id = dc_create_chat_by_contact_id(context, contact_id);
dc_send_text_msg(context, chat_id, "Hi, here is my first message!");

dc_send_text_msg() returns immediately; the sending itself is done in the background. If you check the testing address (bob), you should receive a normal e-mail. Answer this e-mail in any e-mail program with "Got it!", and the IO you started above will receive the message.

You can then list all messages of a chat as follows:

dc_array_t* msglist = dc_get_chat_msgs(context, chat_id, 0, 0);
for (int i = 0; i < dc_array_get_cnt(msglist); i++)
uint32_t msg_id = dc_array_get_id(msglist, i);
dc_msg_t* msg = dc_get_msg(context, msg_id);
char* text = dc_msg_get_text(msg);
printf("Message %i: %s\n", i+1, text);
An object containing a simple array.
An object representing a single message in memory.

This will output the following two lines:

Message 1: Hi, here is my first message!
Message 2: Got it!

Thread safety

All deltachat-core functions, unless stated otherwise, are thread-safe. In particular, it is safe to pass the same dc_context_t pointer to multiple functions running concurrently in different threads.

All the functions are guaranteed not to use the reference passed to them after returning. If the function spawns a long-running process, such as dc_configure() or dc_imex(), it will ensure that the objects passed to them are not deallocated as long as they are needed. For example, it is safe to call dc_imex(context, ...) and call dc_context_unref(context) immediately after return from dc_imex(). It is however not safe to call dc_context_unref(context) concurrently until dc_imex() returns, because dc_imex() may have not increased the reference count of dc_context_t yet.

This means that the context may be still in use after dc_context_unref() call. For example, it is possible to start the import/export process, call dc_context_unref(context) immediately after and observe DC_EVENT_IMEX_PROGRESS events via the event emitter. Once dc_get_next_event() returns NULL, it is safe to terminate the application.

It is recommended to create dc_context_t in the main thread and only call dc_context_unref() once other threads that may use it, such as the event loop thread, are terminated. Common mistake is to use dc_context_unref() as a way to cause dc_get_next_event() return NULL and terminate event loop this way. If event loop thread is inside a function taking dc_context_t as an argument at the moment dc_context_unref() is called on the main thread, the behavior is undefined.

Recommended way to safely terminate event loop and shutdown the application is to use a boolean variable indicating that the event loop should stop and check it in the event loop thread every time before calling dc_get_next_event(). To terminate the event loop, main thread should:

  1. Notify background threads, such as event loop (blocking in dc_get_next_event()) and message processing loop (blocking in dc_wait_next_msgs()), that they should terminate by atomically setting the boolean flag in the memory shared between the main thread and background loop threads.
  2. Call dc_stop_io() or dc_accounts_stop_io(), depending on whether a single account or account manager is used. Stopping I/O is guaranteed to emit at least one event and interrupt the event loop even if it was blocked on dc_get_next_event(). Stopping I/O is guaranteed to interrupt a single dc_wait_next_msgs().
  3. Wait until the event loop thread notices the flag, exits the event loop and terminates.
  4. Call dc_context_unref() or dc_accounts_unref().
  5. Keep calling dc_get_next_event() in a loop until it returns NULL, indicating that the contexts are deallocated.
  6. Terminate the application.

When using C API via FFI in runtimes that use automatic memory management, such as CPython, JVM or Node.js, take care to ensure the correct shutdown order and avoid calling dc_context_unref() or dc_accounts_unref() on the objects still in use in other threads, e.g. by keeping a reference to the wrapper object. The details depend on the runtime being used.

Class reference

For a class reference, see the "Classes" link atop.

Further hints

Here are some additional, unsorted hints that may be useful.

If you need further assistance, please do not hesitate to contact us through the channels shown at

Please keep in mind, that your derived work must respect the Mozilla Public License 2.0 of libdeltachat and the respective licenses of the libraries libdeltachat links with.

See you.