Changelog

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.

4.13.4

(fix): Fix smart-casing snake_case names dropping the word boundary after a number. Names like ConversationsV2Configuration now produce the snake_case conversations_v2_configuration instead of conversations_v2configuration, matching the camelCase and pascalCase variants.

4.13.3

(chore): Upgrade the Java SDK generator to consume IR v67. The generated SDK output is unchanged.

4.13.2

(fix): Fix the OAuth OAuthTokenSupplier generator to derive each get-token request property’s Java type from its IR TypeReference (via PoetTypeNameMapper) instead of hardcoding String. Previously a non-string token-request property (e.g. permissions: Optional<List<String>>) generated a String field and a .permissions(String) builder call that did not match the request builder’s typed setter, breaking compilation of the entire SDK. The generated custom property fields, constructor parameters, client-builder setters, and request builder calls now use the property’s real type.

4.13.1

(fix): Make the generated User-Agent comply with the RFC 7230 token grammar. The Maven coordinate in the product token now uses a dot instead of a colon, since a colon is not a valid token character (e.g. com.fern:imdb/0.0.1 becomes com.fern.imdb/0.0.1); this applies to both the plain header value and the structured value emitted when includePlatformHeaders is enabled. For the structured value, the architecture label is also normalized so the 64-bit x86 aliases (x64, amd64, x86_64) all report as the canonical x86_64.

4.13.0

(feat): Add opt-in support for a structured User-Agent header on generated SDK clients, of the form {sdkName}/{sdkVersion} ({os}; {arch}) {runtime}/{runtimeVersion} (e.g. com.fern:sdk/0.0.1 (linux; amd64) Java/17.0.13). The os (System.getProperty("os.name"), lowercased), arch (System.getProperty("os.arch")), and runtime version (System.getProperty("java.version")) are resolved at runtime; any segment that cannot be determined is omitted rather than emitted as null. This is gated behind a new includePlatformHeaders config option and is disabled by default, so existing generated output is unchanged. When enabled, the header is still subject to omit-fern-headers.

4.12.1

(fix): Decompress gzip and deflate encoded response bodies via a new ResponseDecompressionInterceptor added to the generated OkHttpClient. OkHttp only performs transparent gzip decompression when it adds the Accept-Encoding header itself, so responses to requests with a spec-defined Accept-Encoding: gzip header were previously returned as raw gzip bytes.

4.12.0

(feat): Add client builder options to customize the retry backoff schedule, alongside the existing maxRetries option: initialRetryDelayMillis (default 1000), maxRetryDelayMillis (default 60000), and retryJitterFactor (default 0.2). The options are available on both the root client builder and ClientOptions.Builder.

1Client client = Client.builder()
2 .maxRetries(3)
3 .initialRetryDelayMillis(500)
4 .maxRetryDelayMillis(30000)
5 .retryJitterFactor(0.1)
6 .build();

4.11.2

(fix): Fix a thread-safety bug in the generated OAuth and inferred-auth token caches. OAuthTokenSupplier and InferredAuthTokenSupplier now use double-checked locking with volatile cache fields, so the token/header cache (a singleton shared across all request threads) is refreshed exactly once under concurrent load (single-flight) and cached values are safely published across threads. The cache-hit path stays lock-free. OAuthAuthProvider’s existing double-checked locking is corrected by marking its accessToken/expiresAt fields volatile. No public API changes; generated code remains Java 8 compatible.

4.11.1

(fix): Fix misleading “Network error” message when deserialization fails. JSON deserialization errors (e.g. unsupported date formats) are now reported as “Failed to deserialize response” instead of being incorrectly wrapped as network errors.

(fix): Add RFC 1123 (RFC 2822) date format support to DateTimeDeserializer. Dates like “Thu, 07 May 2026 14:23:38 +0000” are now parsed correctly.

4.11.0

(fix): Fix URL building for paths with non-slash-separated suffixes (e.g., /{Sid}.json). The suffix is now correctly concatenated with the path parameter value instead of being added as a separate path segment, which previously caused an extra / before the suffix.

(feat): Add per-request maxRetries override on RequestOptions. A non-null per-request value overrides the client-wide ClientOptions.maxRetries; when unset, behavior is unchanged. Mirrors the existing per-request timeout override pattern and matches the behavior of the TypeScript, Python, Go, C#, PHP, Rust, and Swift SDKs.

4.10.19

(fix): Remove duplicate JavaDoc descriptions in staged builder implementation methods. Previously, builder methods would display the field description twice in IDE inline documentation.