***

title: Use Kotlin SDKs generated in Postman
max-toc-depth: 2
topictype: reference
---------------------

For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://learning.postman.com/llms.txt. For full content including API reference and SDK examples, see https://learning.postman.com/llms-full.txt.

This guide provides instructions for using Kotlin SDK generated by the Postman SDK Generator. It covers how to import the SDK into your Kotlin project, run example code, and best practices for using the SDK effectively in your applications.

Postman SDK Generator generates Java SDKs that are fully compatible with Kotlin. Since Kotlin runs on the JVM and has seamless Java interoperability, you can use the generated Java SDK directly in your Kotlin projects. The SDK structure, build configuration, and publishing process are identical to the [Java SDK](/docs/sdk-generator/use/java-sdk/). This guide provides Kotlin-specific code examples for all SDK features.

## SDK structure

The Java SDK includes the following key files:

* `[SdkName].java` — Main SDK client, instantiated with `[SdkName]Config`.
* `services/` — Service classes with both sync and async methods.
* `models/` — Data models (POJOs with Lombok builders, enums, typed exceptions) with Jackson serialization.
* `config/` — Configuration classes for auth, retry, and SDK client settings.
* `pom.xml` — Specifies dependencies (OkHttp, Jackson, Lombok).

The following is an example of the typical structure of a generated Java SDK:

<Folder name="your-sdk" defaultOpen>
  <Files>
    <File name="README.md" comment="Main documentation" />

    <File name="pom.xml" comment="Maven project configuration" />

    <File name=".gitignore" comment="Git ignore rules" />

    <File name="LICENSE" comment="License information" />

    <File name=".env.example" comment="Example environment variables" />
  </Files>

  <Folder name=".devcontainer">
    <File name="devcontainer.json" comment="Dev container configuration" />
  </Folder>

  <Folder name=".idea">
    <File name="..." comment="IntelliJ workspace files" />
  </Folder>

  <Folder name="src">
    <Folder name="main">
      <Folder name="java">
        <Folder name="[groupId]">
          <Folder name="[artifactId]">
            <File name="[SdkName].java" comment="Main SDK client" />

            <Folder name="config">
              <Files>
                <File name="[SdkName]Config.java" comment="Configuration classes" />

                <File name="RetryConfig.java" comment="Retry configuration" />

                <File name="[AuthType]Config.java" comment="Authentication configuration" />
              </Files>
            </Folder>

            <Folder name="services">
              <File name="[ServiceName].java" comment="Service classes" />
            </Folder>

            <Folder name="models">
              <File name="[ModelName].java" comment="Data models" />
            </Folder>

            <Folder name="exceptions">
              <File name="ApiError.java" comment="Exception classes" />
            </Folder>

            <Folder name="validation">
              <File name="..." comment="Validation utilities" />
            </Folder>

            <Folder name="http">
              <Files>
                <File name="Environment.java" comment="HTTP utilities" />

                <File name="HttpMethod.java" comment="HTTP method utilities" />
              </Files>

              <Folder name="interceptors">
                <Files>
                  <File name="RetryInterceptor.java" comment="Request interceptors" />

                  <File name="..." comment="Additional interceptors" />
                </Files>
              </Folder>

              <Folder name="util">
                <File name="RequestBuilder.java" comment="HTTP utilities" />
              </Folder>

              <Folder name="serialization">
                <File name="..." comment="Serialization utilities" />
              </Folder>
            </Folder>

            <Folder name="hook">
              <File name="CustomHook.java" comment="Custom hooks" />
            </Folder>
          </Folder>
        </Folder>
      </Folder>
    </Folder>
  </Folder>

  <Folder name="examples">
    <Folder name="src">
      <Folder name="main">
        <Folder name="kotlin">
          <File name="Main.kt" comment="Example usage" />
        </Folder>
      </Folder>
    </Folder>

    <Files>
      <File name="build.gradle.kts" comment="Gradle build configuration" />

      <File name="settings.gradle.kts" comment="Gradle settings" />

      <File name="run.sh" comment="Script to run examples" />

      <File name="README.md" comment="Example documentation" />
    </Files>
  </Folder>

  <Folder name="documentation">
    <Folder name="snippets">
      <File name="snippets.json" comment="Mapping of snippet names to code sections in the SDK" />

      <File name="[snippetName].md" comment="Code snippets for documentation" />
    </Folder>

    <Folder name="services">
      <File name="[ServiceName].md" comment="Service documentation" />
    </Folder>

    <Folder name="models">
      <File name="[ModelName].md" comment="Model documentation" />
    </Folder>
  </Folder>
