> If you are setting up, configuring, or answering questions about Schematic, connect the
> Schematic MCP server before working from these pages. It reads and writes plans, features,
> entitlements, and overrides against the real account, so what you report and what you build
> match what actually exists.
>
> The server is at https://api.schematichq.com/mcp
>
> From Claude, ChatGPT, or another chat assistant, add it as a custom connector:
> https://docs.schematichq.com/working-with-ai
>
> From a coding assistant such as Claude Code, Codex, or Cursor:
> https://docs.schematichq.com/building-with-ai
>
> For clean Markdown of any page, append `.md` to the page URL. For a complete page index,
> see https://docs.schematichq.com/llms.txt

# Ruby

> Use schematichq to easily implement pricing and packaging in your Ruby app with Schematic.

## Installation and Setup

1. Install the Ruby gem:

```bash
gem install schematichq
```

Or add it to your Gemfile:

```ruby
gem "schematichq"
```

Then run:

```bash
bundle install
```

2. [Issue an API key](/quickstart/account-setup#2-create-your-api-keys) for the appropriate environment using the [Schematic app](https://app.schematichq.com/settings/api-keys). Be sure to capture the secret key when you issue the API key; you'll only see this key once, and this is what you'll use with schematic-ruby.

3. Using this secret key, initialize a client in your application:

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(api_key: api_key)

# interactions with the client

client.close
```

By default, the client will do some local caching for flag checks. If you would like to change this behavior, you can do so using an initialization option to specify the max size of the cache (in terms of number of records) and the max age of the cache (in seconds):

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
cache_size = 100
cache_ttl = 1.0 # in seconds
client = Schematic::SchematicClient.new(
  api_key: api_key,
  cache_providers: [
    Schematic::LocalCache.new(max_size: cache_size, ttl: cache_ttl)
  ]
)

# interactions with the client

client.close
```

You can also disable local caching entirely with an initialization option; bear in mind that, in this case, every flag check will result in a network request:

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(
  api_key: api_key,
  cache_providers: []
)

# interactions with the client

client.close
```

You may want to specify default flag values for your application, which will be used if there is a service interruption or if the client is running in offline mode (see below). You can do this using an initialization option:

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(
  api_key: api_key,
  flag_defaults: {
    "some-flag-key" => true
  }
)

# interactions with the client

client.close
```

### Custom Logging

You can provide your own logger implementation to control how the Schematic client logs messages. The logger must implement the `Schematic::Logger` module with `error`, `warn`, `info`, and `debug` methods:

```ruby
require "schematichq"

class CustomLogger
  include Schematic::Logger

  def error(message, *args)
    # Your custom error logging logic
  end

  def warn(message, *args)
    # Your custom warning logging logic
  end

  def info(message, *args)
    # Your custom info logging logic
  end

  def debug(message, *args)
    # Your custom debug logging logic
  end
end

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(
  api_key: api_key,
  logger: CustomLogger.new
)

# interactions with the client

client.close
```

You can also adjust the log level of the built-in console logger via the `log_level` option:

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(
  api_key: api_key,
  log_level: :debug
)
```

If no logger is provided, the client will use a default console logger at the `:warn` level that outputs to stderr. The `log_level` option only applies to this built-in logger; when you supply your own `logger`, its level is left untouched.

## Usage Examples

A number of these examples use `keys` to identify companies and users. Learn more about keys [here](/developer_resources/key_management).

### Sending identify events

Create or update users and companies using identify events.

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(api_key: api_key)

client.identify({
  company: {
    keys: { "id" => "your-company-id" },
    name: "Acme, Inc.",
    traits: { "city" => "Atlanta" }
  },
  keys: {
    "email" => "wcoyote@acme.net",
    "user_id" => "your-user-id"
  },
  name: "Wile E. Coyote",
  traits: {
    "enemy" => "Bugs Bunny",
    "login_count" => 24,
    "is_staff" => false
  }
})

client.close
```

This call is non-blocking and there is no response to check.

### Sending track events

Track activity in your application using track events; these events can later be used to produce metrics for targeting.

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(api_key: api_key)

client.track({
  event: "some-action",
  company: { "id" => "your-company-id" },
  user: {
    "email" => "wcoyote@acme.net",
    "user_id" => "your-user-id"
  }
})

client.close
```

This call is non-blocking and there is no response to check.

If you want to record large numbers of the same event at once, or perhaps measure usage in terms of a unit like tokens or memory, you can optionally specify a quantity for your event:

```ruby
client.track({
  event: "query-tokens",
  company: { "id" => "your-company-id" },
  user: {
    "email" => "wcoyote@acme.net",
    "user_id" => "your-user-id"
  },
  quantity: 1500
})
```

#### Event options

Both `track` and `identify` accept an `options:` keyword for optional event metadata. Only fields you set are sent.

```ruby
client.track(
  { event: "query-tokens", company: { "id" => "your-company-id" }, quantity: 1500 },
  options: {
    # Dedupe key. Duplicate events with the same key are dropped
    # server-side for 24 hours.
    idempotency_key: "evt_2026_05_21_query_tokens",
    # Timestamp the event occurred (Time or ISO 8601 String). Required
    # when trusted_client_clock is true.
    sent_at: Time.now.utc,
    # Use sent_at as the effective event time instead of server receipt
    # time. Requires a secret API key and sent_at.
    trusted_client_clock: true,
    # Import historical data without affecting billing. Requires a secret
    # API key and trusted_client_clock.
    backfill: false
  }
)

client.identify(
  { keys: { "user_id" => "your-user-id" }, name: "Wile E. Coyote" },
  # identify only supports idempotency_key.
  options: { idempotency_key: "ident_your-user-id" }
)
```

### Creating and updating companies

Although it is faster to create companies and users via identify events, if you need to handle a response, you can use the companies API to upsert companies. Because you use your own identifiers to identify companies, rather than a Schematic company ID, creating and updating companies are both done via the same upsert operation:

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(api_key: api_key)

response = client.companies.upsert_company(
  keys: { "id" => "your-company-id" },
  name: "Acme Widgets, Inc.",
  traits: {
    "city" => "Atlanta",
    "high_score" => 25,
    "is_active" => true
  }
)
puts response.data

client.close
```

You can define any number of company keys; these are used to address the company in the future, for example by updating the company's traits or checking a flag for the company.

You can also define any number of company traits; these can then be used as targeting parameters.

### Creating and updating users

Similarly, you can upsert users using the Schematic API, as an alternative to using identify events. Because you use your own identifiers to identify users, rather than a Schematic user ID, creating and updating users are both done via the same upsert operation:

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(api_key: api_key)

response = client.companies.upsert_user(
  keys: {
    "email" => "wcoyote@acme.net",
    "user_id" => "your-user-id"
  },
  company: { "id" => "your-company-id" },
  name: "Wile E. Coyote",
  traits: {
    "city" => "Atlanta",
    "login_count" => 24,
    "is_staff" => false
  }
)
puts response.data

client.close
```

You can define any number of user keys; these are used to address the user in the future, for example by updating the user's traits or checking a flag for the user.

You can also define any number of user traits; these can then be used as targeting parameters.

### Checking flags

When checking a flag, you'll provide keys for a company and/or keys for a user. You can also provide no keys at all, in which case you'll get the default value for the flag.

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(api_key: api_key)

is_flag_on = client.check_flag(
  "some-flag-key",
  company: { "id" => "your-company-id" },
  user: {
    "email" => "wcoyote@acme.net",
    "user_id" => "your-user-id"
  }
)

if is_flag_on
  # Flag is on
else
  # Flag is off
end

client.close
```

### Checking flags with entitlement details

If you need more detail about how a flag check was resolved, including any entitlement associated with the check, use `check_flag_with_entitlement`. This returns a response object with the flag value, the reason for the evaluation result, and entitlement details such as usage, allocation, and credit balances when applicable.

```ruby
require "schematichq"

api_key = ENV["SCHEMATIC_API_KEY"]
client = Schematic::SchematicClient.new(api_key: api_key)

resp = client.check_flag_with_entitlement(
  "some-flag-key",
  company: { "id" => "your-company-id" },
  user: {
    "email" => "wcoyote@acme.net",
    "user_id" => "your-user-id"
  }
)

puts "Flag: #{resp.flag_key}, Value: #{resp.value}, Reason: #{resp.reason}"

if resp.entitlement
  puts "Feature allocation: #{resp.feature_allocation}"
  puts "Feature usage: #{resp.feature_usage}"
end

client.close
```

### Credit Leases and Reservations

For features metered by credit burndown (e.g. inference tokens), `check` reserves credits for the work about to run and `track_with_reservation` settles the reservation with actual usage. The SDK gates in one of two modes:

* **Client mode** acquires a **lease**, a tranche of credits held against the company's balance, and carves a per-request **reservation** out of it locally, so a check needs no API call. It requires [DataStream](#datastream) (or [Replicator Mode](#replicator-mode)) and, across multiple processes, a shared Redis so every instance gates against the same lease.
* **Server mode** makes one `check-and-reserve` API call per check. No lease, no Redis, no local state.

`credit_leases[:mode]` defaults to `:auto`: client when DataStream is enabled, server otherwise. Client mode suits high-throughput gating; server mode suits low-volume checks and operations that run for seconds.

Durations are in milliseconds, as in the other Schematic SDKs.

#### Setup

The `redis` gem is not a dependency; pass a client you already have.

```ruby
require "redis"
require "schematichq"

redis_client = Redis.new(url: "redis://localhost:6379")

client = Schematic::SchematicClient.new(
  api_key: ENV["SCHEMATIC_API_KEY"],
  use_data_stream: true,
  datastream_options: {
    redis_client: redis_client # also backs lease and reservation state
  },
  credit_leases: {
    default_lease_size: 10_000,           # credits requested per lease
    default_lease_duration: 5 * 60 * 1000, # lease lifetime (ms)
    default_reservation_ttl: 60 * 1000     # how long a reservation is held if no track settles it (ms)
  }
)
```

Set `credit_leases[:redis_client]` to keep lease state in a different Redis than the DataStream cache.

Server mode needs only a TTL:

```ruby
client = Schematic::SchematicClient.new(
  api_key: ENV["SCHEMATIC_API_KEY"],
  credit_leases: {
    default_reservation_ttl: 60 * 1000 # how long the server holds the credits if no track settles them (ms, max 1 hour)
  }
)
```

Only `mode` and `default_reservation_ttl` apply in server mode; the SDK warns at startup if a client-only option is set.

#### Checking and tracking

```ruby
# Reserve up to max_tokens for this operation.
result = client.check(
  "inference",
  company: { "id" => "your-company-id" },
  usage: max_tokens,                  # upper bound for this operation
  event_subtype: "inference_tokens"   # the metered event
)
raise "credit balance exceeded" unless result.allowed?

inference = run_inference(...)

# Report actual usage; the unused slice of the reservation is refunded.
if result.reservation
  client.track_with_reservation(result.reservation, inference.tokens_used)
else
  client.track({
    event: "inference_tokens",
    company: { "id" => "your-company-id" },
    quantity: inference.tokens_used
  })
end
```

A check can allow without reserving credits (the feature is not credit-metered, `usage` is 0, or the check failed open), and that usage still has to be tracked.

Without `credit_leases` configured, or without a `usage`, `check` falls through to a plain flag check with no reservation. `usage` is still sent as a preflight, locally or to the API, so the verdict accounts for what the call is about to spend. Preflighted verdicts are not cached.

`usage` may be fractional. A client-mode reservation is sized in whole event units, `ceil(usage) x consumption_rate`, and a settle debits the lease by `ceil(actual) x consumption_rate`, so the local ledger moves by exactly what the track event bills. The reservation still records the fractional quantity the caller declared. Everything on the wire rounds up with the debit: the track event's quantity, a server-mode reservation, and the preflight quantity, whether it goes to the API or the local engine.

`result.entitlement` is a symbol-keyed hash with the field names of `Schematic::Types::FeatureEntitlement`, the same in both modes.

An unsettled reservation expires after `default_reservation_ttl` and its credits return to the lease. A late settle still bills the usage (the track event carries a deterministic idempotency key, so it never double-bills) but does not re-debit the local lease, so set `default_reservation_ttl` above the longest expected gap between `check` and `track_with_reservation`.

#### Pre-warming

Pre-warm leases when the user is identified, so a session's first check does not wait on a lease acquire:

```ruby
client.identify(
  {
    keys: { "user_id" => "your-user-id" },
    company: { keys: { "id" => "your-company-id" } }
  },
  prewarm: ["credit-type-id"] # credit type IDs to acquire leases for
)
```

Or call `client.prewarm(credit_type_ids, company:)` directly. Both are no-ops in server mode.

Pre-warming resolves the company the way the server does: it looks the keys up first, whatever they are named, and only when nothing matches does it read a value carrying Schematic's `comp_` prefix as the company id.

#### Failure behavior

A check that cannot be gated (API unreachable, Redis down, lease exhausted) fails closed by default. Override per check:

```ruby
result = client.check(
  "inference",
  company: { "id" => "your-company-id" },
  usage: max_tokens,
  event_subtype: "inference_tokens",
  on_acquire_failure: :fail_open
)
```

In server mode, a check that times out after the server has already reserved leaves those credits reserved until the TTL expires, so keep `default_reservation_ttl` short there.

In client mode, `:fail_open` still evaluates the flag's rules with the credit balance assumed sufficient, so plan targeting and all non-credit conditions apply and only the credit gate is bypassed. In server mode it returns the flag's default value, which is `false` unless you pass `default_value` or configure a `flag_defaults` entry.

`default_value` also applies when a check falls back to a plain flag check and that check fails. Pass a boolean or a callable.

`check` accepts `timeout_ms`, but no timeout is configurable yet: the generated HTTP transport ignores per-request timeouts and the client exposes no setting. The option is carried on the request so it takes effect once the transport reads it.

#### Configuration options

| Option                       | Type                          | Default                                 | Description                                                                                                                        |
| ---------------------------- | ----------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `mode`                       | `:client`, `:server`, `:auto` | `:auto`                                 | Where credits are reserved; `:auto` picks client when DataStream is enabled, server otherwise                                      |
| `default_reservation_ttl`    | `Integer`                     | 60000 (60 seconds)                      | How long an unsettled reservation is held (ms)                                                                                     |
| `default_lease_duration`     | `Integer`                     | 300000 (5 minutes)                      | (client mode) Lease lifetime (ms)                                                                                                  |
| `default_lease_size`         | `Numeric`                     | 10000                                   | (client mode) Credits requested per lease acquire or extend                                                                        |
| `low_water_mark`             | `Float`                       | 0.25                                    | (client mode) Extend in the background when the lease balance dips below this fraction                                             |
| `sweep_interval_ms`          | `Integer`                     | 1000                                    | (client mode) Sweep interval for expired reservations (ms)                                                                         |
| `prewarm_resolve_timeout_ms` | `Integer`                     | 5000                                    | (client mode) How long `prewarm` waits for a freshly identified company to surface (ms); 0 resolves from the DataStream cache only |
| `redis_client`               | Redis client                  | `datastream_options[:redis_client]`     | (client mode) Redis client for lease and reservation state                                                                         |
| `redis_key_prefix`           | `String`                      | `datastream_options[:redis_key_prefix]` | (client mode) Key prefix for lease and reservation keys                                                                            |
| `overrides`                  | `Hash`                        | none                                    | (client mode) Per-credit-type overrides of the above, keyed by credit type ID                                                      |

### Other API operations

The Schematic API supports many operations beyond these, accessible via the API modules on the client: `accounts`, `billing`, `companies`, `credits`, `entitlements`, `events`, `features`, and `plans`.

## Webhook Verification

Schematic can send webhooks to notify your application of events. To ensure the security of these webhooks, Schematic signs each request using HMAC-SHA256. The SDK provides utility functions to verify these signatures.

### Verifying Webhook Signatures

When your application receives a webhook request from Schematic, you should verify its signature to ensure it's authentic:

```ruby
require "schematichq"

webhook_secret = "your-webhook-secret" # Get this from the Schematic app

# Verify a signature directly
Schematic::Webhooks.verify_signature(payload, signature, timestamp, webhook_secret)
# Raises Schematic::Webhooks::InvalidSignatureError if the signature is invalid
# Raises Schematic::Webhooks::MissingSignatureError if the signature is missing
# Raises Schematic::Webhooks::MissingTimestampError if the timestamp is missing
```

### Computing Signatures

You can compute a hex-encoded signature for a given payload:

```ruby
hex_sig = Schematic::Webhooks.compute_hex_signature(body, timestamp, secret)
```

### Rack / Rails Integration

The SDK can verify webhook signatures from Rack-compatible request objects (including Rails controllers):

```ruby
# In a Rails controller
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    webhook_secret = ENV["SCHEMATIC_WEBHOOK_SECRET"]

    begin
      Schematic::Webhooks.verify_webhook_request(request, webhook_secret)

      # Signature is valid, process the webhook
      payload = JSON.parse(request.raw_post)
      # ... handle the event ...

      head :ok
    rescue Schematic::Webhooks::WebhookSignatureError => e
      render json: { error: e.message }, status: :bad_request
    end
  end
end
```

For a Sinatra application:

```ruby
require "sinatra"
require "schematichq"

post "/webhooks/schematic" do
  webhook_secret = ENV["SCHEMATIC_WEBHOOK_SECRET"]

  begin
    Schematic::Webhooks.verify_webhook_request(request, webhook_secret)

    payload = JSON.parse(request.body.read)
    request.body.rewind
    # ... handle the event ...

    status 200
    json status: "ok"
  rescue Schematic::Webhooks::WebhookSignatureError => e
    status 400
    json error: e.message
  end
end
```

### Webhook Test Server

The SDK includes a standalone webhook test server for local development:

```bash
ruby scripts/webhook_test_server.rb <webhook_secret>
# or
SCHEMATIC_WEBHOOK_SECRET=your-secret ruby scripts/webhook_test_server.rb
```

This starts a WEBrick server on port 8080 (configurable via `PORT` env var) that listens for POST requests at `/webhook`, verifies signatures, and prints the payload.

## DataStream

DataStream enables local flag evaluation by maintaining a WebSocket connection to Schematic and caching flag rules, company, and user data locally. Flag checks are evaluated using a local WASM rules engine, significantly reducing latency and network calls.

### Setup

```ruby
require "schematichq"

client = Schematic::SchematicClient.new(
  api_key: ENV["SCHEMATIC_API_KEY"],
  use_data_stream: true
)

# Flag checks are now evaluated locally
is_flag_on = client.check_flag(
  "some-flag-key",
  company: { "id" => "your-company-id" }
)

client.close
```

### Configuration Options

You can customize DataStream behavior via the `datastream_options` hash:

| Option                       | Type      | Default                       | Description                                                  |
| ---------------------------- | --------- | ----------------------------- | ------------------------------------------------------------ |
| `cache_ttl`                  | `Integer` | 86400 (24 hours)              | Cache TTL in seconds                                         |
| `replicator_mode`            | `Boolean` | `false`                       | Enable replicator mode (see below)                           |
| `replicator_health_url`      | `String`  | `http://localhost:8090/ready` | URL to poll for replicator health                            |
| `replicator_health_interval` | `Integer` | 30                            | Health check interval in seconds                             |
| `max_frame_size`             | `Integer` | 20971520 (20 MB)              | Maximum size, in bytes, of a single incoming WebSocket frame |

```ruby
require "schematichq"

client = Schematic::SchematicClient.new(
  api_key: ENV["SCHEMATIC_API_KEY"],
  use_data_stream: true,
  datastream_options: {
    cache_ttl: 3600 # 1 hour
  }
)
```

### Frame size limit

DataStream sends the full flag set as a single WebSocket frame on connect. If that
frame exceeds the limit, the SDK cannot decode it and logs an error naming the
limit; raise `max_frame_size` to accept a larger payload.

```ruby
client = Schematic::SchematicClient.new(
  api_key: ENV["SCHEMATIC_API_KEY"],
  use_data_stream: true,
  datastream_options: {
    max_frame_size: 64 * 1024 * 1024 # 64 MB
  }
)
```

The underlying `websocket` gem stores this limit in a module-level global
(`WebSocket.max_frame_size`), so it has no per-connection setting. The SDK leaves
that global alone unless you pass `max_frame_size`, and when you do it only ever
raises the limit, never lowers it, so it cannot shrink the ceiling for other
WebSocket users in the same process.

## Replicator Mode

When running the `schematic-datastream-replicator` service, you can configure the Schematic client to operate in Replicator Mode. In this mode, the client reads from a shared cache populated by the external replicator rather than establishing its own WebSocket connection.

### Setup

```ruby
require "schematichq"

client = Schematic::SchematicClient.new(
  api_key: ENV["SCHEMATIC_API_KEY"],
  use_data_stream: true,
  datastream_options: {
    replicator_mode: true,
    replicator_health_url: "http://localhost:8090/ready",
    replicator_health_interval: 30
  }
)

# Flag checks use the shared cache
is_flag_on = client.check_flag(
  "some-flag-key",
  company: { "id" => "your-company-id" }
)

client.close
```

When running in Replicator Mode, the client will:

* Skip establishing WebSocket connections
* Periodically check if the replicator service is ready
* Use cached data populated by the external replicator service
* Fall back to direct API calls if the replicator is not available

## Testing

### Offline Mode

In development or testing environments, you may want to avoid making network requests to the Schematic API. You can run Schematic in offline mode by specifying the `offline` option; in this case, it does not matter what API key you specify:

```ruby
require "schematichq"

client = Schematic::SchematicClient.new(offline: true)

client.close
```

You can also enable offline mode by not providing an API key:

```ruby
require "schematichq"

client = Schematic::SchematicClient.new
# Logs a warning and automatically enables offline mode

client.close
```

Offline mode works well with flag defaults:

```ruby
require "schematichq"

client = Schematic::SchematicClient.new(
  offline: true,
  flag_defaults: { "some-flag-key" => true }
)

# interactions with the client

client.close
```

In an automated testing context, you may also want to use offline mode and specify single flag responses for test cases:

```ruby
require "schematichq"

# In your test setup
client = Schematic::SchematicClient.new(offline: true)
client.set_flag_default("some-flag-key", true)

# test code that expects the flag to be on
assert client.check_flag("some-flag-key")

client.close
```

## Errors

Failed API calls will raise errors that can be rescued from granularly:

```ruby
require "schematichq"

client = Schematic::SchematicClient.new(api_key: ENV["SCHEMATIC_API_KEY"])

begin
  result = client.accounts.create_api_key(name: "name")
rescue Schematic::Errors::TimeoutError
  puts "API didn't respond before our timeout elapsed"
rescue Schematic::Errors::ServiceUnavailableError
  puts "API returned status 503, is probably overloaded, try again later"
rescue Schematic::Errors::ServerError
  puts "API returned some other 5xx status, this is probably a bug"
rescue Schematic::Errors::ResponseError => e
  puts "API returned an unexpected status other than 5xx: #{e.code} #{e.message}"
rescue Schematic::Errors::ApiError => e
  puts "Some other error occurred when calling the API: #{e.message}"
end
```

Note that `check_flag` and `check_flag_with_entitlement` never raise exceptions -- errors are logged and default values are returned.

## Advanced

### Retries

The SDK is instrumented with automatic retries. A request will be retried as long as the request is deemed retryable and the number of retry attempts has not grown larger than the configured retry limit (default: 2).

A request is deemed retryable when any of the following HTTP status codes is returned:

* [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout)
* [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests)
* [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors)

Use the `max_retries` option to configure this behavior:

```ruby
require "schematichq"

client = Schematic::SchematicClient.new(
  api_key: ENV["SCHEMATIC_API_KEY"],
  max_retries: 3 # Configure max retries (default is 2)
)
```

### Timeouts

The SDK defaults to a 60 second timeout. Use the `timeout` option to configure this behavior:

```ruby
response = client.accounts.create_api_key(
  name: "name",
  timeout: 30 # 30 second timeout
)
```