# `Spectral.OpenAPI`
[🔗](https://github.com/andreashasse/spectral/blob/main/lib/spectral/openapi.ex#L1)

Elixir wrapper for spectra OpenAPI specification generation.

This module provides idiomatic Elixir functions for generating OpenAPI 3.1
specifications from Elixir type definitions using spectra.

## Response Builder Pattern

Responses are built using a fluent API:

    response = Spectral.OpenAPI.response(200, "Success")
      |> Spectral.OpenAPI.response_with_body(Person, :t)
      |> Spectral.OpenAPI.response_with_header("X-Rate-Limit", RateLimit, %{
        schema: :t,
        description: "Remaining requests",
        required: false
      })

    endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}")
      |> Spectral.OpenAPI.add_response(response)

# `operation`

```elixir
@type operation() ::
  :spectra_openapi.endpoint_spec() | :spectra_openapi.webhook_spec()
```

An endpoint or a webhook. The builder functions accept both.

# `add_response`

```elixir
@spec add_response(operation(), :spectra_openapi.response_spec()) :: operation()
```

Adds a complete response specification to an endpoint.

This function adds a response that was built using the response builder pattern.

## Parameters

- `endpoint` - The endpoint to add the response to
- `response` - Response specification built with `response/2` and related functions

## Returns

- `endpoint` - Updated endpoint with the response added

## Example

    response = Spectral.OpenAPI.response(200, "User found")
      |> Spectral.OpenAPI.response_with_body(Person, :t)

    endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}")
      |> Spectral.OpenAPI.add_response(response)

# `endpoint`

```elixir
@spec endpoint(
  :spectra_openapi.http_method(),
  binary(),
  :spectra_openapi.endpoint_doc()
) ::
  :spectra_openapi.endpoint_spec()
```

Creates a new OpenAPI endpoint definition.

## Parameters

- `method` - HTTP method as an atom (`:get`, `:post`, `:put`, `:delete`, `:patch`, etc.)
- `path` - URL path as a binary (e.g., `"/users/{id}"`)
- `doc` - Optional documentation map with optional keys:
  - `:summary` - Short summary of the endpoint
  - `:description` - Longer description of the endpoint
  - `:operationId` - Unique string to identify the operation
  - `:tags` - List of tags for grouping endpoints
  - `:deprecated` - Whether the endpoint is deprecated (boolean)
  - `:externalDocs` - Map with `:url` (required) and `:description` (optional)
  - `:security` - List of Security Requirement Objects for this operation alone,
    overriding the global `:security` default. `[]` opts the operation out of the
    global requirement entirely.

## Returns

- `endpoint` - OpenAPI endpoint structure

## Example

    iex> endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}", %{summary: "Get user by ID"})
    iex> endpoint.doc
    %{summary: "Get user by ID"}

# `endpoint`

```elixir
@spec endpoint(
  :spectra_openapi.http_method(),
  binary(),
  module(),
  atom(),
  non_neg_integer()
) ::
  :spectra_openapi.endpoint_spec()
```

Creates a new OpenAPI endpoint definition using metadata from a `spectral/1` macro
placed before a function definition.

The function's metadata (set via `spectral summary: "...", description: "..."` before
the corresponding `@spec`) is retrieved from the module's type info and used as the
endpoint documentation.

## Parameters

- `method` - HTTP method as an atom (`:get`, `:post`, `:put`, `:delete`, `:patch`, etc.)
- `path` - URL path as a binary (e.g., `"/users/{id}"`)
- `module` - The module containing the function with `spectral` metadata
- `function_name` - The function name as an atom
- `arity` - The function arity

## Returns

- `endpoint` - OpenAPI endpoint structure with documentation from the function's metadata

## Example

    defmodule MyController do
      use Spectral

      spectral summary: "Get user", description: "Returns a user by ID"
      @spec get_user(map(), map()) :: map()
      def get_user(_conn, _params), do: %{}
    end

    endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}", MyController, :get_user, 2)

# `endpoints_to_openapi`

```elixir
@spec endpoints_to_openapi(:spectra_openapi.openapi_metadata(), [
  :spectra_openapi.endpoint_spec()
]) ::
  {:ok, iodata()} | {:error, [Spectral.Error.t()]}
```

Converts a list of endpoints to a complete OpenAPI specification.

## Parameters

- `metadata` - OpenAPI metadata map with keys:
  - `:title` - API title (required)
  - `:version` - API version (required)
  - `:summary` (optional) - Short summary of the API
  - `:description` (optional) - Longer description of the API
  - `:terms_of_service` (optional) - URL to the terms of service
  - `:contact` (optional) - Contact map with optional `:name`, `:url`, `:email`
  - `:license` (optional) - License map with required `:name` and optional `:url`, `:identifier`
  - `:servers` (optional) - List of server objects, each with required `:url` and optional `:description`
- `endpoints` - List of endpoint definitions

