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:
// 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.
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.
pnpm add -g @repost/cli npm install @repost/client
From the repository root:
repost schema initTypeScript 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.
Edit repost/schema.repost so the model and event match the payload your ad-hoc call sends today. For the user.created example above:
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 covers enums, nested models, and more.
repost schema migrate dev --name initThis 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 explains why.
Create an environment in the dashboard, copy its publish API key into .env, then deploy your schema:
repost auth login
repost schema migrate deployLoad .env through your framework, your process manager, or Node's --env-file option.
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:
import { createRepostClient, type User } from "@repost/client";
const repost = createRepostClient();
const user: User = {
id: "user_123",
email: "[email protected]",
};
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. 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.