---
title: "Add to existing project"
description: "Take a .NET 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 `HttpClient` and a hand-built JSON body. 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:

```csharp
// The ad-hoc send you have today.
var body = JsonSerializer.Serialize(new
{
    type = "book.created",
    data = new { title = book.Title },
});
await httpClient.PostAsync(customer.WebhookUrl, new StringContent(body, Encoding.UTF8, "application/json"));
```

The runtime is server-side only: it holds a publish credential.

<Steps>
  <Step title="Install the CLI and runtime">
    Add the CLI and the `Repost.Client` runtime to your existing project.

    <CodeGroup>
      ```bash CLI install script
      curl -fsSL https://repost.sh/install | sh
      ```

      ```bash CLI from npm
      npm install -g @repost/cli
      ```

      ```bash Runtime from NuGet
      dotnet add package Repost.Client
      ```
    </CodeGroup>
  </Step>

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

    ```bash
    repost schema init --language csharp --output ./Repost
    ```

    This scaffolds a `repost/` workspace with a starter schema and a `.env` file for your key. The generated client is written into your repo and committed.
  </Step>

  <Step title="Model the event you already send">
    Edit `repost/schema.repost` so the model and event match the payload your ad-hoc call sends today. For the `book.created` example above:

    ```repost repost/schema.repost
    generator sdk {
      language    = "csharp"
      output      = "../Repost"
      packageName = "Repost.Example"
      clientName  = "ExampleClient"
    }

    model Book {
      title    String
      price    Float
      currency Currency
    }

    enum Currency {
      USD
      EUR
    }

    type Book {
      created
    }

    event BookCreated {
      type      @type(Book.created)
      data      Book
      timestamp DateTime
    }
    ```

    Add one field per key you send today. [Schema](/docs/send/schema) covers enums, nested models, and more.
  </Step>

  <Step title="Record the schema and generate">
    ```bash
    repost schema migrate dev --name init
    ```

    This records your schema as a migration and generates the client. Commit both the `repost/` and generated directories.
  </Step>

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

    ```bash
    repost auth login
    repost schema migrate deploy
    ```

    The runtime reads the credential from `REPOST_SEND_API_KEY`.
  </Step>
</Steps>

## Replace the ad-hoc call

Swap the raw `HttpClient` post for the generated sender. Repost fans the event out to the customer's registered endpoints, so you no longer track `WebhookUrl` yourself. The client is `IAsyncDisposable`, so an `await using` scopes it:

```csharp
using Repost.Client;
using Repost.Example;

var client = new ExampleClient(new RepostClientOptions
{
    ApiKey = Environment.GetEnvironmentVariable("REPOST_SEND_API_KEY"),
});

await using (client)
{
    SendResult result = await client.Webhooks.Book.CreatedAsync(new BookCreatedInput
    {
        CustomerId = "cus_example",
        Data = new Book
        {
            Title = "Dune",
            Price = 9.99,
            Currency = Currency.Usd,
        },
    });

    Console.WriteLine($"sent {result.Id} as {result.Type}");
}
```

The generated `BookCreatedInput` and `Book` types reject unknown events, missing fields, and incompatible values at compile time. `SendResult` carries `Id`, `Type`, `CustomerId`, and `Timestamp`. The API key resolves from `RepostClientOptions.ApiKey`, falling back to `REPOST_SEND_API_KEY`.

## 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 `HttpClient` call and repeat for the next event type.

## Continue

<Columns cols={3} className="gap-y-4">
  <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="Schema workflow" icon="terminal" href="/docs/cli/schema" cta="Commands" arrow="true">
    The commands behind init, generate, and migrate.
  </Card>

  <Card title="Delivery" icon="shield-check" href="/docs/send/delivery" cta="Delivery" arrow="true">
    How events reach endpoints, with retries and status.
  </Card>
</Columns>