## Returns

- `{:ok, iodata}` - Complete OpenAPI 3.1 specification serialised as JSON iodata
- `{:error, [%Spectral.Error{}]}` - List of errors if generation fails

## Example

    metadata = %{title: "My API", version: "1.0.0"}
    endpoints = [
      Spectral.OpenAPI.endpoint(:get, "/users/{id}")
      |> Spectral.OpenAPI.add_response(
        Spectral.OpenAPI.response(200, "User found")
        |> Spectral.OpenAPI.response_with_body(Person, :t)
      )
    ]

    {:ok, json} = Spectral.OpenAPI.endpoints_to_openapi(metadata, endpoints)

# `endpoints_to_openapi`

```elixir
@spec endpoints_to_openapi(
  :spectra_openapi.openapi_metadata(),
  [:spectra_openapi.endpoint_spec()],
  [
    Spectral.schema_option()
  ]
) :: {:ok, iodata() | dynamic()} | {:error, [Spectral.Error.t()]}
```

Converts a list of endpoints to a complete OpenAPI specification, with options.

Like `endpoints_to_openapi/2` but accepts an options list.

## Parameters

- `metadata` - OpenAPI metadata map (same as `endpoints_to_openapi/2`)
- `endpoints` - List of endpoint definitions
- `opts` - Options list. Supported options:
  - `:pre_encoded` - Return a map instead of iodata, skipping JSON encoding.

## Returns

- `{:ok, iodata()}` - Complete OpenAPI 3.1 spec as JSON iodata (default)
- `{:ok, map()}` - Spec as a decoded map when `:pre_encoded` option is set
- `{:error, [%Spectral.Error{}]}` - List of errors if generation fails

# `response`

```elixir
@spec response(:spectra_openapi.http_status_code(), binary()) ::
  :spectra_openapi.response_spec()
```

Creates a response builder.

This creates a response specification that can be further configured with
`response_with_body/3-4` and `response_with_header/4` before being added
to an endpoint with `add_response/2`.

## Parameters

- `status_code` - HTTP status code (e.g., `200`, `404`, `500`), or an OpenAPI
  status code range from `"1XX"` to `"5XX"`
- `description` - Human-readable description of the response

## Returns

- `response` - Response builder structure

## Example

    response = Spectral.OpenAPI.response(200, "User found successfully")

# `response_with_body`

```elixir
@spec response_with_body(
  :spectra_openapi.response_spec(),
  module(),
  atom() | :spectra.sp_type_or_ref()
) ::
  :spectra_openapi.response_spec()
```

Adds a response body to a response builder.

You can optionally provide a `content_type` as a 4th argument.

## Parameters

- `response` - Response builder from `response/2`
- `module` - Module containing the type definition
- `schema` - Schema reference (typically an atom like `:t`)
- `content_type` - Optional content type (e.g., `"application/json"`, `"application/xml"`)

## Returns

- `response` - Updated response builder with body schema

## Example

    response = Spectral.OpenAPI.response(200, "Success")
      |> Spectral.OpenAPI.response_with_body(Person, :t)

    response_xml = Spectral.OpenAPI.response(200, "Success")
      |> Spectral.OpenAPI.response_with_body(Person, :t, "application/xml")

# `response_with_header`

```elixir
@spec response_with_header(
  :spectra_openapi.response_spec(),
  binary(),
  module(),
  map()
) ::
  :spectra_openapi.response_spec()
```

Adds a header to a response builder.

## Parameters

- `response` - Response builder from `response/2`
- `header_name` - Name of the response header (e.g., `"X-Rate-Limit"`)
- `module` - Module containing the type definition for the header value
- `header_spec` - Header specification map with keys:
  - `:schema` - Schema reference for the header value (atom like `:t`, or a type ref tuple)
  - `:description` (optional) - Description of the header
  - `:required` (optional) - Whether the header is required (default: false)
  - `:deprecated` (optional) - Whether the header is deprecated (boolean)

## Returns

- `response` - Updated response builder with header added

## Example

    response = Spectral.OpenAPI.response(200, "Success")
      |> Spectral.OpenAPI.response_with_header("X-Rate-Limit", RateLimit, %{
        schema: :t,
        description: "Requests remaining",
        required: false
      })

# `to_openapi`

```elixir
@spec to_openapi(
  :spectra_openapi.openapi_metadata(),
  [:spectra_openapi.endpoint_spec()],
  [:spectra_openapi.webhook_spec()],
  [Spectral.schema_option()]
) :: {:ok, iodata() | dynamic()} | {:error, [Spectral.Error.t()]}
```

Converts endpoints and webhooks to a complete OpenAPI specification.

Like `endpoints_to_openapi/3`, but also takes webhooks built with
`webhook/2,3`. They are emitted under the spec's top-level `webhooks` key,
and their schemas share `components/schemas` with the endpoints, so a type
used by both is emitted once.

