Configuration reference

September 7, 2026 · View on GitHub

APISpec is driven by a YAML configuration file. For most projects the bundled per-framework defaults are enough and no config is needed — pass --config only when you want to add OpenAPI metadata, map custom types, or teach the resolver about a framework/wiring style the defaults don't cover.

This document is the field-by-field reference. For a task-oriented introduction with worked examples, see the Configuration section of the README.

How config is loaded and merged

  • No --config — APISpec detects the framework and loads its built-in default config (internal/spec/config_<framework>.go).
  • --config path.yaml — your file is loaded on top of the detected defaults. You only need to specify the keys you want to add or change; the framework patterns you omit still apply.
  • CLI flags win. Values such as --title, --api-version, and --description override the corresponding config-file values.
  • Inspect the effective config. apispec --output-config used-config.yaml (or -oc) writes the fully merged config that was actually used, which is the best starting point for a custom file.
apispec --config apispec.yaml --output openapi.yaml
apispec --output-config used-config.yaml     # dump the effective config

Top-level keys

KeyTypePurpose
infoobjectOpenAPI document metadata (title, version, contact, license).
serverslistOpenAPI servers entries.
tagslistOpenAPI tags definitions.
externalDocsobjectOpenAPI externalDocs block.
typeMappinglistMap a Go type to a fixed OpenAPI schema.
externalTypeslistGive a package/external type a custom schema.
overrideslistPer-handler summary/description/response overrides.
include / excludeobjectFilter which files/packages/functions/types are analysed.
defaultsobjectFallback content types and response status.
namingobjectHow operationIds and component names are spelled.
securitylistDocument-level security requirements.
securitySchemesmapOpenAPI securitySchemes definitions.
securityMappingslistMap detected auth middleware to a scheme.
frameworkobjectFramework detection/extraction patterns (advanced).

info

OpenAPI document metadata. Also settable via CLI flags (--title, --api-version, --description, --terms).

info:
  title: My API
  version: 1.0.0
  description: User management service
  termsOfService: https://example.com/terms
  contact:
    name: API Team
    url: https://example.com/support
    email: api@example.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
FieldTypeNotes
titlestringAPI title.
versionstringAPI version (required by OpenAPI).
descriptionstringLonger description.
termsOfServicestringURL.
contactobjectname, url, email.
licenseobjectname, url.

servers

servers:
  - url: https://api.example.com/v1
    description: Production
  - url: http://localhost:8080
    description: Local
FieldTypeNotes
urlstringServer base URL (required).
descriptionstringHuman-readable label.
variablesmapOpenAPI server-variable substitutions.

typeMapping

Replace a Go type — wherever it appears — with a fixed OpenAPI schema. Use this for well-known value types and for domain enums.

typeMapping:
  - goType: time.Time
    openapiType: { type: string, format: date-time }
  - goType: uuid.UUID
    openapiType: { type: string, format: uuid }
  - goType: domain.UserStatus
    openapiType:
      type: string
      enum: [active, inactive, pending]
FieldTypeNotes
goTypestringGo type name to match (as rendered by the analyser, e.g. time.Time).
openapiTypeschemaThe OpenAPI schema to emit for it.

externalTypes

