---
title: Types and SDKs
description: Generate typed clients and webhook handlers from the dartpilot OpenAPI document.
---

dartpilot publishes a machine-readable OpenAPI document:

```text
https://api.dartpilot.app/v1/openapi.json
```

It describes the public REST API and every webhook delivery, including the
signed request headers. It is public, needs no API key, and allows cross-origin
requests, so generators and browser-based tools can fetch it directly.

## What the document contains

- Every `/v1` endpoint with its request and response schemas.
- The `webhooks.eventDelivery` operation, with the `webhook-id`,
  `webhook-timestamp` and `webhook-signature` headers.
- One envelope component per event (`WebhookEventGameStarted`,
  `WebhookEventTournamentMatchCompleted`, and so on).
- A `WebhookEvent` union with a `discriminator` on `type`, so generated types
  narrow `data` to the right payload: `Game`, `GameDetail`,
  `TournamentDetail`, `TournamentMatch`, or the `ping` test payload.

## Generate TypeScript types

[openapi-typescript](https://openapi-ts.dev) turns the document into a single
`.d.ts` file with no runtime dependency:

```bash
curl -fsS https://api.dartpilot.app/v1/openapi.json -o openapi.json
npx openapi-typescript openapi.json -o dartpilot-api.d.ts
```

The output includes both `paths` and `webhooks`:

```ts
import type { components, webhooks } from "./dartpilot-api";

type WebhookEvent = components["schemas"]["WebhookEvent"];
type WebhookBody =
  webhooks["eventDelivery"]["post"]["requestBody"]["content"]["application/json"];

function handle(event: WebhookEvent) {
  switch (event.type) {
    case "game.completed":
      return event.data.state; // GameDetail
    case "tournament.match.started":
      return event.data.gameId; // TournamentMatch
    case "ping":
      return event.data.webhookId; // test payload
  }
}

const receive = (body: WebhookBody) => handle(body);
```

Because `type` is a literal in each envelope, the `switch` narrows `data` to
the matching payload. If you handle every case, assert `event` is `never` in a
`default` branch and TypeScript fails when a new event type is added.

You can also import the envelopes individually when you only handle one event:

```ts
type GameCompleted = components["schemas"]["WebhookEventGameCompleted"];
```

## Keep generated code in sync

- **Pin a version.** Webhook payloads carry `apiVersion` (`2026-10-20` today)
  and the REST API is versioned in the URL (`/v1`). Regenerate when
  dartpilot announces a change, and compare `apiVersion` before relying on a
  newly added field.
- **Regenerate in CI.** Run your generator against the live document on a
  schedule or in your build, then typecheck the result. The document changes
  only when dartpilot deploys, and the schedule catches otherwise silent drift.
- **Commit the output.** Vendoring the generated file keeps builds reproducible
  and makes API changes visible in code review. Add the raw `openapi.json` next
  to it if your generator runs offline.

## Learn more

- [API Reference](/api-reference) — rendered from the same document.
- [API Keys](/api-keys) — create the key your generated client sends.
- [Webhooks](/webhooks) — event semantics, signing and retries.
