Why OpenAPI
A typed contract beats prose docs for any non-trivial integration. Hand the spec to openapi-generator, openapi-typescript, oapi-codegen (Go), or whatever your language's tooling is, and you get a client that catches breaking changes at compile time. We publish the same spec our internal tooling consumes.
Where it lives
# Always-current production spec GET https://dirtfleet.app/openapi.yaml # Or pin a major # (We don't pin minors. Additive changes — new endpoints, new optional # fields, new enum members — ship without a major bump. Breaking # changes go to /api/v2/.) GET https://dirtfleet.app/openapi.yaml#v1
Quickstart
Generate a typed client
The full per-language guide with copy-paste commands lives at /docs/api/sdk (TS, Go, Python, Ruby + pointers to Rust / C# / Java / Swift / PHP). Quick summary below.
TypeScript / Node
# Generate types only — fast, zero runtime
npx openapi-typescript https://dirtfleet.app/openapi.yaml \
-o ./src/dirtfleet-types.ts
# Then in your code
import type { paths } from "./dirtfleet-types";
type Flag = paths["/api/v1/flags"]["get"]["responses"]["200"]["content"]["application/json"]["flags"][number];Go
# Install: go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest oapi-codegen -package dirtfleet \ -generate types,client \ https://dirtfleet.app/openapi.yaml > dirtfleet/dirtfleet.go
Python
# pip install datamodel-code-generator datamodel-codegen \ --url https://dirtfleet.app/openapi.yaml \ --output dirtfleet_models.py \ --output-model-type pydantic_v2.BaseModel
Browse
Render in Swagger UI
Open the official Swagger Editor at editor-next.swagger.io (the URL pre-loads our spec). For a self-hosted reader, npx @scalar/cli reference https://dirtfleet.app/openapi.yaml is the smallest setup.
Postman / Insomnia
One-click import
Two options. Easiest: import the hand-curated collection at /postman_collection.json — Postman File → Import → Link or Insomnia Create → File → URL. It pre-builds every endpoint with example bodies and an apiKey collection variable; replace the variable with your key and you're running requests in 30 seconds.
Alternatively, import the OpenAPI spec directly — both tools accept the YAML URL and generate the collection on import. The hand-curated collection wins on day one because the example bodies are real; the spec wins when you want every path including ones we haven't curated yet.
Versioning + deprecation policy
- v1 is current. Additive changes ship in-place — new endpoints, new optional response fields, new enum members.
- Breaking changes go to v2. Removing fields, changing types, tightening required-ness, or renaming paths bumps the major and lives at
/api/v2/. - 6-month deprecation window. When v2 ships, v1 stays available for at least six months with a deprecation header on every response and an email to integrators we can identify.
What's in the spec today
GET /api/v1/me— whoami. No scope required.GET /api/v1/changelog— public release notes (no auth).GET / POST /api/v1/assets+POST /api/v1/assets/batch+GET / PATCH /api/v1/assets/{id}— full CRUD-lite plus bulk import (cap 50). Each create triggers a Stripe quantity sync. PATCH covers focused fields (identity, plates, renewals, financial summary, lifecycle). Scopesassets:read+assets:write.GET /api/v1/flags+POST+GET /api/v1/flags/{id}+PATCH /api/v1/flags/{id}— list (with severity/status/asset filters, default OPEN), raise a new flag, detail, or resolve. Scopesflags:read+flags:write.GET /api/v1/hours+POST+GET /api/v1/hours/{id}+POST /api/v1/hours/batch— list (filter by asset / since), detail, single create, or bulk-import up to 100 entries with per-entry partial-success semantics. POST runs anomaly detection + AUTO_PM flagging server-side. Scopeshours:read+hours:write. HeaderIdempotency-Keyon single POST; per-entryclientMutationIdon batch.GET /api/v1/work-orders+POST+GET /api/v1/work-orders/{id}+PATCH— full CRUD-lite. Scopesworkorders:read+workorders:write.GET /api/v1/tools+POST+GET /api/v1/tools/{id}+PATCH— full CRUD-lite plusPOST /api/v1/tools/batchfor bulk import (cap 100; each entry returns itsscanTokenfor label printing). Plus three action endpoints —POST /tools/{id}/checkout(idempotent per actor; passforce: trueto override an existing checkout),POST /tools/{id}/checkin(records observed condition; BROKEN auto-spawns a WO), andPOST /tools/{id}/report(failure or replacement request; spawns the WO + marks the tool BROKEN or MISSING). Scopestools:read+tools:write.GET /api/v1/projects+POST+GET /api/v1/projects/{id}+PATCH— full CRUD-lite plusPOST /api/v1/projects/batchfor bulk import (cap 50). Scopesprojects:read+projects:write.GET /api/v1/version+GET /api/v1/health— public deploy metadata (commit, uptime) and a stable namespaced health probe (DB ping, returns 503 on degraded).GET /api/v1/repairs+POST+GET /api/v1/repairs/{id}— labor + parts + cost history. Filter list by asset / project / work-order / since; detail includes themetaJSON blob omitted from listings; POST creates a log and marks any linked work order DONE. Reusesworkorders:read+workorders:writescopes.PATCH /api/v1/flags/{id}— resolve an open flag.status=RESOLVEDonly.GET / POST /api/v1/yards+POST /api/v1/yards/batch+GET / PATCH / DELETE /api/v1/yards/{id}— full CRUD plus bulk import (cap 50), with org-unique name handling. Detail includes asset + tool counts. Delete unassigns related assets/tools via SET NULL (no cascade). Scopesyards:read+yards:write.GET / POST / DELETE /api/v1/webhooks+GET / PATCH /api/v1/webhooks/{id}+GET /api/v1/webhooks/{id}/deliveries+POST /api/v1/webhooks/{id}/test— full subscription management, partial update (toggle active / URL / events without losing the secret), recent delivery history with status filter, and synthetic test deliveries. Scopesevents:read+events:write.- Webhook envelope schema (
WebhookEnvelope) with the full event-type enum. - Error contract (
Error) shared by every endpoint, plus the canonical 401/403/422/429 response shapes (ValidationErrorfor 422).
More endpoints land per integrator demand. If you need POST /api/v1/hours, GET /api/v1/work-orders, or anything else, open a ticket at /support with your integration use case — that's how the priority queue gets ordered.