> 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 16, 2026

## 3.107.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 15, 2026

## 3.107.2

**`(fix):`** With `flattenAnyAuthOptions` enabled, dynamic snippets now keep nested auth options for APIs that use
endpoint-level security, which matches the generated SDK. Only `auth: any` snippets are flattened.

## 3.107.1

**`(fix):`** Reject `.` and `..` as path parameter values. `encodeURIComponent` leaves dots unencoded, so URL resolution
removed these dot segments and sent the request, with its auth headers, to a different endpoint (for example,
`/users/{userId}/roles` with `userId: ".."` was sent to `/roles`). The SDK now throws its generic API error
(e.g. `MyClientError`) before the request is sent. SDKs generated with `neverThrowErrors` throw a plain `Error`.

## 3.107.0

**`(feat):`** Add the `flattenAnyAuthOptions` config option. When it is enabled and the API uses `auth: any`, each scheme's
client options are flat (e.g. `{ apiKey }`, `{ clientId, clientSecret }`, `{ token }`) instead of nested under the
scheme key (e.g. `{ apiKey: { apiKey } }`). This matches the Python SDK. Options with the same name, such as the
bearer `token` and the OAuth `token` override, are shared, and an OAuth `token` skips the client-credentials flow.
The default is `false`, so existing SDKs keep the nested shape.

**`(fix):`** Fix `auth: any` SDKs ignoring an OAuth token override (`oauth: { token }`). Previously the OAuth provider was only
used when `clientId` and `clientSecret` were set, so a token by itself sent no `Authorization` header.

## October 13, 2026

## 3.106.1

**`(fix):`** The generated API error's `requestId` getter now works without configuration for
APIs that return the request ID under a vendor-specific header: it reads
`X-Request-Id` and otherwise falls back to the first response header whose name
ends in `request-id` / `requestid` (e.g. `Twilio-Request-Id`, `x-amzn-RequestId`).
Removed the `requestIdHeader` config option introduced in 3.106.0.

## 3.106.0

**`(feat):`** The generated API error class's `requestId` getter now reads the canonical
`X-Request-Id` header (previously the lowercase `x-request-id`), matching the
casing of every other header the SDK emits. Added the `requestIdHeader`
config option so SDKs can point `requestId` at a different response header,
e.g. `requestIdHeader: Twilio-Request-Id`.

## 3.105.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 9, 2026

## 3.105.1

**`(fix):`** `reference.md` is now generated for every output mode, matching the Go and Python generators. It was
previously skipped unless the CLI passed a snippet path (`local-file-system` output without
`snippets: path:` in generators.yml). `README.md` is unchanged and still requires snippets. Source-only output (`outputSourceFiles`, the default for local-file-system) now also includes the root `README.md`, `reference.md` and `CONTRIBUTING.md` when they were generated.

## 3.105.0

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

