# One webhook schema. Six SDKs that never drift.

> Hand-maintained clients drift the week you ship. Generate all six from one schema file, and let CI fail the pull request that forgets one.

- Author: Quentin Mousset
- Published: 2026-08-17
- Canonical: https://repost.sh/blog/one-webhook-schema-six-sdks

![A schema.repost box lists id String, amount Float and currency String, with currency circled as the field just added. Six dashed arrows fan out to six generated clients: typescript, go, java, kotlin and csharp each carry a check mark, while python carries a coral cross annotated still on the old shape. A brace gathers all six toward repost schema generate --check, where the note reads build fails here, not in production.](/images/blog/figure-one-schema-six-clients.webp)

You declare an <code>order.created</code> event once, and ship. A quarter later the TypeScript client carries a currency field,
  the Python one still doesn't, and the docs your customers integrate against describe a third shape. Nobody wrote
  bad code. Everybody edited their own copy of the same contract.

A contract lives in four places at once: the publisher's types, every consumer
SDK, the bytes on the wire, and the docs page a customer reads before writing
their handler. Change a field and you owe all four an edit, by hand. Miss one
and nothing breaks loudly, which is why you hear about it from a customer.
More discipline won't fix that. Removing the copies will.

## One file, six clients

[The schema](/docs/send/schema) is small enough to learn in a sitting. A
`model` describes a payload, a `type` names your events, an `event` ties the
two together, and a `generator` picks a language to emit.

```ts
generator api {
  language = "typescript"
}

generator payments {
  language = "go"
  output   = "../services/payments/internal/repostclient"
}

type Order {
  /// An order was created
  created
}

model Order {
  id     String
  amount Float
}

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

`repost schema generate` runs every generator, and the write is all-or-nothing:
if one output fails, the ones already written roll back. Two services can't
come out of the same run on different versions of the schema.

Each language gets a [native client](/docs/send/sdks): typed models, one method
per event type, and a compile error the moment you misspell a field or pass a
string where the schema says `Float`.

Every client reads the same declarations, so fields come out in the same order
everywhere. Six languages, one set of bytes. Which matters more than it sounds:
[a signature covers bytes](/blog/signature-verification-end-to-end), not
intentions.

No SDK for your language? The [HTTP endpoint](/docs/send/publishing) underneath
all six is one POST. And tell us which one is missing:
[hello@repost.sh](mailto:hello@repost.sh). Adding a language is our favorite
kind of feature request.

## Your customers read generated pages too

The `///` comment above an event member is not decoration. It becomes the
generated method's documentation, the event's description in your environment,
and the text on your [public event docs](/docs/send/event-docs), which Repost
rebuilds on every deploy.

So the page your customers integrate against cannot fall behind the payload.
Nobody writes it, so nobody forgets it.

## Let CI do the remembering

```console
$ repost schema languages
typescript
go
python
java
kotlin
csharp

$ repost schema generate --check
Generated outputs are up to date.
```

`--check` recomputes every generator's output and exits non-zero on drift,
listing the changed and stale files per output
([all the flags](/docs/cli/schema/generate)). Put it in your pull request job
next to `repost schema migrate status`.

Committed clients then land in `git diff` beside the migration that caused
them, so a reviewer sees the contract change and its blast radius on one
screen.

Anything you can hand-edit will drift. So don't hand-edit the client. Edit the
schema, and let CI diff the rest.

**See it on your own events.** Open a live inbox and inspect your first webhook in seconds (50K events/month free).
