The clientGenerated client library

Every client ships with the parts that take the longest

What comes out is a library someone would be glad to inherit. Typed all the way through, with one place where the HTTP happens and the behaviour your API expects of a good client already in it.

  • Typed models and typed errors, so a field that moved fails at compile time, where someone will see it
  • One request layer everything goes through, with the status, the request id and how long it took on the way back
  • Your auth wired in, read from the security schemes your spec already declares
  • Doc comments on every method and model, so your users read your documentation inside their editor
  • A test suite generated with it, derived from your schemas, and yours to leave out
  • The same surface in all ten, so the method a Go user calls is the method a Python user calls
One call to a generated TypeScript client, and everything the client does underneath it. The code on the left is twelve lines: construct the client with a bearer token read from the environment, then await acme.payments.create with a customer, an amount and a currency. On the right, in order: the arguments are checked against the generated CreatePaymentParams type; the Authorization header is attached from the bearerAuth scheme the OpenAPI document declares; an Idempotency-Key is minted once, before the retry loop, so every attempt carries the same one. The first attempt posts to /v1/payments with a 30 second timeout and comes back 503 Service Unavailable, carrying a retry-after of 2 seconds, which the client catches under its own retry policy. It waits the two seconds the response asked for, since a retry-after header takes precedence over the client's own exponential backoff and is capped at 8 seconds. The second attempt sends the same key, because a write is repeated on 429 and 503, and comes back 201 Created in 934 milliseconds. The body is decoded into a Payment, with wire names like created_at renamed to the model's createdAt. The caller gets the Payment back, and the envelope beside it records the status, the request id, the latency and that this took two attempts. None of the retrying, signing, keying or decoding appears in the twelve lines on the left.

Retries and timeouts

How many attempts, how long to wait between them, and which statuses are worth trying again. Set for the whole client, or for the one method that needs longer.

Idempotency keys

Writes carry a generated key, so a request the client retried is applied once. Which methods send one, and under which header, is yours.

Pagination

Cursor, offset or page, with an iterator that walks every page so the caller never has to write the loop.

Streaming

Server-sent events and chunked responses come back as typed events a caller can loop over.

And a command line

The same spec also makes a runnable CLI

One switch, and the generated package ships a command-line client beside the library. Every operation you included becomes a subcommand with its parameters as flags.

  • A subcommand per endpoint, named the way the method is
  • Your auth, read the way a terminal expects it
  • Distributed with the package, so there is nothing extra to release
  • Off unless you want it, and the library is untouched either way
LanguagesTen SDK languages

Ten languages, each one idiomatic to itself

A language is more than a syntax to emit. Each generator ships the transport, the model style, the argument shape and the naming that language's own libraries use, so what your users install looks like the packages they already depend on.

SDKs are generated for TypeScript, Python, Go, Java, Dart, Ruby, PHP, Rust, Swift, Kotlin.

TypeScript

Autodeploys vianpm

fetch out of the box, so the client pulls in no HTTP dependency at all.

Python

Autodeploys viaPyPI

pydantic response models by default, plain dataclasses when you would rather.

Go

Autodeploys viaa git tag

net/http, and the generated banner Go tooling expects on generated files.

Java

Autodeploys viaMaven Central

java.net.http, with a builder for the operations wide enough to need one.

Kotlin

Autodeploys viaMaven Central

OkHttp for the JVM and Android, Ktor when you are going multiplatform.

Ruby

Autodeploys viaRubyGems

net/http, and method names that read the way a Ruby developer expects.

PHP

Autodeploys viaPackagist

cURL underneath, installed with Composer like everything else.

Rust

Autodeploys viacrates.io

ureq, with typed results a caller can match on.

Swift

Autodeploys viaa git tag

URLSession, and labelled arguments at the call site.

Dart

Autodeploys viapub.dev

the http client, which is the one a Flutter app already has.

Go modules and Swift packages resolve straight from a repository, so those two have no registry account to connect. Octri publishes them by pushing a tagged commit. For the other eight, connecting the account once is the only setup there is. SDK guides in the docs →

The studioSDK configuration

Every choice a generator makes, you make first

SDK Studio is where the client is shaped before a file of it exists. Change something and the code beside it changes with it, so what you are reading is the SDK your users will read.

  • The shape of the client: free functions or a client object, and whether operations are grouped by tag or by path
  • How methods are named, so an operation your spec calls one thing can be called what your users would call it
  • How the source is laid out, one file or a folder per resource
  • The base URL and the package name compiled into what you publish, so nobody installs a client pointed at staging
  • A live preview in the language you are editing, updating as you go