</Folder>

## Example usage

Each generated SDK includes an `examples/` directory with a working project demonstrating SDK usage.

The example includes the following features:

* SDK initialization with config builder pattern
* Synchronous API calls
* Working with model builders
* Exception handling

<Tabs>
  <Tab title="Location">
    `examples/src/main/kotlin/Main.kt`
  </Tab>

  <Tab title="Run the example">
    ```bash
    cd examples
    ./run.sh
    ```

    Or manually:

    ```bash
    cd path/to/your-sdk
    mvn clean install
    cd examples
    gradle run
    ```
  </Tab>

  <Tab title="Example code structure">
    ```kotlin
    import com.example.sdk.Sdk
    import com.example.sdk.config.SdkConfig
    import com.example.sdk.config.ApiKeyAuthConfig
    import com.example.sdk.models.User

    fun main() {
        // Initialize the SDK
        val config = SdkConfig.builder()
            .apiKeyAuthConfig(
                ApiKeyAuthConfig.builder()
                    .apiKey("your-api-key")
                    .build()
            )
            .build()

        val sdk = Sdk(config)

        try {
            // Call an API endpoint
            val user = sdk.usersService.getUser("123")
            println("User: $user")
        } catch (e: Exception) {
            System.err.println("Error: ${e.message}")
        }
    }
    ```
  </Tab>
</Tabs>

## Import the SDK locally

You can import the generated SDK into your Kotlin project using Gradle, Maven, or project file path. Below are instructions for each method.

### Import using Gradle

To import the generated SDK using Gradle, first install it to your local Maven repository:

```bash
cd path/to/your-sdk
mvn clean install
```

Then, add the dependency to your build file:

<Tabs>
  <Tab title="Kotlin DSL (build.gradle.kts)">
    ```kotlin
    repositories {
        mavenLocal()
        mavenCentral()
    }

    dependencies {
        implementation("com.example:your-sdk:1.0.0")
    }
    ```
  </Tab>

  <Tab title="Groovy DSL (build.gradle)">
    ```gradle
    dependencies {
        implementation group: 'com.example', name: 'your-sdk', version: '1.0.0'
    }
    ```
  </Tab>
</Tabs>

Alternatively, if you want to reference the SDK as a JAR file, first build the fat JAR:

```bash
cd path/to/your-sdk
mvn compile assembly:single
```

Then, add the JAR to your build file:

<Tabs>
  <Tab title="Kotlin DSL (build.gradle.kts)">
    ```kotlin
    dependencies {
        implementation(files("../path/to/your-sdk/target/your-sdk-1.0.0-jar-with-dependencies.jar"))
    }
    ```
  </Tab>

  <Tab title="Groovy DSL (build.gradle)">
    ```gradle
    dependencies {
        implementation files('../path/to/your-sdk/target/your-sdk-1.0.0-jar-with-dependencies.jar')
    }
    ```
  </Tab>
</Tabs>

### Import using a Maven local repository

To import the generated SDK using a local Maven repository, do the following:

1. Install SDK to local Maven repository.

   ```bash
   cd path/to/your-sdk
   mvn clean install
   ```

2. Add dependency to your project's `pom.xml`.

   ```xml
   <dependency>
       <groupId>com.example</groupId>
       <artifactId>your-sdk</artifactId>
       <version>1.0.0</version>
   </dependency>
   ```

3. Use the SDK in your code.

```kotlin
import com.example.sdk.Sdk
import com.example.sdk.config.SdkConfig

val config = SdkConfig.builder().build()
val sdk = Sdk(config)
```

### Import using the project file path

To import the generated SDK using the project file path, do the following:

1. Add SDK as a module in your project's `pom.xml`.

   ```xml
   <modules>
       <module>../path/to/your-sdk</module>
       <module>your-app</module>
   </modules>
   ```

