Skip to content
FeedbackDashboard
Getting started
Configure SDKs

Client settings

Configure your SDK's client level settings, including authentication, custom arguments, default headers, retries with exponential backoff, timeouts, idempotency keys, and file uploads.

[reference]

The Stainless Generator defines your SDK’s authentication using the #/security and #/components/securitySchemes particulars from your OpenAPI spec. By default, Stainless SDKs are set to authenticate using an environment variable:

client_settings:
opts:
auth_token:
type: string
# Whether this client option is required to instantiate the client:
nullable: false
# Whether this value should be read from an env:
read_env: ORG_AUTH_TOKEN
auth: { security_scheme: BearerAuth }
# optional, overrides the OpenAPI spec's top-level security key, required if it isn't present
security:
- BearerAuth: []

For more complicated authentication schemes, or should the Stainless Generator fail to configure yours correctly, see the examples below.

The Stainless generator uses the top-level security as the security configuration supported by the SDKs, but you may want to specify a different combination of security than the one used in your OpenAPI spec. The security and security_schemes in the Stainless config overrides the values in the spec.

HTTP Bearer Authorization: Bearer <bearer-token>

The HTTP Bearer authentication method is configured like so:

# OpenAPI
components:
security_schemes:
MyBearerAuth:
type: http
scheme: bearer
# optional, documentation purpose only
bearerFormat: JWT
security:
- MyBearerAuth: {}
# Stainless config
client_settings:
opts:
my_bearer_token: # or `token`, `bearer_token`, `api_key`, etc.
type: string
read_env: ORG_BEARER_TOKEN
auth: { security_scheme: MyBearerAuth }
API Key <Header>: <API Key>

An API key in a header authentication method is configured like so:

# OpenAPI
components:
security_schemes:
MyApiKeyAuth:
type: apiKey
name: My-Api-Key
in: header
security:
- MyApiKeyAuth: {}
# Stainless config
client_settings:
opts:
my_api_key: # or `token`, `auth_token`, etc.
type: string
read_env: ORG_API_KEY_TOKEN
auth: { security_scheme: MyApiKeyAuth }
HTTP Basic Authorization: Basic <base64(username:password)>

The HTTP Basic authentication method is configured like so:

# OpenAPI
components:
security_schemes:
MyBasicAuth:
type: http
scheme: basic
security:
- MyBasicAuth: {}
# Stainless config
client_settings:
opts:
my_username:
type: string
read_env: ORG_MY_USERNAME_TOKEN
auth: { security_scheme: MyBasicAuth, role: 'username' }
my_password:
type: string
read_env: ORG_MY_PASSWORD_TOKEN
auth: { security_scheme: MyBasicAuth, role: 'password' }
OAuth 2.0 Client Credentials

Stainless fully supports OAuth client_credentials. For this grant type, our SDKs handle initial authorization and the entire token management lifecycle.

Stainless uses the OAuth configuration in your OpenAPI spec if present, or you can specify an OAuth configuration in your Stainless config.

Example security configuration in the Stainless config:

security:
- OAuth2: []
security_schemes:
OAuth2:
type: oauth2
flows:
clientCredentials:
tokenUrl: >-
https://example.com/oauth2/token?grant_type=client_credentials
scopes:
# ...

You must also define client options for the client_id and client_secret parameters in your Stainless config so users of your SDKs can provide those values. The names of these parameters in opts can be customized; the important part is to specify the correct auth information for each (especially auth.role, which must be either client_id or client_secret).

client_settings:
opts:
client_id:
type: string
auth:
security_scheme: OAuth2
role: client_id
read_env: ORG_CLIENT_ID
client_secret:
type: string
auth:
security_scheme: OAuth2
role: client_secret
read_env: ORG_CLIENT_SECRET

With a configuration like the one above, users can provide a client_id and client_secret when initializing your SDK to use OAuth.

Behind the scenes, the SDK will fetch an access token from the configured tokenUrl when the first request is made, cache it, and use it for all following requests until the token expires. Upon expiration, the SDK will automatically obtain a new token.

