---
title: "Add to existing project"
description: "Take a Kotlin service that already posts webhooks by hand and move one ad-hoc HTTP call onto a generated, type-safe Repost sender in about 15 minutes."
---


<RepostHighlight />

You already have a service that posts webhooks to your customers with a hand-built JSON body and some HTTP client. This guide replaces one of those calls with a generated, type-safe sender: no rewrite, no big bang. Set aside about 15 minutes.

You start from something like this, scattered wherever an event happens:

```kotlin
// The ad-hoc send you have today.
val body = """{"type":"order.created","data":{"id":"${order.id}"}}"""
httpClient.post(customer.webhookUrl) { setBody(body) }
```

The runtime is published to Maven Central under `sh.repost` and is **server-side only**: it holds a publish credential.

<Note>
  The published runtime is version `1.0.18`, the Gradle plugin id `sh.repost.sdk` is `1.0.18`, and the schema engine is `0.9.0`. Java projects should follow the [Java guide](/docs/send/java/existing-project) 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`.

    ```kotlin build.gradle.kts
    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 settings.gradle.kts
    pluginManagement {
        repositories { gradlePluginPortal() }
    }

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

    The plugin adds no transitive HTTP, JSON, or logging libraries: only `kotlinx-coroutines`, for the `suspend` API.
  </Step>

  <Step title="Initialize a schema in your repo">
    From the repository root:

    ```bash
    repost schema init --language kotlin --output ./src/main/kotlin
    ```

    This scaffolds a `repost/` directory. Model the event you already send with a Kotlin `generator` block:

    ```repost repost/schema.repost
    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
    }
    ```

    Add one field per key you send today. [Code generation](/docs/send/kotlin/generation) explains the four extra generator fields.
  </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
    ./gradlew repostGenerate
    ```
  </Step>

  <Step title="Connect an environment">
    Create an environment in the [dashboard](https://app.repost.sh), copy its publish API key into your environment, then deploy your schema:

    ```bash
    repost auth login
    repost schema migrate deploy
    ```
  </Step>
</Steps>

## Replace the ad-hoc call

Swap the raw HTTP post for the generated sender. Repost fans the event out to the customer's registered endpoints, so you no longer track `webhookUrl` yourself. `RepostClient` is `AutoCloseable`, so `use { }` scopes it; the no-arg constructor reads the credential and endpoint from the environment. Build a model inline with the DSL, or pass a prebuilt one:

```kotlin
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. `close()` (via `use`) returns every owned thread and connection to baseline.

## Verify delivery

Trigger the code path that fires the event, then open the [dashboard](https://app.repost.sh). The send appears in the event stream with its `msg_...` id and per-endpoint delivery status. Once you trust it, delete the old HTTP call and repeat for the next event type.

## 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="Model your events" icon="table" href="/docs/send/schema" cta="Schema" arrow="true">
    Enums, nested models, and the full schema language.
  </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>
