Changelog
1.23.2
(fix): Respect endpoint-level disabled retries. Endpoints configured with
retries: { disabled: true } (x-fern-retries in OpenAPI) now pin the
generated request’s retry count to zero, so they are never retried
regardless of the client-level or per-request retry settings.
1.23.1
(fix): Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is
itself marked as authenticated: the token (and refresh) endpoint stub no longer requires
an Authorization header, since the token request is made to obtain the token and cannot
carry one yet.
1.23.0
(fix): Endpoints whose request body is a referenced type no longer serialize path parameters into
the JSON body. With respectOptionalRequestBody, an omitted optional body on such an endpoint
now sends no body and no Content-Type instead of a body containing the path parameters.
(feat): Add the respectOptionalRequestBody configuration option. When enabled, an endpoint whose
request body the API does not require lets callers omit the body: the request then carries
neither a body nor a Content-Type header, instead of sending {} as application/json.
Disabled by default.
1.22.0
(feat): Add an opt-in preferExplicitAuth configuration option. When enabled (and the API composes
OAuth client-credentials with basic auth via auth: any), auth credentials passed
explicitly to the client constructor take precedence over environment-variable
defaults when selecting the auth scheme — e.g. explicitly provided basic auth
credentials win over OAuth client ID/secret environment variables. Disabled by
default, so existing generated output and runtime behavior are unchanged.
1.21.3
(fix): Wire tests no longer assert an exact Basic Authorization header when an OAuth
scheme is also configured: the test client is constructed with client credentials,
so the OAuth provider’s dynamic Bearer token takes precedence over the static
Basic header at request time.
1.21.2
(fix): Fixed WireMock stubs for wire tests of APIs with auth: any combining Basic and
OAuth/Bearer schemes: stubs now accept either Authorization form instead of
requiring one exact value the client may not send.
1.21.1
(fix): Fix additional_headers in a request’s options being unable to override a global header that was
set at client construction. Every default header was protected from additional_headers, so a
caller who configured a global header on the client (e.g. X-Greenhouse-Id) could not vary it per
request — the per-request value was silently dropped. The API’s non-literal global headers are
now overridable per request, while SDK metadata (User-Agent, X-Fern-*), auth headers, and
literal headers stay protected as before.
1.21.0
(feat): Add an opt-in allowUserAgentAppInfo config (default false). When enabled, the
generated client accepts an optional app_info keyword
({ name:, version:, comment: }) whose product token is appended to the
User-Agent header for all three branches (the structured platform value, the
configured user-agent template value, and the default {package}/{version}),
producing e.g. acme/0.0.1 partner-app/3.1.0 (+https://partner.example) per
RFC 9110. Caller-supplied values are sanitized (name/version percent-encoded to
RFC 7230 tchars; comment delimiters and control characters escaped) and trimmed
before encoding, so untrusted values cannot inject additional header content. The
header is still overridable by an explicit User-Agent and suppressed by
omitFernHeaders. Default-off output is byte-identical.
1.20.2
(fix): Fix gem build producing an empty gem when the generated SDK is not inside a git checkout:
the generated gemspec now falls back to filesystem globbing when git ls-files returns no
files. Also stamp the gem version and name from the IR’s filesystem publish target
(populated by fern generate --version <version> for local-file-system output) instead of
always defaulting to 0.0.0.
1.20.1
(fix): Fix the generated client constructor requiring every auth credential under
auth: endpoint-security. A credential without a configured environment variable (e.g. an
apiKey header) was emitted as a required keyword argument, so a caller authenticating with a
single scheme (such as pure OAuth) could not construct the client without also passing the
other schemes’ credentials. Under endpoint-security every credential now defaults to nil,
matching the per-endpoint auth model. Behavior outside endpoint-security is unchanged.
1.20.0
(feat): Add support for per-endpoint auth routing. When the API-level auth requirement is
ENDPOINT_SECURITY, each endpoint now applies only the auth scheme(s) it declares in
its IR security field (OR across the list of requirements, AND within a requirement,
and no auth when security is empty), instead of applying every configured credential
to every request. Behavior for the ALL and ANY auth requirements (the common cases)
is unchanged.
1.19.0
(feat): Add webhook body-hash binding verification before HMAC signature checks.
(fix): Fix generated verify_signature webhook helper raising instead of returning false
when signature_header or signature_key is an empty string. An empty key was passed
to OpenSSL::HMAC.digest, which raises on OpenSSL 3.x; the input guard now fails
closed on empty (not just nil) signature header and key, matching the never-raise
contract.
1.18.2
(fix): Fix a thread-safety bug in the generated OAuthProvider and
InferredAuthProvider. The token cache is now guarded by a Mutex with
double-checked locking, so only one thread refreshes the token under
concurrent load (e.g. multi-threaded servers like Puma or Sidekiq). The
cache-hit path stays lock-free.
1.18.1
(fix): When a client is constructed with both an environment and server URL variables
(e.g. region), the generated client now renders the SELECTED environment’s URL
template(s) instead of always using the first environment’s template. For
multi-host environments, every host’s URL is formatted from the selected
environment’s templates. An explicitly provided custom base URL or environment
is never overridden.
1.18.0
(feat): Endpoint methods now include a usage example in their YARD doc comment via the
@example tag, so IDEs surface a code snippet on hover.
1.17.8
(fix): Only emit the internal URL-encoded request module (and its root require_relative)
when an endpoint actually sends an application/x-www-form-urlencoded body.
Previously the require_relative ".../internal/url_encoded/request" line was emitted
unconditionally; SDKs generated with a build that shipped the require but not the
as-is file (e.g. an API with no form-urlencoded endpoints) failed to load with a
LoadError. The require and its file are now driven by the same usage check, so they
can never ship apart.
1.17.7
(fix): Revert the smart-casing digit/word boundary behavior introduced in 1.15.5. snake_case
names keep a digit run fused to the following word (conversations_v2configuration),
restoring pre-1.15.5 output. The behavior will become opt-in via the new
smart-casing-digit-word-boundary option in generators.yml once the generator
consumes the IR version that carries the setting.
1.17.6
(fix): Endpoints now send service- and endpoint-level headers whose types are literals
(e.g. Accept-Encoding: literal<"gzip">). Previously these headers were silently
dropped for endpoints without a request wrapper, and service-level literal headers
were never sent at all. Caller-supplied values via the request params still take
precedence over the literal defaults.
1.17.5
(fix): Cap generated type filenames at 100 characters so gem build no longer fails
with Gem::Package::TooLongFileName (RubyGems packages into a tar format whose
header caps filenames at 100 chars). Deeply nested inline types — such as
anonymous oneOf variants — could previously overflow this limit. When a name
is too long it is truncated and a short deterministic hash is appended to keep
it unique. This changes only the filename and its require_relative; the
module/class name is unchanged, so it is non-breaking for consumers, who
reference the constant rather than the file.
1.17.4
(fix): Send request bodies declared with the application/x-www-form-urlencoded content type
(e.g. OAuth token endpoints) as form-urlencoded instead of JSON. A new
Internal::UrlEncoded::Request encodes the body with URI.encode_www_form and sets the
Content-Type: application/x-www-form-urlencoded header.