What your OpenAPI security scheme actually produces in an SDK
A security scheme is not documentation. It is the auth configuration your generated client ships with, and a spec that declares none produces a client with no way to authenticate.

A security scheme is not documentation. It is the auth configuration your generated client ships with, and a spec that declares none produces a client with no way to authenticate.


A one-line spec edit can break every call site your users wrote. Here is the table of what is additive, what is breaking, and which ones your spec diff will not warn you about.

An operationId is not documentation metadata. It is the name your users type. Here is why uniqueness does not save you, and what actually collides.

The honest answer depends on how many languages you ship and how often your API changes. For one language and a stable API, hand-writing wins.
© 2026 Octri, LLC. All rights reserved.
Made by devs who got tired of hand-writing SDKs.
components.securitySchemes is one of the most commonly skipped blocks in an OpenAPI document, and it is skipped for an understandable reason: the authors know how their API authenticates, so writing it down feels like paperwork.
It is not paperwork. It is the only input a generator has for building your client's auth. Leave it out and the SDK ships with no credential handling at all, and every one of your users writes their own header injection.
The scheme is the contract. Here is what a generator does with each one.
apiKey. Becomes a configuration field and a header, query parameter or cookie, depending on in. The name matters: it is the actual header your client will send, so X-API-Key and Api-Key are different clients.
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Keyhttp with scheme: bearer. Becomes a token field and an Authorization: Bearer header. The most common case and the least ambiguous.
http with scheme: basic. Becomes a username and password pair, base64-encoded into Authorization: Basic by the client rather than by your users, which is the point.
oauth2. Becomes a token field and, more importantly, the flow declarations tell a reader where the token comes from. A generated client will send the token; the flows are what stop your users guessing which URL to post their credentials to.
Nothing at all. Becomes a client with no auth configuration. Your users construct requests correctly and get 401s, then read your prose docs, then hand-roll a header. Every one of them separately.
This is the part that surprises people. The key you give a scheme is not internal.
components:
securitySchemes:
ApiKeyAuth: # this name
type: apiKeyThat name propagates into the generated client's auth configuration as a field. Your users will type it:
const acme = new Acme({
baseUrl: "https://api.acme.com/v1",
auth: { apiKeyAuth: process.env.ACME_KEY },
});So ApiKeyAuth reads fine, auth1 does not, and SCHEME_A will haunt you. Name schemes the way you would name a public field, because that is what they are. And renaming one later renames a field your users have already written, which is a breaking change in a place nobody expects to find one.
Two blocks, and specs routinely have one without the other.
components.securitySchemes declares that a scheme exists. security, either at the document root or on an individual operation, declares which operations require it. A document that declares a scheme and never applies it has told the generator that auth exists and never told it which calls need it.
security:
- ApiKeyAuth: [] # applies to every operation
paths:
/health:
get:
security: [] # except this one, explicitly publicThat empty array on /health is worth knowing about. It is how you mark a single operation as public under a document-wide requirement, and without it your generated client will attach credentials to your health check and your docs playground will demand a key to call it.
Plenty of real APIs use more than one scheme: a publishable key for browser calls, a secret key for server calls, sometimes a separate admin credential.
OpenAPI can express this. An array under security is OR, so any one of the listed schemes satisfies the requirement. Multiple keys inside a single array entry is AND, so both must be supplied.
security:
- ApiKeyAuth: [] # either this
- BearerAuth: [] # or this
# versus
security:
- ApiKeyAuth: []
BearerAuth: [] # both, togetherGenerated clients handle a declared multi-scheme setup far better than they handle an undeclared one. If your API genuinely has two credential types and your spec mentions one, the SDK gets built around the one, and the other becomes a support conversation about custom headers.
bearerWorth stating plainly because it is a choice we get asked about.
An Octri project's SDK auth defaults to inherit, which means the generated client authenticates the way your spec's security scheme says it does. It does not default to bearer, even though bearer is the most common answer, because a project that has never opened SDK Studio should not have its SDK and its documentation playground quietly disagreeing about how to authenticate. One spec, one answer, in both places.
You can override it with a concrete scheme when the SDK genuinely needs to differ. The default is that it does not.
A small documentation habit with a large blast radius.
Every code sample you publish is a template somebody will paste. If your sample shows a literal key, some fraction of your users will commit one. Read from the environment in every example, including the short ones:
auth: { bearerAuth: process.env.ACME_TOKEN }Generated SDK readmes should do this by default. Your hand-written guides are where it slips.
Undeclared auth is one of the fourteen rules our spec audit scores, and it is a cheap one to fix: the rule fires if components.securitySchemes is empty, which is a five-line edit for most APIs and changes what every generated client can do.
If you want to see the auth configuration a scheme produces at the call site, the OpenAPI to TypeScript SDK and OpenAPI to Python SDK pages show the constructed client in each language.