diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt
index 911a05eec12604178e961ae9ec1b5489ed6919d6..2ac199acf78194d66dd3a23dd60c9b52efa298ca 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt
@@ -6,8 +6,10 @@ import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
@@ -48,7 +50,7 @@ fun main() = preview(
     "selected" to false,
 ) {
     ContactCard(
-        ContactItem(
+        RealContactItem(
             contactId = ContactId(0),
             authorId = AuthorId(getRandomId()),
             name = getStringParameter("name"),
@@ -81,42 +83,18 @@ fun ContactCard(
     ) {
         Row(horizontalArrangement = Arrangement.SpaceBetween) {
             Row(modifier = Modifier.align(Alignment.CenterVertically).padding(horizontal = 16.dp)) {
-                Box(modifier = Modifier.align(Alignment.CenterVertically)) {
-                    // TODO Pull profile pictures
-                    ProfileCircle(36.dp, contactItem.authorId.bytes)
-                    // Draw new message counter
-                    if (contactItem.unread > 0) {
-                        Box(
-                            modifier = Modifier
-                                .align(Alignment.TopEnd)
-                                .offset(6.dp, (-6).dp)
-                                .height(20.dp)
-                                .widthIn(min = 20.dp, max = Dp.Infinity)
-                                .border(2.dp, outlineColor, CircleShape)
-                                .background(briarSecondary, CircleShape)
-                                .padding(horizontal = 6.dp)
-                        ) {
-                            Text(
-                                modifier = Modifier.align(Alignment.Center),
-                                fontSize = 8.sp,
-                                textAlign = TextAlign.Center,
-                                text = contactItem.unread.toString(),
-                                maxLines = 1
-                            )
-                        }
+                if (contactItem is RealContactItem) {
+                    Box(modifier = Modifier.align(Alignment.CenterVertically)) {
+                        // TODO Pull profile pictures
+                        ProfileCircle(36.dp, contactItem.authorId.bytes)
+                        MessageCounter(contactItem)
                     }
-                }
-                Column(modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp)) {
-                    Text(
-                        contactItem.displayName,
-                        fontSize = 14.sp,
-                        modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp)
-                    )
-                    Text(
-                        if (contactItem.isEmpty) i18n("contacts.card.nothing") else getFormattedTimestamp(contactItem.timestamp),
-                        fontSize = 10.sp,
-                        modifier = Modifier.align(Alignment.Start)
-                    )
+                    RealContactInfo(contactItem)
+                } else if (contactItem is PendingContactItem) {
+                    Box(modifier = Modifier.align(Alignment.CenterVertically)) {
+                        ProfileCircle(36.dp)
+                    }
+                    PendingContactInfo(contactItem)
                 }
             }
             Canvas(
@@ -134,3 +112,65 @@ fun ContactCard(
     }
     HorizontalDivider()
 }
+
+@Composable
+fun BoxScope.MessageCounter(contactItem: ContactItem) {
+    val outlineColor = MaterialTheme.colors.outline
+    val briarSecondary = MaterialTheme.colors.secondary
+    if (contactItem.unread > 0) {
+        Box(
+            modifier = Modifier
+                .align(Alignment.TopEnd)
+                .offset(6.dp, (-6).dp)
+                .height(20.dp)
+                .widthIn(min = 20.dp, max = Dp.Infinity)
+                .border(2.dp, outlineColor, CircleShape)
+                .background(briarSecondary, CircleShape)
+                .padding(horizontal = 6.dp)
+        ) {
+            Text(
+                modifier = Modifier.align(Alignment.Center),
+                fontSize = 8.sp,
+                textAlign = TextAlign.Center,
+                text = contactItem.unread.toString(),
+                maxLines = 1
+            )
+        }
+    }
+}
+
+@Composable
+fun RowScope.RealContactInfo(contactItem: RealContactItem) {
+    Column(modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp)) {
+        Text(
+            contactItem.displayName,
+            fontSize = 14.sp,
+            modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp)
+        )
+        Text(
+            if (contactItem.isEmpty) i18n("contacts.card.nothing") else getFormattedTimestamp(
+                contactItem.timestamp
+            ),
+            fontSize = 10.sp,
+            modifier = Modifier.align(Alignment.Start)
+        )
+    }
+}
+
+@Composable
+fun RowScope.PendingContactInfo(contactItem: PendingContactItem) {
+    Column(modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp)) {
+        Text(
+            contactItem.displayName,
+            fontSize = 14.sp,
+            modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp)
+        )
+        Text(
+            if (contactItem.isEmpty) i18n("contacts.card.pending") else getFormattedTimestamp(
+                contactItem.timestamp
+            ),
+            fontSize = 10.sp,
+            modifier = Modifier.align(Alignment.Start)
+        )
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt
index 43462c4da9fe403ef300db44546669ca03b86490..e68c0c69a673e2628f50b655518a7c93efeb35e3 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactInfoDrawer.kt
@@ -13,7 +13,7 @@ enum class ContactInfoDrawerState {
 
 @Composable
 fun ContactInfoDrawer(
-    contactItem: ContactItem,
+    contactItem: RealContactItem,
     setInfoDrawer: (Boolean) -> Unit,
     drawerState: ContactInfoDrawerState
 ) {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt
index 2ac68bc97de1277ad9e5176b913330398b2cb6a3..7509758e922afb0c223f5222b3a2dfe2fadfc080 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt
@@ -1,51 +1,11 @@
 package org.briarproject.briar.desktop.contact
 
-import org.briarproject.bramble.api.contact.Contact
-import org.briarproject.bramble.api.contact.ContactId
-import org.briarproject.bramble.api.identity.AuthorId
-import org.briarproject.briar.api.client.MessageTracker
-import kotlin.math.max
+sealed interface ContactItem {
 
-data class ContactItem(
-    val contactId: ContactId,
-    val authorId: AuthorId,
-    val name: String,
-    val alias: String?,
-    val isConnected: Boolean,
-    val isEmpty: Boolean,
-    val unread: Int,
+    val contactId: Any
+    val displayName: String
+    val isConnected: Boolean
+    val isEmpty: Boolean
+    val unread: Int
     val timestamp: Long
-) {
-
-    val displayName = if (alias == null) name else "$alias ($name)"
-
-    constructor(contact: Contact, isConnected: Boolean, groupCount: MessageTracker.GroupCount) :
-        this(
-            contactId = contact.id,
-            authorId = contact.author.id,
-            name = contact.author.name,
-            alias = contact.alias,
-            isConnected = isConnected,
-            isEmpty = groupCount.msgCount == 0,
-            unread = groupCount.unreadCount,
-            timestamp = groupCount.latestMsgTime
-        )
-
-    fun updateTimestampAndUnread(timestamp: Long, read: Boolean): ContactItem =
-        copy(
-            isEmpty = false,
-            unread = if (read) unread else unread + 1,
-            timestamp = max(timestamp, this.timestamp)
-        )
-
-    fun updateIsConnected(c: Boolean): ContactItem {
-        return copy(isConnected = c)
-    }
-
-    fun updateAlias(a: String?): ContactItem {
-        return copy(alias = a)
-    }
-
-    fun updateFromMessagesRead(c: Int): ContactItem =
-        copy(unread = unread - c)
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
index fd0d2957599f867b943d1134996179fd6cd8fbc0..66b96b6a6d2bc078e3775f5a325bae90b73084fb 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
@@ -16,7 +16,6 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import org.briarproject.bramble.api.contact.ContactId
 import org.briarproject.briar.desktop.contact.add.remote.AddContactDialog
 import org.briarproject.briar.desktop.theme.surfaceVariant
 import org.briarproject.briar.desktop.ui.Constants.CONTACTLIST_WIDTH
@@ -25,8 +24,8 @@ import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE
 @Composable
 fun ContactList(
     contactList: List<ContactItem>,
-    isSelected: (ContactId) -> Boolean,
-    selectContact: (ContactId) -> Unit,
+    isSelected: (Any) -> Boolean,
+    selectContact: (Any) -> Unit,
     filterBy: String,
     setFilterBy: (String) -> Unit,
 ) {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
index d4b8746edcf03768dac204f63d13f5bf7e4b674e..0e671a526743ca1bc5a6453facd1eab50667e481 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactListViewModel.kt
@@ -3,7 +3,6 @@ package org.briarproject.briar.desktop.contact
 import androidx.compose.runtime.mutableStateOf
 import mu.KotlinLogging
 import org.briarproject.bramble.api.connection.ConnectionRegistry
-import org.briarproject.bramble.api.contact.ContactId
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.contact.event.ContactAliasChangedEvent
 import org.briarproject.bramble.api.db.TransactionManager
@@ -41,16 +40,16 @@ constructor(
     }
 
     private val _filterBy = mutableStateOf("")
-    private val _selectedContactId = mutableStateOf<ContactId?>(null)
+    private val _selectedContactId = mutableStateOf<Any?>(null)
 
     val filterBy = _filterBy.asState()
     val selectedContactId = _selectedContactId.asState()
 
-    fun selectContact(contactId: ContactId) {
+    fun selectContact(contactId: Any) {
         _selectedContactId.value = contactId
     }
 
-    fun isSelected(contactId: ContactId) = _selectedContactId.value == contactId
+    fun isSelected(contactId: Any) = _selectedContactId.value == contactId
 
     override fun filterContactItem(contactItem: ContactItem) =
         contactItem.displayName.contains(_filterBy.value, ignoreCase = true)
@@ -75,15 +74,26 @@ constructor(
         when (e) {
             is ConversationMessageTrackedEvent -> {
                 LOG.info { "Conversation message tracked, updating item" }
-                updateItem(e.contactId) { it.updateTimestampAndUnread(e.timestamp, e.read) }
+                updateItem(e.contactId) {
+                    if (it is RealContactItem)
+                        it.updateTimestampAndUnread(e.timestamp, e.read)
+                    else it
+                }
             }
             // is AvatarUpdatedEvent -> {}
             is ContactAliasChangedEvent -> {
-                updateItem(e.contactId) { it.updateAlias(e.alias) }
+                updateItem(e.contactId) {
+                    if (it is RealContactItem)
+                        it.updateAlias(e.alias) else it
+                }
             }
             is ConversationMessagesReadEvent -> {
                 LOG.info("${e.count} conversation messages read, updating item")
-                updateItem(e.contactId) { it.updateFromMessagesRead(e.count) }
+                updateItem(e.contactId) {
+                    if (it is RealContactItem) {
+                        it.updateFromMessagesRead(e.count)
+                    } else it
+                }
             }
         }
     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
index 564d9b2d5177e013088be1025c4df13d1d488efd..4fc4251f4d8a10f93c92fd05a7f5862739c5ab13 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactsViewModel.kt
@@ -41,17 +41,28 @@ abstract class ContactsViewModel(
 
     protected open fun filterContactItem(contactItem: ContactItem) = true
 
-    open fun loadContacts() = runOnDbThreadWithTransaction(true) { txn ->
-        val contactList = contactManager.getContacts(txn).map { contact ->
-            ContactItem(
-                contact,
-                connectionRegistry.isConnected(contact.id),
-                conversationManager.getGroupCount(txn, contact.id),
+    open fun loadContacts() {
+        val contactList = mutableListOf<ContactItem>()
+        // TODO: move this inside transaction once briar has transactional method for this
+        contactList.addAll(
+            contactManager.pendingContacts.map { contact ->
+                PendingContactItem(contact.first)
+            }
+        )
+        runOnDbThreadWithTransaction(true) { txn ->
+            contactList.addAll(
+                contactManager.getContacts(txn).map { contact ->
+                    RealContactItem(
+                        contact,
+                        connectionRegistry.isConnected(contact.id),
+                        conversationManager.getGroupCount(txn, contact.id),
+                    )
+                }
             )
-        }
-        txn.attach {
-            _fullContactList.clearAndAddAll(contactList)
-            updateFilteredList()
+            txn.attach {
+                _fullContactList.clearAndAddAll(contactList)
+                updateFilteredList()
+            }
         }
     }
 
@@ -70,11 +81,19 @@ abstract class ContactsViewModel(
             }
             is ContactConnectedEvent -> {
                 LOG.info("Contact connected, update state")
-                updateItem(e.contactId) { it.updateIsConnected(true) }
+                updateItem(e.contactId) {
+                    if (it is RealContactItem)
+                        it.updateIsConnected(true)
+                    else it
+                }
             }
             is ContactDisconnectedEvent -> {
                 LOG.info("Contact disconnected, update state")
-                updateItem(e.contactId) { it.updateIsConnected(false) }
+                updateItem(e.contactId) {
+                    if (it is RealContactItem)
+                        it.updateIsConnected(false)
+                    else it
+                }
             }
             is ContactRemovedEvent -> {
                 LOG.info("Contact removed, removing item")
@@ -84,12 +103,23 @@ abstract class ContactsViewModel(
     }
 
     protected open fun updateItem(contactId: ContactId, update: (ContactItem) -> ContactItem) {
-        _fullContactList.replaceFirst({ it.contactId == contactId }, update)
+        _fullContactList.replaceFirst(
+            {
+                if (it is RealContactItem)
+                    it.contactId == contactId
+                else false
+            },
+            update
+        )
         updateFilteredList()
     }
 
     protected open fun removeItem(contactId: ContactId) {
-        _fullContactList.removeFirst { it.contactId == contactId }
+        _fullContactList.removeFirst {
+            if (it is RealContactItem)
+                it.contactId == contactId
+            else false
+        }
         updateFilteredList()
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/PendingContactItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/PendingContactItem.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1472c4c23d50c0ad0ba1b85624f73f404737b7b1
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/PendingContactItem.kt
@@ -0,0 +1,31 @@
+package org.briarproject.briar.desktop.contact
+
+import org.briarproject.bramble.api.contact.PendingContact
+import org.briarproject.bramble.api.contact.PendingContactId
+
+data class PendingContactItem(
+    override val contactId: PendingContactId,
+    val name: String,
+    val alias: String?,
+    override val isConnected: Boolean,
+    override val isEmpty: Boolean,
+    override val unread: Int,
+    override val timestamp: Long
+) : ContactItem {
+
+    override val displayName = if (alias == null) name else "$alias ($name)"
+
+    constructor(contact: PendingContact) : this(
+        contactId = contact.id,
+        name = contact.alias,
+        alias = contact.alias,
+        isConnected = false,
+        isEmpty = true,
+        unread = 0,
+        timestamp = contact.timestamp
+    )
+
+    fun updateAlias(a: String?): PendingContactItem {
+        return copy(alias = a)
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ProfileCircle.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ProfileCircle.kt
index e2fcbf24336dcbed7ecf24ba62fb52cb931eea22..34e723dafe870b63a8f552016aedfd6ec8052723 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ProfileCircle.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ProfileCircle.kt
@@ -35,3 +35,13 @@ fun ProfileCircle(size: Dp, input: ByteArray) {
         Identicon(input, this.size.width, this.size.height).draw(this)
     }
 }
+
+/**
+ * Used for pending contacts.
+ */
+@Composable
+fun ProfileCircle(size: Dp) {
+    Canvas(Modifier.size(size).clip(CircleShape).border(2.dp, MaterialTheme.colors.outline, CircleShape)) {
+        // TODO what to display here?
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/RealContactItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/RealContactItem.kt
new file mode 100644
index 0000000000000000000000000000000000000000..100bc579750a1f7b8fc0f10ac9b09dfc26ca9426
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/RealContactItem.kt
@@ -0,0 +1,50 @@
+package org.briarproject.briar.desktop.contact
+
+import org.briarproject.bramble.api.contact.Contact
+import org.briarproject.bramble.api.contact.ContactId
+import org.briarproject.bramble.api.identity.AuthorId
+import org.briarproject.briar.api.client.MessageTracker
+import kotlin.math.max
+
+data class RealContactItem(
+    override val contactId: ContactId,
+    val authorId: AuthorId,
+    val name: String,
+    val alias: String?,
+    override val isConnected: Boolean,
+    override val isEmpty: Boolean,
+    override val unread: Int,
+    override val timestamp: Long
+) : ContactItem {
+
+    override val displayName = if (alias == null) name else "$alias ($name)"
+
+    constructor(contact: Contact, isConnected: Boolean, groupCount: MessageTracker.GroupCount) : this(
+        contactId = contact.id,
+        authorId = contact.author.id,
+        name = contact.author.name,
+        alias = contact.alias,
+        isConnected = isConnected,
+        isEmpty = groupCount.msgCount == 0,
+        unread = groupCount.unreadCount,
+        timestamp = groupCount.latestMsgTime
+    )
+
+    fun updateTimestampAndUnread(timestamp: Long, read: Boolean): RealContactItem =
+        copy(
+            isEmpty = false,
+            unread = if (read) unread else unread + 1,
+            timestamp = max(timestamp, this.timestamp)
+        )
+
+    fun updateIsConnected(c: Boolean): RealContactItem {
+        return copy(isConnected = c)
+    }
+
+    fun updateAlias(a: String?): RealContactItem {
+        return copy(alias = a)
+    }
+
+    fun updateFromMessagesRead(c: Int): RealContactItem =
+        copy(unread = unread - c)
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
index deeca393355e666042958afdb4135950c970ba6a..3dadbcfb8f8b595fdc6e2de8e817d390ffb5d50e 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
@@ -24,6 +24,7 @@ import androidx.compose.ui.unit.sp
 import org.briarproject.briar.desktop.contact.ContactDropDown
 import org.briarproject.briar.desktop.contact.ContactItem
 import org.briarproject.briar.desktop.contact.ProfileCircle
+import org.briarproject.briar.desktop.contact.RealContactItem
 import org.briarproject.briar.desktop.theme.outline
 import org.briarproject.briar.desktop.theme.surfaceVariant
 import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE
@@ -42,7 +43,8 @@ fun ConversationHeader(
 
     Box(modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp)) {
         Row(modifier = Modifier.align(Alignment.Center)) {
-            ProfileCircle(36.dp, contactItem.authorId.bytes)
+            if (contactItem is RealContactItem)
+                ProfileCircle(36.dp, contactItem.authorId.bytes)
             Canvas(
                 modifier = Modifier.align(Alignment.CenterVertically),
                 onDraw = {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
index b4356dc1cac2ec71607efd126e3618d0591fab00..f41bda85ba9a025d0e3b000a85013b63df0354f5 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
@@ -32,7 +32,7 @@ import org.briarproject.briar.api.messaging.MessagingManager
 import org.briarproject.briar.api.messaging.PrivateMessage
 import org.briarproject.briar.api.messaging.PrivateMessageFactory
 import org.briarproject.briar.api.messaging.PrivateMessageHeader
-import org.briarproject.briar.desktop.contact.ContactItem
+import org.briarproject.briar.desktop.contact.RealContactItem
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.utils.KLoggerUtils.logDuration
 import org.briarproject.briar.desktop.utils.clearAndAddAll
@@ -62,7 +62,7 @@ constructor(
     }
 
     private val _contactId = mutableStateOf<ContactId?>(null)
-    private val _contactItem = mutableStateOf<ContactItem?>(null)
+    private val _contactItem = mutableStateOf<RealContactItem?>(null)
     private val _messages = mutableStateListOf<ConversationItem>()
 
     private val _newMessage = mutableStateOf("")
@@ -163,7 +163,7 @@ constructor(
     private fun loadContact(id: ContactId) = runOnDbThreadWithTransaction(true) { txn ->
         try {
             val start = LogUtils.now()
-            val contactItem = ContactItem(
+            val contactItem = RealContactItem(
                 contactManager.getContact(txn, id),
                 connectionRegistry.isConnected(id),
                 conversationManager.getGroupCount(txn, id),
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt
index 3fc3d795352aa9be3c7f80294c4a992c1ddefcad..0637a7a5b2128ee650536498cad17d9b012c303a 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import org.briarproject.bramble.api.contact.ContactId
 import org.briarproject.briar.desktop.contact.ContactList
 import org.briarproject.briar.desktop.contact.ContactListViewModel
 import org.briarproject.briar.desktop.ui.UiPlaceholder
@@ -27,7 +28,7 @@ fun PrivateMessageScreen(
         VerticalDivider()
         Column(modifier = Modifier.weight(1f).fillMaxHeight()) {
             val id = viewModel.selectedContactId.value
-            if (id != null) {
+            if (id != null && id is ContactId) {
                 ConversationScreen(id)
             } else {
                 UiPlaceholder()
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt
index e2c12e52f778a113df66011e052d9082661ea1b8..b8fe9a3350915d09831c372a3c6c23919e9c0cf6 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt
@@ -27,8 +27,8 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import org.briarproject.briar.desktop.contact.ContactCard
-import org.briarproject.briar.desktop.contact.ContactItem
 import org.briarproject.briar.desktop.contact.ProfileCircle
+import org.briarproject.briar.desktop.contact.RealContactItem
 import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE
 import org.briarproject.briar.desktop.ui.HorizontalDivider
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
@@ -38,7 +38,7 @@ import java.util.Locale
 
 @Composable
 fun ContactDrawerMakeIntro(
-    contactItem: ContactItem,
+    contactItem: RealContactItem,
     setInfoDrawer: (Boolean) -> Unit,
     viewModel: IntroductionViewModel = viewModel(),
 ) {
@@ -64,11 +64,12 @@ fun ContactDrawerMakeIntro(
                 HorizontalDivider()
                 LazyColumn {
                     items(viewModel.contactList) { contactItem ->
-                        ContactCard(
-                            contactItem,
-                            { viewModel.setSecondContact(contactItem) },
-                            false
-                        )
+                        if (contactItem is RealContactItem)
+                            ContactCard(
+                                contactItem,
+                                { viewModel.setSecondContact(contactItem) },
+                                false
+                            )
                     }
                 }
             }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
index 4165c9128b11c948ee0ca9a61e6ba850f5528a89..1d7e290db1c29592944581822368f54beddfe164 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/IntroductionViewModel.kt
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.desktop.contact.ContactItem
 import org.briarproject.briar.desktop.contact.ContactsViewModel
+import org.briarproject.briar.desktop.contact.RealContactItem
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.viewmodel.asState
 import javax.inject.Inject
@@ -27,8 +28,8 @@ constructor(
     contactManager, conversationManager, connectionRegistry, briarExecutors, lifecycleManager, db, eventBus
 ) {
 
-    private val _firstContact = mutableStateOf<ContactItem?>(null)
-    private val _secondContact = mutableStateOf<ContactItem?>(null)
+    private val _firstContact = mutableStateOf<RealContactItem?>(null)
+    private val _secondContact = mutableStateOf<RealContactItem?>(null)
     private val _secondScreen = mutableStateOf(false)
     private val _introductionMessage = mutableStateOf("")
 
@@ -37,13 +38,13 @@ constructor(
     val secondScreen = _secondScreen.asState()
     val introductionMessage = _introductionMessage.asState()
 
-    fun setFirstContact(contactItem: ContactItem) {
+    fun setFirstContact(contactItem: RealContactItem) {
         _firstContact.value = contactItem
         loadContacts()
         backToFirstScreen()
     }
 
-    fun setSecondContact(contactItem: ContactItem) {
+    fun setSecondContact(contactItem: RealContactItem) {
         _secondContact.value = contactItem
         _secondScreen.value = true
     }
@@ -58,6 +59,8 @@ constructor(
     }
 
     override fun filterContactItem(contactItem: ContactItem): Boolean {
-        return _firstContact.value?.contactId != contactItem.contactId
+        return if (contactItem is RealContactItem) {
+            _firstContact.value?.contactId != contactItem.contactId
+        } else false
     }
 }
diff --git a/src/main/resources/strings/BriarDesktop.properties b/src/main/resources/strings/BriarDesktop.properties
index 94117f65d4c199db78e838138b8e23c69be71cd7..c235a3deac97ad262f7b67d769ec7b253218a0c9 100644
--- a/src/main/resources/strings/BriarDesktop.properties
+++ b/src/main/resources/strings/BriarDesktop.properties
@@ -14,6 +14,7 @@ access.swap=Icon showing errors between two contacts
 
 # Contacts
 contacts.card.nothing=No messages.
+contacts.card.pending=Pending contact
 contacts.dropdown.connections=Connections
 contacts.dropdown.connections.title=Connections
 contacts.dropdown.connections.bluetooth=Connect via Bluetooth