← Back to home

Docs · REST API · SDKs

Typed clients, your language.

We don't ship a versioned SDK package — we ship a versioned OpenAPI 3.1 spec. Point one of these generators at /openapi.yaml and you have a typed client in 30 seconds. No npm dep on us, no version drift, fewer rebuilds when the API extends.

Why no published SDK

Every "official" SDK is a versioning trap. Customers end up pinned to @dirtfleet/sdk@1.2.3 while the API ships flag.severity = AUTO_PM, and either we ship a breaking-change SDK release (annoying everyone) or we don't ship the feature (worse). OpenAPI + a generator lets the integrator pick their own re-gen cadence. The spec at /openapi.yaml is the source of truth; we add operations and component schemas without breaking the existing surface.

Bring our spec, pick your generator, get types.

TypeScript / Node

openapi-typescript (types) or openapi-fetch (full client)

Two tiers. openapi-typescript generates types only — pair with native fetch for the smallest dep footprint. openapi-fetch adds a tiny (~2kb) runtime that produces a typed get("/api/v1/assets") method shape.

# Types only — zero runtime
npx openapi-typescript https://dirtfleet.app/openapi.yaml \
  -o ./src/dirtfleet.d.ts

# Then in your code
import type { paths, components } from "./dirtfleet";

type Asset = components["schemas"]["Asset"];

const res = await fetch(
  "https://dirtfleet.app/api/v1/assets?limit=100",
  { headers: { Authorization: `Bearer ${KEY}` } },
);
const body = (await res.json()) as
  paths["/api/v1/assets"]["get"]["responses"]["200"]["content"]["application/json"];
// body.assets is now typed Asset[]
# Or full client with tiny runtime
npm i openapi-fetch
npx openapi-typescript https://dirtfleet.app/openapi.yaml \
  -o ./src/dirtfleet.d.ts

# Then
import createClient from "openapi-fetch";
import type { paths } from "./dirtfleet";

const client = createClient<paths>({
  baseUrl: "https://dirtfleet.app",
  headers: { Authorization: `Bearer ${KEY}` },
});

const { data, error } = await client.GET("/api/v1/assets", {
  params: { query: { limit: 100 } },
});
// data.assets is typed; data is undefined when error is set

Go

oapi-codegen — types + client + servers

# Install
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest

# Generate types + an http.Client-based client
oapi-codegen -package dirtfleet \
  -generate types,client \
  https://dirtfleet.app/openapi.yaml > dirtfleet/dirtfleet.go

# Use it
package main

import (
  "context"
  "fmt"
  "net/http"
  "dirtfleet"
)

func main() {
  base := "https://dirtfleet.app"
  c, _ := dirtfleet.NewClientWithResponses(base, dirtfleet.WithRequestEditorFn(
    func(_ context.Context, req *http.Request) error {
      req.Header.Set("Authorization", "Bearer dfk_...")
      return nil
    },
  ))
  limit := 100
  res, _ := c.ListAssetsWithResponse(context.Background(), &dirtfleet.ListAssetsParams{
    Limit: &limit,
  })
  for _, a := range *res.JSON200.Assets {
    fmt.Println(a.Id, a.Nickname)
  }
}

Python

datamodel-code-generator (Pydantic v2 models)

For a typed Python client, the simplest path is datamodel-code-generator emitting Pydantic v2 models, then any HTTP client (httpx, requests) parsing into them. The fullopenapi-python-client is also good if you want a generated client object.

# Install
pip install datamodel-code-generator httpx

# Generate the model file
datamodel-codegen \
  --url https://dirtfleet.app/openapi.yaml \
  --output dirtfleet_models.py \
  --output-model-type pydantic_v2.BaseModel

# Use it
import os, httpx
from dirtfleet_models import Asset

KEY = os.environ["DIRTFLEET_API_KEY"]
r = httpx.get(
    "https://dirtfleet.app/api/v1/assets",
    params={"limit": 100},
    headers={"Authorization": f"Bearer {KEY}"},
    timeout=30,
)
r.raise_for_status()
assets = [Asset.model_validate(a) for a in r.json()["assets"]]
for a in assets:
    print(a.id, a.nickname, a.meterType)

Ruby

openapi-generator-cli (generates a typed module)

# Install the CLI (Java runtime required)
brew install openapi-generator       # macOS
# or via npm:
npx @openapitools/openapi-generator-cli version

# Generate the ruby client
openapi-generator generate \
  -i https://dirtfleet.app/openapi.yaml \
  -g ruby \
  -o ./dirtfleet_ruby

# Then
require "dirtfleet"

DirtFleet.configure do |c|
  c.access_token = ENV["DIRTFLEET_API_KEY"]
end

client = DirtFleet::AssetsApi.new
resp = client.list_assets(limit: 100)
resp.assets.each { |a| puts "#{a.id} #{a.nickname}" }

Other languages

Long tail

Anything with an OpenAPI 3.1 generator can read /openapi.yaml. Notable ones:

  • Rust: progenitor — generates an async reqwest-based client.
  • C# / .NET: NSwag or kiota.
  • Java / Kotlin: openapi-generator-cli -g java or -g kotlin.
  • Swift: Apple's official swift-openapi-generator plugin.
  • PHP: jane-php/open-api or openapi-generator -g php.

Re-gen workflow

When to regenerate

The spec is additive between major versions: new endpoints, new optional fields, new enum members ship without breaking existing clients. Re-generate when you want to use new endpoints — your existing code keeps working untouched.

We bump to /api/v2/... only on breaking changes, and v1 stays available for at least 6 months after a v2 release. The OpenAPI spec carries both major versions during the deprecation window, so a re-gen during the overlap is safe — your client picks up v2 types alongside the v1 it currently uses.

← Back to the API reference · → Copy-paste examples · → OpenAPI page