A global `:security` requirement in the metadata applies to webhooks too.
Override it per operation with `:security` in the doc map, or `[]` to opt out.

## Parameters

- `metadata` - OpenAPI metadata map (same as `endpoints_to_openapi/2`)
- `endpoints` - List of endpoint definitions
- `webhooks` - List of webhook definitions built with `webhook/2,3`
- `opts` - Options list. Supported options:
  - `:pre_encoded` - Return a map instead of iodata, skipping JSON encoding.

## Returns

- `{:ok, iodata()}` - Complete OpenAPI 3.1 spec as JSON iodata (default)
- `{:ok, map()}` - Spec as a decoded map when `:pre_encoded` option is set
- `{:error, [%Spectral.Error{}]}` - List of errors if generation fails

# `webhook`

```elixir
@spec webhook(
  binary(),
  :spectra_openapi.http_method(),
  :spectra_openapi.endpoint_doc()
) ::
  :spectra_openapi.webhook_spec()
```

Creates a new OpenAPI 3.1 webhook definition.

A webhook is keyed by an event name and emitted under the spec's top-level
`webhooks` key by `to_openapi/4`. Build it with the same functions as an
endpoint. Only `:header` and `:cookie` parameters are allowed.

## Parameters

- `name` - Event name as a binary (e.g., `"userCreated"`)
- `method` - HTTP method your API uses when calling the consumer (usually `:post`)
- `doc` - Optional documentation map, taking the same keys as `endpoint/3`

## Returns

- `webhook` - OpenAPI webhook structure

## Example

    iex> webhook = Spectral.OpenAPI.webhook("userCreated", :post, %{summary: "User created"})
    iex> webhook.name
    "userCreated"

# `with_nil_fields`

```elixir
@spec with_nil_fields(:spectra_openapi.webhook_spec(), :null) ::
  :spectra_openapi.webhook_spec()
```

Marks a webhook whose payload is encoded with `nil_fields: :null`.

Use it when the code that sends the webhook calls `Spectral.encode/5` with
`nil_fields: :null`. The payload schema then lists every field whose type can be
`nil` in `required` and allows `null`, nested types included. Headers and
responses do not change.

The payload type keeps its normal schema name. If the same type is also used
without this option, the webhook gets a second schema with `Nullable` added to
the name. With `config :spectra, :nil_fields, :null` the option changes nothing.
With `:omit`, `to_openapi/4` raises `ArgumentError`, and so it does when the
`Nullable` name is already taken. Any value other than `:null` raises
`ArgumentError`.

## Example

    webhook = Spectral.OpenAPI.webhook("paymentDone", :post)
      |> Spectral.OpenAPI.with_request_body(Payment, {:type, :t, 0})
      |> Spectral.OpenAPI.with_nil_fields(:null)

# `with_parameter`

```elixir
@spec with_parameter(operation(), module(), :spectra_openapi.parameter_input_spec()) ::
  operation()
```

Adds a parameter to an endpoint.

Parameters can be in the path, query string, headers, or cookies.

## Parameters

- `endpoint` - The endpoint or webhook to modify
- `module` - Module containing the type definition for the parameter
- `parameter_spec` - Parameter specification map with keys:
  - `:name` - Parameter name
  - `:in` - Location (`:path`, `:query`, `:header`, `:cookie`)
  - `:required` - Whether the parameter is required
  - `:schema` - Schema for the parameter value (atom or `{:type, name, arity}` tuple)

The `description` and `deprecated` fields in the rendered OpenAPI output are sourced from
the type's `spectral` annotation, not from the parameter spec. Type aliases and remote type
references are followed automatically to inherit their documentation.

## Returns

- `endpoint` - Modified endpoint with parameter added

## Example

    endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}")
      |> Spectral.OpenAPI.with_parameter(User, %{
        name: "id",
        in: :path,
        required: true,
        schema: :string
      })

# `with_request_body`

```elixir
@spec with_request_body(operation(), module(), atom() | :spectra.sp_type_or_ref()) ::
  operation()
```

Adds a request body specification to an endpoint.

You can optionally provide a `content_type` as a 4th argument.

## Parameters

- `endpoint` - The endpoint or webhook to modify
- `module` - Module containing type definitions
- `schema` - Schema reference (typically an atom like `:t`)
- `content_type` - Optional content type binary (e.g., `"application/xml"`; defaults to `"application/json"`)

## Returns

- `endpoint` - Modified endpoint with request body

## Example

    endpoint = Spectral.OpenAPI.endpoint(:post, "/users")
      |> Spectral.OpenAPI.with_request_body(Person, :t)

    endpoint_xml = Spectral.OpenAPI.endpoint(:post, "/users")
      |> Spectral.OpenAPI.with_request_body(Person, :t, "application/xml")

---

*Consult [api-reference.md](api-reference.md) for complete listing*
