> This page is for SDKs, version Legacy.
> For other versions, use one of these documentation indexes:
> - Current (default): https://buildwithfern.com/learn/sdks/current/llms.txt
> - Legacy: https://buildwithfern.com/learn/sdks/legacy/llms.txt

> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt.

# Changelog

## October 19, 2026

## 5.41.4

**`(fix):`** Path parameter values of exactly `.` or `..` now raise a `ValueError` before the request is
sent. These are dot-segments that httpx resolves, so they could change which endpoint the
request was sent to. When `encode_path_params` is off (the default), values are not
percent-encoded, so any `.` or `..` segment inside the value (including `%2e` forms) is also
rejected.

## October 16, 2026

## 5.41.3

**`(fix):`** `WebhooksHelper.verifyRequest` now parses `name=value` settings headers when
`webhook-signature.algorithm-header` sets `key-id-parameter`/`algorithm-parameter`
(e.g. Twilio's `X-Twilio-Webhooks-Signature-Settings: key=<id>,algorithm=HMAC_SHA256`),
so the key resolver receives the bare key ID instead of `key=<id>`. Requires IR 67.33.0.

## October 13, 2026

## 5.41.2

**`(fix):`** The generated `WebhooksHelper` now consumes `algorithm-header` from `api.settings.webhook-signature`:
a request-level `verifyRequest` helper reads the configured signature header first (missing or empty
fails early with "could not run: missing signature header"), then the algorithm-selection header
(e.g. `X-Twilio-Webhook-Signature-Settings: <key_id>;HMAC_SHA256`) to pick the HMAC algorithm from
the configured wire values and hand the key ID to an application-supplied secret resolver. When the
header is absent the configured default algorithm and the plain secret are used, so legacy
auth-token flows keep working. The lower-level `verifySignature` is unchanged and still returns
`false` without throwing.

## October 12, 2026

## 5.41.1

**`(fix):`** Models that keep unknown JSON keys no longer keep one that matches a declared field except for case
(e.g. `VALUE` next to `value`). Such a key used to be sent again after the declared field, so a
case-insensitive decoder on the receiving end (e.g. Go's `encoding/json`) read it instead of the
field's value. Other unknown keys (e.g. `brand_new_field`) are still kept and sent as before.

## October 10, 2026

## 5.41.0

**`(feat):`** Wire tests now cover every example instead of only the first one per endpoint. Each user-specified
example gets its own test (or the first autogenerated success example when there are none), and when
none of an endpoint's user examples is an error example, the autogenerated error examples are added
too. Error tests assert the typed error the client raises for the status code (or the base `ApiError`
when the client can't dispatch to a typed error) along with its `status_code` and `body`. Each test is
served by its own `X-Test-Id`-scoped WireMock stub with the declared status code, and the test client
disables retries. Set `wire_tests.fallback_to_autogenerated_error_examples: false` to leave out
autogenerated error examples.

## October 8, 2026

## 5.40.0

**`(feat):`** Support inferred bearer auth with `type: refresh-token`. The generated `InferredAuthTokenProvider`
always sends `grant_type="refresh_token"` to the get-token endpoint, and `grant_type` is no longer
a client constructor parameter. Users pass only the refresh token (and any other non-literal
token endpoint parameters).

**`(feat):`** Inferred auth now works next to other auth schemes (`auth: any` and endpoint-security) when it has
credentials of its own, e.g. a `type: refresh-token` scheme next to OAuth client credentials.
The client takes the inferred credentials as optional constructor parameters (shared ones such as
`client_id` are added once) and uses the inferred token provider when they are passed, so
`Client(refresh_token=...)` and `Client(client_id=..., client_secret=...)` both work.

**`(feat):`** With `type: refresh-token`, inferred auth only exposes and sends the token endpoint parameters used by
the OAuth refresh token grant (`refresh_token`, `scope`, `client_id`, `client_secret`) plus any
required ones. Optional parameters of other grants on a shared token endpoint, such as `code`,
`code_verifier` and `redirect_uri`, are no longer client constructor parameters.

**`(feat):`** Add the `omit_unset_inferred_auth_params` config option (default `false`). When enabled, the inferred auth
token provider leaves unset optional token request parameters out of the get-token request instead of
sending them as `null` (JSON) or empty values (form-encoded).

**`(feat):`** Inferred auth with `type: refresh-token` now rotates the refresh token. When the token response returns a new
`refresh_token`, the provider uses it for the next refresh, and the client calls the optional
`on_refresh_token_rotated` callback with it so it can be persisted.

**`(fix):`** Fix wire test setup for inferred auth. `tests/wire/conftest.py` now passes a test value for each
required token endpoint parameter (e.g. `client_id`, `client_secret`) instead of always passing
`api_key`, which raised `TypeError` when the client had no `api_key` parameter.

## 5.39.5

**`(fix):`** Fix the root client docstring example for global headers and root path parameters
that have a `client-default`. The example now passes the default as a keyword
argument (e.g. `version="2024-01-01"`) instead of a bare positional value, which
was a `SyntaxError` when it followed another keyword argument.

## 5.39.4

**`(fix):`** SSE unions discriminated by the `event:` line (`x-fern-discriminator-context: protocol`) now parse
both variant styles: envelope variants (`{event, data: Payload}`) are built from the `data:` JSON,
and payload variants (the `data:` JSON itself) get the `event:` value injected as the discriminant.
The event name no longer has to be repeated inside the `data:` JSON.

## 5.39.3

**`(fix):`** Stop automatically replaying requests after server disconnects or incomplete responses (`httpx.RemoteProtocolError`), which could duplicate non-idempotent POST/PATCH operations, even after a 200 status was received. Connection-establishment failures and HTTP-status retries are unchanged. Set `request_options={"retry_remote_protocol_errors": True}` only when replaying the request is safe to explicitly restore the previous behavior.

**`(fix):`** With `pydantic_config.skip_validation: true`, a union value that matches no known member is now
returned as its original data (the raw dict) instead of `None`. Previously a response carrying a
union variant added after the SDK was generated (e.g. a new `product` on a discriminated union), or
one missing its discriminant, silently decoded to `None` and its fields were lost. Known variants
still decode to their classes.

## 5.39.2

**`(fix):`** Generated `core/pydantic_utilities.py` now type-checks under pydantic 2.14, which made mypy report
`var-annotated` on the module-level `TypeAdapter` instances.

## 5.39.1

**`(fix):`** XML (TwiML) fluent child methods (`response.say(...)`, `gather.play(...)`, ...) keep document order on
Python 3.12+. The ordered content list is a pydantic private attribute, and the `runtime_checkable`
Protocol used to find it stopped matching on 3.12 (where `isinstance` uses `inspect.getattr_static`),
so children appended by the fluent methods were ordered after text and `add_child(...)` elements, e.g.
`r.say("a"); r.add_child(XmlElement("Custom")); r.say("b")` rendered `<Custom/><Say>a</Say><Say>b</Say>`.

## October 7, 2026

## 5.39.0

**`(feat):`** XML (TwiML) models can now carry XML comments. `comment(text)` adds `<!--text-->` inside the element at
its position in the ordered content (interleaved with `add_text` segments and child elements), while
`comment_before(text)` / `comment_after(text)` render a comment immediately before or after the element
itself (as a sibling in its parent, or around the root element after the XML declaration). Comments in
parsed documents are preserved by `from_xml` and round-trip through `to_xml`; `XmlElement.add_comment`
and the new `XmlComment` node expose the same for untyped elements.

## 5.38.0

**`(feat):`** Generate a unit test file per XML-encoded (TwiML) type alongside the models, covering
fromXml/toXml round trips, escaping, unknown attributes/children, open enum passthrough,
child ordering, and rejection of malformed XML, wrong root elements and DOCTYPE declarations.

## October 6, 2026

## 5.37.2

**`(fix):`** XML (TwiML) child builders keep their legacy names when they collide with the element's text
field: `dial.number("+15551234567")` and `message.body("b")` now work instead of raising
`TypeError: 'NoneType' object is not callable`. The text is still passed as the same constructor
keyword (`Dial(number="+1555")`) but is stored under a trailing underscore (`dial.number_`) so the
builder method stays callable; `add_number`/`add_body` are no longer generated.

## 5.37.1

**`(fix):`** List-valued XML (TwiML) attributes accept a single space-separated string in addition to
a list, matching the legacy call style: `response.gather(input="speech dtmf")` no longer
raises a validation error and serializes as `input="speech dtmf"`.

## 5.37.0

**`(feat):`** Add opt-in `user_agent_only` config (also accepted as `userAgentOnly`). When enabled,
generated clients send the `User-Agent` header (including the structured form from
`include_platform_headers`) but omit the `X-Fern-Language`, `X-Fern-SDK-Name`,
`X-Fern-SDK-Version`, `X-Fern-Runtime`, and `X-Fern-Platform` headers. Disabled by default,
so existing generated output is unchanged. `omit_fern_headers` still takes precedence and
drops the `User-Agent` too.

## 5.36.1

**`(fix):`** With endpoint-security auth, the client now fetches an OAuth or inferred-auth token only when the
endpoint's security requirements reference that scheme. Previously every authenticated call fetched
one first, so an endpoint that doesn't use it (e.g. the token endpoint itself) made an extra token
request and failed if that fetch failed.

## 5.36.0

**`(feat):`** The generated `WebhooksHelper` HMAC verifier now accepts a per-call algorithm override via an optional `algorithm` parameter on `verifySignature`,
one of `sha1`, `sha256`, `sha384` or `sha512`. When omitted, the algorithm configured in
`webhook-signature` is used, so existing callers are unchanged. This lets one endpoint verify both
legacy HMAC-SHA1 (auth-token) and HMAC-SHA256 (shared-key) signed webhooks.
The verifier also logs a warning distinguishing an early failure (missing signature header) from a
failed verification (signature mismatch); it still returns `false` and never throws.

## 5.35.0

**`(feat):`** XML (TwiML) models keep text segments, typed children and undeclared `XmlElement` children in one
ordered content sequence, so `to_xml()` writes them in insertion order (`say("a")`, `add_child(...)`,
`say("b")` renders `<Say>a</Say><Custom /><Say>b</Say>`) and `from_xml()` preserves document order,
including nested mixed content in undeclared children. Builders gain `add_text(text)` for mixed
content such as `Say("Hi ").break_(...).add_text(" world")`. Typed child properties and
`add_child()` keep working as before.

## 5.34.5

**`(fix):`** XML-encoded (TwiML) types now include the `<?xml version="1.0" encoding="UTF-8"?>`
declaration by default when serialized to XML; pass the declaration flag as `false`
to omit it. Nested child elements and raw `XmlElement`s are still written without it.

## 5.34.4

**`(fix):`** On Python 3.10 and older, `str()` on a generated enum member now returns its value (e.g. `WALMART_US`)
instead of `ClassName.WALMART_US`, matching `enum.StrEnum` on Python 3.11+. Previously, enum query
parameters were sent as `ClassName.MEMBER` on those versions when `enum_type: forward_compatible_python_enums`
or `python_enums` was used.

## October 5, 2026

## 5.34.3

**`(fix):`** Redact every header auth scheme's header (e.g. `PLAID-SECRET`) in debug logs, in addition to
`Authorization` and API-key headers, and merge request headers case-insensitively so a
`plaid-secret` override replaces `PLAID-SECRET` instead of sending both.

**`(fix):`** Map `4XX`/`5XX` wildcard error responses to their typed error classes (concrete status codes
still win) and keep the real HTTP status code on the raised error.

**`(fix):`** Fix offset pagination when the offset is an omitted body parameter (no more `OMIT + 1`), and
advance nested offsets such as `$request.options.offset` on each page, creating `options` when
it was omitted (through its model, so aliased fields keep their wire names) and preserving its
other fields.

**`(fix):`** Read environment-variable-backed client parameters (e.g. API keys) when the client is
constructed instead of when the module is imported. Passing `api_key=None` explicitly now
falls back to the environment variable, the same as omitting the argument.

**`(fix):`** Generate README.md for GitHub output even when no snippet path is configured (e.g. `fern generate --local`),
and document binary (`format: binary`) body parameters as `bytes` in reference.md.

## September 30, 2026

## 5.34.2

**`(fix):`** Accept an `async_token` callable on the async root client when OAuth client credentials are
configured with a token override. Previously only a synchronous `token` callable could be
supplied in token mode, so resolving the token blocked the event loop on every request. The
async client now exposes `async_token: Optional[Callable[[], Awaitable[str]]]`, adds a
constructor overload for it, and forwards it to `AsyncClientWrapper`.

**`(fix):`** When `async_token` is set on an async client, the synchronous `token` is no longer resolved
for async requests. `AsyncClientWrapper.async_get_headers()` now calls
`get_headers(include_token=False)` and uses only the awaited token, so a blocking `token`
callable never runs on the event loop.

## September 29, 2026

## 5.34.1

**`(chore):`** Bump bundled @fern-api/generator-cli to 0.10.3. When the hosted auto-version analysis of the
full SDK diff fails, generator-cli now retries chunk-by-chunk instead of silently applying a
PATCH bump; any remaining unavailable or incomplete analysis adds a "Version bump not verified"
notice to the SDK PR body and disables automerge.

## September 28, 2026

## 5.34.0

**`(feat):`** XML-encoded (TwiML) models now carry inline documentation on their user-facing APIs: the
`__init__` of text-bearing models and every fluent child method (`say.break_(...)`) document
each parameter from the API definition's property descriptions, plus `**extra_attributes`.

## 5.33.2

**`(fix):`** Also emit the SPDX `license` in `[tool.poetry]` alongside the PEP 639 `[project]` license. Poetry 1.x
(including the `poetry` version pinned in the generated publish workflow) ignores the `[project]`
table, so since 5.32.0 packages built with it were published to PyPI without any license metadata.

## 5.33.1

**`(fix):`** Annotate generated WebSocket `__iter__` / `__aiter__` methods with `typing.Iterator[<Socket>Response]` /
`typing.AsyncIterator[<Socket>Response]` so type checkers infer iterated messages as the response union
(matching `recv()`) instead of `bytes | <Response>`.

## September 24, 2026

## 5.33.0

**`(feat):`** Generate the shared `WebhooksHelper` from the API-wide `api.settings.webhook-signature`
setting in `generators.yml` (IR `sdkConfig.webhookSignatureVerification`). The helper is
emitted even when the API definition models no webhooks; webhook-specific signature
overrides still produce their own named helpers.

## 5.32.1

**`(fix):`** Stop emitting deprecated `License ::` PyPI classifiers alongside the PEP 639 `[project] license`
expression; modern setuptools rejects the combination. Inline `metadata.license: "MIT"` /
`"Apache-2.0"` now also writes the standard license text to `LICENSE` and declares it via
`license-files = ["LICENSE"]`.

## September 23, 2026

## 5.32.0

**`(feat):`** License metadata is now emitted in the `[project]` table of the generated `pyproject.toml`
following PEP 639: `metadata.license: "MIT"` / `"Apache-2.0"` produce `license = "<SPDX>"`,
and `metadata.license: { custom: "./LICENSE" }` produces `license-files = ["LICENSE"]`.
When the custom license file is recognized (Apache-2.0, MIT, BSD, GPL, MPL, ISC — same
heuristics as the Java generator), `license = "<SPDX>"` and the matching PyPI classifier are
emitted alongside `license-files`.

## 5.31.2

**`(fix):`** Fix multipart requests where a `list<file>` property declares a `content-type`. The generated
client now applies `core.with_content_type` to each file in the list instead of passing the
whole list to a single call, which failed mypy and would have sent the list as one part.

## September 22, 2026

## 5.31.1

**`(chore):`** Bump bundled @fern-api/generator-cli to 0.10.1. Auto-versioned MAJOR/MINOR bumps no longer
produce a version-only `changelog.md` block when the AI analysis returns an empty changelog
entry; the PR description, version bump reason, or commit message body is used instead.

## September 21, 2026

## 5.31.0

**`(feat):`** Object types carrying IR `encoding.xml` metadata (e.g. Twilio TwiML verbs imported from an OpenAPI
`xml` object) now generate a `to_xml()` method (and `__str__`) that serializes the model as an XML
element: properties marked as attributes become XML attributes, the text property becomes the element
body, and remaining properties are emitted as ordered child elements, honoring wire names,
namespaces/prefixes, wrapped lists and attribute list separators. A `core/xml_utilities.py` helper is
emitted only when the API contains XML-encoded types.

**`(feat):`** XML-encoded object types with a list-valued child element property also generate fluent builder
methods: `append(child)` and one `<tag>(text, *, attributes...)` method per child element type that
constructs the child, appends it and returns it (e.g. `response.say("Hello", voice="Polly.Joanna")`,
`say.break_(time="100ms")`). Method names derive from the XML tag; on a clash with a field they get an
`add_` prefix (`Dial.add_number`, `Message.add_body`).

**`(feat):`** XML-encoded types match the ergonomics of hand-written XML helper libraries: text-bearing elements
accept their body positionally (`Say("Hello", voice="man")`), constructors and builder methods accept
arbitrary `**extra_attributes: str` that are rendered as XML attributes (`Say("Hi", foo="bar")` ->
`<Say foo="bar">Hi</Say>`), and `str()` of a root element (one never nested in another XML type)
includes the `<?xml version="1.0" encoding="UTF-8"?>` declaration. `to_xml(xml_declaration=...)` is unchanged.

**`(feat):`** XML-encoded object types also generate a `from_xml(xml)` classmethod that parses an XML string (or a
parsed element) back into the model: the root element name is validated, attributes/text/child
elements map to their properties (honoring namespaces, wrapped lists and list separators), child
unions dispatch on the element name, and scalar/enum values are converted by the model's normal
validation. Attributes the schema does not declare are kept as extra attributes and child elements it
does not declare are preserved as `XmlElement` nodes (also addable via `add_child(...)`), so
`Model.from_xml(model.to_xml())` round-trips. Parsing uses the standard library with DOCTYPE
declarations rejected; malformed input raises `ValueError`.

## September 15, 2026

## 5.30.2

**`(fix):`** Fix the generated OIDC publish workflow in `ci.yml` so the `pypi` GitHub Environment URL
points to the configured `package-name` (`https://pypi.org/p/<package-name>`) instead of
`github.event.repository.name`.

## September 14, 2026

## 5.30.1

**`(fix):`** The generated README only documents pagination (`SyncPager`, `iter_pages()`) when paginated
clients are actually generated, so the README and `list()` return types stay in sync.

## September 8, 2026

## 5.30.0

**`(feat):`** Add a `license_header` custom config option that emits the configured text as a comment
at the top of every generated Python file, above the auto-generated notice.

```yaml
config:
  license_header: |
    Copyright 2024 Acme, Inc.
    Licensed under the Apache License, Version 2.0.
```

## September 4, 2026

## 5.29.4

**`(fix):`** Fix the generated `tests/utils/test_http_client.py` failing when `retry_status_codes: recommended`
is configured. The retry-status expectations in the test are now derived from the configured mode
instead of always asserting the legacy set (which includes 500, 501 and 599).

_Showing the 20 most recent of 356 entries. Append `/llms.txt` to the changelog URL for the complete index._