The SDK Studio, with its settings on the left and a live preview of the client on the right, working through four decisions in order. The operation being previewed is listInvoices, grouped under Billing then Invoices. First, client style moves from Functions to Class-based: the import changes from configureClient and listInvoices to AcmeClient, and the call changes from listInvoices to acme.listInvoices. Second, namespace moves from None to Tags, so the call surface nests the three operations under billing and then invoices and the call becomes acme.billing.invoices.listInvoices. Answering the namespace enables the third control, method naming, which is disabled until then because it has no effect without one. Moving it from Keep full name to Shortened drops the namespace word from every operation in the group: listInvoices becomes list, getInvoice becomes get, createInvoice becomes create, and the call becomes acme.billing.invoices.list. Fourth, folder structure moves from Flat to By tag, and the single source file src/methods/billingInvoices.ts becomes the folder src/methods/billing with invoices.ts inside it. A rule under the four controls marks that the settings panel carries on past them. Two more answers sit under the controls and are compiled into what gets published: the package name, acme-sdk, which is the string the import reads from, and the base URL, https://api.acme.com, which is the one the generated client is constructed with. At each step only the part of the client that setting owns is redrawn.

Precedence

Decide it once. Then decide it again where it matters.

Most settings exist at three levels, and the narrowest one wins. An endpoint override beats a language override, and a language override beats the project default.

  • Project: the shape every language starts from
  • Language: its package name, its repository, its transport
  • Endpoint: the one operation that does not behave like the rest
Per endpointPer-endpoint SDK settings

Your spec has one name for it. Your users get yours

Every operation in the spec is a row you can open. What it is called, what its documentation says, how it behaves, and whether it goes out at all are decided here.

  • Rename the method without touching the spec, so tidying an operationId later cannot rename something your users have already called a thousand times
  • Write the doc comment that shows up in their autocomplete
  • Keep an internal endpoint out entirely, because anything that ships is public API however your documentation describes it
  • Deprecate one with a message pointing at what replaced it, so callers get a warning at compile time and their code keeps working while they move
  • Say how this one paginates or streams, naming the field the items come back in and the one that advances the page
  • Give it its own retry and idempotency rules when the project-wide ones are wrong for it
  • Arrange them into folders of your own, or adopt the grouping your documentation sidebar already has
SDK Studio's endpoint list above the file somebody who installed the SDK is writing, with their editor's autocomplete open on acme.customers. The spec has 42 operations and the list shows one tag of them, Customers, with Charges beginning underneath. The row for PATCH /v1/customers/{id}opens onto the settings for that one operation. Its function name arrives as updateBillingEntity, which is what the operationId in the spec gives it, and is renamed to update. In the autocomplete below, the entry renames with it and so does the signature, where update now takes params of type UpdateParams and returns a Promise of a Customer, because the generator derives that type name from the method name. The doc comment starts inferred from the endpoint's Docs page description, which reads that the call updates the customer identified by id with the values provided in the request body. Overriding it puts the sentence a caller wants with their hands on the keyboard: only the fields you pass are changed, and anything you leave out keeps its current value. That sentence is what the autocomplete then shows. Last, the eye beside POST /v1/customers/reindex is switched off, because rebuilding the search index is a maintenance job. The row dims, and reindex is no longer one of the methods the autocomplete offers. The four other methods in the group, list, reindex, get and remove, are named by the generator's own rule, which strips the group's word out of each operationId.
Custom codeCustom code hooks

Write into the client, and keep it through the next build

A hook is code you write that the generator compiles into a typed seam around the request. It exists for the things a spec cannot express, like signing a payload on its way out or unwrapping an envelope the API is not ready to drop.

  • A seam before the request, taking the parameters, query, body and headers, and returning what actually goes out
  • A seam after the response, returning what the caller receives
  • Written per language, because a Go client cannot run TypeScript
  • Packages your hook imports are declared once and written into the generated manifest, so the dependency is there when your users install
  • Edited in the browser or kept in a repository, whichever way your team works
