> ## Documentation Index
> Fetch the complete documentation index at: https://repost.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin Quickstart

> Apply the Repost Gradle plugin and BOM, generate a type-safe Kotlin client from your schema, and publish your first event with the coroutine DSL.

export const RepostHighlight = () => {
  useEffect(() => {
    const colors = {
      comment: ['#116329', '#6A9955'],
      constant: ['#0550AE', '#9CDCFE'],
      keyword: ['#CF222E', '#569CD6'],
      name: ['#953800', '#4EC9B0'],
      string: ['#0A3069', '#CE9178'],
      type: ['#0550AE', '#4EC9B0'],
      variable: ['#1F2328', '#9CDCFE']
    };
    const tokenPattern = /"[^"\n]*"|@[A-Za-z_]\w*|\b(?:String|Int|Float|Boolean|DateTime|Json)\b|\b(?:true|false)\b|\b\d+(?:\.\d+)?\b|\b(?:now|uuid|cuid)(?=\s*\()|\b[A-Z]\w*\b|\b[A-Za-z_]\w*\b/g;
    const append = (line, text, color) => {
      if (!text) return;
      if (!color) {
        line.append(text);
        return;
      }
      const span = document.createElement('span');
      span.textContent = text;
      span.style.color = color[0];
      span.style.setProperty('--shiki-dark', color[1]);
      line.append(span);
    };
    const commentStart = text => {
      let quoted = false;
      for (let index = 0; index < text.length - 1; index += 1) {
        if (text[index] === '"' && text[index - 1] !== '\\') quoted = !quoted;
        if (!quoted && text[index] === '/' && text[index + 1] === '/') return index;
      }
      return -1;
    };
    const highlightCode = code => {
      if (code.dataset.repostHighlighted !== undefined || code.querySelector('span[style*="--shiki-dark"]')) {
        return;
      }
      code.dataset.repostHighlighted = '';
      const source = code.textContent ?? '';
      const fragment = document.createDocumentFragment();
      for (const text of source.split('\n')) {
        const line = document.createElement('span');
        line.className = 'line';
        const declaration = text.match(/^(\s*)(generator|model|enum|type|event)(\s+)([A-Za-z_]\w*)(.*)$/);
        if (declaration) {
          append(line, declaration[1]);
          append(line, declaration[2], colors.keyword);
          append(line, declaration[3]);
          append(line, declaration[4], colors.name);
          append(line, declaration[5]);
        } else {
          const start = commentStart(text);
          const codeText = start === -1 ? text : text.slice(0, start);
          const leading = codeText.match(/^(\s*)([A-Za-z_]\w*)(?=\s+(?:@|[A-Z]))/);
          const bare = codeText.match(/^(\s*)([A-Za-z_]\w*)(\s*)$/);
          let offset = 0;
          for (const match of codeText.matchAll(tokenPattern)) {
            append(line, codeText.slice(offset, match.index));
            const token = match[0];
            let color = colors.constant;
            if (token.startsWith('"')) color = colors.string; else if (token.startsWith('@') || (/^(now|uuid|cuid)$/).test(token)) color = colors.name; else if ((/^(String|Int|Float|Boolean|DateTime|Json)$/).test(token)) color = colors.type; else if (leading && match.index === leading[1].length) color = colors.variable; else if (bare && match.index === bare[1].length) color = colors.constant; else if ((/^[A-Z]/).test(token)) color = colors.name; else if ((/^[A-Za-z_]/).test(token)) color = colors.variable;
            append(line, token, color);
            offset = match.index + token.length;
          }
          append(line, codeText.slice(offset));
          if (start !== -1) append(line, text.slice(start), colors.comment);
        }
        fragment.append(line, '\n');
      }
      code.replaceChildren(fragment);
    };
    const highlightRepostBlocks = () => {
      for (const block of document.querySelectorAll('.code-block')) {
        const code = block.querySelector('pre code');
        const filename = block.querySelector('[data-component-part="code-block-header-filename"] [title$=".repost"]');
        const looksLikeRepost = (/^\s*(generator|model|enum|type|event)\s+[A-Za-z_]\w*\s*\{/m).test(code?.textContent ?? '');
        if (code && (filename || looksLikeRepost)) highlightCode(code);
      }
    };
    let frame;
    const schedule = () => {
      if (frame !== undefined) return;
      frame = requestAnimationFrame(() => {
        frame = undefined;
        highlightRepostBlocks();
      });
    };
    const observer = new MutationObserver(schedule);
    observer.observe(document.documentElement, {
      childList: true,
      subtree: true
    });
    schedule();
    return () => {
      observer.disconnect();
      if (frame !== undefined) cancelAnimationFrame(frame);
    };
  }, []);
  return null;
};

<RepostHighlight />