**`(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 auth options.

**`(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 calls the optional `onRefreshTokenRotated`
auth option with it so it can be persisted.

**`(fix):`** Wire tests for inferred auth with `type: refresh-token` now mock the token request with the fixed
`grant_type` and only the parameters the provider sends. When that scheme is an alternative to another
scheme on the same token endpoint, the tests mock only the first scheme, so the two handlers no longer
shadow each other.

## October 8, 2026

## 3.104.5

**`(fix):`** Paginated endpoints no longer modify the request object you pass in. Calling `getNextPage()`
used to write the next cursor or offset into your original request (including nested fields
such as `options.offset`), so reusing that object started from the wrong position. The pager
now sends an updated copy and leaves your request unchanged.

## 3.104.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.

## 3.104.3

**`(fix):`** Malformed JSON no longer loses HTTP details. An error response labelled JSON whose body isn't valid
JSON (for example a `502` HTML page from a proxy) now throws the SDK's API error with the status code,
raw body and response headers, instead of a generic JSON parse error. Error responses with no
`Content-Type` now carry the raw text as the error body. A successful response with a JSON
`Content-Type` (`application/json`, `text/json` or `*+json`) and malformed JSON now throws the SDK's
API error too, instead of resolving the internal `{ ok: false, error: ... }` record as if it were the
endpoint's data. Successful non-JSON bodies, such as `200 OK` as `text/plain` on a void endpoint,
still resolve as before.

**`(fix):`** Cancelling a request with `abortSignal` while the SDK is waiting to retry (for example after a
`429` with `Retry-After`) now stops the wait right away and does not send another request. Before,
the call stayed pending until the backoff finished. The backoff timer and abort listener are always
cleaned up.

## 3.104.2

**`(fix):`** `timeoutInSeconds` now also covers reading the response body. Before, the timer stopped as soon as
headers arrived, so a response whose JSON body was slow to arrive could take far longer than the
timeout. Timeouts are also reported as the SDK's timeout error on every Node version (before, Node 20+
surfaced a generic `"timeout"` error and Node 18 a `TypeError: invalid_argument`), and the timeout timer
is cleared on every exit, including failed requests. An error status (4xx/5xx) whose body is slower than
the timeout now also returns the timeout error. Streaming, SSE and binary responses, and the passthrough
`client.fetch()` (which returns the raw response), still stop the timer when headers arrive. When
`client.fetch()` times out it now rejects like `fetch` does, with an `Error` whose `name` is `"TimeoutError"`;
before, it rejected with the bare string `"timeout"`.

## 3.104.1

**`(fix):`** `guardProcessEnvAccess: true` now also guards the environment-variable fallback of global headers
(headers declared with `env`, e.g. `requestOptions?.version ?? this._options?.version ?? process.env?.["API_VERSION"]`).
Before, only auth and base-URL reads were guarded, so omitting an optional header option still threw
`ReferenceError: process is not defined` in browsers, Deno and Cloudflare Workers. Output is unchanged
when the option is off.

## October 7, 2026

## 3.104.0