One file of a generated TypeScript client for ACME's payouts API, watched across two events. It starts as four generated methods with nothing of yours in it. A hook editor opens on the create operation, showing the declaration the generator owns and will not let you edit, async function createBeforeRequest taking a RequestContext and returning one. Two lines are typed into the body it wraps. They sign the outgoing payload, taking an HMAC of the request body with SHA-256 and setting the result on an Acme-Signature header. Because the body is no longer empty, the generated method below grows the call that threads the request through it, const ctx = await createBeforeRequest with its params, query, body and headers, and what the request sends becomes ctx.body and ctx.headers, which is whatever your code returned. The package your hook imports is then declared, and the generator writes a line at the top of the file naming that dependency, js-sha256 at version 0.11, which is also what goes into the package manifest your users install from. Last, the spec changes and the client is generated again. A pass runs down the file and every generated line repaints as it arrives, including the declaration directly above your code and the return statement directly below it. One line comes back different, because the payout body gained a descriptor field. The two bands with your name on them, the import and the signing lines, are the only part of the file the pass does not touch, and the build finishes with your code still in it.

Or own the whole thing

The generated source can live in your repository

Push each language into a repository you control, and every regeneration after that arrives the way any other change to your codebase does.

  • A repository per language, which is how SDKs are usually kept anyway
  • Regenerations land as a pull request, with a diff you read before it becomes your code
  • Your own checks run on it, and their result is visible from the studio
  • Nothing reaches your production repository until you promote it
TransformsOpenAPI spec transforms

Improve the spec for the SDK without editing the spec

Rewrites applied to the document on its way into the generator. Rename a model, retire a value, or set the thing a control does not cover, all without a pull request against the file your API serves.

  • Rewrites that live with the project, applied on every build and never on the original
  • Each one carries the reason it exists, so the person who finds it in a year knows what it was for
  • A score on your spec, with the operations that will generate badly named one at a time
  • Apply a suggested fix and it goes in for you, or dismiss it and it stops asking
  • The same audit read twice, once for what hurts the generated client here and once for what hurts the reference page in API Studio
A Spec Audit panel scoring an OpenAPI document with 24 operations and 11 models. Its SDK readiness reads 10 out of 10 while the review queue is empty. Three findings then arrive one at a time and the score falls to meet them. A high finding, that 18 operations have no schema on their successful response, costs 1.6. A medium finding, that 12 operations use a path placeholder with no parameter declared for it, costs 0.9. A medium finding, that the operationIds on 15 operations do not read as method names, costs 0.7. The score settles at 6.8 out of 10 and its meter turns amber, since the studio calls anything under 8 usable with gaps worth tightening. Each finding is then resolved, its deduction struck through, and the score climbs back to 10 with the queue clear again.
The buildSDK build pipeline

Ten languages build as ten builds

Every language is generated on its own, so the ones that are fine are never waiting on the one that is not. You watch them go, and what comes out is kept.

Ten columns, one for each SDK language, filling upward from a shared floor towards a shared line. They all start on the same build and reach the line at ten different moments, so at any point in the run the ten are at ten different heights. One of them, Java, stops partway up and turns red while the other nine carry on climbing past it and finish. The build settles as partial, nine ready and one failed. The failed attempt drops away, that one language is retried on its own, and it climbs to the line while nothing else on the build moves. With all ten ready, the whole build is kept.

A failure stays where it is

A language that cannot build is reported with the output that stopped it. The rest of the build ships, and you retry that one on its own.

You can see it working

Each language reports the phase it has reached while the build runs, so a long one shows you where it has got to.

Every build is kept

What shipped, in which languages, at which version, with the files that came out of it, ready to download.

Rebuild without republishing

Regenerate from the last published configuration when you want the artifact fresh and nothing about the config has changed.

PublishingRegistry publishing

Where your users already look: npm install

Connect an account once per language and a release goes out to the registry that language uses. Nobody has to be told where to find your client, because it is where they would have looked first.

  • npm, PyPI, Maven Central, crates.io, RubyGems, Packagist and pub.dev, connected once and reused for every release
  • Go and Swift published by tag, pushed to the repository they resolve from
  • A package name per language, since the conventions differ everywhere you publish
  • The README you write is the registry page, and it stays in step with the one in your repository
  • Repository metadata in the manifest, so the registry page links back to your source
  • Releases pushed for you, and artifacts served from a CDN, from Business. Below that you take the build down as a zip and release it yourself
A package page for the generated SDK, shown once for each of the ten languages in turn. The registry changes as the list walks down, from npm to PyPI, Maven Central twice for Java and Kotlin, crates.io, RubyGems, Packagist and pub.dev, and Go and Swift resolve from a git tag on the repository instead. The package name follows each ecosystem's own convention and the install command is the one that language already uses, so a Ruby developer reads gem install and a Rust developer reads cargo add. The version number, the README and the repository the page links back to are the same at every stop, because all ten come out of one release.
VersionsPackage versioning