The Repost build plugin turns your `.repost` schema into an idiomatic Kotlin client: immutable models with a builder DSL, a `webhooks.<catalog>.<member>()` tree, `suspend` sends that respect structured concurrency, and a shared runtime that handles retries, idempotency, and delivery reconciliation. It sits on the same framework-neutral runtime as the [Java SDK](/docs/send/java/quickstart) and the [TypeScript](/docs/cli/schema), Go, and Python SDKs, pinned by a cross-language conformance suite.

The runtime is published to Maven Central under the `sh.repost` group and is **server-side only** — it holds a publish credential and must not ship in an Android app or any untrusted client.

<Note>
  The published runtime is version `1.0.18`, the Gradle plugin id `sh.repost.sdk` is `1.0.18` on the Gradle Plugin Portal, and the schema engine is `0.9.0` on Maven Central. Java projects should follow the [Java quickstart](/docs/send/java/quickstart) instead.
</Note>

<Steps>
  <Step title="Apply the plugin and BOM">
    Apply the `sh.repost.sdk` plugin, import `repost-bom` so every Repost artifact stays on one version, and depend on `repost-client-kotlin`. A complete working project lives in the repo under `examples/kotlin`.

    ```kotlin build.gradle.kts theme={"languages":{"custom":["/languages/repost.json"]}}
    plugins {
        kotlin("jvm") version "2.1.21"
        id("sh.repost.sdk") version "1.0.18"
    }

    // A Kotlin-only project turns off the Java generator.
    repostSdk.generators.named("javaSdk") { enabled.set(false) }

    dependencies {
        implementation(platform("sh.repost:repost-bom:1.0.18"))
        implementation("sh.repost:repost-client-kotlin")
    }

    kotlin {
        compilerOptions {
            languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_1)
            apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_1)
        }
    }
    ```

    ```kotlin settings.gradle.kts theme={"languages":{"custom":["/languages/repost.json"]}}
    pluginManagement {
        repositories { gradlePluginPortal() }
    }

    dependencyResolutionManagement {
        repositories { mavenCentral() }
    }
    ```

    The plugin adds no transitive HTTP, JSON, or logging libraries — the transport is a source-verified, relocated Apache HttpClient baseline inside the runtime, so it cannot collide with your own dependencies. Only `kotlinx-coroutines` comes along, for the `suspend` API.
  </Step>

  <Step title="Define the schema">
    `repost schema init --language kotlin --output ./src/main/kotlin` scaffolds a `repost/` directory. Declare your events and a Kotlin `generator` block:

    ```repost repost/schema.repost theme={"languages":{"custom":["/languages/repost.json"]}}
    generator kotlinSdk {
      language       = "kotlin"
      output         = "../build/generated/sources/repost/kotlinSdk/kotlin"
      resourceOutput = "../build/generated/resources/repost/kotlinSdk"
      packageName    = "com.example.repost"
      clientName     = "RepostClient"
    }

    model Order {
      id String
    }

    type Order {
      created
    }

    event OrderCreated {
      type      @type(Order.created)
      data      Order
      timestamp DateTime
    }
    ```

    The generator block sets four things the other languages don't need — [Code generation](/docs/send/kotlin/generation) explains each field, the engine pinning, and multi-module builds.
  </Step>

  <Step title="Generate the client">
    The plugin binds `repostGenerate` before `compileKotlin`, so a normal build already has your client. To run generation on its own:

    ```bash theme={"languages":{"custom":["/languages/repost.json"]}}
    ./gradlew repostGenerate
    ```
  </Step>
</Steps>

## Your first send

`RepostClient` is `AutoCloseable`, so `use { }` scopes it; the no-arg constructor reads the credential and endpoint from the environment. `main` here is a `suspend fun`. Build a model inline with the DSL, or pass a prebuilt one:

```kotlin theme={"languages":{"custom":["/languages/repost.json"]}}
val result = repost.webhooks.order.created(customerId = "customer-123") {
    id = "order-123"
}
println(result.id)

val prebuilt = Order { id = "order-456" }
println(repost.webhooks.order.created("customer-123", prebuilt).id)
```

A successful send returns a `SendResult` whose `deliveryState` is `ACCEPTED`; any failure throws a `RepostException` subclass — [Reliability](/docs/send/kotlin/reliability) covers the full outcome model. `close()` (via `use`) returns every owned thread and connection to baseline.

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Code generation" icon="cog" href="/docs/send/kotlin/generation" cta="Generation" arrow="true">
    The generator block, the `repostGenerateCheck` CI gate, and multi-module builds.
  </Card>

  <Card title="Configuration" icon="sliders-horizontal" href="/docs/send/kotlin/configuration" cta="Configure" arrow="true">
    Credentials, timeouts, retries, and the trailing-lambda config DSL.
  </Card>

  <Card title="Reliability" icon="shield-check" href="/docs/send/kotlin/reliability" cta="Outcomes" arrow="true">
    Idempotency, the five delivery states, and coroutine cancellation.
  </Card>
</Columns>