**`(feat):`** XML (TwiML) builders can now carry XML comments. `comment(text)` adds `<!--text-->` inside the element at
its position in the ordered content (interleaved with `addText` segments and child elements), while
`commentBefore(text)` / `commentAfter(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 `fromXml` and round-trip through `toXml`; the new `core.xml.XmlComment`
node exposes the same for untyped `XmlElement` content.

## October 6, 2026

## 3.103.0

**`(feat):`** With `enableForwardCompatibleEnums: true`, enum-typed attributes and text on XML (TwiML) types are open on the
wire: `fromXml` keeps a value the enum does not declare (e.g. a voice added after the SDK was generated) instead
of throwing, and it round-trips through `toXml`. Without the flag the parse error now lists at most 10 of the
allowed values instead of all of them.

## 3.102.1

**`(fix):`** Offset pagers no longer skip a page when a failed `getNextPage()` is retried. The pager now
advances its saved offset only after the next page loads successfully, so a retry re-requests
the same offset instead of moving past it.

## 3.102.0

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

## 3.101.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.

## 3.100.0

**`(feat):`** XML (TwiML) types keep text segments, typed children and undeclared `XmlElement` children in a single
ordered `content` sequence, so `toXml()` preserves the exact insertion/document order across child
types (e.g. `<Say>a</Say><Custom/><Say>b</Say>`) and `fromXml()` reconstructs it. Builders gain
`addText(text)` to interleave text with child elements (`Say.builder({ message: "Hi " }).addBreak(...).addText(" world")`),
and `addChild(XmlElement)` now appends in place rather than after all typed children.

## 3.99.1

**`(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.

## 3.99.0

**`(feat):`** SDK variables that declare an environment variable fallback (`env` in `api.yml` or `x-fern-env` on
`x-fern-sdk-variables`) are now optional client options. `normalizeClientOptions` resolves them from the
option first and then from the environment variable, and throws a clear error at client construction
when neither is set, instead of interpolating `undefined` into request paths.

## October 5, 2026

## 3.98.5

**`(fix):`** Fix `waitForOpen()` on generated WebSocket clients hanging forever when the socket closes without opening.
It now rejects when the socket is already closed, when `close()` is called or the `abortSignal` fires
(including before the underlying WebSocket is created, or when the signal is already aborted), and when the
connection closes with no reconnect pending. `readyState` now reports `CLOSED` after an abort or `close()`
even if the underlying WebSocket was never created.

## October 2, 2026

## 3.98.4

**`(fix):`** Restore WebSocket retries after connection errors and timeouts, and allow manual reconnect after retry exhaustion.
Internal socket cleanup now emits close notifications without consulting `shouldReconnect`, which continues to
control server-initiated closes. Error retries and manual reconnects respect explicit `close()`, abort signals,
and retry limits.

## September 30, 2026

## 3.98.3

**`(fix):`** Redact the API's own credential headers in debug request logs. The generated `SENSITIVE_HEADERS`
set in `core/fetcher/Fetcher.ts` now also includes every header an auth scheme writes to (custom
header auth names such as `PLAID-SECRET`, custom OAuth token headers, and inferred-auth headers),
plus global, service, and endpoint headers whose names identify them as credentials (e.g. `PLAID-CLIENT-ID`).
Previously only a static list of conventional names (`Authorization`, `X-API-Key`, ...) was redacted,
so custom-named auth headers, including request-level overrides, were logged in plaintext when debug
logging was enabled.

## September 29, 2026

## 3.98.2

**`(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.

## 3.98.1

**`(fix):`** Fix invalid TypeScript in generated XML (TwiML) classes and builders when a property key is not a
valid identifier (e.g. `interpret-as` or `default`). The generator now emits bracket access
(`this["interpret-as"]`) and camelCased locals (`interpretAs`) instead of `this.interpret-as`.

## September 25, 2026

## 3.98.0

**`(feat):`** XML-encoded (TwiML) types now carry inline documentation on their user-facing builder API: every
`Builder` setter gets the property description, and child methods (`say.addBreak(...)`, `response.say(...)`)
document the child element plus `@param`/`@returns`.

## September 24, 2026

## 3.97.1

**`(fix):`** Fix generated WebSocket socket classes registering their `open`/`message`/`close`/`error`
forwarding listeners twice when `connect()` is called after construction (the standard
`new Socket(...).connect()` usage). `ReconnectingWebSocket` gains a `hasEventListener(type, listener)`
method and `connect()` only registers each forwarding listener if it is not already attached, so
every consumer `.on()` callback fires exactly once per event while `close()` followed by
`connect()` continues to work.

## 3.97.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.

## September 23, 2026

## 3.96.3

**`(fix):`** Wire tests for paginated endpoints whose page property is nested in the request body (e.g.
`$request.options.offset` or `$request.options.pagination.cursor`) no longer fail on `getNextPage()`.
The generated pager creates the parent object (`options`) when the example omits it, and the mock
server's `jsonBody` matcher treated that new parent as an unexpected field even though the nested leaf
was listed in `ignoredFields`, so the second request never matched the mock and failed with a
connection-refused error. The matcher now ignores a parent object that is absent from the expected body
when every leaf inside it is an ignored field.

## September 22, 2026

## 3.96.2

**`(fix):`** The generated OAuth wire-test mock (`tests/wire/mockAuth.ts`) now expects the `grant_type` the generated
`OAuthAuthProvider` actually sends. When the token endpoint models `grant_type` as a plain (optional) string
and the example omits it, the provider sends `grant_type: "client_credentials"` but the mock previously only
matched the example body, so the token request never matched and every authenticated wire test failed with
a connection-refused error.

## 3.96.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

## 3.96.0

**`(feat):`** Object types with XML encoding metadata (e.g. TwiML schemas imported from an OpenAPI `xml` object) are now
generated as classes with `toXml()`, a static `fromXml()` parser, and a fluent `Builder` (including
`Builder.fromXml(xml).build()` and per-child methods such as `response.say(...)`). Element names,
attributes, text bodies, nested/union children, wrapped lists, list separators, namespaces and prefixes
are honored; undeclared attributes and child elements are preserved as `additionalAttributes` /
`additionalChildren` so documents round-trip. Parsing is strict (root name, scalar/enum validation) and
rejects DOCTYPE declarations. Non-XML types are unaffected and the XML runtime is only emitted when needed.

## September 16, 2026

## 3.95.1

**`(fix):`** TypeScript dynamic snippets honor `flattenRequestParameters` by spreading referenced object bodies instead of emitting `body: {...}`, and nest multi-auth constructor options under the camelCase auth scheme key regardless of `noSerdeLayer` or `retainOriginalCasing`.

## 3.95.0

**`(feat):`** SDKs now read the base URL from the environment variable configured via base-url-env / x-fern-base-url-env when no baseUrl or environment option is passed, respecting guardProcessEnvAccess for the environment-variable read.

## September 15, 2026

## 3.94.2

**`(fix):`** A union member whose discriminant value collides with a JavaScript built-in
no longer has the disambiguation prefix applied to the string literal.
A branch with `condition_type: "Date"` emitted
`conditionType: "globalThis.Date"` — so the property could not be typed
without a cast, since the API expects `"Date"`, and with `noSerdeLayer:
false` the serializer's inferred discriminant no longer matched the declared
literal and the package failed to compile. Qualification exists to
disambiguate a shadowed *type reference*; a literal is a value, and
rewriting one changes what the SDK sends. String literals are now skipped,
which also covers builder expressions and visitor signatures, where the same
rewrite could reach an object literal. Type references are unaffected: a
sibling property typed `Date` still becomes `globalThis.Date`.

## 3.94.1

**`(fix):`** Fix generated wire tests for cursor, URI, and path pagination when the example
response uses an empty string to indicate there is no next page. Terminal URI
and path continuations are preserved in the mocked response, and the tests skip
`hasNextPage()` and `getNextPage()` assertions, matching the generated pager.

## September 14, 2026

## 3.94.0

**`(feat):`** Add a `websocketHandlerMode` config option (`"replace" | "accumulate"`, defaults to `"replace"`)
for generated WebSocket `Socket` classes. With `accumulate`, multiple `on()` handlers for the same
event are all kept and run in registration order instead of the last one replacing the previous.
Generated sockets now also expose `off(event, callback)` to detach a handler in both modes.
Defaults to `replace`, so generated output is unchanged unless the option is enabled.

**`(fix):`** Fix the generated README/reference pagination snippet, which assigned `page.getNextPage()`
without `await` and therefore did not type-check.

**`(feat):`** Add an optional `shouldReconnect(event: CloseEvent) => boolean` option to `ReconnectingWebSocket` and to the
generated WebSocket `connect()` args. Return `false` to treat a close as terminal and suppress the automatic
reconnect (e.g. after the client sent an end-of-stream message and the server closed with a non-1000 code).
Defaults to the existing behaviour of reconnecting on any close code other than 1000. Also clarifies the
`reconnectAttempts` docstring (defaults to 30; set to 0 to disable reconnecting).

## 3.93.1

**`(fix):`** The generated README now always includes a Pagination section (and pagination usage snippets)
when the API has paginated endpoints, matching the client which always returns `core.Page`.
Previously the section was dropped when the CLI ran without org information (e.g. `fern generate --local` without a `FERN_TOKEN`).

## September 11, 2026

## 3.93.0

**`(feat):`** Add an opt-in `esmOnly` configuration flag. When enabled, the generated package ships
only the existing ESM build: `package.json` is marked `type: "module"`, the exports map
only exposes the `.mjs`/`.d.mts` entry points, and the CommonJS build (`tsconfig.cjs.json`,
`build:cjs`) is no longer generated. This avoids the dual package hazard for consumers who
want a pure-ESM package. The default behavior (dual CJS + ESM output, including with
`outputEsm: true`) is unchanged. `esmOnly` is incompatible with `useLegacyExports` and
`bundle`.

**`(fix):`** Fix the `build:esm` script in packages generated with `outputEsm: true`. These packages
are marked `type: "module"`, so Node interpreted the CommonJS helper
`scripts/rename-to-esm-files.js` as ESM and the build failed with
`ReferenceError: require is not defined in ES module scope`. The helper is now emitted
as `scripts/rename-to-esm-files.cjs` whenever the package is `type: "module"`.

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