diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt
index 0d899a75faf449b156edba5c7f6427851a5826ab..858b612887bdbf9c345ec20b8718e2a796763e70 100644
--- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt
+++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/Database.kt
@@ -26,6 +26,10 @@ interface Database : TransactionManager {
     @Throws(DbException::class)
     fun mergeSettings(txn: Transaction, s: Settings, namespace: String)
 
+    /**
+     * Adds a contact to the database. It is the callers responsibility to use [getContact] before
+     * to check if a contact with the same ID already exists.
+     */
     @Throws(DbException::class)
     fun addContact(txn: Transaction, contact: Contact)
 
diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt
index fe61611bcf7ac2f461e05248838b7da3814edbf5..0d3ffe3bea9b09f39a2f4ad439155f4a688fe0ab 100644
--- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt
+++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/db/JdbcDatabase.kt
@@ -45,7 +45,8 @@ abstract class JdbcDatabase(private val dbTypes: DatabaseTypes, private val cloc
                            (contactId INT NOT NULL,
                            token _STRING NOT NULL,
                            inbox _STRING NOT NULL,
-                           outbox _STRING NOT NULL)
+                           outbox _STRING NOT NULL,
+                           PRIMARY KEY (contactId))
         """.trimIndent()
     }
 
diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/contacts/ContactsManagerIntegrationTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/contacts/ContactsManagerIntegrationTest.kt
index cc606b857fed7af0c3e6777b409b02cdd4ebf370..acfd7ef9d5d97fa331ad47552471735e476861bf 100644
--- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/contacts/ContactsManagerIntegrationTest.kt
+++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/contacts/ContactsManagerIntegrationTest.kt
@@ -21,6 +21,8 @@ import org.briarproject.mailbox.core.server.IntegrationTest
 import org.junit.jupiter.api.AfterEach
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
+import kotlin.math.max
+import kotlin.math.min
 import kotlin.test.assertEquals
 
 class ContactsManagerIntegrationTest : IntegrationTest() {
@@ -57,7 +59,7 @@ class ContactsManagerIntegrationTest : IntegrationTest() {
         val response: HttpResponse = httpClient.get("$baseUrl/contacts") {
             authenticateWithToken(ownerToken)
         }
-        assertJson("""{ "contacts": [ ${contact1.contactId}, ${contact2.contactId} ] }""", response)
+        assertJson("""{ "contacts": ${getJsonArray(contact1, contact2)} }""", response)
     }
 
     @Test
@@ -154,7 +156,7 @@ class ContactsManagerIntegrationTest : IntegrationTest() {
             authenticateWithToken(ownerToken)
         }
         assertJson(
-            """{ "contacts": [ ${contact1.contactId}, ${contact2.contactId} ] }""",
+            """{ "contacts": ${getJsonArray(contact1, contact2)} }""",
             response2
         )
     }
@@ -194,7 +196,7 @@ class ContactsManagerIntegrationTest : IntegrationTest() {
             authenticateWithToken(ownerToken)
         }
         assertJson(
-            """{ "contacts": [ ${contact1.contactId}, ${contact2.contactId} ] }""",
+            """{ "contacts": ${getJsonArray(contact1, contact2)} }""",
             response2
         )
     }
@@ -263,4 +265,14 @@ class ContactsManagerIntegrationTest : IntegrationTest() {
         assertEquals(BadRequest, response.status)
         assertEquals("Bad request: Invalid value for parameter contactId", response.readText())
     }
+
+    /**
+     * Getting contacts with a PRIMARY KEY seems to automatically order them by that key.
+     * So we need to sort the JSON array for a proper comparison.
+     */
+    private fun getJsonArray(c1: Contact, c2: Contact): String {
+        val lowId = min(c1.contactId, c2.contactId)
+        val highId = max(c1.contactId, c2.contactId)
+        return "[ $lowId, $highId ]"
+    }
 }
diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt
index d24afe2de61d1b8c71bbc7e5ea5d2ecb72e40769..d28009e730bfe29c9e51fcb940949eadf139ab7b 100644
--- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt
+++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/db/JdbcDatabaseTest.kt
@@ -1,11 +1,13 @@
 package org.briarproject.mailbox.core.db
 
 import org.briarproject.mailbox.core.TestUtils.deleteTestDirectory
+import org.briarproject.mailbox.core.TestUtils.getNewRandomContact
 import org.briarproject.mailbox.core.contacts.Contact
 import org.briarproject.mailbox.core.settings.Settings
 import org.briarproject.mailbox.core.system.Clock
 import org.briarproject.mailbox.core.system.RandomIdManager
 import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
 import org.junit.jupiter.api.io.TempDir
 import java.io.File
 import kotlin.test.assertEquals
@@ -86,6 +88,17 @@ abstract class JdbcDatabaseTest {
         db.close()
     }
 
+    @Test
+    fun `test that there can not be two contacts with same ID`() {
+        val db: Database = open(false)
+        db.write { txn ->
+            db.addContact(txn, getNewRandomContact(id = 1))
+            assertThrows<DbException> {
+                db.addContact(txn, getNewRandomContact(id = 1))
+            }
+        }
+    }
+
     @Test
     @Throws(java.lang.Exception::class)
     open fun testMergeSettings() {
diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/settings/MetadataRouteManagerTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/settings/MetadataRouteManagerTest.kt
index d758217f86d36d3581d142ea09cfc87988afe4ef..2e1a25b145e48abdc628e3ff583de98748f9cbd8 100644
--- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/settings/MetadataRouteManagerTest.kt
+++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/settings/MetadataRouteManagerTest.kt
@@ -6,6 +6,7 @@ import io.ktor.client.statement.readText
 import io.ktor.http.HttpStatusCode
 import kotlinx.coroutines.runBlocking
 import org.briarproject.mailbox.core.server.IntegrationTest
+import org.junit.jupiter.api.AfterEach
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import kotlin.test.assertEquals
@@ -19,6 +20,13 @@ class MetadataRouteManagerTest : IntegrationTest() {
         addContact(contact2)
     }
 
+    @AfterEach
+    fun clearDb() {
+        db.write { txn ->
+            db.clearDatabase(txn)
+        }
+    }
+
     @Test
     fun `owner can access status`(): Unit = runBlocking {
         val response: HttpResponse = httpClient.get("$baseUrl/status") {