Skip to navigation

1.40.4

(fix): README generation in self-hosted GitHub mode can clone a private repository with a personal access token again. Upgrades @fern-api/generator-cli to 0.12.1, which also sends the token as an Authorization header. Before this, git in the generator image (libcurl 8.23+) dropped the token from the clone URL and failed with could not read Username.

1.40.3

(fix): The generator image keeps Alpine 3.22’s libcurl (< 8.23). curl 8.23.0 strips credentials from the effective URL it reports, which made git treat every authenticated https clone as a redirect and drop the token, so README generation and GitHub output for private repositories failed inside the container with “could not read Username for ‘https://github.com‘“.

1.40.2

(fix): XML (TwiML) child builders keep the legacy names even when they match the element’s text attribute: dial { |d| d.number("+1…") } adds a <Number> child and message { |m| m.body("hi") } a <Body> child, while d.number without arguments still returns the attribute (number= sets it); every element with text content also gets text / text= accessors. add_number / add_body remain available.

1.40.1

(fix): Map request bodies (e.g. map<string, string>) can be passed as keyword arguments again, as they could before 1.34.1. create(id: "h", MY_SECRET: "x") sends {"MY_SECRET":"x"} instead of an empty body. Passing the body argument (request:, or the wrapped body key) still takes precedence, and path, query and header arguments are never included in the body. Set allowMapBodyAsKeywordArguments: false to only read map bodies from the body argument, as 1.34.1 through 1.39.x did.

1.40.0

(feat): Add the opt-in omitUnsetModelFields config option. When enabled, request bodies no longer include model fields the caller did not set. Without it, a required field left out of a nested object (e.g. a partial update payload) is sent as null, which APIs with sparse-update semantics reject. With the option on, unset fields are omitted from to_h, and a field set explicitly to nil is still sent as null unless it is optional and not nullable. Disabled by default, so existing request bodies are unchanged.

1.39.8

(fix): Response parsing is faster on large payloads. Model#initialize no longer coerces fields whose value is nil or copies its input hash twice, Utils.coerce returns exact String, Integer, and Float instances without walking the full type match, and type dispatch in Utils.coerce and Model.=== no longer allocates a lambda or ancestor list per call. Parsed output is unchanged.

1.39.7

(fix): The gem’s root file registers generated types with autoload instead of requiring every type file eagerly, so require "<gem>" only loads the runtime and each type is read on first use. Type files also require_relative the root file, so a single type can be required directly (e.g. require "<gem>/twiml_voice/types/voice_response") without a NameError.

1.39.6

(fix): Time and ActiveSupport::TimeWithZone values are now sent as RFC 3339 strings with millisecond precision (e.g. "2026-10-16T01:40:16.000Z") in JSON request bodies, query parameters, form-urlencoded bodies, and multipart form fields. Previously they were sent via Time#to_s (e.g. "2026-10-16 01:40:16 UTC"), which is not RFC 3339. Date and DateTime values are unchanged. Set serializeTimeAsIso8601: false in the generator config to keep the previous output.

1.39.5

(fix): Ruby dynamic snippets now use wire names for keys inside nested hashes (e.g. httpEndpoint: instead of http_endpoint:), pass file upload fields (example file contents as StringIO.new(...), since the SDK reads a String as a file path; a missing required file gets a "/path/to/your/file" path placeholder, and a missing optional file is left out; dynamic IR from CLIs that predate the isOptional flag treats every file as required), and show an undiscriminated union body whose example isn’t an object as the keyword arguments of its first object variant instead of request: with an error.

1.39.4

(fix): XML (TwiML) serialization drops characters outside the XML 1.0 Char production (e.g. \x01) from text and attribute values instead of emitting XML that no parser accepts.

1.39.3

(fix): XML (TwiML) types accept the declaration flag positionally, like the legacy TwiML gem: response.to_s(false) and response.to_xml(false) omit <?xml ...?>; the xml_declaration: keyword still works.

1.39.2

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

1.39.1

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

1.39.0

(fix): Pagination now works when the next cursor, the items, or the has-next-page flag is nested inside the response (e.g. $response.pagination.nextToken). The pager used to read only the last field name from the top-level response (response.next_token), which raised NoMethodError. It now follows the full path (response.pagination.next_token).

(feat): Generated wire tests now cover every example of an endpoint instead of only the first, including autogenerated error examples (one per error) for endpoints without a user-specified error example. Error tests assert the typed error raised for the status code (assert_raises) along with its status code and body. Each test’s X-Test-Id header selects a dedicated WireMock mapping that serves that example’s response, success mappings use the endpoint’s declared status code, and the test client disables retries. Set wireTestsFallbackToAutoGeneratedErrorExamples: false to leave out the autogenerated error examples.

1.38.2

(fix): Models no longer keep an unknown JSON key as an extra field when it matches a declared field case-insensitively (e.g. VALUE next to value). Previously the extra key was re-emitted after the declared field, so a case-insensitive, last-wins JSON decoder downstream (such as Go’s encoding/json) read the extra key’s value instead of the declared field’s. Non-colliding unknown keys are still preserved and re-emitted. XML-encoded models are unchanged, since XML attribute names are case-sensitive.

1.38.1

(fix): Fix paginated results silently skipping records. Every each/map/to_a/first/any?/count over a pager (or its pages) now starts again from the first page, reading pages no longer moves item iteration forward, items are no longer removed from fetched pages as they are read, and each without a block returns an Enumerator. Manual next_element/next_page loops, including retrying after a failed page, behave as before. Because each loop starts again, reading the same pager more than once (or reading both pages and items) requests the pages again, except a first page kept by load_first_page (or fetchFirstPageOnCall), which every loop reuses.

1.38.0

(fix): Cursor and offset paginated methods now document what they return. The YARD @return tag and the reference.md signature name the CursorItemIterator / OffsetItemIterator instead of the response type, the method docs say which items it yields, that pages gives each page’s full response, and when the request is sent and API errors are raised. The README pagination example uses a paginated endpoint even when the default endpoint is not paginated, and shows pages instead of the nonexistent results method. Iterators gain load_first_page, which sends the first request immediately so its API error is raised at that call.

1.37.3

(fix): Connection-level failures now raise the SDK’s new Errors::ConnectionError (a subclass of Errors::ApiError) instead of leaking raw Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError, SocketError (DNS) or OpenSSL::SSL::SSLError, so rescue Errors::ApiError catches every failed call as the README documents. The original exception is kept as cause. Errno::ETIMEDOUT now raises Errors::TimeoutError like the other socket timeouts. Other SystemCallErrors (e.g. file errors) are not wrapped. Code that rescued the wrapped classes directly should rescue Errors::ConnectionError instead (or inspect cause). Transport failures are still not retried. The README errors example shows the new class.

1.37.2

(fix): Parsing an XML (TwiML) element without a text property keeps the text before its first child in the ordered content, so Gather.from_xml('<Gather>Press a key, then <Say>one</Say></Gather>') round-trips instead of dropping Press a key, then . Types with a text property (e.g. Say) are unchanged: that text is still the property’s value.

1.37.1

(fix): Generate float and double fields and parameters as Ruby Float instead of Integer. Decimal values (e.g. a balance of 100.57) previously lost their fractional part when a response was decoded or a request was sent. Integer types stay Integer. Generated wire tests now also assert that numbers in request bodies reach the wire unchanged and that numbers in mocked responses decode unchanged. Integer and Float map keys nested in models now decode as numbers instead of staying symbols.