External package types are usually resolved automatically. Declare an externalTypes entry only when a third-party type needs a custom schema (for example one whose fields aren't exported, or that marshals to a scalar).

externalTypes:
  - name: github.com/gin-gonic/gin.H
    description: Generic JSON object
    openapiType:
      type: object
      additionalProperties: true
  - name: go.mongodb.org/mongo-driver/bson/primitive.ObjectID
    openapiType: { type: string }
FieldTypeNotes
namestringFully-qualified type name (pkgpath.TypeName).
openapiTypeschemaSchema to emit for the type.
descriptionstringOptional; copied into the schema.

Layering note: type-to-schema decisions like these live in the spec layer, not at metadata time — collapsing a type too early loses format information. See TYPE_MODEL.md.

overrides

Manual, per-handler overrides applied by function name. Useful when static analysis can't recover a summary or the intended success response.

overrides:
  - functionName: GetUser
    summary: Fetch a user by ID
    description: Returns the user record for the given ID.
    responseStatus: 200
    responseType: models.User
    tags: [users]
FieldTypeNotes
functionNamestringHandler function name to match.
summarystringOperation summary.
descriptionstringOperation description.
responseStatusintForce a success status code.
responseTypestringForce the success response Go type.
tagslistOperation tags.

include / exclude

Gitignore-style filters that restrict what is analysed. exclude takes precedence over include; empty lists mean "match everything".

include:
  packages:
    - github.com/your-org/service/internal/api/**
exclude:
  files:
    - "**/*_test.go"
  functions:
    - "^debug.*"

Each of include and exclude accepts files, packages, functions, and types lists.

defaults

Fallbacks used when a request/response content type or status can't be inferred.

defaults:
  requestContentType: application/json
  responseContentType: application/json
  responseStatus: 200
FieldTypeNotes
requestContentTypestringDefault request body media type.
responseContentTypestringDefault response media type.
responseStatusintDefault success status when none is detected.

naming

By default every operationId is the fully-qualified Go symbol and every component name is that symbol with separators replaced. Those names are collision-free and reproducible, which is why they are the default — but they also reproduce the module path, the internal package layout and unexported handler names in a document that is usually served over HTTP, and they make 80-character identifiers in a generated client.

naming:
  operationId: full          # full (default) | receiver-method | method-path
  schemaNames: full          # full (default) | short
FieldValueResult
schemaNamesfull (default)github_com_acme_api_internal_estimate_LineInput
shortLineInput
operationIdfull (default)github.com/acme/api/internal/httpapi.estimateHandler.updateLine
receiver-methodestimateHandler.updateLine
method-pathputEstimatesByIdLine

An unknown value logs a warning and keeps full, so a typo cannot silently change the names your consumers depend on.

Collisions

Two packages with a Components type are ordinary, and short names collide. When they do, every member of the colliding group is qualified — never just one of them — with the shortest suffix of its package path that tells them apart, extended a segment at a time:

billing_Components      # from internal/billing
estimate_Components     # from internal/estimate
LineInput               # unique, so it stays bare

Letting one Components keep the bare name would make the winner depend on nothing a reader can see. Groups are resolved in sorted order, so the result is reproducible run to run.

method-path needs no package qualification, but it is not collision-free either: a method and path pair is unique in OpenAPI, while the identifier derived from it is not — every non-alphanumeric character is dropped, so /a-b, /a/b and /aB all read as getAB. A collision there takes a numeric suffix (getAB2), assigned in sorted path order. These ids are also longer than a handler name — deleteReposByOwnerByRepoIssuesByIndex — which is the trade for carrying no Go symbol at all. receiver-method keeps them short, and falls back to the method-path form when a handler serves more than one route.

A component whose type is a pointer or slice is left fully qualified: such a key is an artifact rather than a type anyone references, and shortening it would collide with the component for the type itself.

Trying it on your project

A -c config replaces the framework preset rather than merging with it, so write the effective config out first and edit that:

apispec --dir . --output-config used-config.yaml   # what apispec composed
# add a `naming:` block to used-config.yaml
apispec --dir . -c used-config.yaml -o openapi.yaml

Security: security, securitySchemes, securityMappings

Most auth setups are detected with no config (see the README Security & authentication detection section). Add config only for custom middleware.

# Document-level requirement (applies to all operations unless overridden)
security:
  - bearerAuth: []

# Scheme definitions (only needed for schemes not auto-registered)
securitySchemes:
  bearerAuth:
    type: http
    scheme: bearer
    bearerFormat: JWT

# Map a detected middleware identity to a scheme
securityMappings:
  - functionNameRegex: ^authMiddleware$
    schemes:
      - { bearerAuth: [] }

  # An apiKey middleware that says in its own configuration WHERE the key
  # travels. Without this the scheme can only be documented at the library
  # default, which is wrong for any project that configures one (#370).
  - functionNameRegex: ^KeyAuthWithConfig$
    pkgRegex: ^example\.com/auth$
    schemes:
      - { apiKeyAuth: [] }
    lookupField: KeyLookup     # field holding "<header|query|cookie>:<name>"
    lookupArgIndex: 0          # which argument holds the config (default 0)

securityMappings is framework-agnostic and works together with framework.securityPatterns (which describes scope — router / subtree / route / wrapper). See AUTH_DETECTION_DESIGN.md for the full model.

lookupField / lookupArgIndex

lookupField names the configuration field that states where an API key is read from, in the grammar echo and fiber share ("query:api_key", "cookie:token", "header:X-API-Key"). It is read at the call site, so:

  • a middleware left unconfigured keeps the library default;
  • two scopes configured differently become two schemes, named after the location and key (apiKeyAuthQueryApiKey), so neither claims to be the project's single answer;
  • a value built at runtime, or a source OpenAPI cannot express (a form field), keeps the default and is reported on stderr rather than presented as observed.

The presets for echo's KeyAuth/KeyAuthWithConfig and fiber's keyauth.New already declare it; this is for a house middleware that carries the same kind of configuration.

framework (advanced)

The framework block holds the pattern system that drives route, request-body, response, parameter, mount, and security detection. The bundled defaults cover gin, echo, chi, fiber, gorilla/mux, and net/http; you normally extend this only to support a bespoke wrapper or an unsupported framework.

framework:
  routePatterns:
    - callRegex: ^(?i)(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)$
      recvTypeRegex: ^github\.com/gin-gonic/gin\.\*(Engine|RouterGroup)$
      handlerArgIndex: 1
      methodFromCall: true
      pathFromArg: true
      handlerFromArg: true
  requestBodyPatterns:
    - callRegex: ^(?i)(BindJSON|ShouldBindJSON|ShouldBind)$
      typeFromArg: true
      deref: true
  responsePatterns:
    - callRegex: ^(?i)(JSON|XML|String)$
      typeArgIndex: 1
      statusFromArg: true
      typeFromArg: true
  paramPatterns:
    - callRegex: ^Param$
      # path | query | header | cookie, plus two requestBody facts:
      # formFile (an uploaded file part) and multipart (the body IS multipart)
      paramIn: path
    - callRegex: ^Query$
      paramIn: query
    - callRegex: ^Get$
      paramIn: header
      recvType: net/http.Header
      # net/http.Header is the header map of the request AND the response, so a
      # read only documents a parameter by provenance: w.Header().Get(k) and
      # c.Response().Header().Get(k) read headers the server SENDS. An origin
      # that cannot be resolved keeps the parameter.
      excludeRecvOriginRegex: ^\*?net/http\.\*?ResponseWriter$
  requestContext:          # disambiguate generic decoders (json.Decode, etc.)
    typeRegexes:
      - ^net/http\.\*Request$
    bodyAccessors:
      - ^Body$
  responseContext:         # which types ARE the response writer (see below)
    writerTypeRegexes:
      - ^net/http\.ResponseWriter$

Sub-keys of framework:

KeyPurpose
routePatternsHow routes are registered (method/path/handler extraction).
requestBodyPatternsCalls that bind a request body to a Go type.
responsePatternsCalls that write a response (status + body type). Anchor any pattern that extracts a body type — see below.
paramPatternsCalls that read a parameter, and its in: location.
mountPatternsSub-router mounting (path-prefix composition).
securityPatternsWhere/how auth middleware is applied (scope).
entrypointPatternsStruct fields holding a function a library calls back (a CLI Action/RunE), so routes registered there are reachable. Presets apply from your imports.
handlerInterfaceMethodsMethod names that make a type a handler (ServeHTTP), so a route registered with a handler value is followed into it.
requestContextWhich receivers/accessors mark a "request body" source.
responseContextWhich types are the response writer, for response patterns gated on write destination (requireResponseDestination). Parameter reads state their own exclusion per pattern (excludeRecvOriginRegex).

Anchoring a response pattern

A responsePattern matches by call name. If it also sets typeFromArg and is anchored to nothing else, it documents that call's argument as the endpoint's response body wherever the call appears in the handler's call graph — including where a client marshals a body for an outbound request. The endpoint then carries a response it can never return, and nothing in the spec indicates why.

# Wrong: matches every json.Marshal reached from the handler, including the one
# an HTTP client uses to build its own request body.
- callRegex: ^Marshal$
  typeFromArg: true

Any one of these anchors it — pick whichever describes the real constraint:

anchoruse when
recvType / recvTypeRegexthe call is made on the response writer or a framework renderer
requireResponseDestinationit is a generic encoder whose destination must trace to the response writer (json.NewEncoder(w).Encode(v), with destFromReceiver)
callerPkgPatterns / calleePkgPatternsonly calls made in, or landing in, particular packages count
functionNameRegexthe enclosing function identifies it

APISpec reports unanchored patterns at config load:

[config] 1 response pattern(s) match a bare call name anywhere in the call graph
and may document an outbound request body as a response: responsePatterns[7]
(callRegex "^Marshal$") — scope with recvType/recvTypeRegex, or set
requireResponseDestination

It is advisory, not an error: a project whose serializer really is only reached from a response path is entitled to keep the pattern. Every shipped preset is anchored, so a default run never reports this.

A serializer that returns bytes (json.Marshal) has no destination for requireResponseDestination to check. The supported way to document b, _ := json.Marshal(v); w.Write(b) is not a Marshal response pattern at all — it is responseContext.bodyTransforms, which traces the marshalled bytes to the write on the response writer.

Scoping a pattern to where the call is made

Every pattern above also accepts four filters, shared by all six pattern types:

FieldMatches against
callerPkgPatternsthe package of the function containing the call
callerRecvTypePatternsthe type whose method contains the call
calleePkgPatternsthe package being called into
calleeRecvTypePatternsthe owner type of the call (the list form of recvTypeRegex)

Each is a list of regexes; any entry admits the call, all four are ANDed with each other, and an empty list constrains nothing. A function with no receiver is addressed by its package, the same convention recvTypeRegex uses.

The caller side answers a question nothing else can: two packages may register routes with the identical call, and only where the call is made separates them.

framework:
  routePatterns:
    - callRegex: ^(?i)(Get|Post|Put|Delete)$
      recvTypeRegex: ^github\.com/go-chi/chi(/v\d)?\.\*?(Router|Mux)$
      methodFromCall: true
      pathFromArg: true
      handlerFromArg: true
      handlerArgIndex: 1
      # Document the public surface; the operator endpoints registered the same
      # way from internal/debugroutes stay out of the spec.
      callerPkgPatterns:
        - /internal/api$

These are include filters — there is no "everything except", because Go's regexp engine has no negative lookahead. A pattern that must avoid one caller is written by naming the callers it wants.

Because these patterns are numerous and framework-specific, the authoritative reference is the in-repo default configs (internal/spec/config_*.go) and the struct definitions with doc comments in internal/spec/config.go. The quickest way to author a custom pattern is to dump the effective config with --output-config and edit the relevant block.


See also