Customization guide

Config file

clientsmith generate --config clientsmith.toml. Every key is optional.

Key Meaning
package Package name (CLI --package wins)
templates Directory of template overrides (CLI --templates wins)
[names.operations] operationId = "method_name"; the name is used as-is (still escaped if it is a keyword)
[names.models] SchemaKey = "ClassName"
[retry] Keyword arguments of the generated RetryConfig(...) default: max_retries, backoff_factor, max_backoff, retry_unsafe
[pagination.<operationId>] Pagination hints, below

Pagination

Detection runs on every GET whose success response is a model with an array field. It is deliberately conservative.

Style Detected when Generated iterator
cursor a query parameter named cursor, page_token, after, starting_after, next_token or page_cursor and a response field named next_cursor, next_page_token, next_token, cursor, after or next sends the field back as the parameter until it is empty
next_link no cursor parameter and a response field named next, next_url, next_link, next_page or next_page_url follows the URL as-is (query parameters of the first call are not re-sent)
offset a query parameter named offset or skip increases the offset by the number of items received; stops on an empty page or when a total/total_count/count field says it is done

The item array is the field named items, data, results, records or entries, else the only array field. Override any of this per operation (wire names or Python names both work):

[pagination.listEvents]
style = "cursor"            # cursor | next_link | offset | none  (none disables detection)
items_field = "events"
cursor_param = "page"       # request parameter that carries the cursor
next_field = "nextToken"    # response field holding the next cursor / link
offset_param = "skip"       # offset style
total_field = "totalCount"  # offset style, optional

For an operation list the generator adds list_iter(...) (a generator in the sync resource, an async generator in the async one). It takes the same arguments as list minus the pagination parameter and yields individual items.

Retry and timeouts

Defaults are set in [retry] and can be overridden per client:

from petstore_client import Client, RetryConfig

client = Client(
    "https://api.example.com",
    timeout=10.0,
    retry=RetryConfig(max_retries=5, backoff_factor=1.0, max_backoff=20, retry_unsafe=True),
)

Delay before attempt n (0-based) is min(backoff_factor * 2**n, max_backoff), or the server's Retry-After (seconds or HTTP date) when present. 429 is retried for every method; 500/502/503/504 and transport errors only for idempotent methods (GET HEAD PUT DELETE OPTIONS) unless retry_unsafe=True. A 401 triggers one token refresh for OAuth2 before the request is retried. Pass http_client=httpx.Client(...) to control proxies, mTLS or transports.

Authentication arguments

Client(...) accepts one group of keyword arguments per security scheme in the spec. The first scheme of each kind uses plain names, later ones are prefixed with the scheme name.

Scheme Arguments
http bearer (and OAuth2 flows other than client credentials, OpenID Connect) token
http basic username, password
apiKey (header, query or cookie) api_key
oauth2 clientCredentials client_id, client_secret, scopes, token_url (defaults to the spec's tokenUrl)

Each operation lists the scheme alternatives it accepts; the first alternative whose schemes are all configured is used, and security: [] operations are sent without credentials.

Template overrides

--templates DIR puts DIR in front of the built-in template search path, so you only provide the files you want to change (same file names as in src/clientsmith/templates/):

Template Output
core.py.j2 _core.py runtime (auth, retries, serialization)
models.py.j2 models.py
errors.py.j2 errors.py
resource.py.j2 resources/<tag>.py
resources_init.py.j2 resources/__init__.py
client.py.j2 client.py
init.py.j2 __init__.py
pyproject.toml.j2 pyproject.toml
README.md.j2 README.md

Copy the built-in template and edit it. Python outputs are passed through ruff afterwards (unused imports removed, imports sorted, formatted), so templates can import liberally. Template variables:

Method bodies are produced by render.py (they are too branchy for a template); to change their shape, override resource.py.j2 and call your own macro instead of method(...).

Regeneration and CI

Output is deterministic (sorted models and operations, fixed formatting), so regenerating into a committed directory yields a minimal diff. A typical CI guard:

clientsmith generate openapi.yaml -o sdk/ --config clientsmith.toml
git diff --exit-code sdk/
clientsmith diff openapi.old.yaml openapi.yaml --fail-on-breaking