Generate a Kotlin SDK from your OpenAPI spec

Octri reads your OpenAPI document and writes a Kotlin client with data classes and named arguments, publishes it to Maven Central under your own coordinates, and rebuilds it every time the spec changes.

Install from Maven Central
dependencies {
    implementation("com.acme:acme-api-kotlin:1.0.0")
}

The client they actually get

What your users write once the Kotlin package is installed.

Two call-site shapes come out of the same spec. class-namespaced is the default: a client object whose operations are grouped by resource, which is the surface every widely used SDK ships and the one your own reference docs already assume. functions is the override: no client object, one configure call, and each operation called directly. It is a single setting, per language, and nothing else about the client changes with it.

Optional parameters are Kotlin default arguments, so callers name only what they set. On the default OkHttp engine the calls block; pick Ktor and every operation becomes a suspend function.

Same coordinates and the same groupId check as Java, since both land on Maven Central. Most teams publish the two under one groupId with different artifactIds.

Client object (default)
import acmeapi.*

val acme = Acme(ClientConfig(
    baseUrl = "https://api.acme.com/v1",
    auth = ClientAuthConfig(bearer = System.getenv("ACME_TOKEN")),
))

// The trailing lambda gets each item. Return false to stop paging.
acme.users.listPaginated(limit = 100) { user ->
    println(user.id)
    true
}

val invoice = acme.invoices.create(customerId = "cus_123", amountCents = 4900)
Free functions
import acmeapi.*
import acmeapi.methods.*

SdkConfig.configure(ClientConfig(
    baseUrl = "https://api.acme.com/v1",
    auth = ClientAuthConfig(bearer = System.getenv("ACME_TOKEN")),
))

Users.listUsersPaginated(limit = 100) { user ->
    println(user.id)
    true
}

The package at a glance

What a developer checks before adding the Kotlin package to a project.

Runs on
The JVM, built with a Java 21 toolchain
Transport
OkHttp by default, Ktor CIO as an option
Runtime dependencies
OkHttp or Ktor, plus jackson-module-kotlin
Models
Data classes with nullable optional fields
Pagination
Trailing lambda that returns false to stop
Ships to
Maven Central, consumed from Gradle or Maven

When a call fails

SdkError is a sealed class, so a when over it is exhaustive and the compiler tells your users about a case they skipped. Declared error models arrive decoded on e.data, ready for an is check.

Typed errors
try {
    val invoice = acme.invoices.create(customerId = "cus_123", amountCents = 4900)
} catch (e: SdkRateLimitError) {
    // 429 after the built-in retries. Schedule it for later.
} catch (e: SdkHttpError) {
    logger.error("acme failed: status=${e.statusCode} request=${e.requestId}")
}

What you decide for Kotlin

Kotlin and TypeScript are the two languages with an HTTP engine choice, and on Kotlin it also decides whether calls suspend.

  • HTTP engine

    okhttp (default), ktor

    OkHttp is already on the classpath of most Android apps and JVM services, and its calls block. Ktor brings kotlinx-coroutines, and with it every operation, including the paginated ones, becomes a suspend function.

  • Client style

    class-namespaced (default), class, namespaced, functions

    A client class with a property per resource. The functions style emits an object per resource, Users.listUsers(), for teams that prefer no client instance.

  • Argument style

    positional (default), object

    Named, defaulted parameters, which is the Kotlin idiom and keeps call sites readable without a builder. Object wraps them in a request class.

  • Doc comments

    full (default), minimal

    Full carries your spec’s prose into KDoc, which Android Studio and IntelliJ show on hover.

Method naming and namespacing work the same in every language: operations are grouped by your spec’s tags or by path, and the namespace word is dropped from the method name unless you ask to keep the operationId whole.

Give Kotlin its own artifactId

Java and Kotlin both publish to Maven Central, usually under the same verified groupId, and two packages cannot share one set of coordinates. Set a Kotlin package name such as com.acme:acme-api-kotlin before the first release. Coordinates on Maven Central are permanent, and a release there can never be deleted or replaced.

Maven Central coordinates
com.acme:acme-api-kotlin
  • Not possible: Share coordinates with the Java SDK
  • Not possible: Delete or replace a release
  • Still possible: Relocate users to new coordinates

The same in every generated client

The parts nobody wants to hand-write, and the parts a hand-written client usually skips.

  • Retries with exponential backoff and jitter, on the statuses that mean "try again", with a per-attempt timeout.
  • Idempotency keys on the methods that need them, so a retried write does not become two.
  • Auth wired in from your spec’s security scheme, read from wherever your users keep secrets.
  • Typed errors split by cause: a failed constraint, an HTTP status, a network failure and a timeout are four different things.
  • Pagination that follows whichever contract your spec declares, cursor, offset, page number or next URL, without prefetching the collection.
  • Server-sent events as a native stream, for the operations that stream.
  • Error reporting to Octri’s monitoring, pre-wired and switched off until a consumer opts in.
  1. Point it at your spec

    Upload an OpenAPI or Swagger document, paste a URL, or connect the repository it lives in so a merge to main updates it.

  2. Read the audit

    The spec is scored out of 10 on what a generator can do with it, with every missing schema and colliding operationId named. 9 of the fourteen rules carry a button that writes the fix.

  3. Shape the client

    Client style, argument style, namespacing, auth, retries and pagination, set once for the project and overridden per language or per endpoint.

  4. Generate and read it

    The package is generated, compiled and handed to you as source you can read before anyone installs it.

  5. Publish, then keep publishing

    Release to the registry under your own account. Every later spec change regenerates the client, and the version number comes from your own document.

Questions

Yes. Choose the Ktor engine, or switch coroutines on with OkHttp, and operations are generated as suspend functions that honour cancellation. With neither, they are plain blocking calls you can wrap in your own dispatcher.