2. Reference as dependency.

   ```xml
   <dependency>
       <groupId>com.example</groupId>
       <artifactId>your-sdk</artifactId>
       <version>${project.version}</version>
   </dependency>
   ```

## Publish to Maven Central

To share your generated Java SDK with the community, you can publish it to Maven Central. This allows other developers to easily add it as a dependency in their Maven or Gradle projects. Follow the steps below to publish your SDK to Maven Central.

### Prerequisites

To publish your SDK to Maven Central, you need the following:

* Sonatype Central Portal account ([central.sonatype.com](https://central.sonatype.com))
* GPG key for signing artifacts
* Namespace (group ID) verified on the Central Portal

### Initial setup

To set up your environment for publishing to Maven Central, do the following:

1. Create account and verify your namespace:

   * Register at [central.sonatype.com](https://central.sonatype.com).
   * Verify your namespace (group ID, for example, `com.example`) through DNS or GitHub verification.
   * Verification is typically automated and completes within minutes.

2. Generate a GPG key.

   ```bash
   gpg --gen-key
   gpg --list-keys
   gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID
   ```

3. Configure Maven `settings.xml`. Add the following to `~/.m2/settings.xml`:

   ```xml
   <settings>
   <servers>
       <server>
       <id>central</id>
       <username>your-central-portal-token-username</username>
       <password>your-central-portal-token-password</password>
       </server>
   </servers>
   <profiles>
       <profile>
       <id>central</id>
       <activation>
           <activeByDefault>true</activeByDefault>
       </activation>
       <properties>
           <gpg.executable>gpg</gpg.executable>
           <gpg.passphrase>your-gpg-passphrase</gpg.passphrase>
       </properties>
       </profile>
   </profiles>
   </settings>
   ```

### Configure pom.xml

Add the following to your SDK's `pom.xml`:

```xml
<project>
  <!-- Basic information -->
  <groupId>com.example</groupId>
  <artifactId>your-sdk</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <name>Your SDK</name>
  <description>SDK for Your API</description>
  <url>https://github.com/your-org/your-sdk</url>

  <!-- License -->
  <licenses>
    <license>
      <name>MIT License</name>
      <url>https://opensource.org/licenses/MIT</url>
    </license>
  </licenses>

  <!-- Developers -->
  <developers>
    <developer>
      <name>Your Name</name>
      <email>your@email.com</email>
      <organization>Your Organization</organization>
    </developer>
  </developers>

  <!-- SCM -->
  <scm>
    <connection>scm:git:git://github.com/your-org/your-sdk.git</connection>
    <developerConnection>scm:git:ssh://github.com/your-org/your-sdk.git</developerConnection>
    <url>https://github.com/your-org/your-sdk</url>
  </scm>

  <build>
    <plugins>
      <!-- Maven Source Plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <version>3.3.1</version>
        <executions>
          <execution>
            <id>attach-sources</id>
            <goals>
              <goal>jar-no-fork</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <!-- Maven Javadoc Plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.6.3</version>
        <executions>
          <execution>
            <id>attach-javadocs</id>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <!-- Maven GPG Plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-gpg-plugin</artifactId>
        <version>3.2.7</version>
        <executions>
          <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
              <goal>sign</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <!-- Central Publishing Maven Plugin -->
      <plugin>
        <groupId>org.sonatype.central</groupId>
        <artifactId>central-publishing-maven-plugin</artifactId>
        <version>0.6.0</version>
        <extensions>true</extensions>
        <configuration>
          <publishingServerId>central</publishingServerId>
          <autoPublish>true</autoPublish>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

### Publish your Java SDK to Maven Central

1. Deploy to Maven Central.

   ```bash
   mvn clean deploy
   ```

   With `autoPublish` set to `true`, artifacts are automatically published after validation. If set to `false`, you can manually publish from the Central Portal UI at [central.sonatype.com](https://central.sonatype.com).

2. Verify the publication. After deployment, verify that your artifacts are available in the staging repository on the Central Portal, for example, `https://repo1.maven.org/maven2/com/example/your-sdk/`. Once verified, they're released to Maven Central.

### Publish updates

To publish updates to your SDK, do the following:

```bash
# Update version in pom.xml
mvn versions:set -DnewVersion=1.0.1
mvn versions:commit

# Deploy
mvn clean deploy
```

### Best practices for publishing Java SDKs

Use the following best practices when publishing your Java SDK to Maven Central:

* Use semantic versioning.
* Sign all artifacts with GPG.
* Include source and javadoc JARs.
* Provide comprehensive documentation.
* Tag releases in Git.
* Consider using the Maven Release Plugin for automated releases.

## Advanced usage

You can further customize the SDK by modifying the generated code, adding new features, or integrating it with your existing codebase. Here are some advanced topics to explore.

### Authentication

The SDK supports various authentication methods. You can configure authentication when initializing the SDK client.

The SDK includes authentication support based on the security schemes defined in your API specification. The following examples show the possible authentication methods.

<Tabs>
  <Tab title="API Key">
    ```kotlin
    val config = SdkConfig.builder()
        .apiKeyAuthConfig(
            ApiKeyAuthConfig.builder()
                .apiKey("your-api-key")
                .build()
        )
        .build()

    val sdk = Sdk(config)
    ```
  </Tab>

  <Tab title="Bearer Token">
    ```kotlin
    val config = SdkConfig.builder()
        .accessToken("your-bearer-token")
        .build()

    val sdk = Sdk(config)
    ```
  </Tab>

  <Tab title="Basic Auth">
    ```kotlin
    val config = SdkConfig.builder()
        .basicAuthConfig(
            BasicAuthConfig.builder()
                .username("your-username")
                .password("your-password")
                .build()
        )
        .build()

    val sdk = Sdk(config)
    ```
  </Tab>

  <Tab title="OAuth 2.0">
    ```kotlin
    val config = SdkConfig.builder()
        .clientId("your-client-id")
        .clientSecret("your-client-secret")
        .build()

    val sdk = Sdk(config)

    // SDK automatically handles token refresh
    val user = sdk.usersService.getUser("123")
    ```
  </Tab>
</Tabs>

To update the credentials after initialization, run one of the following. The `setApiKey`, `setAccessToken`, and `setBasicAuthCredentials` methods are each only generated when the corresponding auth type is in the specification.

```kotlin
sdk.setApiKey("new-api-key")
sdk.setAccessToken("new-token")
sdk.setBasicAuthCredentials("new-username", "new-password")
```

### Synchronous and asynchronous calls

Every service method has a sync variant (`methodName`) and an async variant (`methodNameAsync`). Sync methods return the type directly (for example, `Pet`), while
async methods return `CompletableFuture<T>` (for example, `CompletableFuture<Pet>`).
For void methods, async returns `CompletableFuture<Void>`.

```kotlin
import java.util.concurrent.CompletableFuture

// Synchronous — blocks until the response is returned
val pet = sdk.petService.getPetById(123)
println("Pet: ${pet.name}")

// Asynchronous — returns a CompletableFuture
val futurePet: CompletableFuture<Pet> = sdk.petService.getPetByIdAsync(123)

// Option 1: Non-blocking with callbacks
futurePet
    .thenAccept { p -> println("Pet: ${p.name}") }
    .exceptionally { e ->
        System.err.println("Error: ${e.message}")
        null
    }

// Option 2: Blocking wait when needed
val result = futurePet.get()
```

If you use Kotlin coroutines, you can convert `CompletableFuture` to a suspending call with the `kotlinx-coroutines-jdk8` library:

```kotlin
import kotlinx.coroutines.future.await

suspend fun fetchPet() {
    val pet = sdk.petService.getPetByIdAsync(123).await()
    println("Pet: ${pet.name}")
}
```

### Environment management

The SDK supports multiple environments (for example, production, staging) with different base URLs.

```kotlin
// Use environment enum
sdk.setEnvironment(Environment.PRODUCTION)

// Or set custom base URL
sdk.setBaseUrl("https://custom.api.example.com")
```

### Hierarchical configuration

Java supports all four levels of configuration: SDK (through `SdkConfig` or `sdk.setEnvironment()/sdk.setBaseUrl()`), and service, method, and request levels (through `RequestConfig`). Each level overrides the previous, with request being most specific.

```kotlin
import com.example.sdk.Sdk
import com.example.sdk.config.SdkConfig
import com.example.sdk.config.RequestConfig
import com.example.sdk.http.Environment

// 1. SDK level — default for everything
val config = SdkConfig.builder()
    .baseUrl("https://api.example.com")
    .timeout(10000)
    .build()

val sdk = Sdk(config)

// 2. Service level — override for all methods in usersService
sdk.usersService.setConfig(
    RequestConfig.builder()
        .timeout(15000L)
        .build()
)

// 3. Method level — override for getUser only
sdk.usersService.setGetUserConfig(
    RequestConfig.builder()
        .baseUrl("https://legacy-api.example.com")
        .build()
)

// 4. Request level — override for this single call
val user = sdk.usersService.getUser(
    "123",
    RequestConfig.builder()
        .timeout(30000L)
        .build()
)
```

### Error handling

The SDK raises custom exceptions for API errors, which include details about the error message, status code, and response body. You can catch these exceptions to handle errors gracefully in your application.

To handle errors, do the following:

```kotlin
import com.example.sdk.exceptions.ApiError

try {
    val user = sdk.usersService.getUser("123")
    println(user)
} catch (e: ApiError) {
    System.err.println("API error: ${e.message}")
    System.err.println("Status code: ${e.status}")
    System.err.println("Response: ${e.response}")
} catch (e: Exception) {
    System.err.println("Unexpected error: ${e.message}")
}
```

To handle specific error codes, do the following:

```kotlin
try {
    sdk.usersService.deleteUser("123")
} catch (e: ApiError) {
    when (e.status) {
        404 -> println("User not found")
        403 -> println("Permission denied")
        else -> println("API error: ${e.message}")
    }
}
```

When the API specification defines error response models, the SDK generates typed exception classes (for example, `NotFoundErrorException`) that extend `ApiError` and include the deserialized error model. The exception's `getMessage()` and `getStatus()` work directly, but to access specification-defined error fields, use `getError()`.

```kotlin
import com.example.sdk.models.NotFoundErrorException

try {
    sdk.usersService.getUser("123")
} catch (e: NotFoundErrorException) {
    // message and status available directly from ApiError
    println("Status: ${e.status}")
    println("Message: ${e.message}")

    // spec-defined error model fields require getError()
    val error = e.error
    println("Detail: ${error.detail}")
    println("Request ID: ${error.requestId}")
} catch (e: ApiError) {
    // fallback for unmapped error responses
    println("API error: ${e.status} ${e.message}")
}
```

### Timeouts and retries

To set a global timeout, do the following:

```kotlin
val config = SdkConfig.builder()
    .timeout(10000) // 10 seconds in milliseconds (default: 10000)
    .build()

val sdk = Sdk(config)
```

To pass the retry configuration through `SdkConfig`, do the following:

```kotlin
val retryConfig = RetryConfig.builder()
    .maxRetries(3)
    .initialDelay(150)       // milliseconds between retries (default: 150)
    .maxDelay(5000)          // max delay cap in milliseconds (default: 5000)
    .backoffFactor(2.0)      // exponential backoff multiplier
    .jitter(50)              // random jitter in milliseconds (default: 50)
    .build()

val config = SdkConfig.builder()
    .retryConfig(retryConfig)
    .build()

val sdk = Sdk(config)
```

### Validation

The SDK generates validators that enforce OpenAPI constraints at runtime. These include `minLength`, `maxLength`, `pattern`, `min`, `max`, `uniqueItems`, and more.

Validation happens automatically when making API calls. The SDK validates parameters before sending the request. If validation fails, a `ValidationException` is thrown with a list of `Violation` objects describing what went wrong. You don't need to call validators manually. They're built into the service methods.

```kotlin
import com.example.sdk.validation.exceptions.ValidationException

try {
    // If the spec defines constraints (e.g. minLength: 3 for username),
    // the SDK validates before sending the request
    sdk.usersService.createUser(user)
} catch (e: ValidationException) {
    // e.message returns a formatted summary of all violations
    System.err.println(e.message)
    // Output: "Validation failed with the following violations:
    //          username: must be at least 3 characters long
    //          age: must be greater than or equal to 0"

    // Access individual violations for programmatic handling
    for (violation in e.violations) {
        System.err.println("${violation.path}: ${violation.message}")
    }
} catch (e: ApiError) {
    System.err.println("API error: ${e.message}")
}
```

The list of supported constraints can also be grouped by type like this:

* String — `minLength`, `maxLength`, `pattern` (regex)
* Numeric — `min`, `max` (inclusive or exclusive)
* List/array — `minItems`, `maxItems`, `uniqueItems`
* Required fields — Validated automatically, based on the specification.

### Optional and nullable field handling

The SDK uses `JsonNullable<T>` wrappers for optional fields, providing clear distinction between undefined, null, and present values. This is important for users building request models. Omitting an optional field versus explicitly setting it to null produces different JSON payloads.

```kotlin

// Example 1: Creating an object with all field types
val model = ModelWithOptionalNullableDefaultValues.builder()
    .requiredString("requiredString")           // Required Non-Nullable
    .requiredInteger(8L)                        // Required Non-Nullable
    .requiredNullableString("not null")         // Required Nullable
    .requiredNullableInteger(null)              // Required Nullable (can be null)
    .optionalString("optionalString")           // Optional Non-Nullable
    .optionalInteger(9L)                        // Optional Non-Nullable
    .optionalNullableString("optional")         // Optional Nullable
    .optionalNullableInteger(null)              // Optional Nullable (can be null)
    .build()

// Example 2: Optional fields omitted entirely
val minimalModel = ModelWithOptionalNullableDefaultValues.builder()
    .requiredString("requiredString")
    .requiredInteger(8L)
    .requiredNullableString(null)               // Still required, but can be null
    .requiredNullableInteger(null)              // Still required, but can be null
    // Optional fields omitted - they will be omitted from JSON when serialized and sent to API
    .build()

// Example 3: Optional fields with mixed values
val mixedModel = ModelWithOptionalNullableDefaultValues.builder()
    .requiredString("requiredString")
    .requiredInteger(8L)
    .requiredNullableString("not null")
    .requiredNullableInteger(null)
    .optionalString("present")                  // Optional non-nullable with value
    .optionalNullableString(null)               // Optional nullable set to null
    // optionalInteger and optionalNullableInteger omitted
    .build()

// What the server receives for Example 1 (all fields set):
// {
//   "requiredString": "requiredString",
//   "requiredInteger": 8,
//   "requiredNullableString": "not null",
//   "requiredNullableInteger": null,
//   "optionalString": "optionalString",
//   "optionalInteger": 9,
//   "optionalNullableString": "optional",
//   "optionalNullableInteger": null
// }

// What the server receives for Example 2 (minimal model):
// {
//   "requiredString": "requiredString",
//   "requiredInteger": 8,
//   "requiredNullableString": null,
//   "requiredNullableInteger": null
//   // Note: optional fields are completely omitted from JSON
// }

// What the server receives for Example 3 (mixed model):
// {
//   "requiredString": "requiredString",
//   "requiredInteger": 8,
//   "requiredNullableString": "not null",
//   "requiredNullableInteger": null,
//   "optionalString": "present",
//   "optionalNullableString": null
//   // Note: optionalInteger and optionalNullableInteger are omitted
// }
```

## Additional resources

Consider the following resources for using and customizing your Java SDK from Kotlin:

* SDK documentation — Check the `documentation/` directory in your SDK.
* Javadoc — Generate with `mvn javadoc:javadoc`.
* Example usage — Refer to the `examples/` directory for a working code sample demonstrating basic SDK usage.
* Kotlin-Java interop — Refer to the [Kotlin documentation on calling Java from Kotlin](https://kotlinlang.org/docs/java-interop.html) for details on how Kotlin interacts with Java libraries.
* Coroutines integration — Use [kotlinx-coroutines-jdk8](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-jdk8/) to convert `CompletableFuture` to Kotlin coroutines with `.await()`.
* Dependencies — The SDK uses the following dependencies:
  * OkHttp: An HTTP client.
  * Jackson: JSON serialization and `jackson-databind-nullable`, a dependency used for optional nullable field handling.
  * Lombok: Reduce boilerplate code.