Home › Documentation › API › OpenAPI

The OpenAPI document

The whole API is described by an OpenAPI 3.1 document served from the same process that answers the requests. It needs no key.

The document

https://outcomedev.com/api/v1/openapi.json
curl -s https://outcomedev.com/api/v1/openapi.json | head -20
{
  "openapi": "3.1.0",
  "info": {
    "title": "OutcomeDev API",
    "version": "v1",
    "summary": "The desk, without a browser.",
    "description": "OutcomeDev reads a business, works out everything it has to keep running, and works that list. This is the same desk, reachable by anything that can hold a key.\n\nEvery route here calls the same module the interface calls, so a row's action, its prepared message, its waiting state and its permissions are the ones a person sees.\n\n**One key, one desk.** There is no route that takes a desk as a parameter, by design: a leaked key is a key to one customer rather than to every customer.\n\n**Nothing sends itself.** Messages go out only where the owner approved them or granted the desk permission for that class of work.",
    "contact": { "name": "OutcomeDev", "url": "https://outcomedev.com", "email": "support@outcomedev.com" },
    "license": { "name": "Proprietary" }
  },
  "servers": [{ "url": "https://outcomedev.com", "description": "This deployment" }],
  "security": [{ "bearerKey": [] }],
  "tags": [
    { "name": "Meta", "description": "The API, the key, and the desk's own vocabulary" },
    { "name": "Work", "description": "What is in flight, and what can be done about it" }
  ],
  "paths": { }
}

No key is needed to fetch it. This is the only route in the API that answers without one, and it is deliberate. A description of an API is not a secret, and an editor or a code generator has to be able to read it before it has a key — that is the entire point of pointing a tool at this URL.

Using it with a generator

Any OpenAPI 3.1 generator can read the document. The command below writes a TypeScript client; swap the generator for the language in use.

npx openapi-typescript https://outcomedev.com/api/v1/openapi.json -o outcomedev.d.ts
npx @hey-api/openapi-ts \
  -i https://outcomedev.com/api/v1/openapi.json \
  -o ./src/client \
  -c @hey-api/client-fetch

Two things a generated client will not do for you, and which are worth handling by hand.

  • Set the key. The document declares a bearer scheme named bearerKey, so a generator emits a place to put it. Most will not read it from an environment variable; set the header yourself.
  • Generate an Idempotency-Key per intent. The header is declared, so a generated method will accept it, and the generator cannot know that a retry must reuse the same value. See Idempotency.

The document is also the fastest way to check a claim on these pages. Every path, every parameter and every error status is in it, and it is generated by the same code that serves the routes.

Using it with an editor

Point an OpenAPI-aware editor or a scratch pad at the URL and it will offer every path and field with autocomplete, and can send a request with the key attached.

ToolHow
Bruno, Insomnia, PostmanImport from a URL. Give the collection a bearer token of od_... and every request inherits it.
Stoplight Studio, Swagger EditorOpen the URL directly. Useful for reading the document as a tree rather than as JSON.
VS CodeAny OpenAPI extension can load the URL as a workspace document; the JSON Schema behind it gives completion while editing a request.
Scalar or RedocStandalone renderers can be pointed at the URL to produce a browsable reference from the live document.

Because the document is served live rather than built, an editor pointed at it always shows the API as it is today, including any field added since the editor's cache was filled.

What is in the document

SectionContents
pathsAll twenty routes, with parameters, request bodies and the error statuses each can return.
components.schemas.WorkItemThe full work item, including the action object and its eight types.
components.schemas.DeskSectionOne block of the desk's screen.
components.schemas.ErrorThe error body: code, message and optional details.
components.securitySchemes.bearerKeyAn HTTP bearer scheme, described as an od_ key created from inside the product, shown once.
tagsSix groups: Meta, Work, Arrangements, Record, Desk, Chat.

What the document does not tell you

A schema is a shape, and some of the things that matter most about this API are not shapes. All of the following are true, and none of them can be expressed in OpenAPI:

  • kind is an open string. It is the desk's own word for a sort of work, so the document cannot enumerate its values. GET /desk reports the kinds a particular desk uses.
  • action.label should be shown as it arrives. The label is the same word the product's screen puts on the button, and it changes with the situation. The type is the enum; the label is copy.
  • A 403 does not tell you which 403 it is. The status is shared by no_desk and not_permitted, and the difference is in the body's code.
  • Some 200s are refusals. drafted: false, ok: false and timed_out: true are all successful responses carrying unwelcome news.
  • Timing is not in the schema. /work/{id}/run takes minutes and /chat can take four, and no schema has a field for that.

This is why the document is written by hand rather than derived by reflecting over the handlers. Reflection cannot express which fields are computed, which values are the desk's vocabulary rather than the API's, or what a 403 from /work/{id}/run means. The risk of a hand-written document is drift, and the mitigation is a check in the build that asserts every documented path exists and every path that exists is documented.

  • API overview — the same surface as a table.
  • MCP server — the same endpoints as sixteen tools, if a client is not the goal.
  • Errors — the bodies the document can only summarise.