diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd0273c9397..5b72a876f49 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
 
 ### Enhancements
 * <New feature description> (PR [#????](https://github.com/realm/realm-core/pull/????))
-* None.
+* Refactored how KeyPathArrays are created in the C-API by adding `realm_create_key_path_array` which allows a SDK to pass in the string representation of the keypath and then let Core calculate the correct TableKey/ColKey pairs instead of doing this on the SDK side. (Issue [#7087](https://github.com/realm/realm-core/pull/7087))
 
 ### Fixed
 * <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
diff --git a/src/realm.h b/src/realm.h
index 47721658038..f9d102c59db 100644
--- a/src/realm.h
+++ b/src/realm.h
@@ -65,6 +65,7 @@ typedef void (*realm_free_userdata_func_t)(realm_userdata_t userdata);
 typedef realm_userdata_t (*realm_clone_userdata_func_t)(const realm_userdata_t userdata);
 typedef void (*realm_on_object_store_thread_callback_t)(realm_userdata_t userdata);
 typedef bool (*realm_on_object_store_error_callback_t)(realm_userdata_t userdata, const char*);
+typedef struct realm_key_path_array realm_key_path_array_t;
 
 /* Accessor types */
 typedef struct realm_object realm_object_t;
@@ -201,18 +202,6 @@ typedef struct realm_value {
     } RLM_ANON_UNION_MEMBER(values);
     realm_value_type_e type;
 } realm_value_t;
-typedef struct realm_key_path_elem {
-    realm_class_key_t object;
-    realm_property_key_t property;
-} realm_key_path_elem_t;
-typedef struct realm_key_path {
-    size_t nb_elements;
-    realm_key_path_elem_t* path_elements;
-} realm_key_path_t;
-typedef struct realm_key_path_array {
-    size_t nb_elements;
-    realm_key_path_t* paths;
-} realm_key_path_array_t;
 typedef struct realm_query_arg {
     size_t nb_args;
     bool is_list;
@@ -1606,6 +1595,16 @@ RLM_API realm_class_key_t realm_object_get_table(const realm_object_t* object);
  */
 RLM_API realm_link_t realm_object_as_link(const realm_object_t* object);
 
+/**
+ * Helper method for making it easier to to convert SDK input to the underlying
+ * `realm_key_path_array_t`.
+ *
+ * @return A pointer to the converted key path array. NULL in case of an error.
+ */
+RLM_API realm_key_path_array_t* realm_create_key_path_array(const realm_t* realm,
+                                                            const realm_class_key_t object_class_key,
+                                                            size_t num_key_paths, const char** user_key_paths);
+
 /**
  * Subscribe to notifications for this object.
  *
@@ -1613,7 +1612,7 @@ RLM_API realm_link_t realm_object_as_link(const realm_object_t* object);
  */
 RLM_API realm_notification_token_t* realm_object_add_notification_callback(realm_object_t*, realm_userdata_t userdata,
                                                                            realm_free_userdata_func_t userdata_free,
-                                                                           realm_key_path_array_t*,
+                                                                           realm_key_path_array_t* key_path_array,
                                                                            realm_on_object_change_func_t on_change);
 
 /**
@@ -1872,7 +1871,7 @@ RLM_API bool realm_list_remove_all(realm_list_t*);
  */
 RLM_API realm_notification_token_t* realm_list_add_notification_callback(realm_list_t*, realm_userdata_t userdata,
                                                                          realm_free_userdata_func_t userdata_free,
-                                                                         realm_key_path_array_t*,
+                                                                         realm_key_path_array_t* key_path_array,
                                                                          realm_on_collection_change_func_t on_change);
 
 /**
@@ -2165,7 +2164,7 @@ RLM_API bool realm_set_remove_all(realm_set_t*);
  */
 RLM_API realm_notification_token_t* realm_set_add_notification_callback(realm_set_t*, realm_userdata_t userdata,
                                                                         realm_free_userdata_func_t userdata_free,
-                                                                        realm_key_path_array_t*,
+                                                                        realm_key_path_array_t* key_path_array,
                                                                         realm_on_collection_change_func_t on_change);
 /**
  * Get an set from a thread-safe reference, potentially originating in a
@@ -2345,10 +2344,9 @@ RLM_API bool realm_dictionary_clear(realm_dictionary_t*);
  *
  * @return A non-null pointer if no exception occurred.
  */
-RLM_API realm_notification_token_t*
-realm_dictionary_add_notification_callback(realm_dictionary_t*, realm_userdata_t userdata,
-                                           realm_free_userdata_func_t userdata_free, realm_key_path_array_t*,
-                                           realm_on_dictionary_change_func_t on_change);
+RLM_API realm_notification_token_t* realm_dictionary_add_notification_callback(
+    realm_dictionary_t*, realm_userdata_t userdata, realm_free_userdata_func_t userdata_free,
+    realm_key_path_array_t* key_path_array, realm_on_dictionary_change_func_t on_change);
 
 /**
  * Get an dictionary from a thread-safe reference, potentially originating in a
@@ -2696,7 +2694,7 @@ RLM_API bool realm_results_average(realm_results_t*, realm_property_key_t, realm
 RLM_API realm_notification_token_t* realm_results_add_notification_callback(realm_results_t*,
                                                                             realm_userdata_t userdata,
                                                                             realm_free_userdata_func_t userdata_free,
-                                                                            realm_key_path_array_t*,
+                                                                            realm_key_path_array_t* key_path_array,
                                                                             realm_on_collection_change_func_t);
 
 /**
diff --git a/src/realm/CMakeLists.txt b/src/realm/CMakeLists.txt
index ea00171cb4a..73ed848a139 100644
--- a/src/realm/CMakeLists.txt
+++ b/src/realm/CMakeLists.txt
@@ -234,7 +234,6 @@ set(REALM_INSTALL_HEADERS
     util/memory_stream.hpp
     util/misc_errors.hpp
     util/misc_ext_errors.hpp
-    util/miscellaneous.hpp
     util/optional.hpp
     util/overload.hpp
     util/platform_info.hpp
diff --git a/src/realm/alloc_slab.cpp b/src/realm/alloc_slab.cpp
index 7aef66d5a26..cede4a52b82 100644
--- a/src/realm/alloc_slab.cpp
+++ b/src/realm/alloc_slab.cpp
@@ -37,7 +37,6 @@
 
 #include <realm/util/errno.hpp>
 #include <realm/util/encrypted_file_mapping.hpp>
-#include <realm/util/miscellaneous.hpp>
 #include <realm/util/terminate.hpp>
 #include <realm/util/thread.hpp>
 #include <realm/util/scope_exit.hpp>
diff --git a/src/realm/group.cpp b/src/realm/group.cpp
index c792021430f..d3d3f6f6a5a 100644
--- a/src/realm/group.cpp
+++ b/src/realm/group.cpp
@@ -27,7 +27,6 @@
 
 #include <realm/util/file_mapper.hpp>
 #include <realm/util/memory_stream.hpp>
-#include <realm/util/miscellaneous.hpp>
 #include <realm/util/thread.hpp>
 #include <realm/impl/destroy_guard.hpp>
 #include <realm/utilities.hpp>
diff --git a/src/realm/group_writer.cpp b/src/realm/group_writer.cpp
index 41225a60753..9243d23e0b6 100644
--- a/src/realm/group_writer.cpp
+++ b/src/realm/group_writer.cpp
@@ -28,7 +28,6 @@
 #include <realm/disable_sync_to_disk.hpp>
 #include <realm/impl/destroy_guard.hpp>
 #include <realm/impl/simulated_failure.hpp>
-#include <realm/util/miscellaneous.hpp>
 #include <realm/util/safe_int_ops.hpp>
 
 using namespace realm;
diff --git a/src/realm/object-store/c_api/notifications.cpp b/src/realm/object-store/c_api/notifications.cpp
index 55ca16b27db..a48440d1bd2 100644
--- a/src/realm/object-store/c_api/notifications.cpp
+++ b/src/realm/object-store/c_api/notifications.cpp
@@ -1,5 +1,6 @@
 #include <realm/object-store/c_api/types.hpp>
 #include <realm/object-store/c_api/util.hpp>
+#include <realm/object-store/keypath_helpers.hpp>
 
 namespace realm::c_api {
 namespace {
@@ -65,24 +66,79 @@ struct DictionaryNotificationsCallback {
 
 std::optional<KeyPathArray> build_key_path_array(realm_key_path_array_t* key_path_array)
 {
-    if (key_path_array) {
-        KeyPathArray ret;
-        for (size_t i = 0; i < key_path_array->nb_elements; i++) {
-            realm_key_path_t* key_path = key_path_array->paths + i;
-            ret.emplace_back();
-            KeyPath& kp = ret.back();
-            for (size_t j = 0; j < key_path->nb_elements; j++) {
-                realm_key_path_elem_t* path_elem = key_path->path_elements + j;
-                kp.emplace_back(TableKey(path_elem->object), ColKey(path_elem->property));
+    std::optional<KeyPathArray> ret;
+    if (key_path_array && key_path_array->size()) {
+        ret.emplace(std::move(*key_path_array));
+    }
+    return ret;
+}
+
+static KeyPathArray create_key_path_array(Group& g, const ObjectSchema& object_schema, const Schema& schema,
+                                          const char** all_key_paths_begin, const char** all_key_paths_end)
+{
+    KeyPathArray resolved_key_path_array;
+    for (auto it = all_key_paths_begin; it != all_key_paths_end; it++) {
+        KeyPath resolved_key_path;
+        const ObjectSchema* schema_at_index = &object_schema;
+        const Property* prop = nullptr;
+        // Split string based on '.'
+        const char* path = *it;
+        do {
+            auto p = find_chr(path, '.');
+            StringData property(path, p - path);
+            path = p;
+            if (!schema_at_index) {
+                throw InvalidArgument(
+                    util::format("Property '%1' in KeyPath '%2' is not a collection of objects or an object "
+                                 "reference, so it cannot be used as an intermediate keypath element.",
+                                 prop->public_name, *it));
             }
-        }
-        return ret;
+            prop = schema_at_index->property_for_public_name(property);
+            if (prop) {
+                ColKey col_key = prop->column_key;
+                TableKey table_key = schema_at_index->table_key;
+                if (prop->type == PropertyType::Object || prop->type == PropertyType::LinkingObjects) {
+                    auto found_schema = schema.find(prop->object_type);
+                    if (found_schema != schema.end()) {
+                        schema_at_index = &*found_schema;
+                        if (prop->type == PropertyType::LinkingObjects) {
+                            // Find backlink column key
+                            auto origin_prop = schema_at_index->property_for_name(prop->link_origin_property_name);
+                            auto origin_table = ObjectStore::table_for_object_type(g, schema_at_index->name);
+                            col_key = origin_table->get_opposite_column(origin_prop->column_key);
+                        }
+                    }
+                }
+                resolved_key_path.emplace_back(table_key, col_key);
+            }
+            else {
+                throw InvalidArgument(util::format("Property '%1' in KeyPath '%2' is not a valid property in %3.",
+                                                   property, *it, schema_at_index->name));
+            }
+        } while (*path++ == '.');
+        resolved_key_path_array.push_back(std::move(resolved_key_path));
     }
-    return std::nullopt;
+    return resolved_key_path_array;
 }
 
 } // namespace
 
+RLM_API realm_key_path_array_t* realm_create_key_path_array(const realm_t* realm,
+                                                            const realm_class_key_t object_class_key,
+                                                            size_t num_key_paths, const char** user_key_paths)
+{
+    return wrap_err([&]() {
+        KeyPathArray ret;
+        if (user_key_paths) {
+            const Schema& schema = (*realm)->schema();
+            const ObjectSchema& object_schema = schema_for_table(*realm, TableKey(object_class_key));
+            ret = create_key_path_array((*realm)->read_group(), object_schema, schema, user_key_paths,
+                                        user_key_paths + num_key_paths);
+        }
+        return new realm_key_path_array_t(std::move(ret));
+    });
+}
+
 RLM_API realm_notification_token_t* realm_object_add_notification_callback(realm_object_t* obj,
                                                                            realm_userdata_t userdata,
                                                                            realm_free_userdata_func_t free,
diff --git a/src/realm/object-store/c_api/types.hpp b/src/realm/object-store/c_api/types.hpp
index f9f5ddce403..b3a521e55c0 100644
--- a/src/realm/object-store/c_api/types.hpp
+++ b/src/realm/object-store/c_api/types.hpp
@@ -411,6 +411,13 @@ struct realm_dictionary : realm::c_api::WrapC, realm::object_store::Dictionary {
     }
 };
 
+struct realm_key_path_array : realm::c_api::WrapC, realm::KeyPathArray {
+    explicit realm_key_path_array(realm::KeyPathArray kpa)
+        : realm::KeyPathArray(std::move(kpa))
+    {
+    }
+};
+
 struct realm_object_changes : realm::c_api::WrapC, realm::CollectionChangeSet {
     explicit realm_object_changes(realm::CollectionChangeSet changes)
         : realm::CollectionChangeSet(std::move(changes))
diff --git a/src/realm/query_engine.hpp b/src/realm/query_engine.hpp
index c912fadff60..cea9ae49db0 100644
--- a/src/realm/query_engine.hpp
+++ b/src/realm/query_engine.hpp
@@ -88,7 +88,6 @@ TConditionValue:    Type of values in condition column. That is, int64_t, float,
 #include <realm/table.hpp>
 #include <realm/column_integer.hpp>
 #include <realm/unicode.hpp>
-#include <realm/util/miscellaneous.hpp>
 #include <realm/util/serializer.hpp>
 #include <realm/utilities.hpp>
 #include <realm/index_string.hpp>
diff --git a/src/realm/table.cpp b/src/realm/table.cpp
index f1a53e03c4a..cbffc93e1c6 100644
--- a/src/realm/table.cpp
+++ b/src/realm/table.cpp
@@ -34,7 +34,6 @@
 #include <realm/replication.hpp>
 #include <realm/table_view.hpp>
 #include <realm/util/features.h>
-#include <realm/util/miscellaneous.hpp>
 #include <realm/util/serializer.hpp>
 
 #include <stdexcept>
diff --git a/src/realm/util/miscellaneous.hpp b/src/realm/util/miscellaneous.hpp
deleted file mode 100644
index c45e4f3999a..00000000000
--- a/src/realm/util/miscellaneous.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*************************************************************************
- *
- * Copyright 2016 Realm Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- **************************************************************************/
-
-#ifndef REALM_UTIL_MISCELLANEOUS_HPP
-#define REALM_UTIL_MISCELLANEOUS_HPP
-
-#include <type_traits>
-
-namespace realm {
-namespace util {
-
-// FIXME: Replace this with std::add_const_t when we switch over to C++14 by
-// default.
-/// \brief Adds const qualifier, unless T already has the const qualifier
-template <class T>
-using add_const_t = typename std::add_const<T>::type;
-
-// FIXME: Replace this with std::as_const when we switch over to C++17 by
-// default.
-/// \brief Forms an lvalue reference to const T
-template <class T>
-constexpr add_const_t<T>& as_const(T& v) noexcept
-{
-    return v;
-}
-
-/// \brief Disallows rvalue arguments
-template <class T>
-add_const_t<T>& as_const(const T&&) = delete;
-
-} // namespace util
-} // namespace realm
-
-#endif // REALM_UTIL_MISCELLANEOUS_HPP
diff --git a/src/realm/utilities.hpp b/src/realm/utilities.hpp
index e699cfc1ff8..90a51031eb2 100644
--- a/src/realm/utilities.hpp
+++ b/src/realm/utilities.hpp
@@ -361,6 +361,14 @@ constexpr inline size_t round_down(size_t p, size_t align)
     return r & (~(align - 1));
 }
 
+// return pointer to found character or to terminating NUL
+static inline const char* find_chr(const char* p, char c)
+{
+    while (*p && *p != c) {
+        ++p;
+    }
+    return p;
+}
 
 #ifdef _WIN32
 typedef HANDLE FileDesc;
diff --git a/test/object-store/c_api/c_api.cpp b/test/object-store/c_api/c_api.cpp
index 4eb56d06fa7..9565402a1ff 100644
--- a/test/object-store/c_api/c_api.cpp
+++ b/test/object-store/c_api/c_api.cpp
@@ -2752,28 +2752,88 @@ TEST_CASE("C API - properties", "[c_api]") {
                         CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
                     });
 
-                    realm_key_path_elem_t bar_strings[] = {{class_bar.key, bar_doubles_key}};
-                    realm_key_path_t key_path_bar_strings[] = {{1, bar_strings}};
-                    realm_key_path_array_t key_path_array = {1, key_path_bar_strings};
-                    auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
-                                                                                   &key_path_array, on_change));
-                    checked(realm_refresh(realm, nullptr));
-
-                    state.called = false;
-                    write([&]() {
-                        checked(realm_set_value(obj2.get(), bar_doubles_key, rlm_double_val(5.0), false));
-                    });
-                    REQUIRE(state.called);
-                    CHECK(!state.error);
-                    CHECK(state.changes);
-
-                    state.called = false;
-                    write([&]() {
-                        checked(realm_list_insert(strings.get(), 0, str1));
-                        checked(realm_list_insert(strings.get(), 1, str2));
-                        checked(realm_list_insert(strings.get(), 2, null));
-                    });
-                    REQUIRE(!state.called);
+                    SECTION("using valid key") {
+                        const char* bar_strings[1] = {"doubles"};
+                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
+                        REQUIRE(key_path_array);
+                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
+                                                                                       key_path_array, on_change));
+                        realm_release(key_path_array);
+                        checked(realm_refresh(realm, nullptr));
+
+                        state.called = false;
+                        write([&]() {
+                            checked(realm_set_value(obj2.get(), bar_doubles_key, rlm_double_val(5.0), false));
+                        });
+                        REQUIRE(state.called);
+                        CHECK(!state.error);
+                        CHECK(state.changes);
+
+                        state.called = false;
+                        write([&]() {
+                            checked(realm_list_insert(strings.get(), 0, str1));
+                            checked(realm_list_insert(strings.get(), 1, str2));
+                            checked(realm_list_insert(strings.get(), 2, null));
+                        });
+                        REQUIRE(!state.called);
+                    }
+                    SECTION("using invalid key") {
+                        const char* bar_strings[1] = {"dobles"};
+                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
+                        REQUIRE(!key_path_array);
+                        realm_clear_last_error();
+                    }
+                    SECTION("using valid nesting") {
+                        realm_property_info_t info;
+                        bool found = false;
+                        realm_find_property(realm, class_bar.key, "sub", &found, &info);
+                        auto bar_sub_key = info.key;
+                        realm_find_property(realm, class_embedded.key, "int", &found, &info);
+                        auto embedded_int_key = info.key;
+                        CPtr<realm_object_t> embedded;
+                        write([&]() {
+                            embedded = cptr_checked(realm_set_embedded(obj2.get(), bar_sub_key));
+                        });
+
+                        const char* bar_strings[1] = {"sub.int"};
+                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
+                        REQUIRE(key_path_array);
+                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
+                                                                                       key_path_array, on_change));
+                        realm_release(key_path_array);
+                        checked(realm_refresh(realm, nullptr));
+
+                        state.called = false;
+                        write([&]() {
+                            checked(realm_set_value(embedded.get(), embedded_int_key, rlm_int_val(999), false));
+                        });
+                        REQUIRE(state.called);
+                        CHECK(!state.error);
+                        CHECK(state.changes);
+                    }
+                    SECTION("using backlink") {
+                        const char* bar_strings[1] = {"linking_objects.public_int"};
+                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
+                        REQUIRE(key_path_array);
+                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
+                                                                                       key_path_array, on_change));
+                        realm_release(key_path_array);
+                        checked(realm_refresh(realm, nullptr));
+
+                        state.called = false;
+                        write([&]() {
+                            checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
+                        });
+                        REQUIRE(state.called);
+                        CHECK(!state.error);
+                        CHECK(state.changes);
+                    }
+                    SECTION("using invalid nesting") {
+                        const char* bar_strings[1] = {"doubles.age"};
+                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
+                        REQUIRE(!key_path_array);
+                        realm_clear_last_error();
+                    }
                 }
 
                 SECTION("insertion, deletion, modification, modification after") {
@@ -3914,11 +3974,12 @@ TEST_CASE("C API - properties", "[c_api]") {
                 CHECK(n == 0);
             }
             SECTION("modifying the object while observing a specific value") {
-                realm_key_path_elem_t origin_value[] = {{class_foo.key, foo_int_key}};
-                realm_key_path_t key_path_origin_value[] = {{1, origin_value}};
-                realm_key_path_array_t key_path_array = {1, key_path_origin_value};
+                const char* foo_strings[1] = {"public_int"};
+                auto key_path_array = realm_create_key_path_array(realm, class_foo.key, 1, foo_strings);
+                REQUIRE(key_path_array);
                 auto token = cptr(
-                    realm_object_add_notification_callback(obj1.get(), &state, nullptr, &key_path_array, on_change));
+                    realm_object_add_notification_callback(obj1.get(), &state, nullptr, key_path_array, on_change));
+                realm_release(key_path_array);
                 checked(realm_refresh(realm, nullptr));
                 state.called = false;
                 write([&]() {