Changelog

4.19.0

(fix): With respect-optional-request-body enabled, a call that leaves out an optional request body now passes a null body to OkHttp instead of an empty one, so the request carries no body at all. The methods OkHttp requires a body for (POST, PUT, PATCH) still send an empty body, which is what endpoints with no request body at all already send.

(feat): Add a respect-optional-request-body option. When enabled, an endpoint whose request body the API does not require also gets an overload without the body parameter, and calling that overload sends no body. The body parameter keeps its own type, so alpha(String id, Body body) gains a sibling alpha(String id) rather than becoming alpha(String id, Optional<Body> body). Examples that supply no body render as client.alpha(id). That call also leaves out the Content-Type header, which now only goes out when a body does. The option defaults to false, so existing signatures and snippets are unchanged.

4.18.2

(fix): Build an empty request body in snippets for an endpoint example that omits a body the wrapped request requires, instead of leaving the builder’s required body stage unset.

4.18.1

(fix): Fix the generated OAuth token supplier when the get-token request has required properties other than the client credentials. The builder calls are now ordered by the request’s required properties, so the staged builder’s stages are satisfied in order, and hardcoded values (such as grant_type) are written using the property’s type, which means an enumerated grant_type gets the generated enum constant instead of a string literal.

4.18.0

(feat): Add support for the FERN_JAVA_SKIP_FORMATTING environment variable. When set to a truthy value (1, true, yes, or on), the generator skips the ./gradlew :spotlessApply pass that normally runs after code generation, so generation makes no Gradle invocation at all. This unblocks restricted networks that cannot reach the Gradle distribution or plugin repositories, and shortens generation time.

4.17.1

(fix): Fixed wire tests generated for APIs with auth: any combining Basic and OAuth schemes. Tests no longer assert both a Bearer and a Basic Authorization header on the same request; only the OAuth Bearer assertion is emitted since the client sends the OAuth token when OAuth credentials are configured.

4.17.0

(feat): Add opt-in allowUserAgentAppInfo config. When enabled, the generated client’s builder exposes an appInfo(name, version, comment) option whose product token is appended to the User-Agent header the SDK would otherwise send ({sdk}/{version} ... {name}/{version} ({comment})), following RFC 9110. Disabled by default, so existing generated output is unchanged, and it composes with includePlatformHeaders, the runtime-version jar-manifest path, the configured user-agent template value, and the default {coordinate}/{version}. Caller-supplied values are trimmed and sanitized (name/version percent-encoded to RFC 7230 tchars; comment delimiters, control characters and any non-ASCII characters percent-encoded as UTF-8 bytes so OkHttp’s header validation never rejects them), so untrusted values cannot inject header content. Still overridable by an explicit User-Agent and suppressed by omit-fern-headers.

4.16.0

(feat): Add an opt-in runtime-version config flag. When enabled, the SDK version reported in the telemetry headers (X-Fern-SDK-Version and the version segment of User-Agent) is resolved at runtime from the jar manifest’s Implementation-Version attribute instead of being baked in as a literal at generation time. The generated build.gradle records the project version in the jar manifest, so an external tool such as release-please only has to rewrite a single version line for the reported version to track the actually-published artifact — rather than a version the SDK may never publish. Falls back to the generation-time version when the manifest attribute is absent (e.g. running from unpackaged classes). Disabled by default, so existing generated output is unchanged, and subject to omitFernHeaders.

4.15.5

(fix): Resolve the X-Fern-SDK-Name header from the configured Maven coordinate in GitHub output mode. Previously the header was read from the (Fiddle-synthesized) publish config, so it emitted com.<org>.fern:<workspace>-sdk instead of the coordinate the user configured under output.coordinate, leaving it inconsistent with the User-Agent product token. The header now reads output.mode.github.publishInfo.maven.coordinate, which is populated for both remote and --local generation. Also emits X-Fern-SDK-Version in cases where a coordinate resolves without a version previously being set.

4.15.4

(fix): Global headers now honor their env and client-default values. The builder initializes the header from the environment variable when set, otherwise the client default, and an explicitly provided value still takes precedence. Previously a global header was only sent when the caller set it explicitly.

4.15.3

