---
title: "Add to existing project"
description: "Take a Node.js app that already fires webhooks by hand and move one ad-hoc HTTP call onto a generated, typed Repost sender in about 15 minutes."
---


<RepostHighlight />

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

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

```ts
// The ad-hoc send you have today.
await fetch(customer.webhookUrl, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    type: "user.created",
    data: { id: user.id, email: user.email },
  }),
});
```

Node.js 20 or later is required.

<Steps>
  <Step title="Install the CLI and runtime">
    Add the CLI and the `@repost/client` runtime to your existing project. The generated client lands in `node_modules` and `@repost/client` re-exports it, so your imports stay stable.

    <CodeGroup>
      ```bash npm
      npm install -g @repost/cli
      npm install @repost/client
      ```

      ```bash Install script
      curl -fsSL https://repost.sh/install | sh
      npm install @repost/client
      ```
    </CodeGroup>
  </Step>

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

    ```bash
    repost schema init
    ```

    TypeScript is the default, so no flags are needed. This scaffolds `repost/schema.repost` with a starter event and a `.env` file holding an empty `REPOST_SEND_API_KEY`. With no `output` in the generator block, generation targets `node_modules/.repost/client`.
  </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 `user.created` example above:

    ```repost repost/schema.repost
    generator sdk {
      language = "typescript"
    }

    model User {
      id    String
      email String
    }

    type User {
      created
    }

    event UserCreated {
      type      @type(User.created)
      data      User
      timestamp DateTime
    }
    ```

    Add one field per key you send today. Optional keys become optional fields. [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 into `node_modules`. Add `repost schema generate` as a `postinstall` script so fresh clones and CI regenerate automatically. [Code generation](/docs/send/typescript/generation) explains why.
  </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
    ```

    Load `.env` through your framework, your process manager, or Node's `--env-file` option.
  </Step>
</Steps>

## Replace the ad-hoc call

Swap the raw `fetch` for the generated sender. Repost fans the event out to the customer's registered endpoints, so you no longer track `webhookUrl` yourself:

```ts
import { createRepostClient, type User } from "@repost/client";

const repost = createRepostClient();

const user: User = {
  id: "user_123",
  email: "ada@example.com",
};

const result = await repost.webhooks.user.created({
  customerId: "customer_123",
  data: user,
  idempotencyKey: "user_123:created",
});

console.log(result.id); // msg_...
```

TypeScript now rejects unknown events, missing fields, and incompatible values before the send ever leaves your process. `createRepostClient()` never throws; the API key resolves from `REPOST_SEND_API_KEY` at send time. The `idempotencyKey` is optional: reusing it with the same payload is safe.

## 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 `fetch` 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/typescript/generation" cta="Generation" arrow="true">
    What lands in `node_modules`, and how regeneration works.
  </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/typescript/reliability" cta="Errors" arrow="true">
    Retries, idempotency, and the error surface.
  </Card>
</Columns>
