skills/speakeasy-api/agent-skills/customize-sdk-runtime

customize-sdk-runtime

SKILL.md

customize-sdk-runtime

Configure runtime behavior for Speakeasy-generated SDKs including retries, timeouts, pagination, server selection, custom code preservation, and error handling.

When to Use

  • Configuring retry logic for SDK operations
  • Setting global or per-operation timeouts
  • Adding pagination support to list endpoints
  • Selecting between multiple server environments
  • Preserving custom code across SDK regeneration
  • Customizing error handling and typed error responses
  • User says: "SDK retries", "timeout configuration", "server selection", "pagination config", "custom code", "error handling"

Inputs

Input Required Description
OpenAPI spec Yes Path to the OpenAPI spec to configure
gen.yaml Sometimes Generation config for error handling customization
Target language Helpful TypeScript, Python, or Go (defaults vary)

Outputs

Output Description
Updated OpenAPI spec Spec with runtime extensions applied
SDK configuration Runtime behavior configured in generated SDK
Custom code files Hook files preserved across regeneration

Prerequisites

  • Speakeasy CLI installed and authenticated
  • An existing OpenAPI spec
  • An existing SDK project (for runtime override examples)

Command

Runtime configuration is primarily done through OpenAPI extensions, not CLI commands. After modifying the spec, regenerate:

speakeasy run --output console

Retry Configuration

Global Retries (OpenAPI Spec)

Add x-speakeasy-retries at the root of your OpenAPI document:

openapi: 3.1.0
info:
  title: My API
  version: 1.0.0
x-speakeasy-retries:
  strategy: backoff
  backoff:
    initialInterval: 500        # milliseconds
    maxInterval: 60000           # milliseconds
    maxElapsedTime: 3600000      # milliseconds (1 hour)
    exponent: 1.5
  statusCodes:
    - 5XX
    - 408
    - 429
  retryConnectionErrors: true

Per-Operation Retries

Override retries on a specific operation by adding x-speakeasy-retries at the operation level with the same schema as above.

Retry Options Reference

Option Type Description
strategy string Must be backoff
backoff.initialInterval integer First retry delay in ms
backoff.maxInterval integer Maximum delay between retries in ms
backoff.maxElapsedTime integer Total time limit for all retries in ms
backoff.exponent number Backoff multiplier (e.g., 1.5, 2.0)
statusCodes string[] HTTP status codes to retry (supports 5XX patterns)
retryConnectionErrors boolean Retry on connection failures

Runtime Override (TypeScript)

const res = await sdk.payments.create(
  { amount: 1000 },
  {
    retries: {
      strategy: "backoff",
      backoff: {
        initialInterval: 1000,
        maxInterval: 30000,
        maxElapsedTime: 300000,
        exponent: 2.0,
      },
      retryConnectionErrors: true,
    },
  }
);

Runtime Override (Python)

from sdk.utils import BackoffStrategy, RetryConfig

res = sdk.payments.create(
    amount=1000,
    retries=RetryConfig("backoff",
        backoff=BackoffStrategy(1000, 30000, 300000, 2.0),
        retry_connection_errors=True),
)

Runtime Override (Go)

res, err := sdk.Payments.Create(ctx, req, operations.WithRetries(retry.Config{
    Strategy: "backoff",
    Backoff: &retry.BackoffStrategy{
        InitialInterval: 1000, MaxInterval: 30000,
        MaxElapsedTime: 300000, Exponent: 2.0,
    },
    RetryConnectionErrors: true,
}))

Timeout Configuration

Global Timeout (OpenAPI Spec)

Set a default timeout in milliseconds at the document root:

x-speakeasy-timeout: 30000   # 30 seconds

Per-Operation Timeout

Override timeout on specific operations:

paths:
  /reports/generate:
    post:
      operationId: generateReport
      x-speakeasy-timeout: 120000   # 2 minutes for long-running ops

Runtime Override

TypeScript:

const sdk = new SDK({ timeoutMs: 30000 });                          // global
const res = await sdk.reports.generate({ type: "annual" }, { timeoutMs: 120000 }); // per-call

Python:

sdk = SDK(timeout_ms=30000)                                          # global
res = sdk.reports.generate(type="annual", timeout_ms=120000)         # per-call

Go:

sdk := SDK.New(SDK.WithTimeoutMs(30000))                             // global
res, err := sdk.Reports.Generate(ctx, req, operations.WithTimeoutMs(120000)) // per-call

Pagination Configuration

Add x-speakeasy-pagination to list endpoints:

paths:
  /users:
    get:
      operationId: listUsers
      x-speakeasy-pagination:
        type: offsetLimit
        inputs:
          - name: offset
            in: parameters
            type: offset
          - name: limit
            in: parameters
            type: limit
        outputs:
          results: $.data

Pagination Types

Type Description Use Case
offsetLimit Offset + limit parameters Traditional pagination
cursor Cursor-based navigation Large datasets, real-time data
url Next-page URL in response HATEOAS-style APIs

For cursor type, use type: cursor in inputs and add nextCursor: $.meta.next_cursor to outputs.