Failed OAuth requests are automatically retried based on the retry configuration for the SDK. If all retries fail, an exception will be thrown or an error will be returned (whichever makes sense for the particular language being used).

Please reach out if you need help supporting your configuration.

OAuth 2.0 Authorization Codes

If you’re using authorization codes, we recommend you set up Bearer Authentication and add custom code to help your users convert authorization codes to access tokens. If you’re interested in generated helpers for this, or if you need additional guidance, reach out and let us know!

With this approach, initial authorization and token management are not handled by Stainless. We recommend you point your users to an OAuth SDK in their preferred language for handling these parts of the OAuth flow.

Once Stainless is configured to use Bearer Authentication, as seen in the example below, users of your SDKs will be able to authenticate with their access token.

OpenAPI spec:

securitySchemes:
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://example.com/oauth2/auth
tokenUrl: https://example.com/oauth2/token
scopes:
# ...

Stainless config:

client_settings:
opts:
access_token:
type: string
auth:
security_scheme: BearerAuth
read_env: MY_TEAM_ACCESS_TOKEN
security:
- BearerAuth: []
security_schemes:
BearerAuth:
type: http
scheme: bearer

Please reach out if you need help supporting your configuration.

Optional Auth

To specify that you accept no authentication, declare a security configuration with no key and no properties like so:

security:
- BearerAuth: {}
- {} # <= No auth
Multiple API Keys

If your API requires different keys for different endpoints, you can configure multiple security schemes in your OpenAPI spec and corresponding client options in your Stainless config:

# OpenAPI
components:
securitySchemes:
ReadKeyAuth:
type: apiKey
in: header
name: X-Read-Api-Key
WriteKeyAuth:
type: apiKey
in: header
name: X-Write-Api-Key
security:
- ReadKeyAuth: []
- WriteKeyAuth: []
# Stainless config
client_settings:
opts:
read_key:
type: string
nullable: true
auth:
security_scheme: ReadKeyAuth
read_env: READ_API_KEY
write_key:
type: string
nullable: true
auth:
security_scheme: WriteKeyAuth
read_env: WRITE_API_KEY

The generated SDK accepts multiple keys during initialization. When both keys are provided, all configured auth credentials are sent with each request:

const client = new MyAPI({
readKey: 'read_key_123',
writeKey: 'write_key_456',
});
// Both keys are sent with requests
client.resources.list();
client.resources.create({...});

[reference]

You can define extra client arguments, which generally appears as an extra argument on the client constructor of each SDK (in Go, it appears as an extra RequestOption). These are generally used for supplying values for authentication methods, but can be also used for extra headers and more.

client_settings:
opts:
pet_store_version:
type: string # can be a string, boolean, or a number
nullable: true # makes this an optional argument
default: v3 # the default pet store version to use
read_env: PETSTORE_VERSION
send_in_header: 'X-Petstore-Version'
const client = new Petstore({
apiKey: '...',
petStoreVersion: 'v2', // sends 'X-Petstore-Version: v2'
});

[reference]

Default headers are headers we add to every request made by the SDK. We send platform headers so that you can collect metrics on the languages and platforms your users use. For all SDKs, we send the following:

HeadersDescription
X-Stainless-ArchThe architecture, such as x32, x64, arm, aarch64, or other:xxx.
X-Stainless-LangThe language, such as typescript, python, java, kotlin, or go.
X-Stainless-OSThe OS, such as Android, MacOS, Windows, FreeBSD, OpenBSD, Linux, or Other:xxx.
X-Stainless-Package-VersionThe package version, such as v2.3.1.
X-Stainless-Read-TimeoutThe timeout, in seconds, between receiving response chunks, such as 3. Not sent if no read timeout is configured.
X-Stainless-Retry-CountWhich retry number the request is for, such as 0 for the first request, 1 for the first retry, 2 for the second retry, and so on.
X-Stainless-RuntimeThe runtime, such as node or CPython.
X-Stainless-Runtime-VersionThe runtime version, such as v14.8.0.
X-Stainless-TimeoutThe timeout, in seconds, for the entire request, such as 10. Not sent if no overall timeout is configured.

