> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/wevm/incur/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAPI Integration

> Mount OpenAPI-compliant HTTP servers as typed CLI commands

Mount any HTTP server with an OpenAPI specification as typed CLI commands. incur automatically generates subcommands with arguments, options, and descriptions extracted from your spec.

## Basic Usage

Pass an OpenAPI spec alongside the `fetch` property to generate typed subcommands:

```ts theme={null}
import { Cli } from 'incur'
import { app, spec } from './my-hono-openapi-app.js'

Cli.create('my-cli', { description: 'My CLI' })
  .command('api', { fetch: app.fetch, openapi: spec })
  .serve()
```

```sh theme={null}
$ my-cli api --help
# Commands:
#   listUsers    List users
#   createUser   Create a user
#   getUser      Get a user by ID
```

## Generated Command Structure

incur parses your OpenAPI spec and generates commands with:

* **Operation ID as command name** — `operationId` becomes the command name
* **Path parameters as arguments** — path params like `{id}` become positional arguments
* **Query parameters as options** — query params become `--flag` options
* **Request body as options** — JSON body properties become `--flag` options
* **Descriptions from spec** — `summary` and `description` fields flow through to help text

### Example Commands

```sh theme={null}
# GET /users?limit=5
$ my-cli api listUsers --limit 5
# → users: ...

# GET /users/42
$ my-cli api getUser 42
# → id: 42
# → name: Alice

# POST /users with JSON body {"name":"Bob"}
$ my-cli api createUser --name Bob
# → created: true
# → name: Bob
```

## Without OpenAPI

You can also mount a fetch handler without a spec. All argv tokens are interpreted as path segments and curl-style flags:

```ts theme={null}
import { Cli } from 'incur'
import { Hono } from 'hono'

const app = new Hono()
  .get('/users', (c) => c.json({ users: [{ id: 1, name: 'Alice' }] }))
  .post('/users', async (c) => c.json({ created: true, ...(await c.req.json()) }, 201))

Cli.create('my-cli', {
  description: 'My CLI',
  fetch: app.fetch,
}).serve()
```

```sh theme={null}
$ my-cli api users
# → users:
# →   - id: 1
# →     name: Alice

$ my-cli api users -X POST -d '{"name":"Bob"}'
# → created: true
# → name: Bob
```

### Curl-style Flags

When using `fetch` without OpenAPI, incur supports:

| Flag                      | Description                                      |
| ------------------------- | ------------------------------------------------ |
| `-X, --method <METHOD>`   | HTTP method (default: GET, POST if body present) |
| `-H, --header "Key: Val"` | Set a request header (repeatable)                |
| `-d, --data <json>`       | Request body (implies POST)                      |
| `--body <json>`           | Request body (implies POST)                      |
| `--<key> <value>`         | Query string parameter                           |

## Mounting as Commands

You can mount fetch handlers onto specific commands instead of the root:

```ts theme={null}
import { Cli } from 'incur'
import { Hono } from 'hono'

const app = new Hono()
  .get('/users', (c) => c.json({ users: [{ id: 1, name: 'Alice' }] }))
  .post('/users', async (c) => c.json({ created: true, ...(await c.req.json()) }, 201))

Cli.create('my-cli', { description: 'My CLI' })
  .command('users', { fetch: app.fetch })
  .serve()
```

```sh theme={null}
$ my-cli users api users
# → users:
# →   - id: 1
# →     name: Alice
```

## Base Path

Use `basePath` to prefix all requests:

```ts theme={null}
Cli.create('my-cli', { description: 'My CLI' })
  .command('api', {
    fetch: app.fetch,
    openapi: spec,
    basePath: '/v1',
  })
  .serve()
```

## Framework Compatibility

incur works with any framework that exposes a [Web Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) handler:

```ts theme={null}
// Hono
fetch: app.fetch

// Bun
fetch: bunApp.fetch

// Deno
fetch: denoApp.fetch

// Elysia
fetch: elysiaApp.fetch
```

## Type Safety

incur resolves all `$ref` pointers in your OpenAPI spec and converts JSON Schema types to Zod schemas with automatic coercion for path and query parameters (which arrive as strings from argv).

```ts theme={null}
// OpenAPI: path parameter {id} with type: number
// → becomes: z.coerce.number()

// OpenAPI: query parameter ?active with type: boolean
// → becomes: z.coerce.boolean().optional()
```

## Error Handling

HTTP errors are automatically converted to incur error envelopes:

```sh theme={null}
$ my-cli api getUser 999
# Error (HTTP_404): User not found
```

If the response body contains a `message` field, it's used as the error message. Otherwise, the entire body or a generic `HTTP {status}` message is shown.
