> For the complete documentation index, see [llms.txt](/llms.txt)

# Ruby

The Ruby SDK is an idiomatic gem emitted from the same `GlottoIR` as every other target. It uses
snake_case methods, keyword args, immutable `Data.define` models with `from_json`, and YARD doc
comments generated from the operation prose in the spec.

Manual pages expose items, response metadata and explicit `next_page` navigation. Per-call `request_options:` controls headers, deadlines, retries, extra parameters and cancellation. Binary operations return an owned `BinaryDownload` with bounded `read_all`, `pipe`, response metadata and `close`; block iteration releases the response on break or error. See [pagination](/docs/pagination), [request retries and controls](/docs/retries), [streaming](/docs/streaming), and [file transfers](/docs/file-transfers).

## Quickstart

```bash
gem install petstore
```

```ruby
require 'petstore'

client = Petstore::Client.new(token: '<token>')
result = client.pets.get(pet_id: '...')
puts result
```

## Resource objects

Operations are methods on resource objects — `client.pets.get(id)`, `client.pets.photos.add(...)` —
the Stripe-Ruby service-accessor shape, rather than flat methods on `Client`.

## Persistent connections

Requests reuse a persistent per-host connection (via `net-http-persistent`) instead of opening a
fresh socket per call, so throughput holds up under load and across threads.

## Typed errors

Non-2xx responses raise `ApiError`, a `StandardError` hierarchy carrying the parsed, typed error
body, so you `rescue RateLimitError` rather than inspecting status codes. Discriminated-union
bodies resolve to the right variant. See [Errors](/docs/errors).

## Pagination

List methods return an `Enumerator` that walks every page lazily, advancing the cursor for you.
See [Pagination](/docs/pagination).

## Retries & backoff

Transient failures (`5xx`, `429`, transport errors) retry with exponential backoff and jitter,
configurable per client. See [Retries & timeouts](/docs/retries), [Streaming](/docs/streaming),
and [Authentication](/docs/authentication).

## Unknown response fields

Your API can add a response field without it being a breaking change — but a generated `Data` class has only the members it declares, and its `from_json` factory reads only those. The generated
models keep it instead: a field the SDK wasn't generated from is retained on decode, readable
through an accessor, and written back out when the model is re-serialized.

```ruby
pet = client.pets.create_pet(body)

# A field your API started returning after this SDK was generated.
species = pet.extra_fields["species"]

# Re-encoding preserves it — a read-modify-write never silently drops it.
json = JSON.generate(pet.to_h)
```

`extra_fields` returns a frozen `Hash`, so nested objects and arrays survive intact, and retention
is recursive.

Use `pet.with(name: "Rex")` to create an updated immutable value. The copy keeps unknown
response fields and retained timestamps for members you did not change. Replacing a timestamp
explicitly replaces its retained wire value too.
If a declared member already uses `with`, the copy helper uses the first free name starting
with `with_2`, preserving the member's reader.

The retained fields are held in an instance variable rather than as a `Data` member, so
`members`, `inspect`, `==` and pattern matching on your models are all unchanged — and both
`Pet.new(id: …)` and `Pet.new('p1', …)` still work.

The retained fields are read-only by design. To *send* a field your spec doesn't model yet, use the
per-call extra-body escape hatch rather than writing to the retained bag.