We also send some extra headers for each language:

Python HeadersDescription
X-Stainless-AsyncWhether or not the AsyncClient was used.
Java/Kotlin HeadersDescription
X-Stainless-OS-VersionThe OS version, such as 14.4.

For requests made by a Stainless-generated MCP server, we will also send some extra headers:

MCP HeadersDescription
X-Stainless-MCPAlways true.

Our clients retry connection errors (for example, a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, and >=500 Internal errors.

By default, our clients retry 2 times (so a total of 3 requests) using the exponential backoff strategy with an initial delay of 0.5s and a max delay of 8s. We also add a jitter of 25% to spread out requests.

This can be configured by you for your users:

client_settings:
default_retries:
# 5 retries are made, with the interval [1, 2, 4, 8, 10 (capped by the max)]
# not accounting for jitter
max_retries: 5
initial_delay_seconds: 1
max_delay_seconds: 10

Or it can be changed by the user in each language’s SDK:

client = new Petstore({
maxRetry: 3,
});

Each request includes an X-Stainless-Retry-Count header so you know how often clients retry. See Default headers for details.

Retries can be disabled in two ways. Your users can disable this at at the client level by setting max_retries to 0. For example:

client = new Petstore({
maxRetry: 0, // Doesn't retry at all
});

Alternatively, your API can direct our SDKs to not retry by sending the X-Should-Retry header in your responses.

Our SDKs also respect the Retry-After header sent by the API, which defines in integers how many seconds we should wait before making another request. We also support the Retry-After-Ms header which is less standard but gives more fine-grained control over timings in milliseconds.

The clients only respect values that are “reasonable” which are positive values less than a minute.

Additionally, the clients support the X-Should-Retry header, which can be used to explicitly control whether a request should be retried. This header must be explicitly set to either X-Should-Retry: true or X-Should-Retry: false to be used as an override.

When set to true, it forces a retry attempt even if the request would not normally be retried based on the default retry rules. When set to false, it prevents a retry attempt even if the request would normally be retried. If the header is not present or set to any other value, the default retry behavior is used.

We recommend using the header over other methods to control the retry behavior on special status codes or other situations specific to your API, as it can be used by all consumers of the API and can be configured independently of the version of the SDK.

[reference]

In addition to retries, our clients also have a default timeout of 60 seconds, which can be configured.

client_settings:
default_timeout: PT60S # ISO8601 or number of milliseconds. This is 60 seconds

[reference]

Idempotency Keys can prevent errors where multiple retried requests are interpreted as separate requests. Our clients retry connection errors and certain status codes by default to create robust integrations, so we recommend that your API supports idempotency keys, especially for critical endpoints.

You can configure idempotency keys by specifying client_settings.idempotency like so:

client_settings:
idempotency:
header: 'Idempotency-Key' # or a header you prefer, like 'X-Request-Id'

We send the configured header with a value in the format stainless-retry-{random_uuid} on all non-GET requests. This header is also possible to override in every SDK that we generate.

Our SDKs support file uploads out of the box, via multipart/form-data requests.

File parameters can be defined in the OpenAPI spec by specifying a multipart/form-data request with parameters that are type: string and format: binary.

paths:
/files:
post:
summary: Upload a file
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: The file to upload
file_metadata:
type: string
description: >
Some other property that you can add, serialized as a field in
multipart/form-data
required:
- file
responses:
'200':
description: File uploaded successfully

Each SDK provides convenient ways to handle file uploads appropriate for that language. Below is an example in TypeScript:

await client.files.create({ file: fs.createReadStream('./local-file.txt') });
// or
await client.files.create({ file: new File(['my bytes'], 'content.txt') });

Similar helpers exist in other languages. For example, in Python:

with open('content.txt', 'rb') as f:
client.files.create(file=f)

See the README.md of each SDK for language-specific documentation.