One version, in every package you ship

A build records the spec that produced it, and publishing carries that version into every manifest. There is no second number to keep in step, and nothing to remember to bump twice.

  • One version, set where it belongs. Change it in the spec and the manifests follow on the next build
  • A changelog written from what actually changed, entry by entry, and yours to edit before it goes out
  • The publish screen shows what is about to happen: which languages will rebuild, and where each is going
  • Deprecate for a release or two, then remove, so an operation leaving is a warning first
One version number, shown once, with the OpenAPI document it is set in named at its left and the packages it ships as at its right. A caret lands in the field and rolls the minor digit up by one, a pulse of light runs the length of the field, and the marks of the ten languages beneath it light one after another as it passes. The number is written into the package manifest of every ecosystem that has one, and applied as a git tag for Go, PHP and Swift, which do not.
SyncGitHub spec sync

Merge the spec change. The clients follow

Connect the repository your OpenAPI document lives in, and a merged pull request is all it takes. The API you shipped and the libraries people call it with stop being two things you maintain.

  • One merged PR rebuilds the clients, the pages and the changelog together, in under 90 seconds
  • Available on every tier, Free included
  • Builds start by hand, from a spec change, or from a push, and the history records which
  • An endpoint you added is a method your users have the next time they update
A commit history for an API repository, drawn beside the history of the client library generated from it. Each merged pull request on the left names what it changed in the OpenAPI document, and a line ties it across to the change that arrived in the client. A path added becomes a method, a query parameter becomes an argument, a date-time format becomes a typed field, and an operation marked deprecated is flagged as deprecated in the client too. The newest merge completes on screen, its branch joining the main line and the client change landing after it, and the history steps up as the next one arrives. Octri watches the branch and the document path you connected, so every push that touches that file is picked up.
DownstreamDocs, MCP and telemetry

Configure it once. Four surfaces change

What you set here is not only what generates. Your documentation, the tools you hand to agents, and the telemetry coming back from production are all reading the same configuration.

Snippets on every page

Your reference pages show the call in your own SDK, using the method name you chose and the client shape you picked.

Behaviour, documented

Retries, pagination and idempotency show up on the endpoint page as things a reader can rely on, without anybody writing them down.

Tools for agents

Every endpoint you included becomes a tool on your MCP server, named and described the way you named and described it here.

Telemetry from the wild

Switch monitoring on and the client reports what it hits in production, from wherever your users installed it.

MigrationMigrating your SDKs

Bring the configuration you already wrote

Years of decisions go into an SDK config, and they are the part nobody wants to make again. Point Octri at the one you have and it reads it: the spec, the languages, the package naming, and the per-endpoint choices buried in your vendor extensions.

  • Speakeasy, Stainless, Fern, liblab and Mintlify configs read directly
  • Method renames and pagination markers survive, lifted out of the extensions you set on each operation
  • A preview before anything exists, listing what came across and what needs your attention
  • Nothing is published until you say so, so the packages your users depend on today keep coming from wherever they come from now

We are also happy to do the move with you while the product is in beta. Comparisons on the blog →

The rest of itTests, enums and artifacts

And the parts you only notice when they are missing

Shaping

  • Argument style per language, since a params object is not idiomatic everywhere
  • Enums that survive a value you have not shipped yet
  • Generated tests, or a package without them
  • Doc comments in full, or a leaner tree

Getting it out

  • Download the build as a zipevery tier, including Free
  • Rebuild from the last published config without publishing again
  • Retry one language on its own
  • Artifacts served from a CDNBusiness and above

Working in it

  • Search a thousand operations by path or by name
  • Apply one setting to every language at once
  • Adopt the grouping your documentation sidebar already uses
  • A draft nobody sees until you publish it

One pipeline, from your spec to their import statement

One document and one configuration produce all four: the library your users install, the page that documents it, the tools an agent calls it with, and the monitoring that reports what production hits. Rename a method or add an endpoint and every one of them follows, because none is maintained separately.

Questions

One on Free, two on Starter, four on Growth, and all ten on Business. On Starter and Growth an extra language is $50/mo. Adding one takes effect straight away; dropping one applies at the end of the billing period.

Your users are writing the client anyway

You may as well be the one who ships it.