Changelog

5.26.0

(feat): Add an encode_path_params config option (default false). When enabled, path parameter values are percent-encoded when substituted into the request path, so a value containing / or .. can no longer change which endpoint the request resolves to. The default false preserves the existing (unencoded) behavior; TypeScript, Go, Java, and C# already encode path params.

5.25.0

(feat): Add opt-in allow_user_agent_app_info config (camelCase alias allowUserAgentAppInfo). When enabled, the generated client accepts an optional app_info ({"name": ..., "version"?: ..., "comment"?: ...}) constructor argument whose product token is appended to the User-Agent header ({sdk}/{version} ... {product}/{product-version} ({comment})), following RFC 9110. Disabled by default, so existing generated output is unchanged. It composes with include_platform_headers, the user-agent template config, and the default {package}/{version} value, and survives the runtime_version path. Caller-supplied values are sanitized (name/version token-encoded, comment delimiters and control characters escaped), and it is still overridable by an explicit User-Agent header and suppressed by omit_fern_headers.

5.24.2

(fix): Fix two issues with the opt-in allow_user_agent_app_info client option:

  • The app_info constructor parameter no longer leaks a bare positional None into generated usage snippets and docstrings, which produced invalid Python (a positional argument after keyword arguments). It remains an optional keyword argument defaulting to None.
  • Non-ASCII characters in the app_info comment are now percent-encoded (UTF-8). Previously they were passed through into the User-Agent header, causing httpx to raise UnicodeEncodeError on every request.

5.24.1

(fix): Respect the user-agent config template when include_platform_headers or runtime_version is enabled. The structured User-Agent previously always used the package name and version, ignoring the configured template.

5.24.0

(feat): Add an opt-in optional-auth config flag. When enabled, client auth parameters (bearer token, basic auth username/password, header auth) are made optional even when the API spec mandates auth on all endpoints (isAuthMandatory=true). Auth headers are only sent when credentials are provided. Useful for hand-maintained wrapper clients that authenticate via external means (e.g. cloud provider credentials). Defaults to false, so existing behavior is unchanged.

5.23.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 via importlib.metadata.version(<dist>) instead of being baked in as a literal at generation time. This lets the reported version track the actually-installed package version — for example when an external tool such as release-please sets the published version after generation — rather than a version the SDK may never publish. Falls back to the generation-time version when the distribution is not installed (e.g. running from source). Disabled by default, so existing generated output is unchanged, and subject to omit_fern_headers.

5.22.2

(fix): Fix multipart form list-of-primitive body properties being sent as a single JSON-encoded field instead of repeated form fields. A property such as Optional[List[str]] was serialized as json.dumps(jsonable_encoder(value)), producing one field whose value was the whole JSON array (e.g. keyterms=["a","b"]); servers that validate each item then rejected it. Such properties are now passed through jsonable_encoder and handed directly to httpx’s data=, emitting repeated fields (keyterms=a, keyterms=b). Lists of objects, maps, and other complex types remain JSON-encoded as before.

5.22.1

(fix): Loosen the generated httpx-aiohttp optional dependency constraint from an exact pin (0.1.8) to a caret range (^0.1.8, i.e. >=0.1.8,<0.2.0). This lets the aiohttp extra coexist with other packages that require a newer 0.1.x of httpx-aiohttp (e.g. >0.1.9) without introducing a breaking major bump.

5.22.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. Inferred (token-endpoint) auth is now also wired under ENDPOINT_SECURITY when other schemes are present, and generated wire tests assert per endpoint that only the declared scheme’s header is sent.

5.21.0

(feat): Add webhook body-hash binding support to verify_signature. When a webhook’s HMAC signature declares a bodyHashBinding in the IR, the generated helper first recomputes the raw request body’s hash (using the binding’s own algorithm/encoding), extracts the configured query parameter from the notification URL, and timing-safe-compares them (failing closed on a missing or mismatched hash) before performing the existing HMAC verification over the verbatim notification URL. Adds stdlib-only compute_hash (unkeyed digest) and read-only get_webhook_query_parameter core utilities. Output is unchanged when no body-hash binding is present.

5.20.2

(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 instead of always using the default (first) environment’s template. For example, Client(environment=Environment.STAGING, region="ie1") now resolves to the Staging template (https://api.stage.ie1.example.com) rather than the Production one. An explicitly provided base_url is no longer overridden by URL variables.

5.20.1

(fix): Stop passing required literal query parameters as keyword arguments in generated snippets and wire tests. Required literals (e.g. response_type="code") are hardcoded inside the generated method and omitted from its signature, so passing them raised TypeError: got an unexpected keyword argument. Optional literal query parameters remain in the signature and are still included.

5.20.0

(feat): Add opt-in, platform-guarded TCP keepalive for the generated default HTTP transport. Enable it via the new tcp_keepalive custom config (enabled, idle_seconds, interval_seconds, count); it is disabled by default so existing generated output is unchanged. When enabled, the default httpx transport applies SO_KEEPALIVE plus the platform’s idle/interval/count knobs (with a macOS TCP_KEEPIDLE -> TCP_KEEPALIVE fallback and guards for older/minimal platforms) so long, non-streaming requests survive idle-connection reaping by a firewall/load balancer/NAT. A user-supplied httpx_client or a custom_transport http_client always takes precedence over the keepalive default. Opting in requires httpx>=0.25.

5.19.3

(fix): Revert the smart-casing digit/word boundary behavior introduced in 5.18.4. snake_case names keep a digit run fused to the following word (conversations_v2configuration), restoring pre-5.18.4 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.

5.19.2

(fix): Fix the OAuth token provider crashing with “Access token not present in OAuth response” when the token response does not include a refresh token. Client-credentials token responses often omit refresh_token; the provider now treats it as optional instead of raising.

5.19.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 user-supplied option.

5.19.0

(feat): Add automatic idempotency-key generation, driven by the centralized idempotencyKeyGeneration config in the IR. When enabled, the generated SDK attaches a UUIDv4 idempotency-key header to requests whose HTTP method is listed in the IR. A caller-supplied key still takes precedence.

5.18.4

(fix): Fix smart-casing snake_case names dropping the word boundary after a number. Sub-client accessors and methods like ConversationsV2Configuration now generate conversations_v2_configuration instead of conversations_v2configuration.

5.18.3

(fix): Expose basic auth credentials (e.g. account_sid / auth_token) on the top-level client when the API also configures OAuth (auth: any). Previously the root client constructor only accepted OAuth client credentials or a bearer token, even though the underlying client wrapper supported basic auth. The credentials are now optional constructor parameters that are forwarded to the client wrapper, and the client can be instantiated with basic auth alone without raising an error.

5.18.2

(fix): Preserve leading and trailing underscores in generated namespace/module names. Previously a subpackage named _agents (via x-fern-sdk-group-name: _agents) was transformed to agents, stripping the underscore. Now it generates a private _agents/ Python module with a matching client._agents accessor, enabling the pattern of hiding autogenerated clients behind a hand-written public layer.