diff --git a/ijs.tree b/ijs.tree
index e4d8ce383..0c1016747 100644
--- a/ijs.tree
+++ b/ijs.tree
@@ -244,6 +244,7 @@
+
diff --git a/images/basics/testing_plugins/integration_tests/remote-support.png b/images/basics/testing_plugins/integration_tests/remote-support.png
new file mode 100644
index 000000000..5d0138929
Binary files /dev/null and b/images/basics/testing_plugins/integration_tests/remote-support.png differ
diff --git a/topics/basics/testing_plugins/integration_tests/integration_tests.md b/topics/basics/testing_plugins/integration_tests/integration_tests.md
index 99fa4688e..fbeb32e3b 100644
--- a/topics/basics/testing_plugins/integration_tests/integration_tests.md
+++ b/topics/basics/testing_plugins/integration_tests/integration_tests.md
@@ -15,5 +15,5 @@ There are several important reasons to create integration tests, including:
1. [](integration_tests_intro.md)
2. [](integration_tests_ui.md)
-3. API Interaction (will be available soon)
+3. [](integration_tests_api.md)
diff --git a/topics/basics/testing_plugins/integration_tests/integration_tests_api.md b/topics/basics/testing_plugins/integration_tests/integration_tests_api.md
new file mode 100644
index 000000000..ef58f14a6
--- /dev/null
+++ b/topics/basics/testing_plugins/integration_tests/integration_tests_api.md
@@ -0,0 +1,198 @@
+
+
+# Integration Tests: API Interaction
+
+
+
+Walkthrough how to interact with API in integration tests.
+
+> This page is part of the [](integration_tests.md) tutorial.
+
+For introduction and setting up dependencies, refer to [](integration_tests_intro.md).
+
+## Java Management Extension
+
+The IDE and tests are running in different processes, which means some way to communicate between them is required.
+
+Driver framework uses Java Management Extensions (or JMX for short).
+JMX supports different connectors to manage the state of the JVM.
+Driver framework uses a standard Java Remote Method Invocation (RMI) protocol.
+This protocol allows tests to access objects and invoke methods in the JVM running the IDE.
+
+The architecture of RMI protocol is as follows:
+```mermaid
+sequenceDiagram
+ box rgb(240,240,240) Test Process
+ participant Test as Test (Client)
+ end
+
+ box rgb(240,240,240) IDE Process
+ participant IDE as IDE (Server)
+ participant Registry as RMI registry
+ end
+
+ Test->>Registry: 1. Look up remote object
+ Registry-->>Test: 2. Return stub reference
+
+ Note over Test: Stub is obtained
for remote reference
+
+ activate Test
+ Test->>IDE: 3. Remote method call via stub
+ IDE->>IDE: 4. Process request and invoke actual method
+ IDE-->>Test: 5. Return serialized result
+ deactivate Test
+
+ Note over IDE,Test: Each remote call involves:
- Serialization of parameters
- Network transfer
- Deserialization
- Method invocation
- Serialization of result
+```
+
+When a test needs to invoke a method on a remote object:
+
+1. The test looks up the remote object in the RMI registry (the IDE).
+2. The IDE returns a reference to the stub, which implements the same interface as the remote object.
+3. When the client calls a method on the stub:
+ 1. The stub serializes the method call, including any parameters.
+ 2. It sends the serialized data over the network to the remote server.
+ 3. The RMI runtime inside the IDE unmarshals the request and invokes the corresponding method on the actual remote object.
+ 4. The method executes, and the result is serialized and sent back to the client via the stub.
+ 5. The stub unmarshals the response and returns the result to the caller.
+
+Thus, the stub makes remote calls feel like local method calls, without the caller worrying about low-level networking, data conversion, or request handling.
+
+## Creating Stubs
+
+To demonstrate how this works in practice, add the following code to the plugin:
+
+```kotlin
+package com.example.demo
+
+object PluginStorage {
+ @JvmStatic
+ fun getPluginStorage() = Storage("static method", listOf("static1", "static2"))
+}
+
+@Service
+class PluginService {
+ fun someMethod(): Unit = Unit
+ fun getAnswer(): Int = 42
+}
+
+@Service(Service.Level.PROJECT)
+class PluginProjectService {
+ fun getStrings(): Array = arrayOf("foo", "bar")
+}
+
+data class Storage(val key: String, val attributes: List)
+```
+
+This file contains one class with the static method `getPluginStorage`, and two [light services](plugin_services.md#light_services) – one application-level `PluginService` and one project-level `PluginProjectService`.
+
+The test needs to call the methods of these classes and verify their return values.
+To do this, the following stubs have to be created:
+
+```kotlin
+import com.intellij.driver.client.Remote
+
+@Remote("com.example.demo.PluginStorage", plugin = "com.example.demo")
+interface PluginStorage {
+ fun getPluginStorage(): Storage
+}
+
+@Remote("com.example.demo.PluginService", plugin = "com.example.demo")
+interface PluginService {
+ fun getAnswer(): Int
+}
+
+@Remote("com.example.demo.PluginProjectService", plugin = "com.example.demo")
+interface PluginProjectService {
+ fun getStrings(): Array
+}
+
+@Remote("com.example.demo.Storage", plugin = "com.example.demo")
+interface Storage {
+ fun getAttributes(): List
+ fun getKey(): String
+}
+```
+
+As seen above, a stub is an interface for the class with methods that will be used in the test.
+Stubs shouldn't be created for methods not used in tests.
+
+The first parameter is the fully qualified name of the class that will correspond to the stub using the [`Remote`](%gh-ic%/platform/remote-driver/client/src/com/intellij/driver/client/Remote.kt) annotation.
+Strings are used to avoid introducing dependency between production and test code.
+
+The `plugin` parameter specifies the ID of a plugin, where classes are located.
+This parameter is required since IntelliJ-based IDEs use separate [class loaders](plugin_class_loaders.md) for each plugin, and the code that will call methods on the IDE side (Invoker) needs to know where to search for them.
+
+There is built-in support for `@Remote` annotation inside IntelliJ IDEA:
+{width="717"}
+
+Rename and move the target class refactorings will update the annotation accordingly.
+Gutter icons can be used to navigate to the target class from a stub.
+
+## Calling IDE Methods from Tests
+
+The complete API test:
+
+```kotlin
+@Test
+fun testStubs() {
+ Starter.newContext(
+ "testExample",
+ TestCase(
+ IdeProductProvider.IC,
+ GitHubProject.fromGithub(
+ branchName = "master",
+ repoRelativeUrl = "JetBrains/ij-perf-report-aggregator"
+ )
+ )
+ .withVersion("2024.3")
+ )
+ .apply {
+ val pathToPlugin = System.getProperty("path.to.build.plugin")
+ PluginConfigurator(this).installPluginFromFolder(File(pathToPlugin))
+ }.runIdeWithDriver().useDriverAndCloseIde {
+ val storage = utility().getPluginStorage()
+ val key = storage.getKey()
+ val attributes = storage.getAttributes()
+ Assertions.assertEquals("static method", key)
+ Assertions.assertEquals(listOf("static1", "static2"), attributes)
+ val answer = service().getAnswer()
+ Assertions.assertEquals(42, answer)
+ waitForProjectOpen()
+ val project = singleProject()
+ val strings = service(project).getStrings()
+ Assertions.assertArrayEquals(arrayOf("foo", "bar"), strings)
+ }
+}
+```
+
+There are two methods that allow invoking methods: `service` and `utility`.
+The first one will return an instance of a service, and the second will return an instance of any class.
+
+Project-level services require a `Project` stub.
+To get it, use the `singleProject` method, which is implemented in the same way as demonstrated above:
+
+```kotlin
+service().getOpenProjects().singleOrNull()
+```
+
+> Service and utility proxies can be acquired on each call, there is no need to cache them in clients.
+>
+{style="note"}
+
+## JMX/RMI Limitations
+
+The main inconvenience of using JMX/RMI is that stubs need to be created for all objects which are used in tests.
+On the other hand, they don't require modification of the production code.
+
+As with any protocol, JMX/RMI has its limitations:
+
+* Parameters and return values can only be:
+ * Primitives and their wrappers: `Integer`, `Short`, `Long`, `Double`, `Float`, and `Byte`.
+ * `String`.
+ * `@Remote` references.
+ * An array of primitive values or `String` or `@Remote` references.
+ * Lists of primitive values or `String` or `@Remote` references.
+* Only public methods can be called.
+* JMX/RMI can’t interact with suspend methods.
+