(fix): Fix a NullPointerException thrown at runtime on every OAuth (or inferred-auth) authenticated request under auth: endpoint-security. To fetch a token, the OAuth auth provider builds an internal client for the (unauthenticated) token endpoint whose ClientOptions has no auth provider; ClientOptions.getAuthHeaders(...) then dereferenced the null provider and threw. It now returns no auth headers when no auth provider is configured, so token fetching (and therefore all OAuth-authenticated calls) works.

4.15.2

(fix): Fix a NullPointerException when generating an SDK with auth: endpoint-security whose OAuth (or inferred-auth) token endpoint is grouped under a subpackage not named auth. The RoutingAuthProvider setup previously located the auth client by scanning for a subpackage literally named auth, yielding null (and an NPE) when the token endpoint lived in a differently-named group (e.g. token). It now resolves the client from the token endpoint’s actual subpackage, falling back to the core AuthClient when the token endpoint sits at the API root.

4.15.1

(fix): Fix ConsoleLogger dropping DEBUG-level log messages. The ConsoleHandler used by the default logger keeps java.util.logging’s default INFO handler level, so FINE records (SDK debug logs, including HTTP request/response logging) were silently discarded even when LogConfig was configured with LogLevel.DEBUG and silent(false). The handler level is now set to ALL so the configured LogLevel controls filtering.

4.15.0

(feat): Add webhook body-hash binding support. When a webhook’s HMAC signature verification declares a bodyHashBinding in the IR, the generated verifySignature helper now recomputes the encoded hash of the raw request body using the binding’s own algorithm/encoding, extracts the configured query parameter from the notification URL, and timing-safe-compares them (failing closed) before performing the existing HMAC verification over the verbatim notification URL. A stdlib-only WebhookBodyHash core utility (unkeyed digest plus read-only query-parameter extraction) is emitted alongside the existing signature utility. Output is unchanged when no binding is present.

4.14.6

(fix): Fixed an issue where the client builder rendered the first environment’s URL template instead of the selected environment’s template when both a named environment and server URL variables (e.g. region) were provided. The builder now resolves the template(s) from the selected environment, falls back to the first templated environment when no environment is selected, and no longer overrides a custom environment or explicitly provided base URL.

4.14.5

(fix): Honor includePlatformHeaders when generating to local file system output. Previously the flag was only read for GitHub/publish output, so locally generated SDKs always emitted the static {package}/{version} User-Agent instead of the structured {package}/{version} (os; arch) Java/{version} form.

4.14.4

(fix): Make the smart-casing digit/word boundary behavior opt-in via the new smart-casing-digit-word-boundary option in generators.yml. By default, snake_case names keep a digit run fused to the following word (conversations_v2configuration), restoring pre-4.13.4 output. When enabled, the word boundary after the digit run is preserved (conversations_v2_configuration).

4.14.3

(fix): Endpoints without a request wrapper (no request object, or a request that is just a body) now send service- and endpoint-level headers whose types are literals (e.g. Accept-Encoding: literal<"gzip">). Previously these headers were silently dropped because there was no request object to carry them.

4.14.2

(fix): Fix a NullPointerException when constructing an OAuth-only client under multi-scheme (any) auth combining OAuth client-credentials with an API-key header. Building via withCredentials(...) (no apiKey(...)) previously baked a null API-key header into ClientOptions, which crashed okhttp Headers.of on the first HTTP call (the token fetch). The generated setAuthentication now guards the API-key header add on a non-null value, and ClientOptions.Builder.addHeader(String, String) defensively skips null values. Supplying both credentials and an API key still sends both the Authorization: Bearer and the API-key header, and an API-key-only client still validates the key at build time.

4.14.1

(fix): OAuth client credentials token requests now send grant_type: "client_credentials" when the token endpoint declares a non-literal grant_type request property. The property is synthesized in the token request and is no longer surfaced as a builder option.

4.14.0

(feat): Idempotency-key auto-generation is now driven by the IR (SdkConfig.idempotencyKeyGeneration) instead of a per-generator config flag. When enabled, the SDK adds the configured idempotency header on the configured HTTP methods unless the caller already supplied one. The header name and eligible methods come from the IR, and generated output is unchanged when the feature is off.