SDK Usage (TypeScript)

// Auto-iterate through all pages
for await (const user of await sdk.users.list({ limit: 50 })) {
  console.log(user.name);
}

// Or manually page with next()
let page = await sdk.users.list({ limit: 50 });
while (page) {
  for (const user of page.data) { console.log(user.name); }
  page = await page.next();
}

Server Configuration

Multiple Servers in Spec

servers:
  - url: https://api.example.com
    description: Production
    x-speakeasy-server-id: production
  - url: https://sandbox.example.com
    description: Sandbox
    x-speakeasy-server-id: sandbox

Runtime Server Selection

TypeScript:

const sdk = new SDK({ server: "sandbox" });
// or custom URL:
const sdk = new SDK({ serverURL: "https://custom.example.com" });

Python: sdk = SDK(server="sandbox") or sdk = SDK(server_url="...")

Go: sdk := SDK.New(SDK.WithServer("sandbox")) or SDK.WithServerURL("...")


Custom Code Preservation

Files in src/hooks/ (TypeScript) or the equivalent hooks directory are preserved during SDK regeneration.

Key Rules

  • registration.ts is generated on the first run, then never overwritten
  • Any new files you add to src/hooks/ are preserved across regeneration
  • Register custom hooks in registration.ts to integrate them with the SDK lifecycle

Example: Custom Hook Registration

// src/hooks/registration.ts
import { Hooks } from "./types";
import { initLoggingHook } from "./logging";
import { initCustomAuthHook } from "./custom-auth";

export function initHooks(hooks: Hooks) {
  initLoggingHook(hooks);
  initCustomAuthHook(hooks);
}

Error Handling

Custom Error Schemas (gen.yaml)

generation:
  errors:
    statusCodes:
      - statusCode: "4XX"
        schema: "#/components/schemas/ClientError"
      - statusCode: "5XX"
        schema: "#/components/schemas/ServerError"

Define typed error responses in your OpenAPI spec by adding response schemas for specific status codes (e.g., 404, 422). The SDK generates typed error classes for each.

SDK Error Handling (TypeScript)

import { NotFoundError, ValidationError } from "./sdk/models/errors";

try {
  const user = await sdk.users.get({ id: "123" });
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log("User not found:", err.message);
  } else if (err instanceof ValidationError) {
    console.log("Validation failed:", err.errors);
  } else {
    throw err;
  }
}

Error Transform Hooks

Add custom error transformation via hooks:

// src/hooks/error-transform.ts
export function initErrorTransformHook(hooks: Hooks) {
  hooks.registerAfterError((_hookCtx, response, error) => {
    if (error) { console.error("API error:", error.message); }
    return { response, error };
  });
}

Example

Scenario: Configure retries, a 30-second global timeout, and sandbox server selection.

  1. Add extensions to your OpenAPI spec:
openapi: 3.1.0
info:
  title: Payments API
  version: 1.0.0
x-speakeasy-retries:
  strategy: backoff
  backoff:
    initialInterval: 500
    maxInterval: 60000
    maxElapsedTime: 300000
    exponent: 1.5
  statusCodes: [5XX, 429]
  retryConnectionErrors: true
x-speakeasy-timeout: 30000
servers:
  - url: https://api.payments.com
    x-speakeasy-server-id: production
  - url: https://sandbox.payments.com
    x-speakeasy-server-id: sandbox
  1. Regenerate: speakeasy run --output console

  2. Use with runtime overrides:

const sdk = new SDK({ server: "sandbox", timeoutMs: 30000 });

const payment = await sdk.payments.create(
  { amount: 5000, currency: "usd" },
  {
    retries: {
      strategy: "backoff",
      backoff: { initialInterval: 2000, maxInterval: 30000,
                 maxElapsedTime: 120000, exponent: 2.0 },
    },
    timeoutMs: 60000,
  }
);

What NOT to Do

  • Do NOT set excessively long maxElapsedTime values that could hang requests indefinitely
  • Do NOT retry on 4XX status codes (except 408 and 429) as they indicate client errors
  • Do NOT modify generated files outside of src/hooks/ as they will be overwritten on regeneration
  • Do NOT set timeouts to 0 expecting "no timeout" -- omit the extension instead
  • Do NOT hardcode server URLs in application code -- use x-speakeasy-server-id and select at runtime

Troubleshooting

Issue Cause Solution
Retries not working Extension at wrong level Ensure x-speakeasy-retries is at document root or operation level
Timeout not applying Value not in milliseconds x-speakeasy-timeout expects milliseconds, not seconds
Pagination next() undefined Missing extension Add x-speakeasy-pagination to the list operation
Custom hooks lost on regen Files outside hooks dir Move custom code to src/hooks/ directory
Server ID not recognized Missing extension Add x-speakeasy-server-id to each server entry
Typed errors not generated Missing error schemas Define error response schemas per status code in OpenAPI spec
Hook not executing Not registered Register your hook in registration.ts
Weekly Installs
1
GitHub Stars
6
First Seen
Jan 29, 2026
Installed on
github-copilot1