1 week to SSO with Stytch
Sri RaghavanFirst of all, let’s get the big announcement out of the way: starting today, SDKs for Orb are available in Python, Node.js/Typescript, and Go. We’re pretty excited about the ergonomic wins these SDKs bring when it comes to integrating with Orb. They offer a wide range of benefits including autocomplete and static checking, retries, idempotency support, and more!
Better yet, if an Orb SDK isn’t available in your language of choice, our OpenAPI spec can help you get up and running with one quickly!
Before we dive deeper into what led us on this path…
As a billing data platform, our web API is the primary surface area that technical folks interact with; compared to raw cURL commands, SDKs in your language of choice can lead to a much better developer experience, and make it quicker to get up and running. Our API is ever-evolving and handwritten SDKs could fall out-of-date (especially in languages we don’t use regularly in-house).
An increasingly common approach to this problem is as follows:
Additionally, OpenAPI specs provide for another important need - developer documentation! While it’s possible to read through the code of open-source SDK, it’s often not the quickest or most pleasant way to determine “how to use this endpoint”, “what does this endpoint return”, etc. OpenAPI specs can be used to produce high-quality documentation meant to be consumed by humans.
OpenAPI specs are great - you can turn them into SDKs in your language of choice, and you can use them to produce great documentation. But how useful a spec is depends directly on how accurate it is - if endpoints are added to the API without being added to the spec, or if fields are typed incompletely or incorrectly, any artifacts downstream of the spec will retain those issues. Trust is paramount - if the spec isn’t consistently accurate, developer confidence is quickly undermined, and no engineer likes to play guess-and-check when integrating business-critical APIs.
But how do we make sure the spec is (and stays) correct?
This is a tale as old as time - if you have multiple sources of truth, they may disagree (and over time, it’s likely that they do). Solution? Consolidate to a single source of truth!
There are roughly two options here for consolidating “API shape” into a single source of truth:
At Orb, we use Python and Flask for our backend and APIs. The last few versions of Python have introduced increasingly robust support for static typing, and this plays a significant role in our ability to maintain API shape correctness. We can use a static type-checker like Mypy to verify that our request-handling code handles the inputs it receives. Additionally (unlike Typescript, and other languages that completely elide types at runtime), Python’s type system has powerful runtime introspection capabilities - generating a JSON schema for a Python function is as simple as importing the function, accessing its types via typing.get_type_hints, and transforming the result into valid JSON.
Pydantic is a powerful library that links together parsing/validation and serialization with the Python type system and automatically handles marshaling between JSON and Python types. Using Pydantic, we can declare endpoint inputs as statically typed Python classes, and then a decorator that understands those types can validate the JSON input against them (automatically raising HTTP 400 invalid-request errors as required), and pass the resulting instance of the request params into the handler function. Pydantic, additionally, includes functionality for producing valid JSONSchema types for declared models - we can leverage this later to construct the OpenAPI spec fragment for each operation.
Readers familiar with the Python ecosystem will note that this is very similar to the FastAPI layer on top of Starlette - we considered migrating to FastAPI, but the costs of migration (in particular, rewriting code to use Starlette patterns and primitives vs those of Flask) were likely too great to make this approach feasible.
Additionally, we can use the same decorator (@router.route above) to declare important OpenAPI-related metadata about the endpoint: the operation_id, tags, and more. We can even use Python’s standard patterns for documentation to describe the endpoint - when we’re generating the OpenAPI spec, we can pull this documentation in as its description.
At this point, producing an OpenAPI spec is as simple as importing our router to get the set of endpoints, collecting the relevant input and output types and generating a JSONSchema for each, and producing an operation for each endpoint with the appropriate metadata (including references to those input and output types).
We can wire up our spec generator into our CI/CD pipeline so that we’re automatically publishing a matching version of our spec when we deploy new API code. (This is what you see at https://docs.withorb.com/spec.json)
Additionally, via Docusaurus, we can produce up-to-date documentation from our spec (also updated at PR and deploy time). First, an obvious massive boon: our docs are automatically kept up-to-date with our API as it’s deployed. Second, this allows for an even better ergonomic experience for our engineers - add a new endpoint, or modify types for an existing one, and you’ll automatically get a Vercel preview deployment that shows the resulting docs changes.
We transform our OpenAPI spec into a Postman collection as well, allowing any engineer to “fork and go” to explore our APIs without downloading a thing.
Last, but certainly not least…
With Stainless and our OpenAPI spec, we can produce idiomatic SDKs in various languages. Python, Node.JS/Typescript, and Go are available today, with Java coming soon - let us know if there’s a language you’d like to see an Orb SDK for!
Because Stainless and our OpenAPI spec are wired into our CI/CD pipeline, our SDKs update automatically, so it’s easier than ever for our customers to start using new API features.
Orb’s SDKs have type safety built-in - autocomplete makes your life easier as an engineer, and static checking helps verify inputs and outputs are being handled correctly!
Orb’s SDKs handle the important, but sometimes-tedious parts of building a reliable API integration:
Stainless’ SDKs come with other ergonomic niceties in various languages:
. access, the same way you might with dataclasses, while request params are passed as keyword arguments (avoiding boilerplate - though you can import the request params’ corresponding TypedDict and construct that explicitly for even more type safety)for customer in client.customers.list(): ...
See how AI companies are removing the friction from invoicing, billing and revenue.